From 236ba1c4e53b2b61dfe9f163c6118146117c36ef Mon Sep 17 00:00:00 2001 From: Teo Mahnic Date: Wed, 16 Sep 2026 11:14:45 +0200 Subject: [PATCH 1/2] dap: fix DP SELECT cache handling --- pyocd/coresight/dap.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pyocd/coresight/dap.py b/pyocd/coresight/dap.py index b820fd73c..0bcd531b5 100644 --- a/pyocd/coresight/dap.py +++ b/pyocd/coresight/dap.py @@ -1,5 +1,5 @@ # pyOCD debugger -# Copyright (c) 2015-2020,2025 Arm Limited +# Copyright (c) 2015-2020,2025-2026 Arm Limited # Copyright (c) 2021-2023 Chris Reed # Copyright (c) 2022 Clay McClure # Copyright (c) 2022 Toshiba Electronic Devices & Storage Corporation @@ -723,7 +723,6 @@ def _write_dp_select(self, mask: int, value: int) -> None: # Update the SELECT register and cache. self.write_dp(DP_SELECT, select) - self._cached_dp_select = select def _set_dpbanksel(self, addr: int, is_write: bool) -> bool: """@brief Updates the DPBANKSEL field of the SELECT register as required. @@ -836,6 +835,9 @@ def write_dp(self, addr: int, data: int) -> None: try: TRACE.debug("write_dp:%06d (addr=0x%08x) = 0x%08x", num, addr, data) self.probe.write_dp(addr & DPADDR_MASK, data) + # Update cached DP SELECT if writing to it. + if (addr & DPADDR_MASK) == DP_SELECT: + self._cached_dp_select = data except exceptions.TargetError as error: self._handle_error(error, num) raise From bfe00b8e4251fa6446556e0525a6b32ab592e0cc Mon Sep 17 00:00:00 2001 From: Teo Mahnic Date: Thu, 17 Sep 2026 11:07:05 +0200 Subject: [PATCH 2/2] ap: fix CSW cache handling --- pyocd/coresight/ap.py | 15 ++++++++------- pyocd/coresight/dap.py | 15 +++++++++++++-- pyocd/probe/jlink_probe.py | 5 +---- pyocd/probe/stlink_probe.py | 11 +++++++---- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/pyocd/coresight/ap.py b/pyocd/coresight/ap.py index 190e0d989..8f6ebdb82 100644 --- a/pyocd/coresight/ap.py +++ b/pyocd/coresight/ap.py @@ -45,11 +45,7 @@ ## Offset of IDR register in an APv2. APv2_IDR = 0xDFC -A32 = 0x0c APSEL_SHIFT = 24 -APSEL = 0xff000000 -APBANKSEL = 0x000000f0 -APSEL_APBANKSEL = APSEL | APBANKSEL ## @brief Mask for register address within the AP address space. # @@ -606,6 +602,10 @@ def __init__( ## Cached current CSW value. self._cached_csw: int = -1 + # Register the CSW cache with the DP so direct AP accesses can update it. + csw_address = self.address.address + self._reg_offset + MEM_AP_CSW + self.dp._cached_ap_cb[csw_address] = self._update_cache + ## Original CSW value read during init(). self.original_csw: Optional[int] = None @@ -1047,8 +1047,6 @@ def write_reg(self, addr: int, data: int) -> None: TRACE.debug("write_ap:%06d cached (ap=0x%x; addr=0x%08x) = 0x%08x", num, self.address.nominal_address, addr, data) return - self._cached_csw = data - try: self.dp.write_ap(self.address.address + addr, data) except exceptions.ProbeError: @@ -1067,12 +1065,15 @@ def restore_original_csw_if_cached_modified(self) -> None: return if self._cached_csw != -1 and self._cached_csw != self.original_csw: self.write_reg(self._reg_offset + MEM_AP_CSW, self.original_csw) - self._cached_csw = self.original_csw def _invalidate_cache(self) -> None: """@brief Invalidate cached registers associated with this AP.""" self._cached_csw = -1 + def _update_cache(self, value: int) -> None: + """@brief Update cached value after an AP register access.""" + self._cached_csw = value + def _reset_did_occur(self, notification: Notification) -> None: """@brief Handles reset notifications to invalidate CSW cache.""" # We clear the cache on all resets just to be safe. diff --git a/pyocd/coresight/dap.py b/pyocd/coresight/dap.py index 0bcd531b5..ed38a200b 100644 --- a/pyocd/coresight/dap.py +++ b/pyocd/coresight/dap.py @@ -29,7 +29,6 @@ from ..core.target_delegate import DelegateHavingMixIn from ..probe.debug_probe import DebugProbe from ..probe.swj import SWJSequenceSender -from .ap import APSEL_APBANKSEL from ..utility.sequencer import CallSequence from ..utility.timeout import Timeout @@ -86,6 +85,11 @@ # DP SELECT register fields. SELECT_DPBANKSEL_MASK = 0x0000000f +# ADIv5 AP SELECT register fields. +SELECT_APSEL_MASK = 0xff000000 +SELECT_APBANKSEL_MASK = 0x000000f0 +SELECT_AP_MASK = SELECT_APSEL_MASK | SELECT_APBANKSEL_MASK +# ADIv6 AP SELECT register fields. SELECT_APADDR_MASK = 0xfffffff0 DPIDR_REVISION_MASK = 0xf0000000 @@ -316,6 +320,7 @@ def __init__(self, probe: DebugProbe, target: Target) -> None: self.aps: Dict[APAddressBase, AccessPort] = {} self._access_number: int = 0 self._cached_dp_select: Optional[int] = None + self._cached_ap_cb: Dict[int, Callable[[int], None]] = {} self._protocol: Optional[DebugProbe.Protocol] = None self._probe_managed_ap_select: bool = False self._probe_managed_dpbanksel: bool = False @@ -845,6 +850,10 @@ def write_dp(self, addr: int, data: int) -> None: if did_lock: self.unlock() + def _update_ap_cache(self, addr: int, value: int) -> None: + if (cb := self._cached_ap_cb.get(addr)) is not None: + cb(value) + def _select_ap(self, addr: int) -> bool: """@brief Write DP_SELECT to choose the given AP. @@ -860,7 +869,7 @@ def _select_ap(self, addr: int) -> bool: # Write DP SELECT to select the probe. self.lock() if self.adi_version == ADIVersion.ADIv5: - self._write_dp_select(APSEL_APBANKSEL, addr & APSEL_APBANKSEL) + self._write_dp_select(SELECT_AP_MASK, addr & SELECT_AP_MASK) elif self.adi_version == ADIVersion.ADIv6: self._write_dp_select(SELECT_APADDR_MASK, addr & SELECT_APADDR_MASK) else: @@ -877,6 +886,7 @@ def write_ap(self, addr: int, data: int) -> None: did_lock = self._select_ap(addr) TRACE.debug("write_ap:%06d (addr=0x%08x) = 0x%08x", num, addr, data) self.probe.write_ap(addr, data) + self._update_ap_cache(addr, data) except exceptions.TargetError as error: self._handle_error(error, num) raise @@ -922,6 +932,7 @@ def read_ap(self, addr: int, now: bool = True) -> Union[int, Callable[[], int]]: def read_ap_cb() -> int: try: result = result_cb() + self._update_ap_cache(addr, result) TRACE.debug("read_ap:%06d %s(addr=0x%08x) -> 0x%08x", num, "" if now else "...", addr, result) return result except exceptions.TargetError as error: diff --git a/pyocd/probe/jlink_probe.py b/pyocd/probe/jlink_probe.py index 0fbd159a7..7fbdf64c3 100644 --- a/pyocd/probe/jlink_probe.py +++ b/pyocd/probe/jlink_probe.py @@ -1,5 +1,5 @@ # pyOCD debugger -# Copyright (c) 2020,2025 Arm Limited +# Copyright (c) 2020,2025-2026 Arm Limited # Copyright (c) 2021-2022 Chris Reed # Copyright (c) 2023 Marian Muller Rebeyrol # Copyright (c) 2026 Christophe Dufaza @@ -50,9 +50,6 @@ class JLinkProbe(DebugProbe): # Bitmasks for AP register address fields. A32 = 0x0000000c - APBANKSEL = 0x000000f0 - APSEL = 0xff000000 - APSEL_APBANKSEL = APSEL | APBANKSEL @classmethod def _get_jlink(cls) -> Optional[pylink.JLink]: diff --git a/pyocd/probe/stlink_probe.py b/pyocd/probe/stlink_probe.py index 34ca61c15..6307b3fd6 100644 --- a/pyocd/probe/stlink_probe.py +++ b/pyocd/probe/stlink_probe.py @@ -1,5 +1,5 @@ # pyOCD debugger -# Copyright (c) 2018-2020,2022 Arm Limited +# Copyright (c) 2018-2020,2022,2026 Arm Limited # Copyright (c) 2021-2023 Chris Reed # SPDX-License-Identifier: Apache-2.0 # @@ -24,7 +24,7 @@ from ..core.memory_interface import MemoryInterface from ..core.plugin import Plugin from ..core.options import OptionInfo -from ..coresight.ap import (APVersion, APSEL, APSEL_SHIFT, APv1Address) +from ..coresight.ap import (APVersion, APv1Address) from .stlink.usb import STLinkUSBInterface from .stlink.stlink import STLink from .stlink.detect.factory import create_mbed_detector @@ -38,6 +38,9 @@ class StlinkProbe(DebugProbe): """@brief Wraps an STLink as a DebugProbe.""" + APSEL_SHIFT = 24 + APSEL_MASK = 0xff000000 + _board_id: Optional[str] # Shared cache for the STLink Mbed board IDs read from MSD volumes. @@ -228,7 +231,7 @@ def write_dp(self, addr, data): self._link.write_dap_register(STLink.DP_PORT, addr, data) def read_ap(self, addr, now=True): - apsel = (addr & APSEL) >> APSEL_SHIFT + apsel = (addr & self.APSEL_MASK) >> self.APSEL_SHIFT result = self._link.read_dap_register(apsel, addr & 0xffff) def read_ap_result_callback(): @@ -237,7 +240,7 @@ def read_ap_result_callback(): return result if now else read_ap_result_callback def write_ap(self, addr, data): - apsel = (addr & APSEL) >> APSEL_SHIFT + apsel = (addr & self.APSEL_MASK) >> self.APSEL_SHIFT self._link.write_dap_register(apsel, addr & 0xffff, data) def read_ap_multiple(self, addr, count=1, now=True):