diff --git a/docs/open_cmsis_pack_support.md b/docs/open_cmsis_pack_support.md index 202917c06..76eaa3c48 100644 --- a/docs/open_cmsis_pack_support.md +++ b/docs/open_cmsis_pack_support.md @@ -105,9 +105,15 @@ Only top-level sequences can be disabled individually. If a debug sequence is ca This section documents details of the debug sequence engine provided by pyOCD, supported features, and any notable differences with other debuggers (primarily Keil MDK, which provided the first implementation and against which Packs are generally most thoroughly tested by their authors). -### CPU-specific DebugPort sequences +### Core-specific sequences -Like all other debug sequences, `DebugPortSetup`, `DebugPortStart`, and `DebugPortStop` can be customised per CPU core. If a DFP has multiple CPU-specific instances of these sequences, they may behave differently in pyOCD than other debuggers. Many debuggers only "connect" to a single CPU chosen by the user when debugging or running a project. PyOCD is somewhat different in that it connects to the device as a whole, and then debugs a chosen core after the connection is established (which more closely reflects the hardware situation). +The DFP debug sequence architecture is currently based on the fact that most debuggers only "connect" to a single CPU core chosen by the user when debugging or running a project. All debug sequences can be customised per core, and there can be separate sequences for each core. + +PyOCD is somewhat different in that it connects to the device as a whole, and then debugs one or more cores after the connection is established. This more closely reflects the hardware situation. + +This primarily impacts the `DebugPortSetup`, `DebugPortStart`, `DebugPortStop`, and `DebugDeviceUnlock` debug sequences that affect the entire SoC. These relate to the connect procedure for the Arm ADI DP (Debug Port) used for SWD/JTAG communications. While most sequences can be run separately for each core, these are run only once per target connection. The core-specific variant that is selected can affect the rest of the debugging session. (Technically, this is also true for debuggers that are presented as debugging a single core, in cases where a second instance of that debugger can be started to debug another core. But, the way it's presented to the user is different.) + +The `primary_core` session option is used to select which core-specific version of the `DebugPort*`/`DebugDeviceUnlock` sequences is run during the target connection process. For all other sequences, the core-specific version that is run depends on which core is performing the action. ### Custom default reset sequences diff --git a/pyocd/coresight/coresight_target.py b/pyocd/coresight/coresight_target.py index a98eeeb60..49887d6f9 100644 --- a/pyocd/coresight/coresight_target.py +++ b/pyocd/coresight/coresight_target.py @@ -1,6 +1,6 @@ # pyOCD debugger # Copyright (c) 2015-2020 Arm Limited -# Copyright (c) 2021-2022 Chris Reed +# Copyright (c) 2021-2023 Chris Reed # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -163,7 +163,7 @@ def primary_core_pname(self) -> str: raise exceptions.Error(f"invalid 'primary_core' session option '{primary_core}' " f"(valid values are {', '.join(str(i) for i, _ in enumerate(ap_map.values()))})") - def _call_pre_discovery_debug_sequence(self, sequence: str) -> bool: + def call_pre_discovery_debug_sequence(self, sequence: str) -> bool: """@brief Run a debug sequence before discovery has been performed. The primary core's pname cannot be looked up via the `node_name` property of the core @@ -192,7 +192,7 @@ def unlock_device(self) -> None: if self.delegate_implements('unlock_device'): self.call_delegate('unlock_device') else: - self._call_pre_discovery_debug_sequence('DebugDeviceUnlock') + self.call_pre_discovery_debug_sequence('DebugDeviceUnlock') def create_discoverer(self) -> None: """@brief Init task to create the discovery object. @@ -215,7 +215,7 @@ def pre_connect(self) -> None: # Set the state variable indicating we're running ResetHardware for pre-reset, used # by the debug sequence delegate's get_connection_type() method. self.session.context_state.is_performing_pre_reset = True - if not self._call_pre_discovery_debug_sequence('ResetHardware'): + if not self.call_pre_discovery_debug_sequence('ResetHardware'): self.dp.reset() finally: self.session.context_state.is_performing_pre_reset = False diff --git a/pyocd/coresight/dap.py b/pyocd/coresight/dap.py index 995913270..741c72b49 100644 --- a/pyocd/coresight/dap.py +++ b/pyocd/coresight/dap.py @@ -21,7 +21,7 @@ import logging from enum import Enum -from typing import (Callable, Dict, List, NamedTuple, Optional, Sequence, Tuple, TYPE_CHECKING, Union, overload) +from typing import (cast, Callable, Dict, List, NamedTuple, Optional, Sequence, Tuple, TYPE_CHECKING, Union, overload) from typing_extensions import Literal from ..core import (exceptions, memory_interface) @@ -439,23 +439,22 @@ def _get_probe_capabilities(self) -> None: self._probe_supports_apv2_addresses = (DebugProbe.Capability.APv2_ADDRESSES in caps) self._have_probe_capabilities = True + # Usually when we call a debug sequence, we first check if the sequence exists. For the below + # methods, we rely on .call_pre_discovery_debug_sequence() to do this for us. def connect_debug_port_hook(self) -> Optional[bool]: - if self.has_debug_sequence('DebugPortSetup'): - assert self.debug_sequence_delegate - self.debug_sequence_delegate.run_sequence('DebugPortSetup') - return True + from .coresight_target import CoreSightTarget + cst = cast(CoreSightTarget, self.session.target) + return cst.call_pre_discovery_debug_sequence('DebugPortSetup') def enable_debug_port_hook(self) -> Optional[bool]: - if self.has_debug_sequence('DebugPortStart'): - assert self.debug_sequence_delegate - self.debug_sequence_delegate.run_sequence('DebugPortStart') - return True + from .coresight_target import CoreSightTarget + cst = cast(CoreSightTarget, self.session.target) + return cst.call_pre_discovery_debug_sequence('DebugPortStart') def disable_debug_port_hook(self) -> Optional[bool]: - if self.has_debug_sequence('DebugPortStop'): - assert self.debug_sequence_delegate - self.debug_sequence_delegate.run_sequence('DebugPortStop') - return True + from .coresight_target import CoreSightTarget + cst = cast(CoreSightTarget, self.session.target) + return cst.call_pre_discovery_debug_sequence('DebugPortStop') def _connect(self) -> None: # Connect the probe.