├── .gitignore ├── Image ├── inclosing.png ├── inopening.png ├── serial.ico ├── 截屏.gif └── 截屏.jpg ├── LICENSE ├── README.md ├── RTTView.py ├── RTTView.ui ├── jlink.py ├── libusb-1.0.24 ├── MinGW64 │ └── dll │ │ └── libusb-1.0.dll └── VS2019 │ └── MS64 │ └── dll │ └── libusb-1.0.dll ├── pyocd ├── __init__.py ├── core │ ├── __init__.py │ ├── coresight_target.py │ ├── exceptions.py │ ├── helpers.py │ ├── memory_interface.py │ ├── memory_map.py │ ├── options.py │ ├── session.py │ ├── target.py │ └── target_delegate.py ├── coresight │ ├── __init__.py │ ├── ap.py │ ├── component.py │ ├── cortex_m.py │ ├── dap.py │ ├── dwt.py │ ├── fpb.py │ ├── itm.py │ ├── rom_table.py │ └── tpiu.py ├── debug │ ├── __init__.py │ ├── breakpoints │ │ ├── __init__.py │ │ ├── manager.py │ │ ├── provider.py │ │ └── software.py │ ├── cache.py │ ├── context.py │ ├── elf │ │ ├── __init__.py │ │ ├── decoder.py │ │ ├── elf.py │ │ ├── flash_reader.py │ │ └── symbols.py │ ├── semihost.py │ ├── svd.py │ └── symbols.py ├── probe │ ├── __init__.py │ ├── aggregator.py │ ├── cmsis_dap_probe.py │ ├── debug_probe.py │ └── pydapaccess │ │ ├── __init__.py │ │ ├── cmsis_dap_core.py │ │ ├── dap_access_api.py │ │ ├── dap_access_cmsis_dap.py │ │ ├── dap_settings.py │ │ └── interface │ │ ├── __init__.py │ │ ├── common.py │ │ ├── hidapi_backend.py │ │ ├── interface.py │ │ ├── pyusb_backend.py │ │ ├── pyusb_v2_backend.py │ │ └── pywinusb_backend.py └── utility │ ├── __init__.py │ ├── cmdline.py │ ├── compatibility.py │ ├── conversion.py │ ├── graph.py │ ├── hex.py │ ├── mask.py │ ├── notification.py │ ├── progress.py │ ├── sequencer.py │ ├── server.py │ ├── sockets.py │ └── timeout.py └── xlink.py /.gitignore: -------------------------------------------------------------------------------- 1 | setting.ini 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # Environments 87 | .env 88 | .venv 89 | env/ 90 | venv/ 91 | ENV/ 92 | env.bak/ 93 | venv.bak/ 94 | 95 | # Spyder project settings 96 | .spyderproject 97 | .spyproject 98 | 99 | # Rope project settings 100 | .ropeproject 101 | 102 | # mkdocs documentation 103 | /site 104 | 105 | # mypy 106 | .mypy_cache/ 107 | -------------------------------------------------------------------------------- /Image/inclosing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XIVN1987/RTTView/a9cfb5810751771655ee7581d53cd8cdafbe7a74/Image/inclosing.png -------------------------------------------------------------------------------- /Image/inopening.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XIVN1987/RTTView/a9cfb5810751771655ee7581d53cd8cdafbe7a74/Image/inopening.png -------------------------------------------------------------------------------- /Image/serial.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XIVN1987/RTTView/a9cfb5810751771655ee7581d53cd8cdafbe7a74/Image/serial.ico -------------------------------------------------------------------------------- /Image/截屏.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XIVN1987/RTTView/a9cfb5810751771655ee7581d53cd8cdafbe7a74/Image/截屏.gif -------------------------------------------------------------------------------- /Image/截屏.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XIVN1987/RTTView/a9cfb5810751771655ee7581d53cd8cdafbe7a74/Image/截屏.jpg -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 XIVN1987 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RTTView 2 | SEGGER-RTT Client for J-LINK and DAPLink 3 | 4 | To run software, you need python 3.6+, pyqt5 and pyqtchart. 5 | 6 | To use DAPLink, you need additional pyusb for CMSIS-DAPv2 and another usb-backend for CMSIS-DAPv1 (hidapi or pywinusb for windows, hidapi for mac, pyusb for linux). 7 | 8 | ``` shell 9 | pip install PyQt5 PyQtChart pyusb hidapi six pyelftools 10 | ``` 11 | 12 | ![](./Image/截屏.gif) 13 | 14 | data format for wave show: 15 | + 1 wave: 11, 22, 33, 16 | + 2 wave: 11 22, 33 44, 55 66, 17 | + 3 wave: 11 22 33, 44 55 66, 77 88 99, 18 | + 4 wave: 11 22 33 44, 55 66 77 88, 99 11 22 33, 19 | 20 | 21 | ## J-Scope HSS mode 22 | When select elf file path in address combobox, RTTView read selected variable directly from memory at specified address, rather from RTT buffer. 23 | 24 | ![](./Image/截屏.jpg) 25 | 26 | Double-click the table cell to bring up the variable adding dialog. 27 | -------------------------------------------------------------------------------- /libusb-1.0.24/MinGW64/dll/libusb-1.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XIVN1987/RTTView/a9cfb5810751771655ee7581d53cd8cdafbe7a74/libusb-1.0.24/MinGW64/dll/libusb-1.0.dll -------------------------------------------------------------------------------- /libusb-1.0.24/VS2019/MS64/dll/libusb-1.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XIVN1987/RTTView/a9cfb5810751771655ee7581d53cd8cdafbe7a74/libusb-1.0.24/VS2019/MS64/dll/libusb-1.0.dll -------------------------------------------------------------------------------- /pyocd/__init__.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2015 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | -------------------------------------------------------------------------------- /pyocd/core/__init__.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2017 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | -------------------------------------------------------------------------------- /pyocd/core/exceptions.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2018-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | class Error(RuntimeError): 18 | """! @brief Parent of all errors pyOCD can raise""" 19 | pass 20 | 21 | class ProbeError(Error): 22 | """! @brief Error communicating with device""" 23 | pass 24 | 25 | class TransferError(ProbeError): 26 | """! @brief Error ocurred with a transfer over SWD or JTAG""" 27 | pass 28 | 29 | class TransferTimeoutError(TransferError): 30 | """! @brief An SWD or JTAG timeout occurred""" 31 | pass 32 | 33 | class TransferFaultError(TransferError): 34 | """! @brief An SWD Fault occurred""" 35 | def __init__(self, faultAddress=None, length=None): 36 | super(TransferFaultError, self).__init__(faultAddress) 37 | self._address = faultAddress 38 | self._length = length 39 | 40 | @property 41 | def fault_address(self): 42 | return self._address 43 | 44 | @fault_address.setter 45 | def fault_address(self, addr): 46 | self._address = addr 47 | 48 | @property 49 | def fault_end_address(self): 50 | return (self._address + self._length - 1) if (self._length is not None) else self._address 51 | 52 | @property 53 | def fault_length(self): 54 | return self._length 55 | 56 | @fault_length.setter 57 | def fault_length(self, length): 58 | self._length = length 59 | 60 | def __str__(self): 61 | desc = "SWD/JTAG Transfer Fault" 62 | if self._address is not None: 63 | desc += " @ 0x%08x" % self._address 64 | if self._length is not None: 65 | desc += "-0x%08x" % self.fault_end_address 66 | return desc 67 | 68 | class FlashFailure(RuntimeError): 69 | """! @brief Exception raised when flashing fails for some reason. """ 70 | pass 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /pyocd/core/helpers.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2018 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from __future__ import print_function 18 | from .session import Session 19 | from ..probe.aggregator import DebugProbeAggregator 20 | from time import sleep 21 | import colorama 22 | import six 23 | 24 | # Init colorama here since this is currently the only module that uses it. 25 | colorama.init() 26 | 27 | ## @brief Helper class for streamlining the probe discovery and session creation process. 28 | # 29 | # This class provides several static methods that wrap the DebugProbeAggregator methods 30 | # with a simple command-line user interface, or provide a single method that performs 31 | # a common access pattern. 32 | class ConnectHelper(object): 33 | 34 | ## @brief Return a list of Session objects for all connected debug probes. 35 | # 36 | # This method is useful for listing detailed information about connected probes, especially 37 | # those that have associated boards, as the Session object will have a Board instance. 38 | # 39 | # The returned list of sessions is sorted by the combination of the debug probe's 40 | # description and unique ID. 41 | # 42 | # @param blocking Specifies whether to wait for a probe to be connected if there are no 43 | # available probes. 44 | # @param unique_id String to match against probes' unique IDs using a contains match. If the 45 | # default of None is passed, then all available probes are matched. 46 | # @param options Dictionary of user options. 47 | # @param kwargs User options passed as keyword arguments. 48 | # 49 | # @return A list of Session objects. The returned Session objects are not yet active, in that 50 | # open() has not yet been called. If _blocking_ is True, the list will contain at least 51 | # one session. If _blocking_ is False and there are no probes connected then an empty list 52 | # will be returned. 53 | @staticmethod 54 | def get_sessions_for_all_connected_probes(blocking=True, unique_id=None, options=None, **kwargs): 55 | probes = ConnectHelper.get_all_connected_probes(blocking=blocking, unique_id=unique_id) 56 | sessions = [Session(probe, options=options, **kwargs) for probe in probes] 57 | return sessions 58 | 59 | ## @brief Return a list of DebugProbe objects for all connected debug probes. 60 | # 61 | # The returned list of debug probes is always sorted by the combination of the probe's 62 | # description and unique ID. 63 | # 64 | # @param blocking Specifies whether to wait for a probe to be connected if there are no 65 | # available probes. A message will be printed 66 | # @param unique_id String to match against probes' unique IDs using a contains match. If the 67 | # default of None is passed, then all available probes are matched. 68 | # @param print_wait_message Whether to print a message to the command line when waiting for a 69 | # probe to be connected and _blocking_ is True. 70 | # 71 | # @return A list of DebugProbe instances. If _blocking_ is True, the list will contain at least 72 | # one probe. If _blocking_ is False and there are no probes connected then an empty list 73 | # will be returned. 74 | @staticmethod 75 | def get_all_connected_probes(blocking=True, unique_id=None, print_wait_message=True): 76 | printedMessage = False 77 | while True: 78 | allProbes = DebugProbeAggregator.get_all_connected_probes(unique_id=unique_id) 79 | sortedProbes = sorted(allProbes, key=lambda probe:probe.description + probe.unique_id) 80 | 81 | if not blocking: 82 | break 83 | elif len(sortedProbes): 84 | break 85 | else: 86 | if print_wait_message and not printedMessage: 87 | print(colorama.Fore.YELLOW + "Waiting for a debug probe to be connected..." + colorama.Style.RESET_ALL) 88 | printedMessage = True 89 | sleep(0.01) 90 | assert len(sortedProbes) == 0 91 | 92 | return sortedProbes 93 | 94 | ## @brief List the connected debug probes. 95 | # 96 | # @return List of zero or more DebugProbe objects that are currently connected, sorted by the 97 | # combination of the probe's description and unique ID. 98 | @staticmethod 99 | def list_connected_probes(): 100 | allProbes = ConnectHelper.get_all_connected_probes(blocking=False) 101 | if len(allProbes): 102 | ConnectHelper._print_probe_list(allProbes) 103 | else: 104 | print(colorama.Fore.RED + "No available debug probes are connected" + colorama.Style.RESET_ALL) 105 | 106 | ## @brief Create a session with a probe possibly chosen by the user. 107 | # 108 | # This method provides an easy to use command line interface for selecting one of the 109 | # connected debug probes, then creating and opening a Session instance. It has several 110 | # parameters that control filtering of probes by unique ID, automatic selection of the first 111 | # discovered probe, and whether to automaticallty open the created Session. In addition, you 112 | # can pass user options to the Session either with the _options_ parameter or directly as 113 | # keyword arguments. 114 | # 115 | # If, after application of the _unique_id_ and _return_first_ parameter, there are still 116 | # multiple debug probes to choose from, the user is presented with a simple command-line UI 117 | # to select a probe (or abort the selection process). 118 | # 119 | # @param blocking Specifies whether to wait for a probe to be connected if there are no 120 | # available probes. 121 | # @param return_first If more than one probe is connected, a _return_first_ of True will select 122 | # the first discovered probe rather than present a selection choice to the user. 123 | # @param unique_id String to match against probes' unique IDs using a contains match. If the 124 | # default of None is passed, then all available probes are matched. 125 | # @param board_id Deprecated alias of _unique_id_. 126 | # @param open_session Indicates whether the returned Session object should be opened for the 127 | # caller. If opening causes an exception, the Session will be automatically closed. 128 | # @param init_board Deprecated alias of _open_session_. 129 | # @param options Dictionary of user options. 130 | # @param kwargs User options passed as keyword arguments. 131 | # 132 | # @return Either None or a Session instance. If _open_session_ is True then the session will 133 | # have already been opened, the board and target initialized, and is ready to be used. 134 | @staticmethod 135 | def session_with_chosen_probe(blocking=True, return_first=False, 136 | unique_id=None, board_id=None, # board_id param is deprecated 137 | open_session=True, init_board=None, # init_board param is deprecated 138 | options=None, **kwargs): 139 | # Get all matching probes, sorted by name. 140 | board_id = unique_id or board_id 141 | allProbes = ConnectHelper.get_all_connected_probes(blocking=blocking, unique_id=board_id) 142 | 143 | # Print some help if the user specified a unique ID, but more than one probe matches. 144 | if (board_id is not None) and (len(allProbes) > 1) and not return_first: 145 | print(colorama.Fore.RED + "More than one debug probe matches unique ID '%s':" % board_id + colorama.Style.RESET_ALL) 146 | board_id = board_id.lower() 147 | for probe in allProbes: 148 | head, sep, tail = probe.unique_id.lower().rpartition(board_id) 149 | highlightedId = head + colorama.Fore.RED + sep + colorama.Style.RESET_ALL + tail 150 | print("%s | %s" % ( 151 | probe.description, 152 | highlightedId)) 153 | return None 154 | 155 | # Return if no boards are connected. 156 | if allProbes is None or len(allProbes) == 0: 157 | if board_id is None: 158 | print("No connected debug probes") 159 | else: 160 | print("A debug probe matching unique ID '%s' is not connected, or no connected probe matches" % board_id) 161 | return None # No boards to close so it is safe to return 162 | 163 | # Select first board 164 | if return_first: 165 | allProbes = allProbes[0:1] 166 | 167 | # Ask user to select boards if there is more than 1 left 168 | if len(allProbes) > 1: 169 | ConnectHelper._print_probe_list(allProbes) 170 | print(colorama.Fore.RED + " q => Quit") 171 | while True: 172 | print(colorama.Style.RESET_ALL) 173 | print("Enter the number of the debug probe to connect:") 174 | line = six.moves.input("> ") 175 | valid = False 176 | if line.strip().lower() == 'q': 177 | return None 178 | try: 179 | ch = int(line) 180 | valid = 0 <= ch < len(allProbes) 181 | except ValueError: 182 | pass 183 | if not valid: 184 | print(colorama.Fore.YELLOW + "Invalid choice: %s\n" % line) 185 | Session._print_probe_list(allProbes) 186 | print(colorama.Fore.RED + " q => Exit" + colorama.Style.RESET_ALL) 187 | else: 188 | break 189 | allProbes = allProbes[ch:ch + 1] 190 | 191 | # Let deprecated init_board override open_session if it was specified. 192 | if init_board is not None: 193 | open_session = init_board 194 | 195 | assert len(allProbes) == 1 196 | session = Session(allProbes[0], options=options, **kwargs) 197 | if open_session: 198 | try: 199 | session.open() 200 | except: 201 | session.close() 202 | raise 203 | return session 204 | 205 | @staticmethod 206 | def _print_probe_list(probes): 207 | print(colorama.Fore.BLUE + "## => Board Name | Unique ID") 208 | print("-- -- ----------------------") 209 | for index, probe in enumerate(probes): 210 | print(colorama.Fore.GREEN + "%2d => %s | " % (index, probe.description) + 211 | colorama.Fore.CYAN + probe.unique_id + colorama.Style.RESET_ALL) 212 | print(colorama.Style.RESET_ALL, end='') 213 | -------------------------------------------------------------------------------- /pyocd/core/memory_interface.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2018 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from ..utility import conversion 18 | 19 | ## @brief Interface for memory access. 20 | class MemoryInterface(object): 21 | 22 | ## @brief Write a single memory location. 23 | # 24 | # By default the transfer size is a word. 25 | def write_memory(self, addr, data, transfer_size=32): 26 | raise NotImplementedError() 27 | 28 | ## @brief Read a memory location. 29 | # 30 | # By default, a word will be read. 31 | def read_memory(self, addr, transfer_size=32, now=True): 32 | raise NotImplementedError() 33 | 34 | ## @brief Write an aligned block of 32-bit words. 35 | def write_memory_block32(self, addr, data): 36 | raise NotImplementedError() 37 | 38 | ## @brief Read an aligned block of 32-bit words. 39 | def read_memory_block32(self, addr, size): 40 | raise NotImplementedError() 41 | 42 | # @brief Shorthand to write a 32-bit word. 43 | def write32(self, addr, value): 44 | self.write_memory(addr, value, 32) 45 | 46 | # @brief Shorthand to write a 16-bit halfword. 47 | def write16(self, addr, value): 48 | self.write_memory(addr, value, 16) 49 | 50 | # @brief Shorthand to write a byte. 51 | def write8(self, addr, value): 52 | self.write_memory(addr, value, 8) 53 | 54 | # @brief Shorthand to read a 32-bit word. 55 | def read32(self, addr, now=True): 56 | return self.read_memory(addr, 32, now) 57 | 58 | # @brief Shorthand to read a 16-bit halfword. 59 | def read16(self, addr, now=True): 60 | return self.read_memory(addr, 16, now) 61 | 62 | # @brief Shorthand to read a byte. 63 | def read8(self, addr, now=True): 64 | return self.read_memory(addr, 8, now) 65 | 66 | ## @brief Read a block of unaligned bytes in memory. 67 | # @return an array of byte values 68 | def read_memory_block8(self, addr, size): 69 | res = [] 70 | 71 | # try to read 8bits data 72 | if (size > 0) and (addr & 0x01): 73 | mem = self.read8(addr) 74 | res.append(mem) 75 | size -= 1 76 | addr += 1 77 | 78 | # try to read 16bits data 79 | if (size > 1) and (addr & 0x02): 80 | mem = self.read16(addr) 81 | res.append(mem & 0xff) 82 | res.append((mem >> 8) & 0xff) 83 | size -= 2 84 | addr += 2 85 | 86 | # try to read aligned block of 32bits 87 | if (size >= 4): 88 | mem = self.read_memory_block32(addr, size // 4) 89 | res += conversion.u32le_list_to_byte_list(mem) 90 | size -= 4*len(mem) 91 | addr += 4*len(mem) 92 | 93 | if (size > 1): 94 | mem = self.read16(addr) 95 | res.append(mem & 0xff) 96 | res.append((mem >> 8) & 0xff) 97 | size -= 2 98 | addr += 2 99 | 100 | if (size > 0): 101 | mem = self.read8(addr) 102 | res.append(mem) 103 | 104 | return res 105 | 106 | ## @brief Write a block of unaligned bytes in memory. 107 | def write_memory_block8(self, addr, data): 108 | size = len(data) 109 | idx = 0 110 | 111 | #try to write 8 bits data 112 | if (size > 0) and (addr & 0x01): 113 | self.write8(addr, data[idx]) 114 | size -= 1 115 | addr += 1 116 | idx += 1 117 | 118 | # try to write 16 bits data 119 | if (size > 1) and (addr & 0x02): 120 | self.write16(addr, data[idx] | (data[idx+1] << 8)) 121 | size -= 2 122 | addr += 2 123 | idx += 2 124 | 125 | # write aligned block of 32 bits 126 | if (size >= 4): 127 | data32 = conversion.byte_list_to_u32le_list(data[idx:idx + (size & ~0x03)]) 128 | self.write_memory_block32(addr, data32) 129 | addr += size & ~0x03 130 | idx += size & ~0x03 131 | size -= size & ~0x03 132 | 133 | # try to write 16 bits data 134 | if (size > 1): 135 | self.write16(addr, data[idx] | (data[idx+1] << 8)) 136 | size -= 2 137 | addr += 2 138 | idx += 2 139 | 140 | #try to write 8 bits data 141 | if (size > 0): 142 | self.write8(addr, data[idx]) 143 | 144 | -------------------------------------------------------------------------------- /pyocd/core/options.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2018-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from collections import namedtuple 18 | 19 | OptionInfo = namedtuple('OptionInfo', 'name type help') 20 | 21 | OPTIONS_INFO = { 22 | # Common options 23 | 'allow_no_cores': OptionInfo('allow_no_cores', bool, "Prevents raising an error if no core were found after CoreSight discovery."), 24 | 'auto_unlock': OptionInfo('auto_unlock', bool, "Whether to unlock secured target by erasing."), 25 | 'chip_erase': OptionInfo('chip_erase', str, "Whether to perform a chip erase or sector erases when programming flash."), 26 | 'config_file': OptionInfo('config_file', str, "Path to custom config file."), 27 | 'enable_multicore_debug': OptionInfo('enable_multicore', bool, "Whether to put pyOCD into multicore debug mode."), 28 | 'fast_program': OptionInfo('fast_program', str, "Setting this option to True will use CRC checks of existing flash sector contents to determine whether pages need to be programmed."), 29 | 'frequency': OptionInfo('frequency', int, "SWD/JTAG frequency in Hertz."), 30 | 'halt_on_connect': OptionInfo('halt_on_connect', bool, "Whether to halt CPU when connecting."), 31 | 'hide_programming_progress': OptionInfo('hide_programming_progress', str, "Disables flash programming progress bar."), 32 | 'no_config': OptionInfo('no_config', bool, "Do not use default config file."), 33 | 'pack': OptionInfo('pack', (str, list), "Path or list of paths to CMSIS Device Family Packs. Devices defined in the pack(s) are added to the list of available targets."), 34 | 'project_dir': OptionInfo('project_dir', str, "Path to the session's project directory. Defaults to the working directory when the pyocd tool was executed."), 35 | 'reset_type': OptionInfo('reset_type', str, "Which type of reset to use by default ('default', 'hw', 'sw', 'sw_sysresetreq', 'sw_vectreset', 'sw_emulated'). The default is 'sw'."), 36 | 'resume_on_disconnect': OptionInfo('resume_on_disconnect', bool, "Whether to run target on disconnect."), 37 | 'target_override': OptionInfo('target_override', str, "Name of target to use instead of default."), 38 | 'test_binary': OptionInfo('test_binary', str, "Name of test firmware binary."), 39 | 'user_script': OptionInfo('user_script', str, "Path of the user script file."), 40 | 41 | # GDBServer options 42 | 'enable_semihosting': OptionInfo('enable_semihosting', str, "Set to True to handle semihosting requests."), 43 | 'enable_swv': OptionInfo('enable_swv', bool, "Whether to enable SWV printf output over the semihosting console. Requires the swv_system_clock option to be set. The SWO baud rate can be controlled with the swv_clock option."), 44 | 'gdbserver_port': OptionInfo('gdbserver_port', str, "Base TCP port for the gdbserver."), 45 | 'persist': OptionInfo('persist', str, "If True, the GDB server will not exit after GDB disconnects."), 46 | 'report_core_number': OptionInfo('report_core_number', str, "Whether gdb server should report core number as part of the per-thread information."), 47 | 'semihost_console_type': OptionInfo('semihost_console_type', str, "If set to \"telnet\" then the semihosting telnet server will be started, otherwise semihosting will print to the console."), 48 | 'semihost_use_syscalls': OptionInfo('semihost_use_syscalls', str, "Whether to use GDB syscalls for semihosting file access operations."), 49 | 'serve_local_only': OptionInfo('serve_local_only', str, "When this option is True, the GDB server and semihosting telnet ports are only served on localhost."), 50 | 'soft_bkpt_as_hard': OptionInfo('soft_bkpt_as_hard', str, "Whether to force all breakpoints to be hardware breakpoints."), 51 | 'step_into_interrupt': OptionInfo('step_into_interrupt', str, "Enable interrupts when performing step operations."), 52 | 'swv_clock': OptionInfo('swv_clock', int, "Frequency in Hertz of the SWO baud rate. Default is 1 MHz."), 53 | 'swv_system_clock': OptionInfo('swv_system_clock', int, "Frequency in Hertz of the target's system clock. Used to compute the SWO baud rate divider. No default."), 54 | 'telnet_port': OptionInfo('telnet_port', str, "Base TCP port number for the semihosting telnet server."), 55 | 'vector_catch': OptionInfo('vector_catch', str, "Enable vector catch sources."), 56 | } 57 | -------------------------------------------------------------------------------- /pyocd/core/target.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from .memory_interface import MemoryInterface 18 | from ..utility.notification import Notifier 19 | from .memory_map import MemoryMap 20 | from enum import Enum 21 | 22 | class Target(MemoryInterface, Notifier): 23 | 24 | TARGET_RUNNING = 1 # Core is executing code. 25 | TARGET_HALTED = 2 # Core is halted in debug mode. 26 | TARGET_RESET = 3 # Core is being held in reset. 27 | TARGET_SLEEPING = 4 # Core is sleeping due to a wfi or wfe instruction. 28 | TARGET_LOCKUP = 5 # Core is locked up. 29 | 30 | class ResetType(Enum): 31 | """! @brief Available reset methods.""" 32 | ## Hardware reset via the nRESET signal. 33 | HW = 1 34 | ## Software reset using the core's default software reset method. 35 | SW = 2 36 | ## Software reset using the AIRCR.SYSRESETREQ bit. 37 | SW_SYSRESETREQ = 3 38 | ## Software reset using the AIRCR.VECTRESET bit. 39 | # 40 | # v6-M and v8-M targets do not support VECTRESET, so they will fall back to SW_EMULATED. 41 | SW_VECTRESET = 4 42 | ## Emulated software reset. 43 | SW_EMULATED = 5 44 | 45 | # Types of breakpoints. 46 | # 47 | # Auto will select the best type given the 48 | # address and available breakpoints. 49 | BREAKPOINT_HW = 1 50 | BREAKPOINT_SW = 2 51 | BREAKPOINT_AUTO = 3 52 | 53 | WATCHPOINT_READ = 1 54 | WATCHPOINT_WRITE = 2 55 | WATCHPOINT_READ_WRITE = 3 56 | 57 | # Vector catch option masks. 58 | CATCH_NONE = 0 59 | CATCH_HARD_FAULT = (1 << 0) 60 | CATCH_BUS_FAULT = (1 << 1) 61 | CATCH_MEM_FAULT = (1 << 2) 62 | CATCH_INTERRUPT_ERR = (1 << 3) 63 | CATCH_STATE_ERR = (1 << 4) 64 | CATCH_CHECK_ERR = (1 << 5) 65 | CATCH_COPROCESSOR_ERR = (1 << 6) 66 | CATCH_CORE_RESET = (1 << 7) 67 | CATCH_ALL = (CATCH_HARD_FAULT | CATCH_BUS_FAULT | CATCH_MEM_FAULT | CATCH_INTERRUPT_ERR \ 68 | | CATCH_STATE_ERR | CATCH_CHECK_ERR | CATCH_COPROCESSOR_ERR | CATCH_CORE_RESET) 69 | 70 | # Events 71 | EVENT_POST_CONNECT = 1 72 | EVENT_PRE_DISCONNECT = 2 73 | EVENT_PRE_RUN = 3 # data is run type 74 | EVENT_POST_RUN = 4 # data is run type 75 | EVENT_PRE_HALT = 5 # data is halt reason 76 | EVENT_POST_HALT = 6 # data is halt reason 77 | EVENT_PRE_RESET = 7 78 | EVENT_POST_RESET = 8 79 | EVENT_PRE_FLASH_PROGRAM = 9 80 | EVENT_POST_FLASH_PROGRAM = 10 81 | 82 | # Run types 83 | RUN_TYPE_RESUME = 1 84 | RUN_TYPE_STEP = 2 85 | 86 | # Halt reasons 87 | HALT_REASON_USER = 1 88 | HALT_REASON_DEBUG = 2 89 | 90 | VENDOR = "Generic" 91 | 92 | def __init__(self, session, memoryMap=None): 93 | super(Target, self).__init__() 94 | self._session = session 95 | self._delegate = None 96 | self.root_target = None 97 | self.vendor = self.VENDOR 98 | self.part_families = [] 99 | self.part_number = "" 100 | self.memory_map = memoryMap or MemoryMap() 101 | self.halt_on_connect = False # session.options.get('halt_on_connect', True) 102 | self.auto_unlock = False # session.options.get('auto_unlock', True) 103 | self._svd_location = None 104 | self._svd_device = None 105 | 106 | @property 107 | def session(self): 108 | return self._session 109 | 110 | @property 111 | def delegate(self): 112 | return self._delegate 113 | 114 | @delegate.setter 115 | def delegate(self, the_delegate): 116 | self._delegate = the_delegate 117 | 118 | def delegate_implements(self, method_name): 119 | return (self._delegate is not None) and (hasattr(self._delegate, method_name)) 120 | 121 | def call_delegate(self, method_name, *args, **kwargs): 122 | if self.delegate_implements(method_name): 123 | return getattr(self._delegate, method_name)(*args, **kwargs) 124 | else: 125 | # The default action is always taken if None is returned. 126 | return None 127 | 128 | @property 129 | def svd_device(self): 130 | return self._svd_device 131 | 132 | def is_locked(self): 133 | return False 134 | 135 | def create_init_sequence(self): 136 | raise NotImplementedError() 137 | 138 | def init(self): 139 | raise NotImplementedError() 140 | 141 | def disconnect(self, resume=True): 142 | pass 143 | 144 | def flush(self): 145 | self.ap.dp.flush() 146 | 147 | def halt(self): 148 | raise NotImplementedError() 149 | 150 | def step(self, disable_interrupts=True): 151 | raise NotImplementedError() 152 | 153 | def resume(self): 154 | raise NotImplementedError() 155 | 156 | def mass_erase(self): 157 | raise NotImplementedError() 158 | 159 | def read_core_register(self, id): 160 | raise NotImplementedError() 161 | 162 | def write_core_register(self, id, data): 163 | raise NotImplementedError() 164 | 165 | def read_core_register_raw(self, reg): 166 | raise NotImplementedError() 167 | 168 | def read_core_registers_raw(self, reg_list): 169 | raise NotImplementedError() 170 | 171 | def write_core_register_raw(self, reg, data): 172 | raise NotImplementedError() 173 | 174 | def write_core_registers_raw(self, reg_list, data_list): 175 | raise NotImplementedError() 176 | 177 | def find_breakpoint(self, addr): 178 | raise NotImplementedError() 179 | 180 | def set_breakpoint(self, addr, type=BREAKPOINT_AUTO): 181 | raise NotImplementedError() 182 | 183 | def get_breakpoint_type(self, addr): 184 | raise NotImplementedError() 185 | 186 | def remove_breakpoint(self, addr): 187 | raise NotImplementedError() 188 | 189 | def set_watchpoint(self, addr, size, type): 190 | raise NotImplementedError() 191 | 192 | def remove_watchpoint(self, addr, size, type): 193 | raise NotImplementedError() 194 | 195 | def reset(self, reset_type=None): 196 | raise NotImplementedError() 197 | 198 | def reset_and_halt(self, reset_type=None): 199 | raise NotImplementedError() 200 | 201 | def get_state(self): 202 | raise NotImplementedError() 203 | 204 | @property 205 | def run_token(self): 206 | return 0 207 | 208 | def is_running(self): 209 | return self.get_state() == Target.TARGET_RUNNING 210 | 211 | def is_halted(self): 212 | return self.get_state() == Target.TARGET_HALTED 213 | 214 | def get_memory_map(self): 215 | return self.memory_map 216 | 217 | def set_vector_catch(self, enableMask): 218 | raise NotImplementedError() 219 | 220 | def get_vector_catch(self): 221 | raise NotImplementedError() 222 | 223 | # GDB functions 224 | def get_target_xml(self): 225 | raise NotImplementedError() 226 | 227 | def get_target_context(self, core=None): 228 | raise NotImplementedError() 229 | 230 | def get_root_context(self, core=None): 231 | raise NotImplementedError() 232 | 233 | def set_root_context(self, context, core=None): 234 | raise NotImplementedError() 235 | -------------------------------------------------------------------------------- /pyocd/core/target_delegate.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | class TargetDelegateInterface(object): 18 | """! @brief Abstract class defining the delegate interface for targets. 19 | 20 | Note that delegates don't actually have to derive from this class due to Python's 21 | dynamic method dispatching. 22 | """ 23 | 24 | def __init__(self, session): 25 | self._session = session 26 | 27 | def will_connect(self, board): 28 | """! @brief Pre-init hook for the board. 29 | @param self 30 | @param board A Board instance that is about to be initialized. 31 | @return Ignored. 32 | """ 33 | pass 34 | 35 | def did_connect(self, board): 36 | """! @brief Post-initialization hook for the board. 37 | @param self 38 | @param board A Board instance. 39 | @return Ignored. 40 | """ 41 | pass 42 | 43 | def will_init_target(self, target, init_sequence): 44 | """! @brief Hook to review and modify init call sequence prior to execution. 45 | @param self 46 | @param target A CoreSightTarget object about to be initialized. 47 | @param init_sequence The CallSequence that will be invoked. Because call sequences are 48 | mutable, this parameter can be modified before return to change the init calls. 49 | @return Ignored. 50 | """ 51 | pass 52 | 53 | def did_init_target(self, target): 54 | """! @brief Post-initialization hook. 55 | @param self 56 | @param target A CoreSightTarget. 57 | @return Ignored. 58 | """ 59 | pass 60 | 61 | def will_start_debug_core(self, core): 62 | """! @brief Hook to enable debug for the given core. 63 | @param self 64 | @param core A CortexM object about to be initialized. 65 | @retval True Do not perform the normal procedure to start core debug. 66 | @retval "False or None" Continue with normal behaviour. 67 | """ 68 | pass 69 | 70 | def did_start_debug_core(self, core): 71 | """! @brief Post-initialization hook. 72 | @param self 73 | @param core A CortexM object. 74 | @return Ignored. 75 | """ 76 | pass 77 | 78 | def will_stop_debug_core(self, core): 79 | """! @brief Pre-cleanup hook for the core. 80 | @param self 81 | @param core A CortexM object. 82 | @retval True Do not perform the normal procedure to disable core debug. 83 | @retval "False or None" Continue with normal behaviour. 84 | """ 85 | pass 86 | 87 | def did_stop_debug_core(self, core): 88 | """! @brief Post-cleanup hook for the core. 89 | @param self 90 | @param core A CortexM object. 91 | @return Ignored. 92 | """ 93 | pass 94 | 95 | def will_disconnect(self, target, resume): 96 | """! @brief Pre-disconnect hook. 97 | @param self 98 | @param target Either a CoreSightTarget or CortexM object. 99 | @param resume The value of the `disconnect_on_resume` option. 100 | @return Ignored. 101 | """ 102 | pass 103 | 104 | def did_disconnect(self, target, resume): 105 | """! @brief Post-disconnect hook. 106 | @param self 107 | @param target Either a CoreSightTarget or CortexM object. 108 | @param resume The value of the `disconnect_on_resume` option. 109 | @return Ignored.""" 110 | pass 111 | 112 | def will_reset(self, core, reset_type): 113 | """! @brief Pre-reset hook. 114 | @param self 115 | @param core A CortexM instance. 116 | @param reset_type One of the Target.ResetType enumerations. 117 | @retval True 118 | @retval "False or None" 119 | """ 120 | pass 121 | 122 | def did_reset(self, core, reset_type): 123 | """! @brief Post-reset hook. 124 | @param self 125 | @param core A CortexM instance. 126 | @param reset_type One of the Target.ResetType enumerations. 127 | @return Ignored. 128 | """ 129 | pass 130 | 131 | def set_reset_catch(self, core, reset_type): 132 | """! @brief Hook to prepare target for halting on reset. 133 | @param self 134 | @param core A CortexM instance. 135 | @param reset_type One of the Target.ResetType enumerations. 136 | @retval True 137 | @retval "False or None" 138 | """ 139 | pass 140 | 141 | def clear_reset_catch(self, core, reset_type): 142 | """! @brief Hook to clean up target after a reset and halt. 143 | @param self 144 | @param core A CortexM instance. 145 | @param reset_type 146 | @return Ignored. 147 | """ 148 | pass 149 | 150 | def mass_erase(self, target): 151 | """! @brief Hook to override mass erase. 152 | @param self 153 | @param target A CoreSightTarget object. 154 | @retval True Indicate that mass erase was performed by the hook. 155 | @retval "False or None" Mass erase was not overridden and the caller should proceed with the standard 156 | mass erase procedure. 157 | """ 158 | pass 159 | 160 | def trace_start(self, target, mode): 161 | """! @brief Hook to prepare for tracing the target. 162 | @param self 163 | @param target A CoreSightTarget object. 164 | @param mode The trace mode. Currently always 0 to indicate SWO. 165 | @return Ignored. 166 | """ 167 | pass 168 | 169 | def trace_stop(self, target, mode): 170 | """! @brief Hook to clean up after tracing the target. 171 | @param self 172 | @param target A CoreSightTarget object. 173 | @param mode The trace mode. Currently always 0 to indicate SWO. 174 | @return Ignored. 175 | """ 176 | pass 177 | 178 | 179 | -------------------------------------------------------------------------------- /pyocd/coresight/__init__.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2017 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | -------------------------------------------------------------------------------- /pyocd/coresight/component.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2018-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from ..utility.graph import GraphNode 18 | 19 | class CoreSightComponent(GraphNode): 20 | """! @brief CoreSight component base class.""" 21 | 22 | @classmethod 23 | def factory(cls, ap, cmpid, address): 24 | """! @brief Common CoreSightComponent factory.""" 25 | cmp = cls(ap, cmpid, address) 26 | if ap.core: 27 | ap.core.add_child(cmp) 28 | return cmp 29 | 30 | def __init__(self, ap, cmpid=None, addr=None): 31 | """! @brief Constructor.""" 32 | super(CoreSightComponent, self).__init__() 33 | self._ap = ap 34 | self._cmpid = cmpid 35 | self._address = addr or (cmpid.address if cmpid else None) 36 | 37 | @property 38 | def ap(self): 39 | return self._ap 40 | 41 | @property 42 | def cmpid(self): 43 | return self._cmpid 44 | 45 | @cmpid.setter 46 | def cmpid(self, newCmpid): 47 | self._cmpid = newCmpid 48 | 49 | @property 50 | def address(self): 51 | return self._address 52 | 53 | @address.setter 54 | def address(self, newAddr): 55 | self._address = newAddr 56 | 57 | -------------------------------------------------------------------------------- /pyocd/coresight/dwt.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2015-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from .fpb import HardwareBreakpoint 18 | from ..core.target import Target 19 | from .component import CoreSightComponent 20 | import logging 21 | 22 | # Need a local copy to prevent circular import. 23 | # Debug Exception and Monitor Control Register 24 | DEMCR = 0xE000EDFC 25 | # DWTENA in armv6 architecture reference manual 26 | DEMCR_TRCENA = (1 << 24) 27 | DEMCR_VC_HARDERR = (1 << 10) 28 | DEMCR_VC_BUSERR = (1 << 8) 29 | DEMCR_VC_CORERESET = (1 << 0) 30 | 31 | class Watchpoint(HardwareBreakpoint): 32 | def __init__(self, comp_register_addr, provider): 33 | super(Watchpoint, self).__init__(comp_register_addr, provider) 34 | self.addr = 0 35 | self.size = 0 36 | self.func = 0 37 | 38 | class DWT(CoreSightComponent): 39 | """! @brief Data Watchpoint and Trace unit""" 40 | 41 | # DWT registers 42 | # 43 | # The addresses are offsets from the base address. 44 | DWT_CTRL = 0x00000000 45 | DWT_CYCCNT = 0x00000004 46 | DWT_CPICNT = 0x00000008 47 | DWT_EXCCNT = 0x0000000C 48 | DWT_SLEEPCNT = 0x00000010 49 | DWT_LSUCNT = 0x00000014 50 | DWT_FOLDCNT = 0x00000018 51 | DWT_PCSR = 0x0000001C 52 | DWT_COMP_BASE = 0x00000020 53 | DWT_MASK_OFFSET = 4 54 | DWT_FUNCTION_OFFSET = 8 55 | DWT_COMP_BLOCK_SIZE = 0x10 56 | 57 | DWT_CTRL_NUM_COMP_MASK = (0xF << 28) 58 | DWT_CTRL_NUM_COMP_SHIFT = 28 59 | DWT_CTRL_CYCEVTENA_MASK = (1 << 22) 60 | DWT_CTRL_FOLDEVTENA_MASK = (1 << 21) 61 | DWT_CTRL_LSUEVTENA_MASK = (1 << 20) 62 | DWT_CTRL_SLEEPEVTENA_MASK = (1 << 19) 63 | DWT_CTRL_EXCEVTENA_MASK = (1 << 18) 64 | DWT_CTRL_CPIEVTENA_MASK = (1 << 17) 65 | DWT_CTRL_EXCTRCENA_MASK = (1 << 16) 66 | DWT_CTRL_PCSAMPLENA_MASK = (1 << 12) 67 | DWT_CTRL_SYNCTAP_MASK = (0x3 << 10) 68 | DWT_CTRL_SYNCTAP_SHIFT = 10 69 | DWT_CTRL_CYCTAP_MASK = (1 << 9) 70 | DWT_CTRL_POSTINIT_MASK = (0xF << 5) 71 | DWT_CTRL_POSTINIT_SHIFT = 5 72 | DWT_CTRL_POSTRESET_MASK = (0xF << 1) 73 | DWT_CTRL_POSTRESET_SHIFT = 1 74 | DWT_CTRL_CYCCNTENA_MASK = (1 << 0) 75 | 76 | WATCH_TYPE_TO_FUNCT = { 77 | Target.WATCHPOINT_READ: 5, 78 | Target.WATCHPOINT_WRITE: 6, 79 | Target.WATCHPOINT_READ_WRITE: 7, 80 | 5: Target.WATCHPOINT_READ, 81 | 6: Target.WATCHPOINT_WRITE, 82 | 7: Target.WATCHPOINT_READ_WRITE, 83 | } 84 | 85 | # Only sizes that are powers of 2 are supported 86 | # Breakpoint size = MASK**2 87 | WATCH_SIZE_TO_MASK = dict((2**i, i) for i in range(0,32)) 88 | 89 | def __init__(self, ap, cmpid=None, addr=None): 90 | super(DWT, self).__init__(ap, cmpid, addr) 91 | self.watchpoints = [] 92 | self.watchpoint_used = 0 93 | self.dwt_configured = False 94 | 95 | ## @brief Inits the DWT. 96 | # 97 | # Reads the number of hardware watchpoints available on the core and makes sure that they 98 | # are all disabled and ready for future use. 99 | def init(self): 100 | # Make sure trace is enabled. 101 | demcr = self.ap.read_memory(DEMCR) 102 | if (demcr & DEMCR_TRCENA) == 0: 103 | demcr |= DEMCR_TRCENA 104 | self.ap.write_memory(DEMCR, demcr) 105 | 106 | dwt_ctrl = self.ap.read_memory(self.address + DWT.DWT_CTRL) 107 | watchpoint_count = (dwt_ctrl & DWT.DWT_CTRL_NUM_COMP_MASK) >> DWT.DWT_CTRL_NUM_COMP_SHIFT 108 | logging.info("%d hardware watchpoints", watchpoint_count) 109 | for i in range(watchpoint_count): 110 | comparatorAddress = self.address + DWT.DWT_COMP_BASE + DWT.DWT_COMP_BLOCK_SIZE * i 111 | self.watchpoints.append(Watchpoint(comparatorAddress, self)) 112 | self.ap.write_memory(comparatorAddress + DWT.DWT_FUNCTION_OFFSET, 0) 113 | 114 | # Enable cycle counter. 115 | self.ap.write32(self.address + DWT.DWT_CTRL, DWT.DWT_CTRL_CYCCNTENA_MASK) 116 | self.dwt_configured = True 117 | 118 | def find_watchpoint(self, addr, size, type): 119 | for watch in self.watchpoints: 120 | if watch.addr == addr and watch.size == size and watch.func == DWT.WATCH_TYPE_TO_FUNCT[type]: 121 | return watch 122 | return None 123 | 124 | ## @brief Set a hardware watchpoint. 125 | def set_watchpoint(self, addr, size, type): 126 | if self.dwt_configured is False: 127 | self.init() 128 | 129 | watch = self.find_watchpoint(addr, size, type) 130 | if watch != None: 131 | return True 132 | 133 | if type not in DWT.WATCH_TYPE_TO_FUNCT: 134 | logging.error("Invalid watchpoint type %i", type) 135 | return False 136 | 137 | for watch in self.watchpoints: 138 | if watch.func == 0: 139 | watch.addr = addr 140 | watch.func = DWT.WATCH_TYPE_TO_FUNCT[type] 141 | watch.size = size 142 | 143 | if size not in DWT.WATCH_SIZE_TO_MASK: 144 | logging.error('Watchpoint of size %d not supported by device', size) 145 | return False 146 | 147 | mask = DWT.WATCH_SIZE_TO_MASK[size] 148 | self.ap.write_memory(watch.comp_register_addr + DWT.DWT_MASK_OFFSET, mask) 149 | if self.ap.read_memory(watch.comp_register_addr + DWT.DWT_MASK_OFFSET) != mask: 150 | logging.error('Watchpoint of size %d not supported by device', size) 151 | return False 152 | 153 | self.ap.write_memory(watch.comp_register_addr, addr) 154 | self.ap.write_memory(watch.comp_register_addr + DWT.DWT_FUNCTION_OFFSET, watch.func) 155 | self.watchpoint_used += 1 156 | return True 157 | 158 | logging.error('No more available watchpoint!!, dropped watch at 0x%X', addr) 159 | return False 160 | 161 | ## @brief Remove a hardware watchpoint. 162 | def remove_watchpoint(self, addr, size, type): 163 | watch = self.find_watchpoint(addr, size, type) 164 | if watch is None: 165 | return 166 | 167 | watch.func = 0 168 | self.ap.write_memory(watch.comp_register_addr + DWT.DWT_FUNCTION_OFFSET, 0) 169 | self.watchpoint_used -= 1 170 | 171 | def remove_all_watchpoints(self): 172 | for watch in self.watchpoints: 173 | if watch.func != 0: 174 | self.remove_watchpoint(watch.addr, watch.size, DWT.WATCH_TYPE_TO_FUNCT[watch.func]) 175 | 176 | @property 177 | def cycle_count(self): 178 | return self.ap.read32(self.address + DWT.DWT_CYCCNT) 179 | 180 | @cycle_count.setter 181 | def cycle_count(self, value): 182 | self.ap.write32(self.address + DWT.DWT_CYCCNT, value) 183 | 184 | -------------------------------------------------------------------------------- /pyocd/coresight/fpb.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2015-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from ..core.target import Target 18 | from .component import CoreSightComponent 19 | from ..debug.breakpoints.provider import (Breakpoint, BreakpointProvider) 20 | import logging 21 | 22 | class HardwareBreakpoint(Breakpoint): 23 | def __init__(self, comp_register_addr, provider): 24 | super(HardwareBreakpoint, self).__init__(provider) 25 | self.comp_register_addr = comp_register_addr 26 | self.type = Target.BREAKPOINT_HW 27 | 28 | class FPB(BreakpointProvider, CoreSightComponent): 29 | """! @brief Flash Patch and Breakpoint unit""" 30 | 31 | # FPB registers 32 | # 33 | # The addresses are offsets from the base address. 34 | FP_CTRL = 0x00000000 35 | FP_CTRL_KEY = 1 << 1 36 | FP_CTRL_REV_MASK = 0xf0000000 37 | FP_CTRL_REV_SHIFT = 28 38 | FP_COMP0 = 0x00000008 39 | 40 | def __init__(self, ap, cmpid=None, addr=None): 41 | CoreSightComponent.__init__(self, ap, cmpid, addr) 42 | BreakpointProvider.__init__(self) 43 | self.hw_breakpoints = [] 44 | self.nb_code = 0 45 | self.nb_lit = 0 46 | self.num_hw_breakpoint_used = 0 47 | self.enabled = False 48 | self.fpb_rev = 1 49 | 50 | @property 51 | def revision(self): 52 | return self.fpb_rev 53 | 54 | ## @brief Inits the FPB. 55 | # 56 | # Reads the number of hardware breakpoints available on the core and disable the FPB 57 | # (Flash Patch and Breakpoint Unit), which will be enabled when the first breakpoint is set. 58 | def init(self): 59 | # setup FPB (breakpoint) 60 | fpcr = self.ap.read32(self.address + FPB.FP_CTRL) 61 | self.fpb_rev = 1 + ((fpcr & FPB.FP_CTRL_REV_MASK) >> FPB.FP_CTRL_REV_SHIFT) 62 | if self.fpb_rev not in (1, 2): 63 | logging.warning("Unknown FPB version %d", self.fpb_rev) 64 | self.nb_code = ((fpcr >> 8) & 0x70) | ((fpcr >> 4) & 0xF) 65 | self.nb_lit = (fpcr >> 7) & 0xf 66 | logging.info("%d hardware breakpoints, %d literal comparators", self.nb_code, self.nb_lit) 67 | for i in range(self.nb_code): 68 | self.hw_breakpoints.append(HardwareBreakpoint(self.address + FPB.FP_COMP0 + 4*i, self)) 69 | 70 | # disable FPB (will be enabled on first bp set) 71 | self.disable() 72 | for bp in self.hw_breakpoints: 73 | self.ap.write_memory(bp.comp_register_addr, 0) 74 | 75 | def bp_type(self): 76 | return Target.BREAKPOINT_HW 77 | 78 | def enable(self): 79 | self.ap.write_memory(self.address + FPB.FP_CTRL, FPB.FP_CTRL_KEY | 1) 80 | self.enabled = True 81 | logging.debug('fpb has been enabled') 82 | return 83 | 84 | def disable(self): 85 | self.ap.write_memory(self.address + FPB.FP_CTRL, FPB.FP_CTRL_KEY | 0) 86 | self.enabled = False 87 | logging.debug('fpb has been disabled') 88 | return 89 | 90 | def available_breakpoints(self): 91 | return len(self.hw_breakpoints) - self.num_hw_breakpoint_used 92 | 93 | ## @brief Test whether an address is supported by the FPB. 94 | # 95 | # For FPBv1, hardware breakpoints are only supported in the range 0x00000000 - 0x1fffffff. 96 | # This was fixed for FPBv2, which supports hardware breakpoints at any address. 97 | def can_support_address(self, addr): 98 | return (self.fpb_rev == 2) or (addr < 0x20000000) 99 | 100 | ## @brief Set a hardware breakpoint at a specific location in flash. 101 | def set_breakpoint(self, addr): 102 | if not self.enabled: 103 | self.enable() 104 | 105 | if not self.can_support_address(addr): 106 | logging.error('Breakpoint out of range 0x%X', addr) 107 | return None 108 | 109 | if self.available_breakpoints() == 0: 110 | logging.error('No more available breakpoint!!, dropped bp at 0x%X', addr) 111 | return None 112 | 113 | for bp in self.hw_breakpoints: 114 | if not bp.enabled: 115 | bp.enabled = True 116 | comp = 0 117 | if self.fpb_rev == 1: 118 | bp_match = (1 << 30) 119 | if addr & 0x2: 120 | bp_match = (2 << 30) 121 | comp = addr & 0x1ffffffc | bp_match | 1 122 | elif self.fpb_rev == 2: 123 | comp = (addr & 0xfffffffe) | 1 124 | self.ap.write32(bp.comp_register_addr, comp) 125 | logging.debug("BP: wrote 0x%08x to comp @ 0x%08x", comp, bp.comp_register_addr) 126 | bp.addr = addr 127 | self.num_hw_breakpoint_used += 1 128 | return bp 129 | return None 130 | 131 | ## @brief Remove a hardware breakpoint at a specific location in flash. 132 | def remove_breakpoint(self, bp): 133 | for hwbp in self.hw_breakpoints: 134 | if hwbp.enabled and hwbp.addr == bp.addr: 135 | hwbp.enabled = False 136 | self.ap.write_memory(hwbp.comp_register_addr, 0) 137 | self.num_hw_breakpoint_used -= 1 138 | return 139 | 140 | -------------------------------------------------------------------------------- /pyocd/coresight/itm.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2017-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import logging 18 | 19 | from ..core.target import Target 20 | from ..core import exceptions 21 | from .component import CoreSightComponent 22 | 23 | # Need a local copy to prevent circular import. 24 | # Debug Exception and Monitor Control Register 25 | DEMCR = 0xE000EDFC 26 | DEMCR_TRCENA = (1 << 24) 27 | 28 | class ITMOptions(object): 29 | def __init__(self): 30 | pass 31 | 32 | class ITM(CoreSightComponent): 33 | """! @brief Instrumentation Trace Macrocell""" 34 | 35 | # Register definitions. 36 | # 37 | # The addresses are offsets from the base address. 38 | STIMn = 0x00000000 39 | TERn = 0x00000e00 40 | TPR = 0x00000e40 41 | 42 | TCR = 0x00000e80 43 | TCR_ITMENA_MASK = (1 << 0) 44 | TCR_TSENA_MASK = (1 << 1) 45 | TCR_SYNCENA_MASK = (1 << 2) 46 | TCR_TXENA_MASK = (1 << 3) 47 | TCR_SWOENA_MASK = (1 << 4) 48 | TCR_TSPRESCALE_MASK = (0x3 << 8) 49 | TCR_TSPRESCALE_SHIFT = 8 50 | TCR_TSPRESCALE_DIV_1 = 0x0 51 | TCR_TSPRESCALE_DIV_4 = 0x1 52 | TCR_TSPRESCALE_DIV_16 = 0x2 53 | TCR_TSPRESCALE_DIV_64 = 0x3 54 | TCR_GTSFREQ_MASK = (0x3 << 10) 55 | TCR_GTSFREQ_SHIFT = 10 56 | TCR_TRACEBUSID_MASK = (0x7f << 16) 57 | TCR_TRACEBUSID_SHIFT = 16 58 | TCR_BUSY_MASK = (1 << 23) 59 | 60 | LAR = 0x00000fb0 61 | LAR_KEY = 0xC5ACCE55 62 | LSR = 0x00000fb4 63 | LSR_SLK_MASK = (1 << 1) 64 | LSR_SLI_MASK = (1 << 0) 65 | 66 | def __init__(self, ap, cmpid=None, addr=None): 67 | super(ITM, self).__init__(ap, cmpid, addr) 68 | self._is_enabled = False 69 | 70 | def init(self): 71 | # Make sure trace is enabled. 72 | demcr = self.ap.read32(DEMCR) 73 | if (demcr & DEMCR_TRCENA) == 0: 74 | demcr |= DEMCR_TRCENA 75 | self.ap.write32(DEMCR, demcr) 76 | 77 | # Unlock if required. 78 | val = self.ap.read32(self.address + ITM.LSR) 79 | if (val & (ITM.LSR_SLK_MASK | ITM.LSR_SLI_MASK)) == (ITM.LSR_SLK_MASK | ITM.LSR_SLI_MASK): 80 | self.ap.write32(self.address + ITM.LAR, ITM.LAR_KEY) 81 | val = self.ap.read32(self.address + ITM.LSR) 82 | if val & ITM.LSR_SLK_MASK: 83 | raise exceptions.Error("Failed to unlock ITM") 84 | 85 | # Disable the ITM until enabled. 86 | self.disable() 87 | 88 | @property 89 | def is_enabled(self): 90 | return self._is_enabled 91 | 92 | def enable(self, enabled_ports=0xffffffff): 93 | self.ap.write32(self.address + ITM.TCR, ((1 << ITM.TCR_TRACEBUSID_SHIFT) 94 | | ITM.TCR_ITMENA_MASK 95 | | ITM.TCR_TSENA_MASK 96 | | ITM.TCR_TXENA_MASK 97 | | (ITM.TCR_TSPRESCALE_DIV_1 << ITM.TCR_TSPRESCALE_SHIFT))) 98 | self.ap.write32(self.address + ITM.TERn, enabled_ports) 99 | self.ap.write32(self.address + ITM.TPR, 0xf) # Allow unprivileged access to all 32 ports. 100 | self._is_enabled = True 101 | 102 | def set_enabled_ports(enabled_ports): 103 | self.ap.write32(self.address + ITM.TERn, enabled_ports) 104 | 105 | def disable(self): 106 | self.ap.write32(self.address + ITM.TERn, 0) 107 | self.ap.write32(self.address + ITM.TCR, 0) 108 | self._is_enabled = False 109 | 110 | -------------------------------------------------------------------------------- /pyocd/coresight/tpiu.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2017-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import logging 18 | 19 | from .component import CoreSightComponent 20 | 21 | class TPIU(CoreSightComponent): 22 | """! @brief Trace Port Interface Unit""" 23 | 24 | # Register definitions. 25 | # 26 | # The addresses are offsets from the base address. 27 | ACPR = 0x00000010 28 | ACPR_PRESCALER_MASK = 0x0000ffff 29 | 30 | SPPR = 0x000000f0 31 | SPPR_TXMODE_MASK = 0x00000003 32 | SPPR_TXMODE_NRZ = 0x00000002 33 | 34 | FFCR = 0x00000304 35 | FFCR_ENFCONT_MASK = (1 << 1) 36 | 37 | DEVID = 0x00000fc8 38 | DEVID_NRZ_MASK = (1 << 11) 39 | 40 | def __init__(self, ap, cmpid=None, addr=None): 41 | """! @brief Standard CoreSight component constructor.""" 42 | super(TPIU, self).__init__(ap, cmpid, addr) 43 | self._has_swo_uart = False 44 | 45 | @property 46 | def has_swo_uart(self): 47 | """! @brief Whether SWO UART mode is supported by the TPIU.""" 48 | return self._has_swo_uart 49 | 50 | def init(self): 51 | """! @brief Configures the TPIU for SWO UART mode.""" 52 | devid = self.ap.read32(self.address + TPIU.DEVID) 53 | self._has_swo_uart = (devid & TPIU.DEVID_NRZ_MASK) != 0 54 | 55 | # Go ahead and configure for SWO. 56 | self.ap.write32(self.address + TPIU.SPPR, TPIU.SPPR_TXMODE_NRZ) # Select SWO UART mode. 57 | self.ap.write32(self.address + TPIU.FFCR, 0) # Disable formatter. 58 | 59 | def set_swo_clock(self, swo_clock, system_clock): 60 | """! @brief Sets the SWO clock frequency based on the system clock. 61 | 62 | @param self 63 | @param Desired SWO baud rate in Hertz. 64 | @param system_clock The frequency of the SWO clock source in Hertz. This is almost always 65 | the system clock, also called the HCLK or fast clock. 66 | @return Boolean indicating if the requested frequency could be set within 3%. 67 | """ 68 | div = (system_clock // swo_clock) - 1 69 | actual = system_clock // (div + 1) 70 | deltaPercent = abs(swo_clock - actual) / swo_clock 71 | if deltaPercent > 0.03: 72 | return False 73 | self.ap.write32(self.address + TPIU.ACPR, div & TPIU.ACPR_PRESCALER_MASK) 74 | return True 75 | 76 | 77 | -------------------------------------------------------------------------------- /pyocd/debug/__init__.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2017 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | -------------------------------------------------------------------------------- /pyocd/debug/breakpoints/__init__.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2016 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | -------------------------------------------------------------------------------- /pyocd/debug/breakpoints/manager.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2015-2017 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from ...core.target import Target 18 | import logging 19 | 20 | ## 21 | # @brief 22 | class BreakpointManager(object): 23 | ## Number of hardware breakpoints to try to keep available. 24 | MIN_HW_BREAKPOINTS = 0 25 | 26 | def __init__(self, core): 27 | self._breakpoints = {} 28 | self._core = core 29 | self._fpb = None 30 | self._providers = {} 31 | 32 | def add_provider(self, provider, type): 33 | self._providers[type] = provider 34 | if type == Target.BREAKPOINT_HW: 35 | self._fpb = provider 36 | 37 | ## @brief Return a list of all breakpoint addresses. 38 | def get_breakpoints(self): 39 | return self._breakpoints.keys() 40 | 41 | def find_breakpoint(self, addr): 42 | return self._breakpoints.get(addr, None) 43 | 44 | ## @brief Set a hardware or software breakpoint at a specific location in memory. 45 | # 46 | # @retval True Breakpoint was set. 47 | # @retval False Breakpoint could not be set. 48 | def set_breakpoint(self, addr, type=Target.BREAKPOINT_AUTO): 49 | logging.debug("set bkpt type %d at 0x%x", type, addr) 50 | 51 | # Clear Thumb bit in case it is set. 52 | addr = addr & ~1 53 | 54 | in_hw_bkpt_range = (self._fpb is not None) and (self._fpb.can_support_address(addr)) 55 | fbp_available = ((self._fpb is not None) and 56 | (self._fpb.available_breakpoints() > 0)) 57 | fbp_below_min = ((self._fpb is None) or 58 | (self._fpb.available_breakpoints() <= self.MIN_HW_BREAKPOINTS)) 59 | 60 | # Check for an existing breakpoint at this address. 61 | bp = self.find_breakpoint(addr) 62 | if bp is not None: 63 | return True 64 | 65 | if self._core.memory_map is None: 66 | # No memory map - fallback to hardware breakpoints. 67 | type = Target.BREAKPOINT_HW 68 | is_flash = False 69 | is_ram = False 70 | else: 71 | # Look up the memory type for the requested address. 72 | region = self._core.memory_map.get_region_for_address(addr) 73 | if region is not None: 74 | is_flash = region.is_flash 75 | is_ram = region.is_ram 76 | else: 77 | # No memory region - fallback to hardware breakpoints. 78 | type = Target.BREAKPOINT_HW 79 | is_flash = False 80 | is_ram = False 81 | 82 | # Determine best type to use if auto. 83 | if type == Target.BREAKPOINT_AUTO: 84 | # Use sw breaks for: 85 | # 1. Addresses outside the supported FPBv1 range of 0-0x1fffffff 86 | # 2. RAM regions by default. 87 | # 3. Number of remaining hw breaks are at or less than the minimum we want to keep. 88 | # 89 | # Otherwise use hw. 90 | if not in_hw_bkpt_range or is_ram or fbp_below_min: 91 | type = Target.BREAKPOINT_SW 92 | else: 93 | type = Target.BREAKPOINT_HW 94 | 95 | logging.debug("using type %d for auto bp", type) 96 | 97 | # Revert to sw bp if out of hardware breakpoint range. 98 | if (type == Target.BREAKPOINT_HW) and not in_hw_bkpt_range: 99 | if is_ram: 100 | logging.debug("using sw bp instead because of unsupported addr") 101 | type = Target.BREAKPOINT_SW 102 | else: 103 | logging.debug("could not fallback to software breakpoint") 104 | return False 105 | 106 | # Revert to hw bp if region is flash. 107 | if is_flash: 108 | if in_hw_bkpt_range and fbp_available: 109 | logging.debug("using hw bp instead because addr is flash") 110 | type = Target.BREAKPOINT_HW 111 | else: 112 | logging.debug("could not fallback to hardware breakpoint") 113 | return False 114 | 115 | # Set the bp. 116 | try: 117 | provider = self._providers[type] 118 | bp = provider.set_breakpoint(addr) 119 | except KeyError: 120 | raise RuntimeError("Unknown breakpoint type %d" % type) 121 | 122 | 123 | if bp is None: 124 | return False 125 | 126 | # Save the bp. 127 | self._breakpoints[addr] = bp 128 | return True 129 | 130 | ## @brief Remove a breakpoint at a specific location. 131 | def remove_breakpoint(self, addr): 132 | try: 133 | logging.debug("remove bkpt at 0x%x", addr) 134 | 135 | # Clear Thumb bit in case it is set. 136 | addr = addr & ~1 137 | 138 | # Get bp and remove from dict. 139 | bp = self._breakpoints.pop(addr) 140 | 141 | assert bp.provider is not None 142 | bp.provider.remove_breakpoint(bp) 143 | except KeyError: 144 | logging.debug("Tried to remove breakpoint 0x%08x that wasn't set" % addr) 145 | 146 | def get_breakpoint_type(self, addr): 147 | bp = self.find_breakpoint(addr) 148 | return bp.type if (bp is not None) else None 149 | 150 | def filter_memory(self, addr, size, data): 151 | for provider in [p for p in self._providers.values() if p.do_filter_memory]: 152 | data = provider.filter_memory(addr, size, data) 153 | return data 154 | 155 | def filter_memory_unaligned_8(self, addr, size, data): 156 | for provider in [p for p in self._providers.values() if p.do_filter_memory]: 157 | for i, d in enumerate(data): 158 | data[i] = provider.filter_memory(addr + i, 8, d) 159 | return data 160 | 161 | def filter_memory_aligned_32(self, addr, size, data): 162 | for provider in [p for p in self._providers.values() if p.do_filter_memory]: 163 | for i, d in enumerate(data): 164 | data[i] = provider.filter_memory(addr + i, 32, d) 165 | return data 166 | 167 | def remove_all_breakpoints(self): 168 | for bp in self._breakpoints.values(): 169 | bp.provider.remove_breakpoint(bp) 170 | self._breakpoints = {} 171 | self._flush_all() 172 | 173 | def _flush_all(self): 174 | # Flush all providers. 175 | for provider in self._providers.values(): 176 | provider.flush() 177 | 178 | def flush(self): 179 | try: 180 | # Flush all providers. 181 | self._flush_all() 182 | finally: 183 | pass 184 | 185 | -------------------------------------------------------------------------------- /pyocd/debug/breakpoints/provider.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2015-2017 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from ...core.target import Target 18 | 19 | class Breakpoint(object): 20 | def __init__(self, provider): 21 | self.type = Target.BREAKPOINT_HW 22 | self.enabled = False 23 | self.addr = 0 24 | self.original_instr = 0 25 | self.provider = provider 26 | 27 | def __repr__(self): 28 | return "<%s@0x%08x type=%d addr=0x%08x>" % (self.__class__.__name__, id(self), self.type, self.addr) 29 | 30 | ## @brief Abstract base class for breakpoint providers. 31 | class BreakpointProvider(object): 32 | def init(self): 33 | raise NotImplementedError() 34 | 35 | def bp_type(self): 36 | return 0 37 | 38 | @property 39 | def do_filter_memory(self): 40 | return False 41 | 42 | def available_breakpoints(self): 43 | raise NotImplementedError() 44 | 45 | def find_breakpoint(self, addr): 46 | raise NotImplementedError() 47 | 48 | def set_breakpoint(self, addr): 49 | raise NotImplementedError() 50 | 51 | def remove_breakpoint(self, bp): 52 | raise NotImplementedError() 53 | 54 | def filter_memory(self, addr, size, data): 55 | return data 56 | 57 | def flush(self): 58 | pass 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /pyocd/debug/breakpoints/software.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2015-2017 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from .provider import (Breakpoint, BreakpointProvider) 18 | from ...core import exceptions 19 | from ...core.target import Target 20 | import logging 21 | 22 | class SoftwareBreakpoint(Breakpoint): 23 | def __init__(self, provider): 24 | super(SoftwareBreakpoint, self).__init__(provider) 25 | self.type = Target.BREAKPOINT_SW 26 | 27 | class SoftwareBreakpointProvider(BreakpointProvider): 28 | ## BKPT #0 instruction. 29 | BKPT_INSTR = 0xbe00 30 | 31 | def __init__(self, core): 32 | super(SoftwareBreakpointProvider, self).__init__() 33 | self._core = core 34 | self._breakpoints = {} 35 | 36 | def init(self): 37 | pass 38 | 39 | def bp_type(self): 40 | return Target.BREAKPOINT_SW 41 | 42 | @property 43 | def do_filter_memory(self): 44 | return True 45 | 46 | def available_breakpoints(self): 47 | return -1 48 | 49 | def find_breakpoint(self, addr): 50 | return self._breakpoints.get(addr, None) 51 | 52 | def set_breakpoint(self, addr): 53 | assert self._core.memory_map.get_region_for_address(addr).is_ram 54 | assert (addr & 1) == 0 55 | 56 | try: 57 | # Read original instruction. 58 | instr = self._core.read16(addr) 59 | 60 | # Insert BKPT #0 instruction. 61 | self._core.write16(addr, self.BKPT_INSTR) 62 | 63 | # Create bp object. 64 | bp = SoftwareBreakpoint(self) 65 | bp.enabled = True 66 | bp.addr = addr 67 | bp.original_instr = instr 68 | 69 | # Save this breakpoint. 70 | self._breakpoints[addr] = bp 71 | return bp 72 | except exceptions.TransferError: 73 | logging.debug("Failed to set sw bp at 0x%x" % addr) 74 | return None 75 | 76 | def remove_breakpoint(self, bp): 77 | assert bp is not None and isinstance(bp, Breakpoint) 78 | 79 | try: 80 | # Restore original instruction. 81 | self._core.write16(bp.addr, bp.original_instr) 82 | 83 | # Remove from our list. 84 | del self._breakpoints[bp.addr] 85 | except exceptions.TransferError: 86 | logging.debug("Failed to remove sw bp at 0x%x" % bp.addr) 87 | 88 | def filter_memory(self, addr, size, data): 89 | for bp in self._breakpoints.values(): 90 | if size == 8: 91 | if bp.addr == addr: 92 | data = bp.original_instr & 0xff 93 | elif bp.addr + 1 == addr: 94 | data = bp.original_instr >> 8 95 | elif size == 16: 96 | if bp.addr == addr: 97 | data = bp.original_instr 98 | elif size == 32: 99 | if bp.addr == addr: 100 | data = (data & 0xffff0000) | bp.original_instr 101 | elif bp.addr == addr + 2: 102 | data = (data & 0xffff) | (bp.original_instr << 16) 103 | 104 | return data 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /pyocd/debug/context.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2016-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from ..core.memory_interface import MemoryInterface 18 | from ..coresight.cortex_m import (CORE_REGISTER, register_name_to_index, is_single_float_register, 19 | is_double_float_register) 20 | from ..utility import conversion 21 | import logging 22 | 23 | ## @brief Viewport for inspecting the system being debugged. 24 | # 25 | # A debug context is used to access registers and other target information. It enables these 26 | # accesses to be redirected to different locations. For instance, if you want to read registers 27 | # from a call frame that is not the topmost, then a context would redirect those reads to 28 | # locations on the stack. 29 | # 30 | # A context always has a specific core associated with it, which cannot be changed after the 31 | # context is created. 32 | class DebugContext(MemoryInterface): 33 | def __init__(self, core): 34 | self._core = core 35 | 36 | @property 37 | def core(self): 38 | return self._core 39 | 40 | def write_memory(self, addr, value, transfer_size=32): 41 | return self._core.write_memory(addr, value, transfer_size) 42 | 43 | def read_memory(self, addr, transfer_size=32, now=True): 44 | return self._core.read_memory(addr, transfer_size, now) 45 | 46 | def write_memory_block8(self, addr, value): 47 | return self._core.write_memory_block8(addr, value) 48 | 49 | def write_memory_block32(self, addr, data): 50 | return self._core.write_memory_block32(addr, data) 51 | 52 | def read_memory_block8(self, addr, size): 53 | return self._core.read_memory_block8(addr, size) 54 | 55 | def read_memory_block32(self, addr, size): 56 | return self._core.read_memory_block32(addr, size) 57 | 58 | def read_core_register(self, reg): 59 | """ 60 | read CPU register 61 | Unpack floating point register values 62 | """ 63 | regIndex = register_name_to_index(reg) 64 | regValue = self.read_core_register_raw(regIndex) 65 | # Convert int to float. 66 | if is_single_float_register(regIndex): 67 | regValue = conversion.u32_to_float32(regValue) 68 | elif is_double_float_register(regIndex): 69 | regValue = conversion.u64_to_float64(regValue) 70 | return regValue 71 | 72 | def read_core_register_raw(self, reg): 73 | """ 74 | read a core register (r0 .. r16). 75 | If reg is a string, find the number associated to this register 76 | in the lookup table CORE_REGISTER 77 | """ 78 | vals = self.read_core_registers_raw([reg]) 79 | return vals[0] 80 | 81 | def read_core_registers_raw(self, reg_list): 82 | return self._core.read_core_registers_raw(reg_list) 83 | 84 | def write_core_register(self, reg, data): 85 | """ 86 | write a CPU register. 87 | Will need to pack floating point register values before writing. 88 | """ 89 | regIndex = register_name_to_index(reg) 90 | # Convert float to int. 91 | if is_single_float_register(regIndex) and type(data) is float: 92 | data = conversion.float32_to_u32(data) 93 | elif is_double_float_register(regIndex) and type(data) is float: 94 | data = conversion.float64_to_u64(data) 95 | self.write_core_register_raw(regIndex, data) 96 | 97 | def write_core_register_raw(self, reg, data): 98 | """ 99 | write a core register (r0 .. r16) 100 | If reg is a string, find the number associated to this register 101 | in the lookup table CORE_REGISTER 102 | """ 103 | self.write_core_registers_raw([reg], [data]) 104 | 105 | def write_core_registers_raw(self, reg_list, data_list): 106 | self._core.write_core_registers_raw(reg_list, data_list) 107 | 108 | def flush(self): 109 | self._core.flush() 110 | 111 | -------------------------------------------------------------------------------- /pyocd/debug/elf/__init__.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2017 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | -------------------------------------------------------------------------------- /pyocd/debug/elf/decoder.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2017 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import sys 18 | import os 19 | from elftools.elf.elffile import ELFFile 20 | from elftools.dwarf.constants import DW_LNE_set_address 21 | from intervaltree import IntervalTree 22 | from collections import namedtuple 23 | from itertools import islice 24 | import logging 25 | 26 | FunctionInfo = namedtuple('FunctionInfo', 'name subprogram low_pc high_pc') 27 | LineInfo = namedtuple('LineInfo', 'cu filename dirname line') 28 | SymbolInfo = namedtuple('SymbolInfo', 'name address size type') 29 | 30 | class ElfSymbolDecoder(object): 31 | def __init__(self, elf): 32 | assert isinstance(elf, ELFFile) 33 | self.elffile = elf 34 | 35 | self.symtab = self.elffile.get_section_by_name('.symtab') 36 | self.symcount = self.symtab.num_symbols() 37 | self.symbol_dict = {} 38 | self.symbol_tree = None 39 | 40 | # Build indices. 41 | self._build_symbol_search_tree() 42 | self._process_arm_type_symbols() 43 | 44 | def get_elf(self): 45 | return self.elffile 46 | 47 | def get_symbol_for_address(self, addr): 48 | try: 49 | return sorted(self.symbol_tree[addr])[0].data 50 | except IndexError: 51 | return None 52 | 53 | def get_symbol_for_name(self, name): 54 | try: 55 | return self.symbol_dict[name] 56 | except KeyError: 57 | return None 58 | 59 | def _build_symbol_search_tree(self): 60 | self.symbol_tree = IntervalTree() 61 | symbols = self.symtab.iter_symbols() 62 | for symbol in symbols: 63 | # Only look for functions and objects. 64 | sym_type = symbol.entry['st_info']['type'] 65 | if sym_type not in ['STT_FUNC', 'STT_OBJECT']: 66 | continue 67 | 68 | sym_value = symbol.entry['st_value'] 69 | sym_size = symbol.entry['st_size'] 70 | 71 | # Cannot put an empty interval into the tree, so ensure symbols have 72 | # at least a size of 1. 73 | real_sym_size = sym_size 74 | if sym_size == 0: 75 | sym_size = 1 76 | 77 | syminfo = SymbolInfo(name=symbol.name, address=sym_value, size=real_sym_size, type=sym_type) 78 | 79 | # Add to symbol dict. 80 | self.symbol_dict[symbol.name] = syminfo 81 | 82 | # Add to symbol tree. 83 | self.symbol_tree.addi(sym_value, sym_value+sym_size, syminfo) 84 | 85 | def _process_arm_type_symbols(self): 86 | type_symbols = self._get_arm_type_symbol_iter() 87 | # map(print, imap(lambda x:"%s : 0x%x" % (x.name, x['st_value']), type_symbols)) 88 | 89 | def _get_arm_type_symbol_iter(self): 90 | # Scan until we find $m symbol. 91 | i = 1 92 | while i < self.symcount: 93 | symbol = self.symtab.get_symbol(i) 94 | if symbol.name == '$m': 95 | break 96 | i += 1 97 | if i >= self.symcount: 98 | return 99 | n = symbol['st_value'] 100 | return islice(self.symtab.iter_symbols(), i, n) 101 | 102 | 103 | class DwarfAddressDecoder(object): 104 | def __init__(self, elf): 105 | assert isinstance(elf, ELFFile) 106 | self.elffile = elf 107 | 108 | if not self.elffile.has_dwarf_info(): 109 | raise Exception("No DWARF debug info available") 110 | 111 | self.dwarfinfo = self.elffile.get_dwarf_info() 112 | 113 | self.subprograms = None 114 | self.function_tree = None 115 | self.line_tree = None 116 | 117 | # Build indices. 118 | self._get_subprograms() 119 | self._build_function_search_tree() 120 | self._build_line_search_tree() 121 | 122 | def get_function_for_address(self, addr): 123 | try: 124 | return sorted(self.function_tree[addr])[0].data 125 | except IndexError: 126 | return None 127 | 128 | def get_line_for_address(self, addr): 129 | try: 130 | return sorted(self.line_tree[addr])[0].data 131 | except IndexError: 132 | return None 133 | 134 | def _get_subprograms(self): 135 | self.subprograms = [] 136 | for CU in self.dwarfinfo.iter_CUs(): 137 | self.subprograms.extend([d for d in CU.iter_DIEs() if d.tag == 'DW_TAG_subprogram']) 138 | 139 | def _build_function_search_tree(self): 140 | self.function_tree = IntervalTree() 141 | for prog in self.subprograms: 142 | try: 143 | name = prog.attributes['DW_AT_name'].value 144 | low_pc = prog.attributes['DW_AT_low_pc'].value 145 | high_pc = prog.attributes['DW_AT_high_pc'].value 146 | 147 | # Skip subprograms excluded from the link. 148 | if low_pc == 0: 149 | continue 150 | 151 | # If high_pc is not explicitly an address, then it's an offset from the 152 | # low_pc value. 153 | if prog.attributes['DW_AT_high_pc'].form != 'DW_FORM_addr': 154 | high_pc = low_pc + high_pc 155 | 156 | fninfo = FunctionInfo(name=name, subprogram=prog, low_pc=low_pc, high_pc=high_pc) 157 | 158 | self.function_tree.addi(low_pc, high_pc, fninfo) 159 | except KeyError: 160 | pass 161 | 162 | def _build_line_search_tree(self): 163 | self.line_tree = IntervalTree() 164 | for cu in self.dwarfinfo.iter_CUs(): 165 | lineprog = self.dwarfinfo.line_program_for_CU(cu) 166 | prevstate = None 167 | skipThisSequence = False 168 | for entry in lineprog.get_entries(): 169 | # Look for a DW_LNE_set_address command with a 0 address. This indicates 170 | # code that is not actually included in the link. 171 | # 172 | # TODO: find a better way to determine the code is really not present and 173 | # doesn't have a real address of 0 174 | if entry.is_extended and entry.command == DW_LNE_set_address \ 175 | and len(entry.args) == 1 and entry.args[0] == 0: 176 | skipThisSequence = True 177 | 178 | # We're interested in those entries where a new state is assigned 179 | if entry.state is None: 180 | continue 181 | 182 | # Looking for a range of addresses in two consecutive states. 183 | if prevstate and not skipThisSequence: 184 | try: 185 | fileinfo = lineprog['file_entry'][prevstate.file - 1] 186 | filename = fileinfo.name 187 | try: 188 | dirname = lineprog['include_directory'][fileinfo.dir_index - 1] 189 | except IndexError: 190 | dirname = "" 191 | except IndexError: 192 | filename = "" 193 | dirname = "" 194 | info = LineInfo(cu=cu, filename=filename, dirname=dirname, line=prevstate.line) 195 | fromAddr = prevstate.address 196 | toAddr = entry.state.address 197 | try: 198 | if fromAddr != 0 and toAddr != 0: 199 | if fromAddr == toAddr: 200 | toAddr += 1 201 | self.line_tree.addi(fromAddr, toAddr, info) 202 | except: 203 | logging.debug("Problematic lineprog:") 204 | self._dump_lineprog(lineprog) 205 | raise 206 | 207 | if entry.state.end_sequence: 208 | prevstate = None 209 | skipThisSequence = False 210 | else: 211 | prevstate = entry.state 212 | 213 | def _dump_lineprog(self, lineprog): 214 | for i, e in enumerate(lineprog.get_entries()): 215 | s = e.state 216 | if s is None: 217 | logging.debug("%d: cmd=%d ext=%d args=%s", i, e.command, int(e.is_extended), repr(e.args)) 218 | else: 219 | logging.debug("%d: %06x %4d stmt=%1d block=%1d end=%d file=[%d]%s", i, s.address, s.line, s.is_stmt, int(s.basic_block), int(s.end_sequence), s.file, lineprog['file_entry'][s.file-1].name) 220 | 221 | def dump_subprograms(self): 222 | for prog in self.subprograms: 223 | name = prog.attributes['DW_AT_name'].value 224 | try: 225 | low_pc = prog.attributes['DW_AT_low_pc'].value 226 | except KeyError: 227 | low_pc = 0 228 | try: 229 | high_pc = prog.attributes['DW_AT_high_pc'].value 230 | except KeyError: 231 | high_pc = 0xffffffff 232 | filename = os.path.basename(prog._parent.attributes['DW_AT_name'].value.replace('\\', '/')) 233 | logging.debug("%s%s%08x %08x %s", name, (' ' * (50-len(name))), low_pc, high_pc, filename) 234 | 235 | 236 | -------------------------------------------------------------------------------- /pyocd/debug/elf/elf.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2017 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | from __future__ import print_function 17 | from ...core.memory_map import (MemoryRange, MemoryMap) 18 | from .decoder import (ElfSymbolDecoder, DwarfAddressDecoder) 19 | from elftools.elf.elffile import ELFFile 20 | from elftools.elf.constants import SH_FLAGS 21 | import logging 22 | import six 23 | 24 | ## 25 | # @brief Memory range for a section of an ELF file. 26 | # 27 | # Objects of this class represent sections of an ELF file. See the ELFBinaryFile class documentation 28 | # for details of how sections are selected and how to get instances of this class. 29 | # 30 | # If a region in the target's memory map can be found that contains the section, it will be 31 | # accessible via the instance's _region_ attribute. Otherwise _region_ will be `None`. A maximum of 32 | # one associated memory region is supported, even if the section spans multiple regions. 33 | # 34 | # The contents of the ELF section can be read via the `data` property as a `bytearray`. The data is 35 | # read from the file only once and cached. 36 | class ELFSection(MemoryRange): 37 | def __init__(self, elf, sect): 38 | self._elf = elf 39 | self._section = sect 40 | self._name = self._section.name 41 | self._data = None 42 | 43 | # Look up the corresponding memory region. 44 | start = self._section['sh_addr'] 45 | length = self._section['sh_size'] 46 | regions = self._elf._memory_map.get_intersecting_regions(start=start, length=length) 47 | region = regions[0] if len(regions) else None 48 | 49 | super(ELFSection, self).__init__(start=start, length=length, region=region) 50 | 51 | @property 52 | def name(self): 53 | return self._name 54 | 55 | @property 56 | def type(self): 57 | return self._section['sh_type'] 58 | 59 | @property 60 | def flags(self): 61 | return self._section['sh_flags'] 62 | 63 | @property 64 | def data(self): 65 | if self._data is None: 66 | self._data = bytearray(self._section.data()) 67 | return self._data 68 | 69 | @property 70 | def flags_description(self): 71 | flags = self.flags 72 | flagsDesc = "" 73 | if flags & SH_FLAGS.SHF_WRITE: 74 | flagsDesc += "WRITE|" 75 | if flags & SH_FLAGS.SHF_ALLOC: 76 | flagsDesc += "ALLOC|" 77 | if flags & SH_FLAGS.SHF_EXECINSTR: 78 | flagsDesc += "EXECINSTR" 79 | if flagsDesc[-1] == '|': 80 | flagsDesc = flagsDesc[:-1] 81 | return flagsDesc 82 | 83 | def __repr__(self): 84 | return "".format( 85 | id(self), self.name, self.type, self.flags_description, hex(self.start), hex(self.length)) 86 | 87 | ## 88 | # @brief An ELF binary executable file. 89 | # 90 | # Examines the ELF and provides several lists of useful data: section objects, and both used 91 | # and unused ranges of memory. 92 | # 93 | # An ELFSection object is created for each of the sections of the file that are loadable code or 94 | # data, or otherwise occupy memory. These are normally the .text, .rodata, .data, and .bss 95 | # sections. More specifically, the list of sections contains any section with a type of 96 | # `SHT_PROGBITS` or `SHT_NOBITS`. Also, at least one of the `SHF_WRITE`, `SHF_ALLOC`, or 97 | # `SHF_EXECINSTR` flags must be set. 98 | # 99 | # The set of sections is compared with the target's memory map to produce a lists of the used 100 | # (occupied) and unused (unoccupied) ranges of memory. Note that if the executable uses ranges 101 | # of memory not mapped with a section of the ELF file, those ranges will not be considered in 102 | # the used/unused lists. Also, only ranges completely contained within a region of the memory 103 | # map are considered. 104 | class ELFBinaryFile(object): 105 | def __init__(self, elf, memory_map=None): 106 | if isinstance(elf, six.string_types): 107 | self._file = open(elf, 'rb') 108 | self._owns_file = True 109 | else: 110 | self._file = elf 111 | self._owns_file = False 112 | self._elf = ELFFile(self._file) 113 | self._memory_map = memory_map or MemoryMap() 114 | 115 | self._symbol_decoder = None 116 | self._address_decoder = None 117 | 118 | self._extract_sections() 119 | self._compute_regions() 120 | 121 | ## @brief Close the ELF file if it is owned by this instance. 122 | def __del__(self): 123 | if self._owns_file: 124 | self.close() 125 | 126 | def _extract_sections(self): 127 | # Get list of interesting sections. 128 | self._sections = [] 129 | sections = self._elf.iter_sections() 130 | for s in sections: 131 | # Skip sections not of these types. 132 | if s['sh_type'] not in ('SHT_PROGBITS', 'SHT_NOBITS'): 133 | continue 134 | 135 | # Skip sections that don't have one of these flags set. 136 | if s['sh_flags'] & (SH_FLAGS.SHF_WRITE | SH_FLAGS.SHF_ALLOC | SH_FLAGS.SHF_EXECINSTR) == 0: 137 | continue 138 | 139 | self._sections.append(ELFSection(self, s)) 140 | self._sections.sort(key=lambda x: x.start) 141 | 142 | def _dump_sections(self): 143 | for s in self._sections: 144 | print("{0:<20} {1:<25} {2:<10} {3:<10}".format( 145 | s.name, s.flags_description, hex(s.start), hex(s.length))) 146 | 147 | def _compute_regions(self): 148 | used = [] 149 | unused = [] 150 | for region in self._memory_map: 151 | current = region.start 152 | for sect in self._sections: 153 | start = sect.start 154 | length = sect.length 155 | 156 | # Skip if this section isn't within this memory region. 157 | if not region.contains_range(start, length=length): 158 | continue 159 | 160 | # Add this section as used. 161 | used.append(MemoryRange(start=start, length=length, region=region)) 162 | 163 | # Add unused segment. 164 | if start > current: 165 | unused.append(MemoryRange(start=current, length=(start - current), region=region)) 166 | 167 | current = start + length 168 | 169 | # Add a final unused segment of the region. 170 | if region.end > current: 171 | unused.append(MemoryRange(start=current, end=region.end, region=region)) 172 | self._used = used 173 | self._unused = unused 174 | 175 | def close(self): 176 | self._file.close() 177 | self._owns_file = False 178 | 179 | def read(self, addr, size): 180 | """! @brief Read program data from the elf file. 181 | 182 | @param addr Physical address (load address) to read from. 183 | @param size Number of bytes to read. 184 | @return Requested data or None if address is unmapped. 185 | """ 186 | for segment in self._elf.iter_segments(): 187 | seg_addr = segment["p_paddr"] 188 | seg_size = min(segment["p_memsz"], segment["p_filesz"]) 189 | if addr >= seg_addr + seg_size: 190 | continue 191 | if addr + size <= seg_addr: 192 | continue 193 | # There is at least some overlap 194 | 195 | if addr >= seg_addr and addr + size <= seg_addr + seg_size: 196 | # Region is fully contained 197 | data = segment.data() 198 | start = addr - seg_addr 199 | return data[start:start + size] 200 | 201 | ## 202 | # @brief Access the list of sections in the ELF file. 203 | # @return A list of ELFSection objects sorted by start address. 204 | @property 205 | def sections(self): 206 | return self._sections 207 | 208 | ## 209 | # @brief Access the list of used ranges of memory in the ELF file. 210 | # @return A list of MemoryRange objects sorted by start address. 211 | @property 212 | def used_ranges(self): 213 | return self._used 214 | 215 | ## 216 | # @brief Access the list of unused ranges of memory in the ELF file. 217 | # @return A list of MemoryRange objects sorted by start address. 218 | @property 219 | def unused_ranges(self): 220 | return self._unused 221 | 222 | @property 223 | def symbol_decoder(self): 224 | if self._symbol_decoder is None: 225 | self._symbol_decoder = ElfSymbolDecoder(self._elf) 226 | return self._symbol_decoder 227 | 228 | @property 229 | def address_decoder(self): 230 | if self._address_decoder is None: 231 | self._address_decoder = DwarfAddressDecoder(self._elf) 232 | return self._address_decoder 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /pyocd/debug/elf/flash_reader.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2016 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from ..context import DebugContext 18 | from ...utility import conversion 19 | import logging 20 | from intervaltree import (Interval, IntervalTree) 21 | 22 | ## @brief Reads flash memory regions from an ELF file instead of the target. 23 | class FlashReaderContext(DebugContext): 24 | def __init__(self, parentContext, elf): 25 | super(FlashReaderContext, self).__init__(parentContext.core) 26 | self._parent = parentContext 27 | self._elf = elf 28 | self._log = logging.getLogger('flashreadercontext') 29 | 30 | self._build_regions() 31 | 32 | def _build_regions(self): 33 | self._tree = IntervalTree() 34 | for sect in [s for s in self._elf.sections if (s.region and s.region.is_flash)]: 35 | start = sect.start 36 | length = sect.length 37 | sect.data # Go ahead and read the data from the file. 38 | self._tree.addi(start, start + length, sect) 39 | self._log.debug("created flash section [%x:%x] for section %s", start, start + length, sect.name) 40 | 41 | def read_memory(self, addr, transfer_size=32, now=True): 42 | length = transfer_size // 8 43 | matches = self._tree.overlap(addr, addr + length) 44 | # Must match only one interval (ELF section). 45 | if len(matches) != 1: 46 | return self._parent.read_memory(addr, transfer_size, now) 47 | section = matches.pop().data 48 | addr -= section.start 49 | 50 | def read_memory_cb(): 51 | self._log.debug("read flash data [%x:%x] from section %s", section.start + addr, section.start + addr + length, section.name) 52 | data = section.data[addr:addr + length] 53 | if transfer_size == 8: 54 | return data[0] 55 | elif transfer_size == 16: 56 | return conversion.byte_list_to_u16le_list(data)[0] 57 | elif transfer_size == 32: 58 | return conversion.byte_list_to_u32le_list(data)[0] 59 | else: 60 | raise ValueError("invalid transfer_size (%d)" % transfer_size) 61 | 62 | if now: 63 | return read_memory_cb() 64 | else: 65 | return read_memory_cb 66 | 67 | def read_memory_block8(self, addr, size): 68 | matches = self._tree.overlap(addr, addr + size) 69 | # Must match only one interval (ELF section). 70 | if len(matches) != 1: 71 | return self._parent.read_memory_block8(addr, size) 72 | section = matches.pop().data 73 | addr -= section.start 74 | data = section.data[addr:addr + size] 75 | self._log.debug("read flash data [%x:%x]", section.start + addr, section.start + addr + size) 76 | return list(data) 77 | 78 | def read_memory_block32(self, addr, size): 79 | return conversion.byte_list_to_u32le_list(self.read_memory_block8(addr, size)) 80 | 81 | def write_memory(self, addr, value, transfer_size=32): 82 | return self._parent.write_memory(addr, value, transfer_size) 83 | 84 | def write_memory_block8(self, addr, value): 85 | return self._parent.write_memory_block8(addr, value) 86 | 87 | def write_memory_block32(self, addr, data): 88 | return self._parent.write_memory_block32(addr, data) 89 | 90 | -------------------------------------------------------------------------------- /pyocd/debug/elf/symbols.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2017 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from ..symbols import SymbolProvider 18 | 19 | ## @brief Get symbol information from an ELF file. 20 | class ELFSymbolProvider(SymbolProvider): 21 | def __init__(self, elf): 22 | self._symbols = elf.symbol_decoder 23 | 24 | def get_symbol_value(self, name): 25 | sym = self._symbols.get_symbol_for_name(name) 26 | if sym is not None: 27 | return sym.address 28 | else: 29 | return None 30 | 31 | -------------------------------------------------------------------------------- /pyocd/debug/svd.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2015 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import threading 18 | import logging 19 | # Make cmsis_svd optional. 20 | try: 21 | from cmsis_svd.parser import SVDParser 22 | IS_CMSIS_SVD_AVAILABLE = True 23 | except ImportError: 24 | IS_CMSIS_SVD_AVAILABLE = False 25 | 26 | class SVDFile(object): 27 | def __init__(self, filename=None, vendor=None, is_local=False): 28 | self.filename = filename 29 | self.vendor = vendor 30 | self.is_local = is_local 31 | self.device = None 32 | 33 | def load(self): 34 | if not IS_CMSIS_SVD_AVAILABLE: 35 | return 36 | 37 | if self.is_local: 38 | self.device = SVDParser.for_xml_file(self.filename).get_device() 39 | else: 40 | self.device = SVDParser.for_packaged_svd(self.vendor, self.filename).get_device() 41 | 42 | ## @brief Thread to read an SVD file in the background. 43 | class SVDLoader(threading.Thread): 44 | def __init__(self, svdFile, completionCallback): 45 | super(SVDLoader, self).__init__(name='load-svd') 46 | self.daemon = True 47 | self._svd_location = svdFile 48 | self._svd_device = None 49 | self._callback = completionCallback 50 | 51 | @property 52 | def device(self): 53 | if not self._svd_device: 54 | self.join() 55 | return self._svd_device 56 | 57 | def load(self): 58 | if not self._svd_device and self._svd_location: 59 | self.start() 60 | 61 | def run(self): 62 | try: 63 | self._svd_location.load() 64 | self._svd_device = self._svd_location.device 65 | if self._callback: 66 | self._callback(self._svd_device) 67 | except IOError: 68 | logging.warning("Failed to load SVD file %s", self._svd_location.filename) 69 | -------------------------------------------------------------------------------- /pyocd/debug/symbols.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2016 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | ## @brief Abstract class for getting information about symbols in the target program. 18 | class SymbolProvider(object): 19 | def get_symbol_value(self, name): 20 | raise NotImplementedError() 21 | 22 | -------------------------------------------------------------------------------- /pyocd/probe/__init__.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2018 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | -------------------------------------------------------------------------------- /pyocd/probe/aggregator.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2018 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from .cmsis_dap_probe import CMSISDAPProbe 18 | 19 | PROBE_CLASSES = [ 20 | CMSISDAPProbe, 21 | ] 22 | 23 | ## @brief Simple class to enable collecting probes of all supported probe types. 24 | class DebugProbeAggregator(object): 25 | 26 | @staticmethod 27 | def get_all_connected_probes(unique_id=None): 28 | probes = [] 29 | for cls in PROBE_CLASSES: 30 | probes += cls.get_all_connected_probes() 31 | 32 | # Filter by unique ID. 33 | if unique_id is not None: 34 | unique_id = unique_id.lower() 35 | probes = [probe for probe in probes if (unique_id in probe.unique_id.lower())] 36 | 37 | return probes 38 | 39 | @classmethod 40 | def get_probe_with_id(cls, unique_id): 41 | for cls in PROBE_CLASSES: 42 | probe = cls.get_probe_with_id(unique_id) 43 | if probe is not None: 44 | return probe 45 | else: 46 | return None 47 | 48 | 49 | -------------------------------------------------------------------------------- /pyocd/probe/debug_probe.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2018 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from enum import Enum 18 | 19 | ## @brief Abstract debug probe class. 20 | class DebugProbe(object): 21 | 22 | ## @brief Debug wire protocols. 23 | class Protocol(Enum): 24 | DEFAULT = 0 25 | SWD = 1 26 | JTAG = 2 27 | 28 | @classmethod 29 | def get_all_connected_probes(cls): 30 | raise NotImplementedError() 31 | 32 | @classmethod 33 | def get_probe_with_id(cls, unique_id): 34 | raise NotImplementedError() 35 | 36 | @property 37 | def description(self): 38 | return self.vendor_name + " " + self.product_name 39 | 40 | @property 41 | def vendor_name(self): 42 | raise NotImplementedError() 43 | 44 | @property 45 | def product_name(self): 46 | raise NotImplementedError() 47 | 48 | @property 49 | def supported_wire_protocols(self): 50 | raise NotImplementedError() 51 | 52 | ## @brief The unique ID of this device. 53 | # 54 | # This property will be valid before open() is called. This value can be passed to 55 | # get_probe_with_id(). 56 | @property 57 | def unique_id(self): 58 | raise NotImplementedError() 59 | 60 | ## @brief Currently selected wire protocol. 61 | # 62 | # If the probe is not connected, i.e., connect() has not been called, then this 63 | # property will be None. 64 | @property 65 | def wire_protocol(self): 66 | raise NotImplementedError() 67 | 68 | @property 69 | def is_open(self): 70 | raise NotImplementedError() 71 | 72 | ## @brief Create a board instance representing the board of which the probe is a component. 73 | # 74 | # If the probe is part of a board, then this property will be a Board instance that represents 75 | # the associated board. Usually, for an on-board debug probe, this would be the Board that 76 | # the probe physically is part of. If the probe does not have an associated board, then this 77 | # method returns None. 78 | # 79 | # @param session Session to pass to the board upon construction. 80 | def create_associated_board(self, session): 81 | return None 82 | 83 | def open(self): 84 | raise NotImplementedError() 85 | 86 | def close(self): 87 | raise NotImplementedError() 88 | 89 | # ------------------------------------------- # 90 | # Target control functions 91 | # ------------------------------------------- # 92 | def connect(self, protocol=None): 93 | """Initailize DAP IO pins for JTAG or SWD""" 94 | raise NotImplementedError() 95 | 96 | def disconnect(self): 97 | """Deinitialize the DAP I/O pins""" 98 | raise NotImplementedError() 99 | 100 | def set_clock(self, frequency): 101 | """Set the frequency for JTAG and SWD in Hz 102 | 103 | This function is safe to call before connect is called. 104 | """ 105 | raise NotImplementedError() 106 | 107 | def reset(self): 108 | """Reset the target""" 109 | raise NotImplementedError() 110 | 111 | def assert_reset(self, asserted): 112 | """Assert or de-assert target reset line""" 113 | raise NotImplementedError() 114 | 115 | def is_reset_asserted(self): 116 | """Returns True if the target reset line is asserted or False if de-asserted""" 117 | raise NotImplementedError() 118 | 119 | def flush(self): 120 | """Write out all unsent commands""" 121 | raise NotImplementedError() 122 | 123 | def read_dp(self, addr, now=True): 124 | raise NotImplementedError() 125 | 126 | def write_dp(self, addr, data): 127 | raise NotImplementedError() 128 | 129 | def read_ap(self, addr, now=True): 130 | raise NotImplementedError() 131 | 132 | def write_ap(self, addr, data): 133 | raise NotImplementedError() 134 | 135 | def read_ap_multiple(self, addr, count=1, now=True): 136 | raise NotImplementedError() 137 | 138 | def write_ap_multiple(self, addr, values): 139 | raise NotImplementedError() 140 | 141 | def get_memory_interface_for_ap(self, apsel): 142 | return None 143 | 144 | def has_swo(self): 145 | """! @brief Returns bool indicating whether the link supports SWO.""" 146 | raise NotImplementedError() 147 | 148 | def swo_start(self, baudrate): 149 | """! @brief Start receiving SWO data at the given baudrate.""" 150 | raise NotImplementedError() 151 | 152 | def swo_stop(self): 153 | """! @brief Stop receiving SWO data.""" 154 | raise NotImplementedError() 155 | 156 | def swo_read(self): 157 | """! @brief Read buffered SWO data from the target. 158 | 159 | @eturn Bytearray of the received data. 160 | """ 161 | raise NotImplementedError() 162 | 163 | def __repr__(self): 164 | return "<{}@{:x} {}>".format(self.__class__.__name__, id(self), self.description) 165 | 166 | 167 | -------------------------------------------------------------------------------- /pyocd/probe/pydapaccess/__init__.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2013 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | from .dap_access_api import DAPAccessIntf 17 | from .dap_access_cmsis_dap import DAPAccessCMSISDAP 18 | 19 | # alias DAPAccessCMSISDAP as main DAPAccess class 20 | DAPAccess = DAPAccessCMSISDAP 21 | -------------------------------------------------------------------------------- /pyocd/probe/pydapaccess/dap_access_api.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2013,2018 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | from enum import Enum 19 | 20 | 21 | class DAPAccessIntf(object): 22 | 23 | class PORT(Enum): 24 | """Physical access ports""" 25 | DEFAULT = 0 26 | SWD = 1 27 | JTAG = 2 28 | 29 | class REG(Enum): 30 | """Register for DAP access functions""" 31 | DP_0x0 = 0 32 | DP_0x4 = 1 33 | DP_0x8 = 2 34 | DP_0xC = 3 35 | AP_0x0 = 4 36 | AP_0x4 = 5 37 | AP_0x8 = 6 38 | AP_0xC = 7 39 | 40 | class ID(Enum): 41 | """Information ID used for call to identify""" 42 | VENDOR = 1 43 | PRODUCT = 2 44 | SER_NUM = 3 45 | FW_VER = 4 46 | DEVICE_VENDOR = 5 47 | DEVICE_NAME = 6 48 | CAPABILITIES = 0xf0 49 | SWO_BUFFER_SIZE = 0xfd 50 | MAX_PACKET_COUNT = 0xfe 51 | MAX_PACKET_SIZE = 0xff 52 | 53 | class Error(Exception): 54 | """Parent of all error DAPAccess can raise""" 55 | pass 56 | 57 | class DeviceError(Error): 58 | """Error communicating with device""" 59 | pass 60 | 61 | class CommandError(DeviceError): 62 | """The host debugger reported failure for the given command""" 63 | pass 64 | 65 | class TransferError(CommandError): 66 | """Error ocurred with a transfer over SWD or JTAG""" 67 | pass 68 | 69 | class TransferTimeoutError(TransferError): 70 | """A SWD or JTAG timeout occurred""" 71 | pass 72 | 73 | class TransferFaultError(TransferError): 74 | """A SWD Fault occurred""" 75 | def __init__(self, faultAddress=None): 76 | super(DAPAccessIntf.TransferFaultError, self).__init__(faultAddress) 77 | self._address = faultAddress 78 | 79 | @property 80 | def fault_address(self): 81 | return self._address 82 | 83 | @fault_address.setter 84 | def fault_address(self, addr): 85 | self._address = addr 86 | 87 | def __str__(self): 88 | desc = "SWD/JTAG Transfer Fault" 89 | if self._address is not None: 90 | desc += " @ 0x%08x" % self._address 91 | return desc 92 | 93 | class TransferProtocolError(TransferError): 94 | """A SWD protocol error occurred""" 95 | pass 96 | 97 | @staticmethod 98 | def get_connected_devices(): 99 | """Return a list of DAPAccess devices""" 100 | raise NotImplementedError() 101 | 102 | @staticmethod 103 | def get_device(device_id): 104 | """Return the DAPAccess device with the give ID""" 105 | raise NotImplementedError() 106 | 107 | @staticmethod 108 | def set_args(arg_list): 109 | """Set arguments to configure behavior""" 110 | raise NotImplementedError() 111 | 112 | @property 113 | def vendor_name(self): 114 | raise NotImplementedError() 115 | 116 | @property 117 | def product_name(self): 118 | raise NotImplementedError() 119 | 120 | @property 121 | def vidpid(self): 122 | """! @brief A tuple of USB VID and PID, in that order.""" 123 | raise NotImplementedError() 124 | 125 | # ------------------------------------------- # 126 | # Host control functions 127 | # ------------------------------------------- # 128 | def open(self): 129 | """Open device and lock it for exclusive access""" 130 | raise NotImplementedError() 131 | 132 | def close(self): 133 | """Close device and unlock it""" 134 | raise NotImplementedError() 135 | 136 | def get_unique_id(self): 137 | """Get the unique ID of this device which can be used in get_device 138 | 139 | This function is safe to call before open is called. 140 | """ 141 | raise NotImplementedError() 142 | 143 | def identify(self, item): 144 | """Return the requested information for this device""" 145 | raise NotImplementedError() 146 | 147 | # ------------------------------------------- # 148 | # Target control functions 149 | # ------------------------------------------- # 150 | def connect(self, port=None): 151 | """Initailize DAP IO pins for JTAG or SWD""" 152 | raise NotImplementedError() 153 | 154 | def swj_sequence(self): 155 | """Send seqeunce to activate JTAG or SWD on the target""" 156 | raise NotImplementedError() 157 | 158 | def disconnect(self): 159 | """Deinitialize the DAP I/O pins""" 160 | raise NotImplementedError() 161 | 162 | def set_clock(self, frequency): 163 | """Set the frequency for JTAG and SWD in Hz 164 | 165 | This function is safe to call before connect is called. 166 | """ 167 | raise NotImplementedError() 168 | 169 | def get_swj_mode(self): 170 | """Return the current port type - SWD or JTAG""" 171 | raise NotImplementedError() 172 | 173 | def reset(self): 174 | """Reset the target""" 175 | raise NotImplementedError() 176 | 177 | def assert_reset(self, asserted): 178 | """Assert or de-assert target reset line""" 179 | raise NotImplementedError() 180 | 181 | def is_reset_asserted(self): 182 | """Returns True if the target reset line is asserted or False if de-asserted""" 183 | raise NotImplementedError() 184 | 185 | def set_deferred_transfer(self, enable): 186 | """Allow reads and writes to be buffered for increased speed""" 187 | raise NotImplementedError() 188 | 189 | def flush(self): 190 | """Write out all unsent commands""" 191 | raise NotImplementedError() 192 | 193 | def vendor(self, index, data=None): 194 | """Send a vendor specific command""" 195 | raise NotImplementedError() 196 | 197 | def has_swo(self): 198 | """Returns bool indicating whether the link supports SWO.""" 199 | raise NotImplementedError() 200 | 201 | def swo_configure(self, enabled, rate): 202 | """Enable or disable SWO and set the baud rate.""" 203 | raise NotImplementedError() 204 | 205 | def swo_control(self, start): 206 | """Pass True to start recording SWO data, False to stop.""" 207 | raise NotImplementedError() 208 | 209 | def get_swo_status(self): 210 | """Returns a 2-tuple with a status mask at index 0, and the number of buffered 211 | SWO data bytes at index 1.""" 212 | raise NotImplementedError() 213 | 214 | def swo_read(self, count=None): 215 | """Read buffered SWO data from the target. The count parameter is optional. If 216 | provided, it is the number of bytes to read, which must be less than the packet size. 217 | If count is not provided, the packet size will be used instead. 218 | 219 | Returns a 3-tuple containing the status mask at index 0, the number of buffered 220 | SWO data bytes at index 1, and a list of the received data bytes at index 2.""" 221 | raise NotImplementedError() 222 | 223 | # ------------------------------------------- # 224 | # DAP Access functions 225 | # ------------------------------------------- # 226 | def write_reg(self, reg_id, value, dap_index=0): 227 | """Write a single word to a DP or AP register""" 228 | raise NotImplementedError() 229 | 230 | def read_reg(self, reg_id, dap_index=0, now=True): 231 | """Read a single word to a DP or AP register""" 232 | raise NotImplementedError() 233 | 234 | def reg_write_repeat(self, num_repeats, reg_id, data_array, dap_index=0): 235 | """Write one or more words to the same DP or AP register""" 236 | raise NotImplementedError() 237 | 238 | def reg_read_repeat(self, num_repeats, reg_id, dap_index=0, now=True): 239 | """Read one or more words from the same DP or AP register""" 240 | raise NotImplementedError() 241 | -------------------------------------------------------------------------------- /pyocd/probe/pydapaccess/dap_settings.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2013 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | class DAPSettings(): 18 | 19 | limit_packets = False 20 | -------------------------------------------------------------------------------- /pyocd/probe/pydapaccess/interface/__init__.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2013 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import os 18 | import logging 19 | from .hidapi_backend import HidApiUSB 20 | from .pyusb_backend import PyUSB 21 | from .pyusb_v2_backend import PyUSBv2 22 | from .pywinusb_backend import PyWinUSB 23 | 24 | INTERFACE = { 25 | 'hidapiusb': HidApiUSB, 26 | 'pyusb': PyUSB, 27 | 'pyusb_v2': PyUSBv2, 28 | 'pywinusb': PyWinUSB, 29 | } 30 | 31 | # Allow user to override backend with an environment variable. 32 | USB_BACKEND = os.getenv('PYOCD_USB_BACKEND', "") # pylint: disable=invalid-name 33 | 34 | # Check validity of backend env var. 35 | if USB_BACKEND and ((USB_BACKEND not in INTERFACE) or (not INTERFACE[USB_BACKEND].isAvailable)): 36 | logging.error("Invalid USB backend specified in PYOCD_USB_BACKEND: " + USB_BACKEND) 37 | USB_BACKEND = "" 38 | 39 | # Select backend based on OS and availability. 40 | if not USB_BACKEND: 41 | if os.name == "nt": 42 | # Prefer hidapi over pyWinUSB for Windows, since pyWinUSB has known bug(s) 43 | if HidApiUSB.isAvailable: 44 | USB_BACKEND = "hidapiusb" 45 | elif PyWinUSB.isAvailable: 46 | USB_BACKEND = "pywinusb" 47 | else: 48 | raise Exception("No USB backend found") 49 | elif os.name == "posix": 50 | # Select hidapi for OS X and pyUSB for Linux. 51 | if os.uname()[0] == 'Darwin': 52 | USB_BACKEND = "hidapiusb" 53 | else: 54 | USB_BACKEND = "pyusb" 55 | else: 56 | raise Exception("No USB backend found") 57 | 58 | USB_BACKEND_V2 = "pyusb_v2" 59 | -------------------------------------------------------------------------------- /pyocd/probe/pydapaccess/interface/common.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # USB class codes. 18 | USB_CLASS_COMPOSITE = 0x00 19 | USB_CLASS_COMMUNICATIONS = 0x02 20 | USB_CLASS_MISCELLANEOUS = 0xef 21 | 22 | CMSIS_DAP_USB_CLASSES = [ 23 | USB_CLASS_COMPOSITE, 24 | USB_CLASS_MISCELLANEOUS, 25 | ] 26 | 27 | CMSIS_DAP_HID_USAGE_PAGE = 0xff00 28 | 29 | # Various known USB VID/PID values. 30 | ARM_DAPLINK_ID = (0x0d28, 0x0204) 31 | KEIL_ULINKPLUS_ID = (0xc251, 0x2750) 32 | NXP_LPCLINK2_ID = (0x1fc9, 0x0090) 33 | 34 | ## List of VID/PID pairs for known CMSIS-DAP USB devices. 35 | KNOWN_CMSIS_DAP_IDS = [ 36 | ARM_DAPLINK_ID, 37 | KEIL_ULINKPLUS_ID, 38 | NXP_LPCLINK2_ID, 39 | ] 40 | 41 | def is_known_cmsis_dap_vid_pid(vid, pid): 42 | """! @brief Test whether a VID/PID pair belong to a known CMSIS-DAP device.""" 43 | return (vid, pid) in KNOWN_CMSIS_DAP_IDS 44 | 45 | def filter_device_by_class(vid, pid, device_class): 46 | """! @brief Test whether the device should be ignored by comparing bDeviceClass. 47 | 48 | This function checks the device's bDeviceClass to determine whether the it is likely to be 49 | a CMSIS-DAP device. It uses the vid and pid for device-specific quirks. 50 | 51 | @retval True Skip the device. 52 | @retval False The device is valid. 53 | """ 54 | # Check valid classes for CMSIS-DAP firmware. 55 | if device_class in CMSIS_DAP_USB_CLASSES: 56 | return False 57 | # Old "Mbed CMSIS-DAP" firmware has an incorrect bDeviceClass. 58 | if ((vid, pid) == ARM_DAPLINK_ID) and (device_class == USB_CLASS_COMMUNICATIONS): 59 | return False 60 | # Any other class indicates the device is not CMSIS-DAP. 61 | return True 62 | 63 | def filter_device_by_usage_page(vid, pid, usage_page): 64 | """! @brief Test whether the device should be ignored by comparing the HID usage page. 65 | 66 | This function performs device-specific tests to determine whether the device is a CMSIS-DAP 67 | interface. The only current test is for the NXP LPC-Link2, which has extra HID interfaces with 68 | usage pages other than 0xff00. No generic tests are done regardless of VID/PID, because it is 69 | not clear whether all CMSIS-DAP devices have the usage page set to the same value. 70 | 71 | @retval True Skip the device. 72 | @retval False The device is valid. 73 | """ 74 | return ((vid, pid) == NXP_LPCLINK2_ID) \ 75 | and (usage_page != CMSIS_DAP_HID_USAGE_PAGE) 76 | 77 | -------------------------------------------------------------------------------- /pyocd/probe/pydapaccess/interface/hidapi_backend.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2013 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from .interface import Interface 18 | from .common import filter_device_by_usage_page 19 | from ..dap_access_api import DAPAccessIntf 20 | from ....utility.compatibility import to_str_safe 21 | import logging 22 | import os 23 | import six 24 | 25 | log = logging.getLogger('hidapi') 26 | 27 | try: 28 | import hid 29 | except: 30 | if os.name == "posix" and os.uname()[0] == 'Darwin': 31 | log.error("cython-hidapi is required on a Mac OS X Machine") 32 | IS_AVAILABLE = False 33 | else: 34 | IS_AVAILABLE = True 35 | 36 | class HidApiUSB(Interface): 37 | """ 38 | This class provides basic functions to access 39 | a USB HID device using cython-hidapi: 40 | - write/read an endpoint 41 | """ 42 | 43 | isAvailable = IS_AVAILABLE 44 | 45 | def __init__(self): 46 | super(HidApiUSB, self).__init__() 47 | # Vendor page and usage_id = 2 48 | self.device = None 49 | 50 | def open(self): 51 | try: 52 | self.device.open_path(self.device_info['path']) 53 | except IOError as exc: 54 | raise six.raise_from(DAPAccessIntf.DeviceError("Unable to open device: " + str(exc)), exc) 55 | 56 | @staticmethod 57 | def get_all_connected_interfaces(): 58 | """ 59 | returns all the connected devices which matches HidApiUSB.vid/HidApiUSB.pid. 60 | returns an array of HidApiUSB (Interface) objects 61 | """ 62 | 63 | devices = hid.enumerate() 64 | 65 | if not devices: 66 | log.debug("No Mbed device connected") 67 | return [] 68 | 69 | boards = [] 70 | 71 | for deviceInfo in devices: 72 | product_name = to_str_safe(deviceInfo['product_string']) 73 | if (product_name.find("CMSIS-DAP") < 0): 74 | # Skip non cmsis-dap devices 75 | continue 76 | 77 | vid = deviceInfo['vendor_id'] 78 | pid = deviceInfo['product_id'] 79 | 80 | # Perform device-specific filtering. 81 | if filter_device_by_usage_page(vid, pid, deviceInfo['usage_page']): 82 | continue 83 | 84 | try: 85 | dev = hid.device(vendor_id=vid, product_id=pid, path=deviceInfo['path']) 86 | except IOError as exc: 87 | log.debug("Failed to open USB device: %s", exc) 88 | continue 89 | 90 | # Create the USB interface object for this device. 91 | new_board = HidApiUSB() 92 | new_board.vendor_name = deviceInfo['manufacturer_string'] 93 | new_board.product_name = deviceInfo['product_string'] 94 | new_board.serial_number = deviceInfo['serial_number'] 95 | new_board.vid = vid 96 | new_board.pid = pid 97 | new_board.device_info = deviceInfo 98 | new_board.device = dev 99 | boards.append(new_board) 100 | 101 | return boards 102 | 103 | def write(self, data): 104 | """ 105 | write data on the OUT endpoint associated to the HID interface 106 | """ 107 | for _ in range(self.packet_size - len(data)): 108 | data.append(0) 109 | #logging.debug("send: %s", data) 110 | self.device.write([0] + data) 111 | return 112 | 113 | 114 | def read(self, timeout=-1): 115 | """ 116 | read data on the IN endpoint associated to the HID interface 117 | """ 118 | return self.device.read(self.packet_size) 119 | 120 | def get_serial_number(self): 121 | return self.serial_number 122 | 123 | def close(self): 124 | """ 125 | close the interface 126 | """ 127 | log.debug("closing interface") 128 | self.device.close() 129 | 130 | def set_packet_count(self, count): 131 | # No interface level restrictions on count 132 | self.packet_count = count 133 | 134 | def set_packet_size(self, size): 135 | self.packet_size = size 136 | -------------------------------------------------------------------------------- /pyocd/probe/pydapaccess/interface/interface.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2013,2018 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | class Interface(object): 19 | 20 | def __init__(self): 21 | self.vid = 0 22 | self.pid = 0 23 | self.vendor_name = "" 24 | self.product_name = "" 25 | self.packet_count = 1 26 | self.packet_size = 64 27 | 28 | @property 29 | def has_swo_ep(self): 30 | return False 31 | 32 | def open(self): 33 | return 34 | 35 | def write(self, data): 36 | return 37 | 38 | def read(self, size=-1, timeout=-1): 39 | return 40 | 41 | def get_info(self): 42 | return self.vendor_name + " " + \ 43 | self.product_name + " (" + \ 44 | str(hex(self.vid)) + ", " + \ 45 | str(hex(self.pid)) + ")" 46 | 47 | def set_packet_count(self, count): 48 | # Unless overridden the packet count cannot be changed 49 | return 50 | 51 | def get_packet_count(self): 52 | return self.packet_count 53 | 54 | def close(self): 55 | return 56 | -------------------------------------------------------------------------------- /pyocd/probe/pydapaccess/interface/pyusb_backend.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2013 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from .interface import Interface 18 | from .common import (filter_device_by_class, is_known_cmsis_dap_vid_pid) 19 | from ..dap_access_api import DAPAccessIntf 20 | import logging 21 | import os 22 | import threading 23 | import six 24 | from time import sleep 25 | import platform 26 | import errno 27 | 28 | log = logging.getLogger('pyusb') 29 | 30 | try: 31 | import usb.core 32 | import usb.util 33 | except: 34 | if os.name == "posix" and not os.uname()[0] == 'Darwin': 35 | log.error("PyUSB is required on a Linux Machine") 36 | IS_AVAILABLE = False 37 | else: 38 | IS_AVAILABLE = True 39 | 40 | class PyUSB(Interface): 41 | """ 42 | This class provides basic functions to access 43 | a USB HID device using pyusb: 44 | - write/read an endpoint 45 | """ 46 | 47 | isAvailable = IS_AVAILABLE 48 | 49 | def __init__(self): 50 | super(PyUSB, self).__init__() 51 | self.ep_out = None 52 | self.ep_in = None 53 | self.dev = None 54 | self.intf_number = None 55 | self.serial_number = None 56 | self.kernel_driver_was_attached = False 57 | self.closed = True 58 | self.thread = None 59 | self.rcv_data = [] 60 | self.read_sem = threading.Semaphore(0) 61 | self.packet_size = 64 62 | 63 | def open(self): 64 | assert self.closed is True 65 | 66 | # Get device handle 67 | dev = usb.core.find(custom_match=FindDap(self.serial_number)) 68 | if dev is None: 69 | raise DAPAccessIntf.DeviceError("Device %s not found" % 70 | self.serial_number) 71 | 72 | # get active config 73 | config = dev.get_active_configuration() 74 | 75 | # Get hid interface 76 | interface = None 77 | interface_number = None 78 | for interface in config: 79 | if interface.bInterfaceClass == 0x03: 80 | interface_number = interface.bInterfaceNumber 81 | break 82 | if interface_number is None or interface is None: 83 | raise DAPAccessIntf.DeviceError("Device %s has no hid interface" % 84 | self.serial_number) 85 | 86 | # Find endpoints 87 | ep_in, ep_out = None, None 88 | for endpoint in interface: 89 | if endpoint.bEndpointAddress & 0x80: 90 | ep_in = endpoint 91 | else: 92 | ep_out = endpoint 93 | 94 | # If there is no EP for OUT then we can use CTRL EP. 95 | # The IN EP is required 96 | if not ep_in: 97 | raise DAPAccessIntf.DeviceError("Unable to open device -" 98 | " no endpoints") 99 | 100 | # Detach kernel driver 101 | kernel_driver_was_attached = False 102 | try: 103 | if dev.is_kernel_driver_active(interface_number): 104 | dev.detach_kernel_driver(interface_number) 105 | kernel_driver_was_attached = True 106 | except NotImplementedError as e: 107 | # Some implementations don't don't have kernel attach/detach 108 | log.debug('Exception detaching kernel driver: %s' % 109 | str(e)) 110 | 111 | # Explicitly claim the interface 112 | try: 113 | usb.util.claim_interface(dev, interface_number) 114 | except usb.core.USBError as exc: 115 | raise six.raise_from(DAPAccessIntf.DeviceError("Unable to open device"), exc) 116 | 117 | # Update all class variables if we made it here 118 | self.ep_out = ep_out 119 | self.ep_in = ep_in 120 | self.dev = dev 121 | self.intf_number = interface_number 122 | self.kernel_driver_was_attached = kernel_driver_was_attached 123 | 124 | # Start RX thread as the last step 125 | self.closed = False 126 | self.start_rx() 127 | 128 | def start_rx(self): 129 | # Flush the RX buffers by reading until timeout exception 130 | try: 131 | while True: 132 | self.ep_in.read(self.ep_in.wMaxPacketSize, 1) 133 | except usb.core.USBError: 134 | # USB timeout expected 135 | pass 136 | 137 | # Start RX thread 138 | self.thread = threading.Thread(target=self.rx_task) 139 | self.thread.daemon = True 140 | self.thread.start() 141 | 142 | def rx_task(self): 143 | try: 144 | while not self.closed: 145 | self.read_sem.acquire() 146 | if not self.closed: 147 | self.rcv_data.append(self.ep_in.read(self.ep_in.wMaxPacketSize, 10 * 1000)) 148 | finally: 149 | # Set last element of rcv_data to None on exit 150 | self.rcv_data.append(None) 151 | 152 | @staticmethod 153 | def get_all_connected_interfaces(): 154 | """ 155 | returns all the connected devices which matches PyUSB.vid/PyUSB.pid. 156 | returns an array of PyUSB (Interface) objects 157 | """ 158 | # find all cmsis-dap devices 159 | all_devices = usb.core.find(find_all=True, custom_match=FindDap()) 160 | 161 | # iterate on all devices found 162 | boards = [] 163 | for board in all_devices: 164 | new_board = PyUSB() 165 | new_board.vid = board.idVendor 166 | new_board.pid = board.idProduct 167 | new_board.product_name = board.product 168 | new_board.vendor_name = board.manufacturer 169 | new_board.serial_number = board.serial_number 170 | boards.append(new_board) 171 | 172 | return boards 173 | 174 | def write(self, data): 175 | """ 176 | write data on the OUT endpoint associated to the HID interface 177 | """ 178 | 179 | report_size = self.packet_size 180 | if self.ep_out: 181 | report_size = self.ep_out.wMaxPacketSize 182 | 183 | for _ in range(report_size - len(data)): 184 | data.append(0) 185 | 186 | self.read_sem.release() 187 | 188 | if not self.ep_out: 189 | bmRequestType = 0x21 #Host to device request of type Class of Recipient Interface 190 | bmRequest = 0x09 #Set_REPORT (HID class-specific request for transferring data over EP0) 191 | wValue = 0x200 #Issuing an OUT report 192 | wIndex = self.intf_number #mBed Board interface number for HID 193 | self.dev.ctrl_transfer(bmRequestType, bmRequest, wValue, wIndex, data) 194 | return 195 | #raise ValueError('EP_OUT endpoint is NULL') 196 | 197 | self.ep_out.write(data) 198 | #logging.debug('sent: %s', data) 199 | return 200 | 201 | 202 | def read(self): 203 | """ 204 | read data on the IN endpoint associated to the HID interface 205 | """ 206 | while len(self.rcv_data) == 0: 207 | sleep(0) 208 | 209 | if self.rcv_data[0] is None: 210 | raise DAPAccessIntf.DeviceError("Device %s read thread exited" % 211 | self.serial_number) 212 | return self.rcv_data.pop(0) 213 | 214 | def set_packet_count(self, count): 215 | # No interface level restrictions on count 216 | self.packet_count = count 217 | 218 | def set_packet_size(self, size): 219 | self.packet_size = size 220 | 221 | def get_serial_number(self): 222 | return self.serial_number 223 | 224 | def close(self): 225 | """ 226 | close the interface 227 | """ 228 | assert self.closed is False 229 | 230 | log.debug("closing interface") 231 | self.closed = True 232 | self.read_sem.release() 233 | self.thread.join() 234 | assert self.rcv_data[-1] is None 235 | self.rcv_data = [] 236 | usb.util.release_interface(self.dev, self.intf_number) 237 | if self.kernel_driver_was_attached: 238 | try: 239 | self.dev.attach_kernel_driver(self.intf_number) 240 | except Exception as exception: 241 | log.warning('Exception attaching kernel driver: %s', 242 | str(exception)) 243 | usb.util.dispose_resources(self.dev) 244 | self.ep_out = None 245 | self.ep_in = None 246 | self.dev = None 247 | self.intf_number = None 248 | self.kernel_driver_was_attached = False 249 | self.thread = None 250 | 251 | 252 | class FindDap(object): 253 | """CMSIS-DAP match class to be used with usb.core.find""" 254 | 255 | def __init__(self, serial=None): 256 | """Create a new FindDap object with an optional serial number""" 257 | self._serial = serial 258 | 259 | def __call__(self, dev): 260 | """Return True if this is a DAP device, False otherwise""" 261 | # Check if the device class is a valid one for CMSIS-DAP. 262 | if filter_device_by_class(dev.idVendor, dev.idProduct, dev.bDeviceClass): 263 | return False 264 | 265 | try: 266 | # First attempt to get the active config. This produces a more direct error 267 | # when you don't have device permissions on Linux 268 | dev.get_active_configuration() 269 | 270 | # Now read the product name string. 271 | device_string = dev.product 272 | except usb.core.USBError as error: 273 | if error.errno == errno.EACCES and platform.system() == "Linux": 274 | msg = ("%s while trying to interrogate a USB device " 275 | "(VID=%04x PID=%04x). This can probably be remedied with a udev rule. " 276 | "See for help." % 277 | (error, dev.idVendor, dev.idProduct)) 278 | # If we recognize this device as one that should be CMSIS-DAP, we can raise 279 | # the level of the log message since it's almost certainly a permissions issue. 280 | if is_known_cmsis_dap_vid_pid(dev.idVendor, dev.idProduct): 281 | log.warning(msg) 282 | else: 283 | log.debug(msg) 284 | else: 285 | log.debug("Error accessing USB device (VID=%04x PID=%04x): %s", 286 | dev.idVendor, dev.idProduct, error) 287 | return False 288 | except (IndexError, NotImplementedError) as error: 289 | log.debug("Error accessing USB device (VID=%04x PID=%04x): %s", dev.idVendor, dev.idProduct, error) 290 | return False 291 | 292 | if device_string is None: 293 | return False 294 | if device_string.find("CMSIS-DAP") < 0: 295 | return False 296 | if self._serial is not None: 297 | if self._serial != dev.serial_number: 298 | return False 299 | return True 300 | -------------------------------------------------------------------------------- /pyocd/probe/pydapaccess/interface/pywinusb_backend.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2013 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from .interface import Interface 18 | from .common import filter_device_by_usage_page 19 | from ..dap_access_api import DAPAccessIntf 20 | from ....utility.timeout import Timeout 21 | import logging 22 | import os 23 | import collections 24 | from time import time, sleep 25 | import six 26 | 27 | OPEN_TIMEOUT_S = 60.0 28 | 29 | log = logging.getLogger('pywinusb') 30 | 31 | try: 32 | import pywinusb.hid as hid 33 | except: 34 | if os.name == "nt": 35 | log.error("PyWinUSB is required on a Windows Machine") 36 | IS_AVAILABLE = False 37 | else: 38 | IS_AVAILABLE = True 39 | 40 | class PyWinUSB(Interface): 41 | """ 42 | This class provides basic functions to access 43 | a USB HID device using pywinusb: 44 | - write/read an endpoint 45 | """ 46 | 47 | isAvailable = IS_AVAILABLE 48 | 49 | def __init__(self): 50 | super(PyWinUSB, self).__init__() 51 | # Vendor page and usage_id = 2 52 | self.report = [] 53 | # deque used here instead of synchronized Queue 54 | # since read speeds are ~10-30% faster and are 55 | # comprable to a based list implmentation. 56 | self.rcv_data = collections.deque() 57 | self.device = None 58 | 59 | # handler called when a report is received 60 | def rx_handler(self, data): 61 | #logging.debug("rcv: %s", data[1:]) 62 | self.rcv_data.append(data[1:]) 63 | 64 | def open(self): 65 | self.device.set_raw_data_handler(self.rx_handler) 66 | 67 | # Attempt to open the device. 68 | # Note - this operation must be retried since 69 | # other instances of pyOCD listing board can prevent 70 | # opening this device with exclusive access. 71 | with Timeout(OPEN_TIMEOUT_S) as t_o: 72 | while t_o.check(): 73 | # Attempt to open the device 74 | try: 75 | self.device.open(shared=False) 76 | break 77 | except hid.HIDError: 78 | pass 79 | 80 | # Attempt to open the device in shared mode to make 81 | # sure it is still there 82 | try: 83 | self.device.open(shared=True) 84 | self.device.close() 85 | except hid.HIDError as exc: 86 | # If the device could not be opened in read only mode 87 | # Then it either has been disconnected or is in use 88 | # by another thread/process 89 | raise six.raise_from(DAPAccessIntf.DeviceError("Unable to open device"), exc) 90 | 91 | else: 92 | # If this timeout has elapsed then another process 93 | # has locked this device in shared mode. This should 94 | # not happen. 95 | assert False 96 | 97 | 98 | @staticmethod 99 | def get_all_connected_interfaces(): 100 | """ 101 | returns all the connected CMSIS-DAP devices 102 | """ 103 | all_devices = hid.find_all_hid_devices() 104 | 105 | # find devices with good vid/pid 106 | all_mbed_devices = [] 107 | for d in all_devices: 108 | if (d.product_name.find("CMSIS-DAP") >= 0): 109 | all_mbed_devices.append(d) 110 | 111 | boards = [] 112 | for dev in all_mbed_devices: 113 | try: 114 | dev.open(shared=True) 115 | 116 | # Perform device-specific filtering. 117 | if filter_device_by_usage_page(dev.vendor_id, dev.product_id, dev.hid_caps.usage_page): 118 | dev.close() 119 | continue 120 | 121 | report = dev.find_output_reports() 122 | if len(report) != 1: 123 | dev.close() 124 | continue 125 | new_board = PyWinUSB() 126 | new_board.report = report[0] 127 | new_board.vendor_name = dev.vendor_name 128 | new_board.product_name = dev.product_name 129 | new_board.serial_number = dev.serial_number 130 | new_board.vid = dev.vendor_id 131 | new_board.pid = dev.product_id 132 | new_board.device = dev 133 | dev.close() 134 | boards.append(new_board) 135 | except Exception as e: 136 | if (str(e) != "Failure to get HID pre parsed data"): 137 | log.error("Receiving Exception: %s", e) 138 | dev.close() 139 | 140 | return boards 141 | 142 | def write(self, data): 143 | """ 144 | write data on the OUT endpoint associated to the HID interface 145 | """ 146 | for _ in range(self.packet_size - len(data)): 147 | data.append(0) 148 | #logging.debug("send: %s", data) 149 | self.report.send([0] + data) 150 | return 151 | 152 | 153 | def read(self, timeout=20.0): 154 | """ 155 | read data on the IN endpoint associated to the HID interface 156 | """ 157 | start = time() 158 | while len(self.rcv_data) == 0: 159 | sleep(0) 160 | if time() - start > timeout: 161 | # Read operations should typically take ~1-2ms. 162 | # If this exception occurs, then it could indicate 163 | # a problem in one of the following areas: 164 | # 1. Bad usb driver causing either a dropped read or write 165 | # 2. CMSIS-DAP firmware problem cause a dropped read or write 166 | # 3. CMSIS-DAP is performing a long operation or is being 167 | # halted in a debugger 168 | raise DAPAccessIntf.DeviceError("Read timed out") 169 | return self.rcv_data.popleft() 170 | 171 | def set_packet_count(self, count): 172 | # No interface level restrictions on count 173 | self.packet_count = count 174 | 175 | def set_packet_size(self, size): 176 | self.packet_size = size 177 | 178 | def get_serial_number(self): 179 | return self.serial_number 180 | 181 | def close(self): 182 | """ 183 | close the interface 184 | """ 185 | log.debug("closing interface") 186 | self.device.close() 187 | -------------------------------------------------------------------------------- /pyocd/utility/__init__.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2015 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | -------------------------------------------------------------------------------- /pyocd/utility/cmdline.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2015 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from ..core.target import Target 18 | from ..utility.compatibility import to_str_safe 19 | 20 | ## @brief Split command line by whitespace, supporting quoted strings. 21 | # 22 | # Accepts 23 | def split_command_line(cmd_line): 24 | result = [] 25 | if type(cmd_line) is str: 26 | args = [cmd_line] 27 | else: 28 | args = cmd_line 29 | for cmd in args: 30 | state = 0 31 | word = '' 32 | open_quote = '' 33 | for c in cmd: 34 | if state == 0: 35 | if c in (' ', '\t', '\r', '\n'): 36 | if word: 37 | result.append(word) 38 | word = '' 39 | elif c in ('"', "'"): 40 | open_quote = c 41 | state = 1 42 | else: 43 | word += c 44 | elif state == 1: 45 | if c == open_quote: 46 | result.append(word) 47 | word = '' 48 | state = 0 49 | else: 50 | word += c 51 | if word: 52 | result.append(word) 53 | return result 54 | 55 | ## Map of vector char characters to masks. 56 | VECTOR_CATCH_CHAR_MAP = { 57 | 'h': Target.CATCH_HARD_FAULT, 58 | 'b': Target.CATCH_BUS_FAULT, 59 | 'm': Target.CATCH_MEM_FAULT, 60 | 'i': Target.CATCH_INTERRUPT_ERR, 61 | 's': Target.CATCH_STATE_ERR, 62 | 'c': Target.CATCH_CHECK_ERR, 63 | 'p': Target.CATCH_COPROCESSOR_ERR, 64 | 'r': Target.CATCH_CORE_RESET, 65 | 'a': Target.CATCH_ALL, 66 | 'n': Target.CATCH_NONE, 67 | } 68 | 69 | ## @brief Convert a vector catch string to a mask. 70 | # 71 | # @exception ValueError Raised if an invalid vector catch character is encountered. 72 | def convert_vector_catch(value): 73 | # Make case insensitive. 74 | value = to_str_safe(value).lower() 75 | 76 | # Handle special vector catch options. 77 | if value == 'all': 78 | return Target.CATCH_ALL 79 | elif value == 'none': 80 | return Target.CATCH_NONE 81 | 82 | # Convert options string to mask. 83 | try: 84 | return sum([VECTOR_CATCH_CHAR_MAP[c] for c in value]) 85 | except KeyError as e: 86 | # Reraise an error with a more helpful message. 87 | raise ValueError("invalid vector catch option '{}'".format(e.args[0])) 88 | 89 | ## @brief Convert a list of session option settings to a dictionary. 90 | # 91 | # 92 | def convert_session_options(option_list): 93 | options = {} 94 | if option_list is not None: 95 | for o in option_list: 96 | if '=' in o: 97 | name, value = o.split('=') 98 | name = name.strip().lower() 99 | value = value.strip() 100 | else: 101 | name = o.strip().lower() 102 | if name.startswith('no-'): 103 | name = name[3:] 104 | value = False 105 | else: 106 | value = True 107 | options[name] = value 108 | return options 109 | 110 | ## Map to convert from reset type names to enums. 111 | RESET_TYPE_MAP = { 112 | 'default': None, 113 | 'hw': Target.ResetType.HW, 114 | 'sw': Target.ResetType.SW, 115 | 'hardware': Target.ResetType.HW, 116 | 'software': Target.ResetType.SW, 117 | 'sw_sysresetreq': Target.ResetType.SW_SYSRESETREQ, 118 | 'sw_vectreset': Target.ResetType.SW_VECTRESET, 119 | 'sw_emulated': Target.ResetType.SW_EMULATED, 120 | 'sysresetreq': Target.ResetType.SW_SYSRESETREQ, 121 | 'vectreset': Target.ResetType.SW_VECTRESET, 122 | 'emulated': Target.ResetType.SW_EMULATED, 123 | } 124 | 125 | def convert_reset_type(value): 126 | """! @brief Convert a reset_type session option value to the Target.ResetType enum. 127 | @param value The value of the reset_type session option. 128 | @exception ValueError Raised if an unknown reset_type value is passed. 129 | """ 130 | value = value.lower() 131 | if value not in RESET_TYPE_MAP: 132 | raise ValueError("unexpected value for reset_type option ('%s')" % value) 133 | return RESET_TYPE_MAP[value] 134 | 135 | -------------------------------------------------------------------------------- /pyocd/utility/compatibility.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2018-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import sys 18 | import functools 19 | 20 | PY3 = sys.version_info[0] == 3 21 | 22 | # iter_single_bytes() returns an iterator over a bytes object that produces 23 | # single-byte bytes objects for each byte in the passed in value. Normally on 24 | # py3 iterating over a bytes will give you ints for each byte, while on py3 25 | # you'll get single-char strs. 26 | if PY3: 27 | iter_single_bytes = functools.partial(map, lambda v: bytes((v,))) # pylint: disable=invalid-name 28 | else: 29 | iter_single_bytes = iter # pylint: disable=invalid-name 30 | 31 | # to_bytes_safe() converts a unicode string to a bytes object by encoding as 32 | # latin-1. It will also accept a value that is already a bytes object and 33 | # return it unmodified. 34 | if PY3: 35 | def to_bytes_safe(v): 36 | if type(v) is str: 37 | return v.encode('utf-8') 38 | else: 39 | return v 40 | else: 41 | def to_bytes_safe(v): 42 | if type(v) is unicode: 43 | return v.encode('utf-8') 44 | else: 45 | return v 46 | 47 | # to_str_safe() converts a bytes object to a unicode string by decoding from 48 | # latin-1. It will also accept a value that is already a str object and 49 | # return it unmodified. 50 | if PY3: 51 | def to_str_safe(v): 52 | if type(v) is str: 53 | return v 54 | else: 55 | return v.decode('utf-8') 56 | else: 57 | def to_str_safe(v): 58 | if type(v) is unicode: 59 | return v.encode('utf-8') 60 | else: 61 | return v 62 | 63 | # Make FileNotFoundError available to Python 2.x. 64 | if not PY3: 65 | class FileNotFoundError(IOError): 66 | pass 67 | 68 | # Symbol to reference either the builtin FNF or our custom subclass. 69 | FileNotFoundError_ = FileNotFoundError 70 | -------------------------------------------------------------------------------- /pyocd/utility/conversion.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2015-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import struct 18 | import binascii 19 | import six 20 | 21 | def byte_list_to_u32le_list(data, pad=0x00): 22 | """! @brief Convert a list of bytes to a list of 32-bit integers (little endian) 23 | 24 | If the length of the data list is not a multiple of 4, then the pad value is used 25 | for the additional required bytes. 26 | """ 27 | res = [] 28 | for i in range(len(data) // 4): 29 | res.append(data[i * 4 + 0] | 30 | data[i * 4 + 1] << 8 | 31 | data[i * 4 + 2] << 16 | 32 | data[i * 4 + 3] << 24) 33 | remainder = (len(data) % 4) 34 | if remainder != 0: 35 | padCount = 4 - remainder 36 | res += byte_list_to_u32le_list(list(data[-remainder:]) + [pad] * padCount) 37 | return res 38 | 39 | def u32le_list_to_byte_list(data): 40 | """! @brief Convert a word array into a byte array""" 41 | res = [] 42 | for x in data: 43 | res.append((x >> 0) & 0xff) 44 | res.append((x >> 8) & 0xff) 45 | res.append((x >> 16) & 0xff) 46 | res.append((x >> 24) & 0xff) 47 | return res 48 | 49 | def u16le_list_to_byte_list(data): 50 | """! @brief Convert a halfword array into a byte array""" 51 | byteData = [] 52 | for h in data: 53 | byteData.extend([h & 0xff, (h >> 8) & 0xff]) 54 | return byteData 55 | 56 | def byte_list_to_u16le_list(byteData): 57 | """! @brief Convert a byte array into a halfword array""" 58 | data = [] 59 | for i in range(0, len(byteData), 2): 60 | data.append(byteData[i] | (byteData[i + 1] << 8)) 61 | return data 62 | 63 | def u32_to_float32(data): 64 | """! @brief Convert a 32-bit int to an IEEE754 float""" 65 | d = struct.pack(">I", data) 66 | return struct.unpack(">f", d)[0] 67 | 68 | def float32_to_u32(data): 69 | """! @brief Convert an IEEE754 float to a 32-bit int""" 70 | d = struct.pack(">f", data) 71 | return struct.unpack(">I", d)[0] 72 | 73 | def u64_to_float64(data): 74 | """! @brief Convert a 64-bit int to an IEEE754 float""" 75 | d = struct.pack(">Q", data) 76 | return struct.unpack(">d", d)[0] 77 | 78 | def float64_to_u64(data): 79 | """! @brief Convert an IEEE754 float to a 64-bit int""" 80 | d = struct.pack(">d", data) 81 | return struct.unpack(">Q", d)[0] 82 | 83 | def u32_to_hex8le(val): 84 | """! @brief Create 8-digit hexadecimal string from 32-bit register value""" 85 | return ''.join("%02x" % (x & 0xFF) for x in ( 86 | val, 87 | val >> 8, 88 | val >> 16, 89 | val >> 24, 90 | )) 91 | 92 | def u64_to_hex16le(val): 93 | """! @brief Create 16-digit hexadecimal string from 64-bit register value""" 94 | return ''.join("%02x" % (x & 0xFF) for x in ( 95 | val, 96 | val >> 8, 97 | val >> 16, 98 | val >> 24, 99 | val >> 32, 100 | val >> 40, 101 | val >> 48, 102 | val >> 56, 103 | )) 104 | 105 | def hex8_to_u32be(data): 106 | """! @brief Build 32-bit register value from big-endian 8-digit hexadecimal string""" 107 | return int(data[6:8] + data[4:6] + data[2:4] + data[0:2], 16) 108 | 109 | def hex16_to_u64be(data): 110 | """! @brief Build 64-bit register value from big-endian 16-digit hexadecimal string""" 111 | return int(data[14:16] + data[12:14] + data[10:12] + data[8:10] + data[6:8] + data[4:6] + data[2:4] + data[0:2], 16) 112 | 113 | def hex8_to_u32le(data): 114 | """! @brief Build 32-bit register value from little-endian 8-digit hexadecimal string""" 115 | return int(data[0:8], 16) 116 | 117 | def hex16_to_u64le(data): 118 | """! @brief Build 64-bit register value from little-endian 16-digit hexadecimal string""" 119 | return int(data[0:16], 16) 120 | 121 | def byte_to_hex2(val): 122 | """! @brief Create 2-digit hexadecimal string from 8-bit value""" 123 | return "%02x" % int(val) 124 | 125 | def hex_to_byte_list(data): 126 | """! @brief Convert string of hex bytes to list of integers""" 127 | return list(six.iterbytes(binascii.unhexlify(data))) 128 | 129 | def hex_decode(cmd): 130 | """! @brief Return the binary data represented by the hexadecimal string.""" 131 | return binascii.unhexlify(cmd) 132 | 133 | def hex_encode(string): 134 | """! @brief Return the hexadecimal representation of the binary data.""" 135 | return binascii.hexlify(string) 136 | -------------------------------------------------------------------------------- /pyocd/utility/graph.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | class GraphNode(object): 18 | """! @brief Simple graph node.""" 19 | 20 | def __init__(self): 21 | """! @brief Constructor.""" 22 | super(GraphNode, self).__init__() 23 | self._parent = None 24 | self._children = [] 25 | 26 | @property 27 | def parent(self): 28 | """! @brief This node's parent in the object graph.""" 29 | return self._parent 30 | 31 | @property 32 | def children(self): 33 | """! @brief Child nodes in the object graph.""" 34 | return self._children 35 | 36 | def add_child(self, node): 37 | """! @brief Link a child node onto this object.""" 38 | node._parent = self 39 | self._children.append(node) 40 | 41 | def find_children(self, predicate, breadth_first=True): 42 | """! @brief Recursively search for children that match a given predicate. 43 | @param self 44 | @param predicate A callable accepting a single argument for the node to examine. If the 45 | predicate returns True, then that node is added to the result list and no further 46 | searches on that node's children are performed. A False predicate result causes the 47 | node's children to be searched. 48 | @param breadth_first Whether to search breadth first. Pass False to search depth first. 49 | @returns List of matching child nodes, or an empty list if no matches were found. 50 | """ 51 | def _search(node, klass): 52 | results = [] 53 | childrenToExamine = [] 54 | for child in node.children: 55 | if predicate(child): 56 | results.append(child) 57 | elif not breadth_first: 58 | results.extend(_search(child, klass)) 59 | elif breadth_first: 60 | childrenToExamine.append(child) 61 | 62 | if breadth_first: 63 | for child in childrenToExamine: 64 | results.extend(_search(child, klass)) 65 | return results 66 | 67 | return _search(self, predicate) 68 | 69 | def get_first_child_of_type(self, klass): 70 | """! @brief Breadth-first search for a child of the given class. 71 | @param self 72 | @param klass The class type to search for. The first child at any depth that is an instance 73 | of this class or a subclass thereof will be returned. Matching children at more shallow 74 | nodes will take precedence over deeper nodes. 75 | @returns Either a node object or None. 76 | """ 77 | matches = self.find_children(lambda c: isinstance(c, klass)) 78 | if len(matches): 79 | return matches[0] 80 | else: 81 | return None 82 | 83 | def dump_graph(node): 84 | """! @brief Draw the object graph.""" 85 | 86 | def _dump(node, level): 87 | print(" " * level + "- " + str(node)) 88 | for child in node.children: 89 | _dump(child, level + 1) 90 | 91 | _dump(node, 0) 92 | -------------------------------------------------------------------------------- /pyocd/utility/hex.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2018 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import sys 18 | 19 | def format_hex_width(value, width): 20 | if width == 8: 21 | return "%02x" % value 22 | elif width == 16: 23 | return "%04x" % value 24 | elif width == 32: 25 | return "%08x" % value 26 | else: 27 | raise ValueError("unrecognized register width (%d)" % width) 28 | 29 | def dump_hex_data(data, startAddress=0, width=8, output=None): 30 | if output is None: 31 | output = sys.stdout 32 | i = 0 33 | while i < len(data): 34 | output.write("%08x: " % (startAddress + (i * (width // 8)))) 35 | 36 | while i < len(data): 37 | d = data[i] 38 | i += 1 39 | if width == 8: 40 | output.write("%02x " % d) 41 | if i % 4 == 0: 42 | output.write(" ") 43 | if i % 16 == 0: 44 | break 45 | elif width == 16: 46 | output.write("%04x " % d) 47 | if i % 8 == 0: 48 | break 49 | elif width == 32: 50 | output.write("%08x " % d) 51 | if i % 4 == 0: 52 | break 53 | output.write("\n") 54 | -------------------------------------------------------------------------------- /pyocd/utility/mask.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2015-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | def bitmask(*args): 18 | """! @brief Returns a mask with specified bit ranges set. 19 | 20 | An integer mask is generated based on the bits and bit ranges specified by the 21 | arguments. Any number of arguments can be provided. Each argument may be either 22 | a 2-tuple of integers, a list of integers, or an individual integer. The result 23 | is the combination of masks produced by the arguments. 24 | 25 | - 2-tuple: The tuple is a bit range with the first element being the MSB and the 26 | second element the LSB. All bits from LSB up to and included MSB are set. 27 | - list: Each bit position specified by the list elements is set. 28 | - int: The specified bit position is set. 29 | 30 | @return An integer mask value computed from the logical OR'ing of masks generated 31 | by each argument. 32 | 33 | Example: 34 | @code 35 | >>> hex(bitmask((23,17),1)) 36 | 0xfe0002 37 | >>> hex(bitmask([4,0,2],(31,24)) 38 | 0xff000015 39 | @endcode 40 | """ 41 | mask = 0 42 | 43 | for a in args: 44 | if type(a) is tuple: 45 | for b in range(a[1], a[0]+1): 46 | mask |= 1 << b 47 | elif type(a) is list: 48 | for b in a: 49 | mask |= 1 << b 50 | elif type(a) is int: 51 | mask |= 1 << a 52 | 53 | return mask 54 | 55 | def invert32(value): 56 | """! @brief Return the 32-bit inverted value of the argument.""" 57 | return 0xffffffff & ~value 58 | 59 | def bfx(value, msb, lsb): 60 | """! @brief Extract a value from a bitfield.""" 61 | mask = bitmask((msb, lsb)) 62 | return (value & mask) >> lsb 63 | 64 | def bfi(value, msb, lsb, field): 65 | """! @brief Change a bitfield value.""" 66 | mask = bitmask((msb, lsb)) 67 | value &= ~mask 68 | value |= (field << lsb) & mask 69 | return value 70 | 71 | def msb(n): 72 | """! @brief Return the bit number of the highest set bit.""" 73 | ndx = 0 74 | while ( 1 < n ): 75 | n = ( n >> 1 ) 76 | ndx += 1 77 | return ndx 78 | 79 | def same(d1, d2): 80 | """! @brief Test whether two sequences contain the same values. 81 | 82 | Unlike a simple equality comparison, this function works as expected when the two sequences 83 | are of different types, such as a list and bytearray. The sequences must return 84 | compatible types from indexing. 85 | """ 86 | if len(d1) != len(d2): 87 | return False 88 | for i in range(len(d1)): 89 | if d1[i] != d2[i]: 90 | return False 91 | return True 92 | 93 | def align_up(value, multiple): 94 | """! @brief Return value aligned up to multiple.""" 95 | return (value + multiple - 1) // multiple * multiple 96 | 97 | -------------------------------------------------------------------------------- /pyocd/utility/notification.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2016 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import logging 18 | 19 | ## 20 | # @brief Class that holds information about a notification to subscribers. 21 | class Notification(object): 22 | def __init__(self, event, source, data=None): 23 | self._event = event 24 | self._source = source 25 | self._data = data 26 | 27 | @property 28 | def event(self): 29 | return self._event 30 | 31 | @property 32 | def source(self): 33 | return self._source 34 | 35 | @property 36 | def data(self): 37 | return self._data 38 | 39 | def __repr__(self): 40 | return "" % (id(self), repr(self.event), repr(self.source), repr(self.data)) 41 | 42 | ## 43 | # @brief Mix-in class that provides notification capabilities. 44 | class Notifier(object): 45 | def __init__(self): 46 | self._subscribers = {} 47 | 48 | def subscribe(self, events, cb): 49 | if not type(events) in (list, tuple): 50 | events = [events] 51 | for event in events: 52 | if event in self._subscribers: 53 | self._subscribers[event].append(cb) 54 | else: 55 | self._subscribers[event] = [cb] 56 | 57 | def unsubscribe(self, events, cb): 58 | pass 59 | 60 | def notify(self, *notifications): 61 | for note in notifications: 62 | # This debug log is commented out because it produces too much output unless you 63 | # are specifically working on notifications. 64 | # logging.debug("Sending notification: %s", repr(note)) 65 | for cb in self._subscribers.get(note.event, []): 66 | cb(note) 67 | 68 | 69 | -------------------------------------------------------------------------------- /pyocd/utility/progress.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2017-2018 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import os 18 | import sys 19 | import logging 20 | 21 | log = logging.getLogger('progress') 22 | 23 | class ProgressReport(object): 24 | """! 25 | @brief Base progress report class. 26 | 27 | This base class implements the logic but no output. 28 | """ 29 | def __init__(self, file=None): 30 | self._file = file or sys.stdout 31 | self.prev_progress = 0 32 | self.backwards_progress = False 33 | self.done = False 34 | self.last = 0 35 | 36 | def __call__(self, progress): 37 | assert progress >= 0.0 38 | # assert progress <= 1.0 # TODO restore this assert when the progress > 1 bug is fixed 39 | # assert (progress == 0 and self.prev_progress == 1.0) or (progress >= self.prev_progress) 40 | 41 | if progress > 1.0: 42 | log.debug("progress out of bounds: %.3f", progress) 43 | 44 | # Reset state on 0.0 45 | if progress == 0.0: 46 | self._start() 47 | 48 | # Check for backwards progress 49 | if progress < self.prev_progress: 50 | self.backwards_progress = True 51 | self.prev_progress = progress 52 | 53 | # print progress bar 54 | if not self.done: 55 | self._update(progress) 56 | 57 | # Finish on 1.0 58 | if progress >= 1.0: 59 | self._finish() 60 | if self.backwards_progress: 61 | log.warning("Progress went backwards!") 62 | 63 | def _start(self): 64 | self.prev_progress = 0 65 | self.backwards_progress = False 66 | self.done = False 67 | self.last = 0 68 | 69 | def _update(self, progress): 70 | raise NotImplemented() 71 | 72 | def _finish(self): 73 | raise NotImplemented() 74 | 75 | class ProgressReportTTY(ProgressReport): 76 | """! 77 | @brief Progress report subclass for TTYs. 78 | 79 | The progress bar is fully redrawn onscreen as progress is updated to give the 80 | impression of animation. 81 | """ 82 | 83 | ## These width constants can't be changed yet without changing the code below to match. 84 | WIDTH = 20 85 | 86 | def _update(self, progress): 87 | self._file.write('\r') 88 | i = int(progress * self.WIDTH) 89 | self._file.write("[%-20s] %3d%%" % ('=' * i, round(progress * 100))) 90 | self._file.flush() 91 | 92 | def _finish(self): 93 | self.done = True 94 | self._file.write("\n") 95 | 96 | class ProgressReportNoTTY(ProgressReport): 97 | """! 98 | @brief Progress report subclass for non-TTY output. 99 | 100 | A simpler progress bar is used than for the TTY version. Only the difference between 101 | the previous and current progress is drawn for each update, making the output suitable 102 | for piping to a file or similar output. 103 | """ 104 | 105 | ## These width constants can't be changed yet without changing the code below to match. 106 | WIDTH = 40 107 | 108 | def _start(self): 109 | super(ProgressReportNoTTY, self)._start() 110 | 111 | self._file.write('[' + '---|' * 9 + '----]\n[') 112 | self._file.flush() 113 | 114 | def _update(self, progress): 115 | i = int(progress * self.WIDTH) 116 | delta = i - self.last 117 | self._file.write('=' * delta) 118 | self._file.flush() 119 | self.last = i 120 | 121 | def _finish(self): 122 | self.done = True 123 | self._file.write("]\n") 124 | self._file.flush() 125 | 126 | def print_progress(file=None): 127 | """! 128 | @brief Progress printer factory. 129 | 130 | This factory function checks whether the output file is a TTY, and instantiates the 131 | appropriate subclass of ProgressReport. 132 | 133 | @param file The output file. Optional. If not provided, or if set to None, then sys.stdout 134 | will be used automatically. 135 | """ 136 | 137 | if file is None: 138 | file = sys.stdout 139 | try: 140 | istty = os.isatty(file.fileno()) 141 | except (OSError, AttributeError): 142 | # Either the file doesn't have a fileno method, or calling it returned an 143 | # error. In either case, just assume we're not connected to a TTY. 144 | istty = False 145 | 146 | klass = ProgressReportTTY if istty else ProgressReportNoTTY 147 | return klass(file) 148 | 149 | -------------------------------------------------------------------------------- /pyocd/utility/sequencer.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2018 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from collections import OrderedDict 18 | from collections.abc import Callable 19 | import logging 20 | 21 | log = logging.getLogger("sequencer") 22 | 23 | ## @brief Call sequence manager. 24 | # 25 | # Contains an ordered sequence of tasks. Each task has a name and associated 26 | # callable. The CallSequence class itself is callable, so instances can be nested 27 | # as tasks within other CallSequences. 28 | # 29 | # When tasks within a sequence are called, they may optionally return a new CallSequence 30 | # instance. If this happens, the new sequence is executed right away, before continuing 31 | # with the next task in the original sequence. 32 | # 33 | # A CallSequence can be iterated over. It will return tuples of (task-name, callable). 34 | class CallSequence(object): 35 | ## @brief Constructor. 36 | # 37 | # The constructor accepts an arbitrary number of parameters describing an ordered 38 | # set of tasks. Each parameter must be a 2-tuple with the first element being the 39 | # task's name and the second element a callable that implements the task. If you 40 | # need to pass parameters to the callable, use a lambda. 41 | def __init__(self, *args): 42 | self._validate_tasks(args) 43 | self._calls = OrderedDict(args) 44 | 45 | def _validate_tasks(self, tasks): 46 | for i in tasks: 47 | assert len(i) == 2 48 | assert type(i[0]) is str 49 | assert isinstance(i[1], Callable) 50 | 51 | ## @brief Returns an OrderedDict of the call sequence. 52 | # 53 | # Task names are keys. 54 | @property 55 | def sequence(self): 56 | return self._calls 57 | 58 | ## @brief Replace the entire call sequence. 59 | # 60 | # Accepts either an OrderedDict or a list of 2-tuples like the constructor. 61 | @sequence.setter 62 | def sequence(self, seq): 63 | if isinstance(seq, OrderedDict): 64 | self._calls = seq 65 | elif type(seq) is list and len(seq) and type(seq[0]) is tuple: 66 | self._calls = OrderedDict(seq) 67 | 68 | ## @brief Returns the number of tasks in the sequence. 69 | @property 70 | def count(self): 71 | return len(self._calls) 72 | 73 | ## @brief Remove all tasks from the sequence. 74 | def clear(self): 75 | self._calls = OrderedDict() 76 | 77 | ## @brief Remove a task with the given name. 78 | # @exception KeyError Raised if no task with the specified name exists. 79 | def remove_task(self, name): 80 | del self._calls[name] 81 | return self 82 | 83 | ## @brief Returns a boolean indicating presence of the named task in the sequence. 84 | def has_task(self, name): 85 | return name in self._calls 86 | 87 | ## @brief Return the callable for the named task. 88 | # @exception KeyError Raised if no task with the specified name exists. 89 | def get_task(self, name): 90 | return self._calls[name] 91 | 92 | ## @brief Change the callable associated with a task. 93 | def replace_task(self, name, replacement): 94 | assert isinstance(replacement, Callable) 95 | if name not in self._calls: 96 | raise KeyError(name) 97 | else: 98 | # OrderedDict preserves the order when changing the value of a key 99 | # that is already in the dict. 100 | self._calls[name] = replacement 101 | return self 102 | 103 | ## @brief Wrap an existing task with a new callable. 104 | # 105 | # The wrapper is expected to take a single parameter, the return value from the 106 | # original task. This allows for easy filtering of a new call sequence returned by 107 | # the original task. 108 | def wrap_task(self, name, wrapper): 109 | if name not in self._calls: 110 | raise KeyError(name) 111 | 112 | # Get original callable. 113 | orig = self._calls[name] 114 | 115 | # OrderedDict preserves the order when changing the value of a key 116 | # that is already in the dict. 117 | self._calls[name] = lambda : wrapper(orig()) 118 | return self 119 | 120 | ## @brief Append a new task or tasks to the sequence. 121 | # 122 | # Like the constructor, this method takes any number of arguments. Each must be a 123 | # 2-tuple task description. 124 | def append(self, *args): 125 | self._validate_tasks(args) 126 | 127 | # Insert iterable. 128 | self._calls.update(args) 129 | return self 130 | 131 | ## @brief Insert a task or tasks before a named task. 132 | # 133 | # @param beforeTaskName The name of an existing task. The new tasks will be inserted 134 | # prior to this task. 135 | # 136 | # After the task name parameter, any number of task description 2-tuples may be 137 | # passed. 138 | # 139 | # @exception KeyError Raised if the named task does not exist in the sequence. 140 | def insert_before(self, beforeTaskName, *args): 141 | self._validate_tasks(args) 142 | 143 | if not self.has_task(beforeTaskName): 144 | raise KeyError(beforeTaskName) 145 | 146 | seq = list(self._calls.items()) 147 | for i, v in enumerate(seq): 148 | if v[0] == beforeTaskName: 149 | for c in args: 150 | # List insert() inserts before the given index. 151 | seq.insert(i, c) 152 | i += 1 153 | break 154 | self._calls = OrderedDict(seq) 155 | return self 156 | 157 | ## @brief Insert a task or tasks after a named task. 158 | # 159 | # @param afterTaskName The name of an existing task. The new tasks will be inserted 160 | # after this task. 161 | # 162 | # After the task name parameter, any number of task description 2-tuples may be 163 | # passed. 164 | # 165 | # @exception KeyError Raised if the named task does not exist in the sequence. 166 | def insert_after(self, afterTaskName, *args): 167 | self._validate_tasks(args) 168 | 169 | if not self.has_task(afterTaskName): 170 | raise KeyError(afterTaskName) 171 | 172 | seq = list(self._calls.items()) 173 | for i, v in enumerate(seq): 174 | if v[0] == afterTaskName: 175 | for c in args: 176 | # List insert() inserts before the given index. 177 | seq.insert(i + 1, c) 178 | i += 1 179 | break 180 | self._calls = OrderedDict(seq) 181 | return self 182 | 183 | ## @brief Execute each task in order. 184 | # 185 | # A task may return a CallSequence, in which case the new sequence is immediately 186 | # executed. 187 | def invoke(self): 188 | for name, call in self._calls.items(): 189 | log.debug("Running task %s", name) 190 | resultSequence = call() 191 | 192 | # Invoke returned call sequence. 193 | if resultSequence is not None and isinstance(resultSequence, CallSequence): 194 | # log.debug("Invoking returned call sequence: %s", resultSequence) 195 | resultSequence.invoke() 196 | 197 | ## @brief Another way to execute the tasks. 198 | # 199 | # Supports nested CallSequences. 200 | def __call__(self, *args, **kwargs): 201 | self.invoke() 202 | 203 | ## @brief Iterate over the sequence. 204 | def __iter__(self): 205 | return iter(self._calls.items()) 206 | 207 | def __repr__(self): 208 | s = "<%s@%x: " % (self.__class__.__name__, id(self)) 209 | for name, task in self._calls.items(): 210 | s += "\n%s: %s" % (name, task) 211 | s += ">" 212 | return s 213 | -------------------------------------------------------------------------------- /pyocd/utility/server.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2015-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from __future__ import absolute_import 18 | import logging 19 | import threading 20 | import socket 21 | from .sockets import ListenerSocket 22 | from .compatibility import to_bytes_safe 23 | 24 | LOG = logging.getLogger(__name__) 25 | 26 | class StreamServer(threading.Thread): 27 | """! @brief File-like object that serves data over a TCP socket. 28 | 29 | The user can connect to the socket with telnet or netcat. 30 | 31 | The server thread will automatically be started by the constructor. To shut down the 32 | server and its thread, call the stop() method. 33 | """ 34 | 35 | def __init__(self, port, serve_local_only=True, name=None, is_read_only=True): 36 | """! @brief Constructor. 37 | 38 | Starts the server immediately. 39 | 40 | @param self 41 | @param port The TCP/IP port number on which the server should listen. If 0 is passed, 42 | then an arbitrary unused port is selected by the OS. In this case, the `port` property 43 | can be used to get the actual port number. 44 | @param serve_local_only Whether to allow connections from remote clients. 45 | @param name Optional server name. 46 | @param is_read_only If the server is read-only, from the perspective of the client, 47 | then any incoming data sent by the client is discarded. Otherwise it is buffered so 48 | it can be read with the read() methods. 49 | """ 50 | super(StreamServer, self).__init__() 51 | self.name = name 52 | self._name = name 53 | self._formatted_name = (name + " ") if (name is not None) else "" 54 | self._is_read_only = is_read_only 55 | self._abstract_socket = None 56 | self._abstract_socket = ListenerSocket(port, 4096) 57 | if serve_local_only: 58 | self._abstract_socket.host = 'localhost' 59 | self._abstract_socket.init() 60 | self._port = self._abstract_socket.port 61 | self._buffer = bytearray() 62 | self._buffer_lock = threading.Lock() 63 | self.connected = None 64 | self._shutdown_event = threading.Event() 65 | self.daemon = True 66 | self.start() 67 | 68 | @property 69 | def port(self): 70 | return self._port 71 | 72 | def stop(self): 73 | self._shutdown_event.set() 74 | self.join() 75 | 76 | def run(self): 77 | LOG.info("%sserver started on port %d", self._formatted_name, self._port) 78 | self.connected = None 79 | try: 80 | while not self._shutdown_event.is_set(): 81 | # Wait for a client to connect. 82 | # TODO support multiple client connections 83 | while not self._shutdown_event.is_set(): 84 | self.connected = self._abstract_socket.connect() 85 | if self.connected is not None: 86 | LOG.debug("%sclient connected", self._formatted_name) 87 | break 88 | 89 | if self._shutdown_event.is_set(): 90 | break 91 | 92 | # Set timeout on new connection. 93 | self._abstract_socket.set_timeout(0.1) 94 | 95 | # Keep reading from the client until we either get a shutdown event, or 96 | # the client disconnects. The incoming data is appended to our read buffer. 97 | while not self._shutdown_event.is_set(): 98 | try: 99 | data = self._abstract_socket.read() 100 | if len(data) == 0: 101 | # Client disconnected. 102 | self._abstract_socket.close() 103 | self.connected = None 104 | break 105 | 106 | if not self._is_read_only: 107 | self._buffer_lock.acquire() 108 | self._buffer += bytearray(data) 109 | self._buffer_lock.release() 110 | except socket.timeout: 111 | pass 112 | finally: 113 | self._abstract_socket.cleanup() 114 | LOG.info("%sserver stopped", self._formatted_name) 115 | 116 | def write(self, data): 117 | """! @brief Write bytes into the connection.""" 118 | # If nobody is connected, act like all data was written anyway. 119 | if self.connected is None: 120 | return 0 121 | data = to_bytes_safe(data) 122 | size = len(data) 123 | remaining = size 124 | while remaining: 125 | count = self._abstract_socket.write(data) 126 | remaining -= count 127 | if remaining: 128 | data = data[count:] 129 | return size 130 | 131 | def _get_input(self, length): 132 | """! @brief Extract requested amount of data from the read buffer.""" 133 | self._buffer_lock.acquire() 134 | try: 135 | if length == -1: 136 | actualLength = len(self._buffer) 137 | else: 138 | actualLength = min(length, len(self._buffer)) 139 | if actualLength: 140 | data = self._buffer[:actualLength] 141 | self._buffer = self._buffer[actualLength:] 142 | else: 143 | data = bytearray() 144 | return data 145 | finally: 146 | self._buffer_lock.release() 147 | 148 | def read(self, size=-1): 149 | """! @brief Return bytes read from the connection.""" 150 | if self.connected is None: 151 | return None 152 | 153 | # Extract requested amount of data from the read buffer. 154 | data = self._get_input(size) 155 | 156 | return data 157 | 158 | def readinto(self, b): 159 | """! @brief Read bytes into a mutable buffer.""" 160 | if self.connected is None: 161 | return None 162 | 163 | # Extract requested amount of data from the read buffer. 164 | b[:] = self._get_input(size) 165 | 166 | if len(b): 167 | return len(b) 168 | else: 169 | return None 170 | 171 | -------------------------------------------------------------------------------- /pyocd/utility/sockets.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2006-2019 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from __future__ import absolute_import 18 | import socket 19 | import select 20 | 21 | class ListenerSocket(object): 22 | def __init__(self, port, packet_size): 23 | self.packet_size = packet_size 24 | self.listener = None 25 | self.conn = None 26 | self.port = port 27 | self.host = '' 28 | 29 | def init(self): 30 | if self.listener is None: 31 | self.listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 32 | self.listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 33 | self.listener.bind((self.host, self.port)) 34 | # If we were asked for port 0, that's treated as "auto". 35 | # Read back the port - allows our user to find (and print) it, 36 | # and means that if we're closed then re-opened, as happens when 37 | # persisting for multiple sessions, we reuse the same port, which 38 | # is convenient. 39 | if self.port == 0: 40 | self.port = self.listener.getsockname()[1] 41 | self.listener.listen(1) 42 | 43 | def connect(self): 44 | self.conn = None 45 | self.init() 46 | rr, _, _ = select.select([self.listener], [], [], 0.5) 47 | if rr: 48 | self.conn, _ = self.listener.accept() 49 | 50 | return self.conn 51 | 52 | def read(self, packet_size=None): 53 | if packet_size is None: 54 | packet_size = self.packet_size 55 | return self.conn.recv(packet_size) 56 | 57 | def write(self, data): 58 | return self.conn.send(data) 59 | 60 | def close(self): 61 | return_value = None 62 | if self.conn is not None: 63 | return_value = self.conn.close() 64 | self.conn = None 65 | 66 | return return_value 67 | 68 | def cleanup(self): 69 | self.close() 70 | if self.listener is not None: 71 | self.listener.close() 72 | self.listener = None 73 | 74 | def set_blocking(self, blocking): 75 | self.conn.setblocking(blocking) 76 | 77 | def set_timeout(self, timeout): 78 | self.conn.settimeout(timeout) 79 | -------------------------------------------------------------------------------- /pyocd/utility/timeout.py: -------------------------------------------------------------------------------- 1 | # pyOCD debugger 2 | # Copyright (c) 2017-2018 Arm Limited 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from time import time 18 | 19 | ## @brief Timeout helper context manager. 20 | # 21 | # The recommended way to use this class is demonstrated here. It uses an else block on a 22 | # while loop to handle the timeout. The code in the while loop must use a break statement 23 | # to exit in the successful case. 24 | # 25 | # @code 26 | # with Timeout(5) as t_o: 27 | # while t_o.check(): # or "while not t_o.did_time_out" 28 | # # Perform some operation, check, etc. 29 | # if foobar: 30 | # break 31 | # sleep(0.1) 32 | # else: 33 | # print("Timed out!") 34 | # @endcode 35 | # 36 | # Another method of using the class is to check the `did_time_out` property from within the 37 | # while loop, as shown below. 38 | # 39 | # @code 40 | # with Timeout(5) as t_o: 41 | # while perform_some_test(): 42 | # # Check for timeout. 43 | # if t_o.did_time_out: 44 | # print("Timed out!") 45 | # break 46 | # sleep(0.1) 47 | # @endcode 48 | # 49 | # You may also combine the call to check() in the while loop with other boolean expressions 50 | # related to the operation being performed. 51 | class Timeout(object): 52 | def __init__(self, timeout): 53 | self._timeout = timeout 54 | self._timed_out = False 55 | self._start = -1 56 | 57 | def __enter__(self): 58 | self._start = time() 59 | return self 60 | 61 | def __exit__(self, exc_type, exc_val, exc_tb): 62 | pass 63 | 64 | def check(self): 65 | if (time() - self._start) > self._timeout: 66 | self._timed_out = True 67 | return not self._timed_out 68 | 69 | @property 70 | def did_time_out(self): 71 | self.check() 72 | return self._timed_out 73 | 74 | -------------------------------------------------------------------------------- /xlink.py: -------------------------------------------------------------------------------- 1 | import os 2 | import ctypes 3 | import operator 4 | 5 | 6 | import jlink 7 | 8 | 9 | class XLink(object): 10 | def __init__(self, xlk): 11 | self.xlk = xlk 12 | 13 | def open(self, mode, core, speed): 14 | if isinstance(self.xlk, jlink.JLink): 15 | self.xlk.open(mode, core, speed) 16 | else: 17 | self.xlk.ap.dp.link.open() 18 | 19 | def write_U8(self, addr, val): 20 | if isinstance(self.xlk, jlink.JLink): 21 | self.xlk.write_U8(addr, val) 22 | else: 23 | self.xlk.write8(addr, val) 24 | 25 | def write_U16(self, addr, val): 26 | if isinstance(self.xlk, jlink.JLink): 27 | self.xlk.write_U16(addr, val) 28 | else: 29 | self.xlk.write16(addr, val) 30 | 31 | def write_U32(self, addr, val): 32 | if isinstance(self.xlk, jlink.JLink): 33 | self.xlk.write_U32(addr, val) 34 | else: 35 | self.xlk.write32(addr, val) 36 | 37 | def write_mem(self, addr, data): 38 | if isinstance(self.xlk, jlink.JLink): 39 | self.xlk.write_mem(addr, data) 40 | else: 41 | self.xlk.write_memory_block8(addr, data) 42 | 43 | def read_mem_U8(self, addr, count): 44 | if isinstance(self.xlk, jlink.JLink): 45 | return self.xlk.read_mem_U8(addr, count) 46 | else: 47 | return [self.xlk.read8(addr+i) for i in range(count)] 48 | 49 | def read_mem_U16(self, addr, count): 50 | if isinstance(self.xlk, jlink.JLink): 51 | return self.xlk.read_mem_U16(addr, count) 52 | else: 53 | return [self.xlk.read16(addr+i*2) for i in range(count)] 54 | 55 | def read_mem_U32(self, addr, count): 56 | if isinstance(self.xlk, jlink.JLink): 57 | return self.xlk.read_mem_U32(addr, count) 58 | else: 59 | return [self.xlk.read32(addr+i*4) for i in range(count)] 60 | 61 | def read_U32(self, addr): 62 | if isinstance(self.xlk, jlink.JLink): 63 | return self.xlk.read_U32(addr) 64 | else: 65 | return self.xlk.read32(addr) 66 | 67 | def read_regs(self, rlist): 68 | if isinstance(self.xlk, jlink.JLink): 69 | return self.xlk.read_regs(rlist) 70 | else: 71 | return dict(zip(rlist, self.xlk.read_core_registers_raw(rlist))) 72 | 73 | def read_reg(self, reg): 74 | if isinstance(self.xlk, jlink.JLink): 75 | return self.xlk.read_reg(reg) 76 | else: 77 | return self.xlk.read_core_register_raw(reg) 78 | 79 | def write_reg(self, reg, val): 80 | if isinstance(self.xlk, jlink.JLink): 81 | self.xlk.write_reg(reg, val) 82 | else: 83 | self.xlk.write_core_register_raw(reg, val) 84 | 85 | def reset(self): 86 | if isinstance(self.xlk, jlink.JLink): 87 | self.xlk.reset() 88 | else: 89 | self.xlk.reset() 90 | 91 | def halt(self): 92 | if isinstance(self.xlk, jlink.JLink): 93 | self.xlk.halt() 94 | else: 95 | self.xlk.halt() 96 | 97 | def go(self): 98 | if isinstance(self.xlk, jlink.JLink): 99 | self.xlk.go() 100 | else: 101 | self.xlk.resume() 102 | 103 | def halted(self): 104 | if isinstance(self.xlk, jlink.JLink): 105 | return self.xlk.halted() 106 | else: 107 | return self.xlk.is_halted() 108 | 109 | def close(self): 110 | if isinstance(self.xlk, jlink.JLink): 111 | self.xlk.close() 112 | else: 113 | self.xlk.ap.dp.link.close() 114 | 115 | def read_core_type(self): 116 | if isinstance(self.xlk, jlink.JLink): 117 | return self.xlk.read_core_type() 118 | else: 119 | self.xlk._read_core_type() 120 | if self.xlk.core_type == 0x132: 121 | return 'STAR-MC1' 122 | from pyocd.coresight import cortex_m 123 | return cortex_m.CORE_TYPE_NAME[self.xlk.core_type] 124 | --------------------------------------------------------------------------------