Repostiory containing DAPLink source code with Reset Pin workaround for HANI_IOT board.
Upstream: https://github.com/ARMmbed/DAPLink
test/msd_test.py@0:01f31e923fe2, 2020-04-07 (annotated)
- Committer:
- Pawel Zarembski
- Date:
- Tue Apr 07 12:55:42 2020 +0200
- Revision:
- 0:01f31e923fe2
hani: DAPLink with reset workaround
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Pawel Zarembski |
0:01f31e923fe2 | 1 | # |
Pawel Zarembski |
0:01f31e923fe2 | 2 | # DAPLink Interface Firmware |
Pawel Zarembski |
0:01f31e923fe2 | 3 | # Copyright (c) 2009-2016, ARM Limited, All Rights Reserved |
Pawel Zarembski |
0:01f31e923fe2 | 4 | # SPDX-License-Identifier: Apache-2.0 |
Pawel Zarembski |
0:01f31e923fe2 | 5 | # |
Pawel Zarembski |
0:01f31e923fe2 | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may |
Pawel Zarembski |
0:01f31e923fe2 | 7 | # not use this file except in compliance with the License. |
Pawel Zarembski |
0:01f31e923fe2 | 8 | # You may obtain a copy of the License at |
Pawel Zarembski |
0:01f31e923fe2 | 9 | # |
Pawel Zarembski |
0:01f31e923fe2 | 10 | # http://www.apache.org/licenses/LICENSE-2.0 |
Pawel Zarembski |
0:01f31e923fe2 | 11 | # |
Pawel Zarembski |
0:01f31e923fe2 | 12 | # Unless required by applicable law or agreed to in writing, software |
Pawel Zarembski |
0:01f31e923fe2 | 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
Pawel Zarembski |
0:01f31e923fe2 | 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
Pawel Zarembski |
0:01f31e923fe2 | 15 | # See the License for the specific language governing permissions and |
Pawel Zarembski |
0:01f31e923fe2 | 16 | # limitations under the License. |
Pawel Zarembski |
0:01f31e923fe2 | 17 | # |
Pawel Zarembski |
0:01f31e923fe2 | 18 | |
Pawel Zarembski |
0:01f31e923fe2 | 19 | from __future__ import absolute_import |
Pawel Zarembski |
0:01f31e923fe2 | 20 | from __future__ import division |
Pawel Zarembski |
0:01f31e923fe2 | 21 | import os |
Pawel Zarembski |
0:01f31e923fe2 | 22 | import time |
Pawel Zarembski |
0:01f31e923fe2 | 23 | import shutil |
Pawel Zarembski |
0:01f31e923fe2 | 24 | import six |
Pawel Zarembski |
0:01f31e923fe2 | 25 | import info |
Pawel Zarembski |
0:01f31e923fe2 | 26 | import intelhex |
Pawel Zarembski |
0:01f31e923fe2 | 27 | from test_info import TestInfo |
Pawel Zarembski |
0:01f31e923fe2 | 28 | |
Pawel Zarembski |
0:01f31e923fe2 | 29 | from pyocd.core.helpers import ConnectHelper |
Pawel Zarembski |
0:01f31e923fe2 | 30 | from pyocd.core.memory_map import MemoryType |
Pawel Zarembski |
0:01f31e923fe2 | 31 | |
Pawel Zarembski |
0:01f31e923fe2 | 32 | def _same(d1, d2): |
Pawel Zarembski |
0:01f31e923fe2 | 33 | assert type(d1) is bytearray |
Pawel Zarembski |
0:01f31e923fe2 | 34 | assert type(d2) is bytearray |
Pawel Zarembski |
0:01f31e923fe2 | 35 | for i in range(min(len(d1), len(d2))): |
Pawel Zarembski |
0:01f31e923fe2 | 36 | if d1[i] != d2[i]: |
Pawel Zarembski |
0:01f31e923fe2 | 37 | return False |
Pawel Zarembski |
0:01f31e923fe2 | 38 | if len(d1) != len(d2): |
Pawel Zarembski |
0:01f31e923fe2 | 39 | return False |
Pawel Zarembski |
0:01f31e923fe2 | 40 | return True |
Pawel Zarembski |
0:01f31e923fe2 | 41 | |
Pawel Zarembski |
0:01f31e923fe2 | 42 | MOCK_DIR_LIST = [ |
Pawel Zarembski |
0:01f31e923fe2 | 43 | "test", |
Pawel Zarembski |
0:01f31e923fe2 | 44 | "blarg", |
Pawel Zarembski |
0:01f31e923fe2 | 45 | "very_long_directory_name", |
Pawel Zarembski |
0:01f31e923fe2 | 46 | "very_long_directory_name/and_subdirectory_name" |
Pawel Zarembski |
0:01f31e923fe2 | 47 | ] |
Pawel Zarembski |
0:01f31e923fe2 | 48 | |
Pawel Zarembski |
0:01f31e923fe2 | 49 | MOCK_FILE_LIST = [ |
Pawel Zarembski |
0:01f31e923fe2 | 50 | (".test", "blarg"), |
Pawel Zarembski |
0:01f31e923fe2 | 51 | ("test/file1", "asdofahweaw"), |
Pawel Zarembski |
0:01f31e923fe2 | 52 | ("file.jpg", "file contents here") |
Pawel Zarembski |
0:01f31e923fe2 | 53 | ] |
Pawel Zarembski |
0:01f31e923fe2 | 54 | |
Pawel Zarembski |
0:01f31e923fe2 | 55 | MOCK_DIR_LIST_AFTER = [ |
Pawel Zarembski |
0:01f31e923fe2 | 56 | "test2", |
Pawel Zarembski |
0:01f31e923fe2 | 57 | "blarg2", |
Pawel Zarembski |
0:01f31e923fe2 | 58 | "very_long_directory_name2", |
Pawel Zarembski |
0:01f31e923fe2 | 59 | "very_long_directory_name2/and_subdirectory_name" |
Pawel Zarembski |
0:01f31e923fe2 | 60 | ] |
Pawel Zarembski |
0:01f31e923fe2 | 61 | |
Pawel Zarembski |
0:01f31e923fe2 | 62 | MOCK_FILE_LIST_AFTER = [ |
Pawel Zarembski |
0:01f31e923fe2 | 63 | (".test2", "blarg"), |
Pawel Zarembski |
0:01f31e923fe2 | 64 | ("test2/file12", "asdofahweaw"), |
Pawel Zarembski |
0:01f31e923fe2 | 65 | ("file2.jpg", "file contents here") |
Pawel Zarembski |
0:01f31e923fe2 | 66 | ] |
Pawel Zarembski |
0:01f31e923fe2 | 67 | |
Pawel Zarembski |
0:01f31e923fe2 | 68 | class MassStorageTester(object): |
Pawel Zarembski |
0:01f31e923fe2 | 69 | |
Pawel Zarembski |
0:01f31e923fe2 | 70 | RETRY_COUNT = 5 |
Pawel Zarembski |
0:01f31e923fe2 | 71 | DELAY_BEFORE_RETRY_S = 30 |
Pawel Zarembski |
0:01f31e923fe2 | 72 | |
Pawel Zarembski |
0:01f31e923fe2 | 73 | def __init__(self, board, parent_test, test_name): |
Pawel Zarembski |
0:01f31e923fe2 | 74 | self.board = board |
Pawel Zarembski |
0:01f31e923fe2 | 75 | self.parent_test = parent_test |
Pawel Zarembski |
0:01f31e923fe2 | 76 | self.test_name = test_name |
Pawel Zarembski |
0:01f31e923fe2 | 77 | self._expected_failure_msg = None |
Pawel Zarembski |
0:01f31e923fe2 | 78 | self._flush_time = 0.1 |
Pawel Zarembski |
0:01f31e923fe2 | 79 | self._load_with_shutils = None |
Pawel Zarembski |
0:01f31e923fe2 | 80 | self._flush_size = None |
Pawel Zarembski |
0:01f31e923fe2 | 81 | self._programming_data = None |
Pawel Zarembski |
0:01f31e923fe2 | 82 | self._mock_file_list = [] |
Pawel Zarembski |
0:01f31e923fe2 | 83 | self._mock_dir_list = [] |
Pawel Zarembski |
0:01f31e923fe2 | 84 | self._mock_file_list_after = [] |
Pawel Zarembski |
0:01f31e923fe2 | 85 | self._mock_dir_list_after = [] |
Pawel Zarembski |
0:01f31e923fe2 | 86 | self._programming_file_name = None |
Pawel Zarembski |
0:01f31e923fe2 | 87 | self._start = 0 |
Pawel Zarembski |
0:01f31e923fe2 | 88 | |
Pawel Zarembski |
0:01f31e923fe2 | 89 | def set_shutils_copy(self, source_file_name): |
Pawel Zarembski |
0:01f31e923fe2 | 90 | """ |
Pawel Zarembski |
0:01f31e923fe2 | 91 | Change mode to shutil file copy |
Pawel Zarembski |
0:01f31e923fe2 | 92 | |
Pawel Zarembski |
0:01f31e923fe2 | 93 | This option cannot be used with set_programming_data. |
Pawel Zarembski |
0:01f31e923fe2 | 94 | """ |
Pawel Zarembski |
0:01f31e923fe2 | 95 | assert type(source_file_name) is str |
Pawel Zarembski |
0:01f31e923fe2 | 96 | assert self._load_with_shutils is None |
Pawel Zarembski |
0:01f31e923fe2 | 97 | self._source_file_name = source_file_name |
Pawel Zarembski |
0:01f31e923fe2 | 98 | self._load_with_shutils = True |
Pawel Zarembski |
0:01f31e923fe2 | 99 | |
Pawel Zarembski |
0:01f31e923fe2 | 100 | def set_programming_data(self, data, file_name): |
Pawel Zarembski |
0:01f31e923fe2 | 101 | """ |
Pawel Zarembski |
0:01f31e923fe2 | 102 | Set data to program over mass storage |
Pawel Zarembski |
0:01f31e923fe2 | 103 | |
Pawel Zarembski |
0:01f31e923fe2 | 104 | Data should be the conetents of the hex or binary file |
Pawel Zarembski |
0:01f31e923fe2 | 105 | being loaded. This option cannot be used with set_shutils_copy. |
Pawel Zarembski |
0:01f31e923fe2 | 106 | """ |
Pawel Zarembski |
0:01f31e923fe2 | 107 | assert type(data) is bytearray |
Pawel Zarembski |
0:01f31e923fe2 | 108 | assert type(file_name) is str |
Pawel Zarembski |
0:01f31e923fe2 | 109 | assert(self._load_with_shutils is False or |
Pawel Zarembski |
0:01f31e923fe2 | 110 | self._load_with_shutils is None) |
Pawel Zarembski |
0:01f31e923fe2 | 111 | self._load_with_shutils = False |
Pawel Zarembski |
0:01f31e923fe2 | 112 | self._programming_data = data |
Pawel Zarembski |
0:01f31e923fe2 | 113 | self._programming_file_name = file_name |
Pawel Zarembski |
0:01f31e923fe2 | 114 | |
Pawel Zarembski |
0:01f31e923fe2 | 115 | def set_flush_size(self, size): |
Pawel Zarembski |
0:01f31e923fe2 | 116 | """Set the block size to simulate a flush of""" |
Pawel Zarembski |
0:01f31e923fe2 | 117 | assert isinstance(size, six.integer_types) |
Pawel Zarembski |
0:01f31e923fe2 | 118 | self._flush_size = size |
Pawel Zarembski |
0:01f31e923fe2 | 119 | |
Pawel Zarembski |
0:01f31e923fe2 | 120 | def set_expected_data(self, data, start=0): |
Pawel Zarembski |
0:01f31e923fe2 | 121 | """Data that should have been written to the device""" |
Pawel Zarembski |
0:01f31e923fe2 | 122 | assert data is None or type(data) is bytearray |
Pawel Zarembski |
0:01f31e923fe2 | 123 | self._expected_data = data |
Pawel Zarembski |
0:01f31e923fe2 | 124 | self._start = start |
Pawel Zarembski |
0:01f31e923fe2 | 125 | |
Pawel Zarembski |
0:01f31e923fe2 | 126 | def set_expected_failure_msg(self, msg, error_type): |
Pawel Zarembski |
0:01f31e923fe2 | 127 | """Set the expected failure message as a string""" |
Pawel Zarembski |
0:01f31e923fe2 | 128 | assert msg is None or type(msg) is str |
Pawel Zarembski |
0:01f31e923fe2 | 129 | self._expected_failure_msg = msg |
Pawel Zarembski |
0:01f31e923fe2 | 130 | self._expected_failure_type = error_type |
Pawel Zarembski |
0:01f31e923fe2 | 131 | |
Pawel Zarembski |
0:01f31e923fe2 | 132 | def add_mock_files(self, file_list): |
Pawel Zarembski |
0:01f31e923fe2 | 133 | """Add a list of tuples containing a file and contents""" |
Pawel Zarembski |
0:01f31e923fe2 | 134 | self._mock_file_list.extend(file_list) |
Pawel Zarembski |
0:01f31e923fe2 | 135 | |
Pawel Zarembski |
0:01f31e923fe2 | 136 | def add_mock_dirs(self, dir_list): |
Pawel Zarembski |
0:01f31e923fe2 | 137 | """Add a list of directoies""" |
Pawel Zarembski |
0:01f31e923fe2 | 138 | self._mock_dir_list.extend(dir_list) |
Pawel Zarembski |
0:01f31e923fe2 | 139 | |
Pawel Zarembski |
0:01f31e923fe2 | 140 | def add_mock_files_after_load(self, file_list): |
Pawel Zarembski |
0:01f31e923fe2 | 141 | """Add a list of tuples containing a file and contents""" |
Pawel Zarembski |
0:01f31e923fe2 | 142 | self._mock_file_list_after.extend(file_list) |
Pawel Zarembski |
0:01f31e923fe2 | 143 | |
Pawel Zarembski |
0:01f31e923fe2 | 144 | def add_mock_dirs_after_load(self, dir_list): |
Pawel Zarembski |
0:01f31e923fe2 | 145 | """Add a list of directoies""" |
Pawel Zarembski |
0:01f31e923fe2 | 146 | self._mock_dir_list_after.extend(dir_list) |
Pawel Zarembski |
0:01f31e923fe2 | 147 | |
Pawel Zarembski |
0:01f31e923fe2 | 148 | def _check_data_correct(self, expected_data, _): |
Pawel Zarembski |
0:01f31e923fe2 | 149 | """Return True if the actual data written matches the expected""" |
Pawel Zarembski |
0:01f31e923fe2 | 150 | data_len = len(expected_data) |
Pawel Zarembski |
0:01f31e923fe2 | 151 | data_loaded = self.board.read_target_memory(self._start, data_len) |
Pawel Zarembski |
0:01f31e923fe2 | 152 | return _same(expected_data, data_loaded) |
Pawel Zarembski |
0:01f31e923fe2 | 153 | |
Pawel Zarembski |
0:01f31e923fe2 | 154 | def run(self): |
Pawel Zarembski |
0:01f31e923fe2 | 155 | for retry_count in range(self.RETRY_COUNT): |
Pawel Zarembski |
0:01f31e923fe2 | 156 | test_info = TestInfo(self.test_name) |
Pawel Zarembski |
0:01f31e923fe2 | 157 | if retry_count > 0: |
Pawel Zarembski |
0:01f31e923fe2 | 158 | test_info.info('Previous attempts %s' % retry_count) |
Pawel Zarembski |
0:01f31e923fe2 | 159 | try: |
Pawel Zarembski |
0:01f31e923fe2 | 160 | self._run(test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 161 | except IOError: |
Pawel Zarembski |
0:01f31e923fe2 | 162 | time.sleep(self.DELAY_BEFORE_RETRY_S) |
Pawel Zarembski |
0:01f31e923fe2 | 163 | # Update board info since a remount could have occurred |
Pawel Zarembski |
0:01f31e923fe2 | 164 | self.board.update_board_info() |
Pawel Zarembski |
0:01f31e923fe2 | 165 | continue |
Pawel Zarembski |
0:01f31e923fe2 | 166 | self.parent_test.attach_subtest(test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 167 | break |
Pawel Zarembski |
0:01f31e923fe2 | 168 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 169 | raise Exception("Flashing failed after %i retries" % |
Pawel Zarembski |
0:01f31e923fe2 | 170 | self.RETRY_COUNT) |
Pawel Zarembski |
0:01f31e923fe2 | 171 | |
Pawel Zarembski |
0:01f31e923fe2 | 172 | def _run(self, test_info): |
Pawel Zarembski |
0:01f31e923fe2 | 173 | # Expected data must be set, even if to None |
Pawel Zarembski |
0:01f31e923fe2 | 174 | assert hasattr(self, '_expected_data') |
Pawel Zarembski |
0:01f31e923fe2 | 175 | |
Pawel Zarembski |
0:01f31e923fe2 | 176 | # Windows 8/10 workaround |
Pawel Zarembski |
0:01f31e923fe2 | 177 | # ---- |
Pawel Zarembski |
0:01f31e923fe2 | 178 | # By default Windows 8 and 10 access and write to removable drives |
Pawel Zarembski |
0:01f31e923fe2 | 179 | # shortly after they are connected. If this occurs at the same time |
Pawel Zarembski |
0:01f31e923fe2 | 180 | # as a file copy the file could be sent out of order causing DAPLink |
Pawel Zarembski |
0:01f31e923fe2 | 181 | # programming to terminate early and report an error. |
Pawel Zarembski |
0:01f31e923fe2 | 182 | # |
Pawel Zarembski |
0:01f31e923fe2 | 183 | # This causes testing to intermittently fail with errors such as: |
Pawel Zarembski |
0:01f31e923fe2 | 184 | # - "The transfer timed out." |
Pawel Zarembski |
0:01f31e923fe2 | 185 | # - "File sent out of order by PC. Target might |
Pawel Zarembski |
0:01f31e923fe2 | 186 | # not be programmed correctly." |
Pawel Zarembski |
0:01f31e923fe2 | 187 | # |
Pawel Zarembski |
0:01f31e923fe2 | 188 | # To prevent Windows from writing to removable drives on connect |
Pawel Zarembski |
0:01f31e923fe2 | 189 | # drive indexing can be turned off with the following procedure: |
Pawel Zarembski |
0:01f31e923fe2 | 190 | # - Start the program "gpedit.msc" |
Pawel Zarembski |
0:01f31e923fe2 | 191 | # - Navigate to "Computer Configuration \ Administrative Templates |
Pawel Zarembski |
0:01f31e923fe2 | 192 | # \ Windows Components \ Search" |
Pawel Zarembski |
0:01f31e923fe2 | 193 | # - Enable the policy "Do not allow locations on removable drives |
Pawel Zarembski |
0:01f31e923fe2 | 194 | # to be added to libraries." |
Pawel Zarembski |
0:01f31e923fe2 | 195 | # |
Pawel Zarembski |
0:01f31e923fe2 | 196 | # Rather than requiring all testers of DAPLink make this setting |
Pawel Zarembski |
0:01f31e923fe2 | 197 | # change the below sleep has been added. This added delay allows |
Pawel Zarembski |
0:01f31e923fe2 | 198 | # windows to complete the writes it performs shortly after connect. |
Pawel Zarembski |
0:01f31e923fe2 | 199 | # This allows testing to be performed without interruption. |
Pawel Zarembski |
0:01f31e923fe2 | 200 | # |
Pawel Zarembski |
0:01f31e923fe2 | 201 | # Note - if drive indexing is turned off as mentioned above then |
Pawel Zarembski |
0:01f31e923fe2 | 202 | # this sleep is not needed. |
Pawel Zarembski |
0:01f31e923fe2 | 203 | time.sleep(2) |
Pawel Zarembski |
0:01f31e923fe2 | 204 | |
Pawel Zarembski |
0:01f31e923fe2 | 205 | # Copy mock files before test |
Pawel Zarembski |
0:01f31e923fe2 | 206 | self._mock_file_list = [] |
Pawel Zarembski |
0:01f31e923fe2 | 207 | for dir_name in self._mock_dir_list: |
Pawel Zarembski |
0:01f31e923fe2 | 208 | dir_path = self.board.get_file_path(dir_name) |
Pawel Zarembski |
0:01f31e923fe2 | 209 | os.mkdir(dir_path) |
Pawel Zarembski |
0:01f31e923fe2 | 210 | for file_name, file_contents in self._mock_file_list: |
Pawel Zarembski |
0:01f31e923fe2 | 211 | file_path = self.board.get_file_path(file_name) |
Pawel Zarembski |
0:01f31e923fe2 | 212 | with open(file_path, 'wb') as file_handle: |
Pawel Zarembski |
0:01f31e923fe2 | 213 | file_handle.write(file_contents) |
Pawel Zarembski |
0:01f31e923fe2 | 214 | |
Pawel Zarembski |
0:01f31e923fe2 | 215 | programming_file_name = None |
Pawel Zarembski |
0:01f31e923fe2 | 216 | if self._programming_file_name is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 217 | programming_file_name = \ |
Pawel Zarembski |
0:01f31e923fe2 | 218 | self.board.get_file_path(self._programming_file_name) |
Pawel Zarembski |
0:01f31e923fe2 | 219 | |
Pawel Zarembski |
0:01f31e923fe2 | 220 | # Write data to the file |
Pawel Zarembski |
0:01f31e923fe2 | 221 | start = time.time() |
Pawel Zarembski |
0:01f31e923fe2 | 222 | if self._load_with_shutils: |
Pawel Zarembski |
0:01f31e923fe2 | 223 | # Copy with shutils |
Pawel Zarembski |
0:01f31e923fe2 | 224 | shutil.copy(self._source_file_name, self.board.get_mount_point()) |
Pawel Zarembski |
0:01f31e923fe2 | 225 | elif self._flush_size is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 226 | # Simulate flushes during the file transfer |
Pawel Zarembski |
0:01f31e923fe2 | 227 | # Note - The file is explicitly opened and closed to more |
Pawel Zarembski |
0:01f31e923fe2 | 228 | # consistently simulate the undesirable behavior flush can |
Pawel Zarembski |
0:01f31e923fe2 | 229 | # cause. On Windows flushing a file causes the data to be |
Pawel Zarembski |
0:01f31e923fe2 | 230 | # written out immediately, but only sometimes causes the |
Pawel Zarembski |
0:01f31e923fe2 | 231 | # filesize to get updated. |
Pawel Zarembski |
0:01f31e923fe2 | 232 | size = len(self._programming_data) |
Pawel Zarembski |
0:01f31e923fe2 | 233 | for addr in range(0, size, self._flush_size): |
Pawel Zarembski |
0:01f31e923fe2 | 234 | data = self._programming_data[addr:addr + self._flush_size] |
Pawel Zarembski |
0:01f31e923fe2 | 235 | with open(programming_file_name, 'ab') as file_handle: |
Pawel Zarembski |
0:01f31e923fe2 | 236 | file_handle.write(data) |
Pawel Zarembski |
0:01f31e923fe2 | 237 | time.sleep(self._flush_time) |
Pawel Zarembski |
0:01f31e923fe2 | 238 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 239 | # Perform a normal copy |
Pawel Zarembski |
0:01f31e923fe2 | 240 | with open(programming_file_name, 'wb') as load_file: |
Pawel Zarembski |
0:01f31e923fe2 | 241 | load_file.write(self._programming_data) |
Pawel Zarembski |
0:01f31e923fe2 | 242 | stop = time.time() |
Pawel Zarembski |
0:01f31e923fe2 | 243 | diff = stop - start |
Pawel Zarembski |
0:01f31e923fe2 | 244 | test_info.info('Loading took %ss' % diff) |
Pawel Zarembski |
0:01f31e923fe2 | 245 | if self._expected_data is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 246 | test_info.info('Programming rate %sB/s' % |
Pawel Zarembski |
0:01f31e923fe2 | 247 | (len(self._expected_data) / diff)) |
Pawel Zarembski |
0:01f31e923fe2 | 248 | if self._programming_data is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 249 | test_info.info('MSD transfer rate %sB/s' % |
Pawel Zarembski |
0:01f31e923fe2 | 250 | (len(self._programming_data) / diff)) |
Pawel Zarembski |
0:01f31e923fe2 | 251 | |
Pawel Zarembski |
0:01f31e923fe2 | 252 | # Copy mock files after loading |
Pawel Zarembski |
0:01f31e923fe2 | 253 | self._mock_file_list = [] |
Pawel Zarembski |
0:01f31e923fe2 | 254 | for dir_name in self._mock_dir_list_after: |
Pawel Zarembski |
0:01f31e923fe2 | 255 | dir_path = self.board.get_file_path(dir_name) |
Pawel Zarembski |
0:01f31e923fe2 | 256 | os.mkdir(dir_path) |
Pawel Zarembski |
0:01f31e923fe2 | 257 | for file_name, file_contents in self._mock_file_list_after: |
Pawel Zarembski |
0:01f31e923fe2 | 258 | file_path = self.board.get_file_path(file_name) |
Pawel Zarembski |
0:01f31e923fe2 | 259 | with open(file_path, 'w') as file_handle: |
Pawel Zarembski |
0:01f31e923fe2 | 260 | file_handle.write(file_contents) |
Pawel Zarembski |
0:01f31e923fe2 | 261 | |
Pawel Zarembski |
0:01f31e923fe2 | 262 | self.board.wait_for_remount(test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 263 | |
Pawel Zarembski |
0:01f31e923fe2 | 264 | # Verify the disk is still valid |
Pawel Zarembski |
0:01f31e923fe2 | 265 | self.board.test_fs(test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 266 | |
Pawel Zarembski |
0:01f31e923fe2 | 267 | # Check various failure cases |
Pawel Zarembski |
0:01f31e923fe2 | 268 | msg, error_type = self.board.get_failure_message_and_type() |
Pawel Zarembski |
0:01f31e923fe2 | 269 | failure_expected = self._expected_failure_msg is not None |
Pawel Zarembski |
0:01f31e923fe2 | 270 | failure_occured = msg is not None |
Pawel Zarembski |
0:01f31e923fe2 | 271 | if failure_occured and not failure_expected: |
Pawel Zarembski |
0:01f31e923fe2 | 272 | test_info.failure('Device reported failure: "%s"' % msg.strip()) |
Pawel Zarembski |
0:01f31e923fe2 | 273 | return |
Pawel Zarembski |
0:01f31e923fe2 | 274 | if failure_expected and not failure_occured: |
Pawel Zarembski |
0:01f31e923fe2 | 275 | test_info.failure('Failure expected but did not occur') |
Pawel Zarembski |
0:01f31e923fe2 | 276 | return |
Pawel Zarembski |
0:01f31e923fe2 | 277 | if failure_expected and failure_occured: |
Pawel Zarembski |
0:01f31e923fe2 | 278 | if msg == self._expected_failure_msg and error_type == self._expected_failure_type: |
Pawel Zarembski |
0:01f31e923fe2 | 279 | test_info.info( |
Pawel Zarembski |
0:01f31e923fe2 | 280 | 'Failure as expected: "%s, %s"' % |
Pawel Zarembski |
0:01f31e923fe2 | 281 | (msg.strip(), error_type.strip())) |
Pawel Zarembski |
0:01f31e923fe2 | 282 | elif msg != self._expected_failure_msg: |
Pawel Zarembski |
0:01f31e923fe2 | 283 | test_info.failure('Failure but wrong string: "%s" vs "%s"' % |
Pawel Zarembski |
0:01f31e923fe2 | 284 | (msg.strip(), |
Pawel Zarembski |
0:01f31e923fe2 | 285 | self._expected_failure_msg.strip())) |
Pawel Zarembski |
0:01f31e923fe2 | 286 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 287 | test_info.failure( |
Pawel Zarembski |
0:01f31e923fe2 | 288 | 'Failure but wrong type: "%s" vs "%s"' % |
Pawel Zarembski |
0:01f31e923fe2 | 289 | (error_type.strip(), self._expected_failure_type.strip())) |
Pawel Zarembski |
0:01f31e923fe2 | 290 | return |
Pawel Zarembski |
0:01f31e923fe2 | 291 | |
Pawel Zarembski |
0:01f31e923fe2 | 292 | # These cases should have been handled above |
Pawel Zarembski |
0:01f31e923fe2 | 293 | assert not failure_expected |
Pawel Zarembski |
0:01f31e923fe2 | 294 | assert not failure_occured |
Pawel Zarembski |
0:01f31e923fe2 | 295 | |
Pawel Zarembski |
0:01f31e923fe2 | 296 | # If there is expected data then compare |
Pawel Zarembski |
0:01f31e923fe2 | 297 | if self._expected_data: |
Pawel Zarembski |
0:01f31e923fe2 | 298 | if self._check_data_correct(self._expected_data, test_info): |
Pawel Zarembski |
0:01f31e923fe2 | 299 | test_info.info("Data matches") |
Pawel Zarembski |
0:01f31e923fe2 | 300 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 301 | test_info.failure('Data does not match') |
Pawel Zarembski |
0:01f31e923fe2 | 302 | |
Pawel Zarembski |
0:01f31e923fe2 | 303 | |
Pawel Zarembski |
0:01f31e923fe2 | 304 | def test_mass_storage(workspace, parent_test): |
Pawel Zarembski |
0:01f31e923fe2 | 305 | """Test the mass storage endpoint |
Pawel Zarembski |
0:01f31e923fe2 | 306 | |
Pawel Zarembski |
0:01f31e923fe2 | 307 | Requirements: |
Pawel Zarembski |
0:01f31e923fe2 | 308 | None |
Pawel Zarembski |
0:01f31e923fe2 | 309 | |
Pawel Zarembski |
0:01f31e923fe2 | 310 | Positional arguments: |
Pawel Zarembski |
0:01f31e923fe2 | 311 | filename - A string containing the name of the file to load |
Pawel Zarembski |
0:01f31e923fe2 | 312 | |
Pawel Zarembski |
0:01f31e923fe2 | 313 | Return: |
Pawel Zarembski |
0:01f31e923fe2 | 314 | True if the test passed, False otherwise |
Pawel Zarembski |
0:01f31e923fe2 | 315 | """ |
Pawel Zarembski |
0:01f31e923fe2 | 316 | test_info = parent_test.create_subtest('test_mass_storage') |
Pawel Zarembski |
0:01f31e923fe2 | 317 | |
Pawel Zarembski |
0:01f31e923fe2 | 318 | # Setup test |
Pawel Zarembski |
0:01f31e923fe2 | 319 | board = workspace.board |
Pawel Zarembski |
0:01f31e923fe2 | 320 | target = workspace.target |
Pawel Zarembski |
0:01f31e923fe2 | 321 | bin_file = target.bin_path |
Pawel Zarembski |
0:01f31e923fe2 | 322 | hex_file = target.hex_path |
Pawel Zarembski |
0:01f31e923fe2 | 323 | with open(bin_file, 'rb') as test_file: |
Pawel Zarembski |
0:01f31e923fe2 | 324 | bin_file_contents = bytearray(test_file.read()) |
Pawel Zarembski |
0:01f31e923fe2 | 325 | with open(hex_file, 'rb') as test_file: |
Pawel Zarembski |
0:01f31e923fe2 | 326 | hex_file_contents = bytearray(test_file.read()) |
Pawel Zarembski |
0:01f31e923fe2 | 327 | blank_bin_contents = bytearray([0xff]) * 0x2000 |
Pawel Zarembski |
0:01f31e923fe2 | 328 | vectors_and_pad = bin_file_contents[0:32] + blank_bin_contents |
Pawel Zarembski |
0:01f31e923fe2 | 329 | locked_when_erased = board.get_board_id() in info.BOARD_ID_LOCKED_WHEN_ERASED |
Pawel Zarembski |
0:01f31e923fe2 | 330 | page_erase_supported = board.get_board_id() in info.BOARD_ID_SUPPORTING_PAGE_ERASE |
Pawel Zarembski |
0:01f31e923fe2 | 331 | bad_vector_table = target.name in info.TARGET_WITH_BAD_VECTOR_TABLE_LIST |
Pawel Zarembski |
0:01f31e923fe2 | 332 | |
Pawel Zarembski |
0:01f31e923fe2 | 333 | intel_hex = intelhex.IntelHex(hex_file) |
Pawel Zarembski |
0:01f31e923fe2 | 334 | addresses = intel_hex.addresses() |
Pawel Zarembski |
0:01f31e923fe2 | 335 | addresses.sort() |
Pawel Zarembski |
0:01f31e923fe2 | 336 | start = addresses[0] |
Pawel Zarembski |
0:01f31e923fe2 | 337 | |
Pawel Zarembski |
0:01f31e923fe2 | 338 | # Test loading a binary file with shutils |
Pawel Zarembski |
0:01f31e923fe2 | 339 | if not bad_vector_table: |
Pawel Zarembski |
0:01f31e923fe2 | 340 | test = MassStorageTester(board, test_info, "Shutil binary file load") |
Pawel Zarembski |
0:01f31e923fe2 | 341 | test.set_shutils_copy(bin_file) |
Pawel Zarembski |
0:01f31e923fe2 | 342 | test.set_expected_data(bin_file_contents, start) |
Pawel Zarembski |
0:01f31e923fe2 | 343 | test.run() |
Pawel Zarembski |
0:01f31e923fe2 | 344 | |
Pawel Zarembski |
0:01f31e923fe2 | 345 | # Test loading a binary file with flushes |
Pawel Zarembski |
0:01f31e923fe2 | 346 | if not bad_vector_table: |
Pawel Zarembski |
0:01f31e923fe2 | 347 | test = MassStorageTester(board, test_info, "Load binary with flushes") |
Pawel Zarembski |
0:01f31e923fe2 | 348 | test.set_programming_data(bin_file_contents, 'image.bin') |
Pawel Zarembski |
0:01f31e923fe2 | 349 | test.set_expected_data(bin_file_contents, start) |
Pawel Zarembski |
0:01f31e923fe2 | 350 | test.set_flush_size(0x1000) |
Pawel Zarembski |
0:01f31e923fe2 | 351 | test.run() |
Pawel Zarembski |
0:01f31e923fe2 | 352 | |
Pawel Zarembski |
0:01f31e923fe2 | 353 | # Test loading a hex file with shutils |
Pawel Zarembski |
0:01f31e923fe2 | 354 | test = MassStorageTester(board, test_info, "Shutil hex file load") |
Pawel Zarembski |
0:01f31e923fe2 | 355 | test.set_shutils_copy(hex_file) |
Pawel Zarembski |
0:01f31e923fe2 | 356 | test.set_expected_data(bin_file_contents, start) |
Pawel Zarembski |
0:01f31e923fe2 | 357 | test.run() |
Pawel Zarembski |
0:01f31e923fe2 | 358 | |
Pawel Zarembski |
0:01f31e923fe2 | 359 | # Test loading a hex file with flushes |
Pawel Zarembski |
0:01f31e923fe2 | 360 | test = MassStorageTester(board, test_info, "Load hex with flushes") |
Pawel Zarembski |
0:01f31e923fe2 | 361 | test.set_programming_data(hex_file_contents, 'image.hex') |
Pawel Zarembski |
0:01f31e923fe2 | 362 | test.set_expected_data(bin_file_contents, start) |
Pawel Zarembski |
0:01f31e923fe2 | 363 | test.set_flush_size(0x1000) |
Pawel Zarembski |
0:01f31e923fe2 | 364 | test.run() |
Pawel Zarembski |
0:01f31e923fe2 | 365 | |
Pawel Zarembski |
0:01f31e923fe2 | 366 | # Test loading a binary smaller than a sector |
Pawel Zarembski |
0:01f31e923fe2 | 367 | if not bad_vector_table: |
Pawel Zarembski |
0:01f31e923fe2 | 368 | test = MassStorageTester(board, test_info, "Load .bin smaller than sector") |
Pawel Zarembski |
0:01f31e923fe2 | 369 | test_data_size = 0x789 |
Pawel Zarembski |
0:01f31e923fe2 | 370 | test_data = bin_file_contents[0:0 + test_data_size] |
Pawel Zarembski |
0:01f31e923fe2 | 371 | test.set_programming_data(test_data, 'image.bin') |
Pawel Zarembski |
0:01f31e923fe2 | 372 | test.set_expected_data(test_data, start) |
Pawel Zarembski |
0:01f31e923fe2 | 373 | test.run() |
Pawel Zarembski |
0:01f31e923fe2 | 374 | |
Pawel Zarembski |
0:01f31e923fe2 | 375 | # Test loading a blank binary - this image should cause a timeout |
Pawel Zarembski |
0:01f31e923fe2 | 376 | # since it doesn't have a valid vector table |
Pawel Zarembski |
0:01f31e923fe2 | 377 | test = MassStorageTester(board, test_info, "Load blank binary") |
Pawel Zarembski |
0:01f31e923fe2 | 378 | test.set_programming_data(blank_bin_contents, 'image.bin') |
Pawel Zarembski |
0:01f31e923fe2 | 379 | test.set_expected_failure_msg("The transfer timed out.", "transient, user") |
Pawel Zarembski |
0:01f31e923fe2 | 380 | test.set_expected_data(None, start) |
Pawel Zarembski |
0:01f31e923fe2 | 381 | test.run() |
Pawel Zarembski |
0:01f31e923fe2 | 382 | |
Pawel Zarembski |
0:01f31e923fe2 | 383 | # Test loading a blank binary with a vector table but padded with 0xFF. |
Pawel Zarembski |
0:01f31e923fe2 | 384 | # A blank image can lock some devices. |
Pawel Zarembski |
0:01f31e923fe2 | 385 | if not bad_vector_table: |
Pawel Zarembski |
0:01f31e923fe2 | 386 | test = MassStorageTester(board, test_info, "Load blank binary + vector table") |
Pawel Zarembski |
0:01f31e923fe2 | 387 | test.set_programming_data(vectors_and_pad, 'image.bin') |
Pawel Zarembski |
0:01f31e923fe2 | 388 | if locked_when_erased: |
Pawel Zarembski |
0:01f31e923fe2 | 389 | test.set_expected_failure_msg("The interface firmware ABORTED programming. Image is trying to set security bits", "user") |
Pawel Zarembski |
0:01f31e923fe2 | 390 | test.set_expected_data(None, start) |
Pawel Zarembski |
0:01f31e923fe2 | 391 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 392 | test.set_expected_data(vectors_and_pad, start) |
Pawel Zarembski |
0:01f31e923fe2 | 393 | test.run() |
Pawel Zarembski |
0:01f31e923fe2 | 394 | |
Pawel Zarembski |
0:01f31e923fe2 | 395 | # Test a normal load with dummy files created beforehand |
Pawel Zarembski |
0:01f31e923fe2 | 396 | test = MassStorageTester(board, test_info, "Extra Files") |
Pawel Zarembski |
0:01f31e923fe2 | 397 | test.set_programming_data(hex_file_contents, 'image.hex') |
Pawel Zarembski |
0:01f31e923fe2 | 398 | test.add_mock_dirs(MOCK_DIR_LIST) |
Pawel Zarembski |
0:01f31e923fe2 | 399 | test.add_mock_files(MOCK_FILE_LIST) |
Pawel Zarembski |
0:01f31e923fe2 | 400 | test.add_mock_dirs_after_load(MOCK_DIR_LIST_AFTER) |
Pawel Zarembski |
0:01f31e923fe2 | 401 | test.add_mock_files_after_load(MOCK_FILE_LIST_AFTER) |
Pawel Zarembski |
0:01f31e923fe2 | 402 | test.set_expected_data(bin_file_contents, start) |
Pawel Zarembski |
0:01f31e923fe2 | 403 | test.run() |
Pawel Zarembski |
0:01f31e923fe2 | 404 | # Note - it is not unexpected for an "Extra Files" test to fail |
Pawel Zarembski |
0:01f31e923fe2 | 405 | # when a binary file is loaded, since there is no way to |
Pawel Zarembski |
0:01f31e923fe2 | 406 | # tell where the end of the file is. |
Pawel Zarembski |
0:01f31e923fe2 | 407 | |
Pawel Zarembski |
0:01f31e923fe2 | 408 | if page_erase_supported: |
Pawel Zarembski |
0:01f31e923fe2 | 409 | # Test page erase, a.k.a. sector erase by generating iHex with discrete addresses, |
Pawel Zarembski |
0:01f31e923fe2 | 410 | # programing the device then comparing device memory against expected content. |
Pawel Zarembski |
0:01f31e923fe2 | 411 | test = MassStorageTester(board, test_info, "Sector Erase") |
Pawel Zarembski |
0:01f31e923fe2 | 412 | with ConnectHelper.session_with_chosen_probe(unique_id=board.get_unique_id(), open_session=False) as session: |
Pawel Zarembski |
0:01f31e923fe2 | 413 | memory_map = session.target.memory_map |
Pawel Zarembski |
0:01f31e923fe2 | 414 | flash_regions = memory_map.get_regions_of_type(MemoryType.FLASH) |
Pawel Zarembski |
0:01f31e923fe2 | 415 | |
Pawel Zarembski |
0:01f31e923fe2 | 416 | max_address = intel_hex.maxaddr() |
Pawel Zarembski |
0:01f31e923fe2 | 417 | # Create an object. We'll add the addresses of unused even blocks to it first, then unused odd blocks for each region |
Pawel Zarembski |
0:01f31e923fe2 | 418 | ih = intelhex.IntelHex() |
Pawel Zarembski |
0:01f31e923fe2 | 419 | # Add the content from test bin first |
Pawel Zarembski |
0:01f31e923fe2 | 420 | expected_bin_contents = bin_file_contents |
Pawel Zarembski |
0:01f31e923fe2 | 421 | for region_index, the_region in enumerate(flash_regions): |
Pawel Zarembski |
0:01f31e923fe2 | 422 | if the_region.is_boot_memory is False: |
Pawel Zarembski |
0:01f31e923fe2 | 423 | continue |
Pawel Zarembski |
0:01f31e923fe2 | 424 | flash_start = the_region.start |
Pawel Zarembski |
0:01f31e923fe2 | 425 | flash_length = the_region.length |
Pawel Zarembski |
0:01f31e923fe2 | 426 | block_size = the_region.blocksize |
Pawel Zarembski |
0:01f31e923fe2 | 427 | |
Pawel Zarembski |
0:01f31e923fe2 | 428 | number_of_blocks = flash_length // block_size |
Pawel Zarembski |
0:01f31e923fe2 | 429 | |
Pawel Zarembski |
0:01f31e923fe2 | 430 | # Sanity check the regions are contiguous |
Pawel Zarembski |
0:01f31e923fe2 | 431 | if region_index: |
Pawel Zarembski |
0:01f31e923fe2 | 432 | assert flash_start == (flash_regions[region_index - 1].start + flash_regions[region_index - 1].length) |
Pawel Zarembski |
0:01f31e923fe2 | 433 | |
Pawel Zarembski |
0:01f31e923fe2 | 434 | if max_address >= (flash_start + flash_length): |
Pawel Zarembski |
0:01f31e923fe2 | 435 | # This bin image crosses this region, don't modify the content, go to the next region |
Pawel Zarembski |
0:01f31e923fe2 | 436 | continue |
Pawel Zarembski |
0:01f31e923fe2 | 437 | elif max_address >= flash_start: |
Pawel Zarembski |
0:01f31e923fe2 | 438 | # This bin image occupies partial region. Skip the used portion to avoid touching any security bits and pad the rest |
Pawel Zarembski |
0:01f31e923fe2 | 439 | expected_bin_contents += bytearray([0xff]) * (flash_start + flash_length - max_address - 1) |
Pawel Zarembski |
0:01f31e923fe2 | 440 | # Calculate the starting block after the image to avoid stumbling upon security bits |
Pawel Zarembski |
0:01f31e923fe2 | 441 | block_start = (max_address - flash_start) // block_size + 1 |
Pawel Zarembski |
0:01f31e923fe2 | 442 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 443 | # This bin image doesn't reach this region |
Pawel Zarembski |
0:01f31e923fe2 | 444 | expected_bin_contents += bytearray([0xff]) * flash_length |
Pawel Zarembski |
0:01f31e923fe2 | 445 | block_start = 0 |
Pawel Zarembski |
0:01f31e923fe2 | 446 | # For all even blocks, overwrite all addresses with 0x55; for all odd blocks, overwrite all addresses with 0xAA |
Pawel Zarembski |
0:01f31e923fe2 | 447 | for pass_number in range (2): |
Pawel Zarembski |
0:01f31e923fe2 | 448 | if pass_number == 0: |
Pawel Zarembski |
0:01f31e923fe2 | 449 | modifier = 0x55 |
Pawel Zarembski |
0:01f31e923fe2 | 450 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 451 | modifier = 0xAA |
Pawel Zarembski |
0:01f31e923fe2 | 452 | block_start += 1 |
Pawel Zarembski |
0:01f31e923fe2 | 453 | for block_idx in range(block_start, number_of_blocks, 2): |
Pawel Zarembski |
0:01f31e923fe2 | 454 | for address_to_modify in range (flash_start + block_idx * block_size, flash_start + (block_idx + 1) * block_size): |
Pawel Zarembski |
0:01f31e923fe2 | 455 | expected_bin_contents[address_to_modify] = modifier |
Pawel Zarembski |
0:01f31e923fe2 | 456 | ih[address_to_modify] = modifier |
Pawel Zarembski |
0:01f31e923fe2 | 457 | if not os.path.exists("tmp"): |
Pawel Zarembski |
0:01f31e923fe2 | 458 | os.makedirs("tmp") |
Pawel Zarembski |
0:01f31e923fe2 | 459 | # Write out the modified iHex to file |
Pawel Zarembski |
0:01f31e923fe2 | 460 | ih.tofile("tmp/interleave.hex", format='hex') |
Pawel Zarembski |
0:01f31e923fe2 | 461 | # Load this hex file with shutils |
Pawel Zarembski |
0:01f31e923fe2 | 462 | test.set_shutils_copy("tmp/interleave.hex") |
Pawel Zarembski |
0:01f31e923fe2 | 463 | test.set_expected_data(expected_bin_contents, start) |
Pawel Zarembski |
0:01f31e923fe2 | 464 | test.run() |
Pawel Zarembski |
0:01f31e923fe2 | 465 | |
Pawel Zarembski |
0:01f31e923fe2 | 466 | # Finally, load a good binary |
Pawel Zarembski |
0:01f31e923fe2 | 467 | test = MassStorageTester(board, test_info, "Load good file to restore state") |
Pawel Zarembski |
0:01f31e923fe2 | 468 | test.set_programming_data(hex_file_contents, 'image.hex') |
Pawel Zarembski |
0:01f31e923fe2 | 469 | test.set_expected_data(bin_file_contents, start) |
Pawel Zarembski |
0:01f31e923fe2 | 470 | test.run() |
Pawel Zarembski |
0:01f31e923fe2 | 471 | |
Pawel Zarembski |
0:01f31e923fe2 | 472 | # Ideas for future tests - contributions welcome |
Pawel Zarembski |
0:01f31e923fe2 | 473 | # -Zero length file |
Pawel Zarembski |
0:01f31e923fe2 | 474 | # -Corrupt hex file |
Pawel Zarembski |
0:01f31e923fe2 | 475 | # -Dummy files loaded before test |
Pawel Zarembski |
0:01f31e923fe2 | 476 | # -Very large file |
Pawel Zarembski |
0:01f31e923fe2 | 477 | # -Any MSD regressions |
Pawel Zarembski |
0:01f31e923fe2 | 478 | # -Security bits in hex files |
Pawel Zarembski |
0:01f31e923fe2 | 479 | # -Hex file with data at the end ** |
Pawel Zarembski |
0:01f31e923fe2 | 480 | # -Hidden files |
Pawel Zarembski |
0:01f31e923fe2 | 481 | # -change file extension |
Pawel Zarembski |
0:01f31e923fe2 | 482 | # -Change size (make smaller) |
Pawel Zarembski |
0:01f31e923fe2 | 483 | # -change starting address |