Skip to content

Commit be325cf

Browse files
committed
DAP: send notifications on reset, use for register cache invalidation.
- The DP reset() and assert_reset() methods send pre- and post-reset notifications. - AccessPort and DebugPort classes subscribe to reset events in order to invalidate their register caches, replacing the AccessPort.reset_did_occur() method. - JLinkProbe uses reset notifications to invalidate its DP_SELECT cache.
1 parent b5beb9d commit be325cf

File tree

5 files changed

+95
-24
lines changed

5 files changed

+95
-24
lines changed

pyocd/coresight/ap.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from enum import Enum
2121

2222
from ..core import (exceptions, memory_interface)
23+
from ..core.target import Target
2324
from ..utility.concurrency import locked
2425

2526
LOG = logging.getLogger(__name__)
@@ -413,10 +414,6 @@ def read_reg(self, addr, now=True):
413414
def write_reg(self, addr, data):
414415
self.dp.write_ap(self.address.address + addr, data)
415416

416-
def reset_did_occur(self):
417-
"""! @brief Invoked by the DebugPort to inform APs that a reset was performed."""
418-
pass
419-
420417
def lock(self):
421418
"""! @brief Lock the AP from access by other threads."""
422419
self.dp.probe.lock()
@@ -523,6 +520,9 @@ def __init__(self, dp, ap_address, idr=None, name="", flags=0, cmpid=None):
523520
self.read_memory = self._read_memory
524521
self.write_memory_block32 = self._write_memory_block32
525522
self.read_memory_block32 = self._read_memory_block32
523+
524+
# Subscribe to reset events.
525+
self.dp.probe.session.subscribe(self._reset_did_occur, (Target.Event.PRE_RESET, Target.Event.POST_RESET))
526526

527527
@property
528528
def supported_transfer_sizes(self):
@@ -837,13 +837,17 @@ def write_reg(self, addr, data):
837837
except exceptions.ProbeError:
838838
# Invalidate cached CSW on exception.
839839
if ap_regaddr == self._reg_offset + MEM_AP_CSW:
840-
self._cached_csw = -1
840+
self._invalidate_cache()
841841
raise
842842

843-
def reset_did_occur(self):
844-
"""! @copydoc AccessPort.reset_did_occur()"""
845-
# TODO use notifications to invalidate CSW cache.
843+
def _invalidate_cache(self):
844+
"""! @brief Invalidate cached registers associated with this AP."""
846845
self._cached_csw = -1
846+
847+
def _reset_did_occur(self, notification):
848+
"""! @brief Handles reset notifications to invalidate CSW cache."""
849+
# We clear the cache on all resets just to be safe.
850+
self._invalidate_cache()
847851

848852
@locked
849853
def _write_memory(self, addr, data, transfer_size=32):
@@ -1045,7 +1049,7 @@ def _read_memory_block32(self, addr, size):
10451049

10461050
def _handle_error(self, error, num):
10471051
self.dp._handle_error(error, num)
1048-
self._cached_csw = -1
1052+
self._invalidate_cache()
10491053

10501054
class AHB_AP(MEM_AP):
10511055
"""! @brief AHB-AP access port subclass.

pyocd/coresight/cortex_m.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -739,12 +739,14 @@ def reset(self, reset_type=None):
739739
740740
After a call to this function, the core is running.
741741
"""
742-
self.session.notify(Target.Event.PRE_RESET, self)
743-
744742
reset_type = self._get_actual_reset_type(reset_type)
745743

746744
LOG.debug("reset, core %d, type=%s", self.core_number, reset_type.name)
747745

746+
# The HW reset type is passed to the DP, which itself sends reset notifications.
747+
if reset_type is not Target.ResetType.HW:
748+
self.session.notify(Target.Event.PRE_RESET, self)
749+
748750
self._run_token += 1
749751

750752
# Give the delegate a chance to overide reset. If the delegate returns True, then it
@@ -766,7 +768,8 @@ def reset(self, reset_type=None):
766768
self.flush()
767769
sleep(0.01)
768770

769-
self.session.notify(Target.Event.POST_RESET, self)
771+
if reset_type is not Target.ResetType.HW:
772+
self.session.notify(Target.Event.POST_RESET, self)
770773

771774
def set_reset_catch(self, reset_type=None):
772775
"""! @brief Prepare to halt core on reset."""

pyocd/coresight/dap.py

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from enum import Enum
2121

2222
from ..core import (exceptions, memory_interface)
23+
from ..core.target import Target
2324
from ..probe.debug_probe import DebugProbe
2425
from ..probe.swj import SWJSequenceSender
2526
from .ap import (MEM_AP_CSW, APSEL, APBANKSEL, APSEL_APBANKSEL, APREG_MASK, AccessPort)
@@ -204,6 +205,7 @@ class DebugPort(object):
204205
def __init__(self, probe, target):
205206
self._probe = probe
206207
self.target = target
208+
self._session = target.session
207209
self.valid_aps = None
208210
self.dpidr = None
209211
self.aps = {}
@@ -221,11 +223,18 @@ def __init__(self, probe, target):
221223
self._errmode = None
222224
self._base_addr = None
223225
self._apacc_mem_interface = None
226+
227+
# Subscribe to reset events.
228+
self._probe.session.subscribe(self._reset_did_occur, (Target.Event.PRE_RESET, Target.Event.POST_RESET))
224229

225230
@property
226231
def probe(self):
227232
return self._probe
228233

234+
@property
235+
def session(self):
236+
return self._session
237+
229238
@property
230239
def adi_version(self):
231240
return ADIVersion.ADIv6 if self._is_dpv3 else ADIVersion.ADIv5
@@ -404,23 +413,64 @@ def power_down_debug(self):
404413

405414
return True
406415

407-
def reset(self):
416+
def _invalidate_cache(self):
417+
"""! @brief Invalidate cached DP registers."""
408418
self._cached_dp_select = None
409-
for ap in self.aps.values():
410-
ap.reset_did_occur()
419+
420+
def _reset_did_occur(self, notification):
421+
"""! @brief Handles reset notifications to invalidate register cache.
422+
423+
The cache is cleared on all resets just to be safe. On most devices, warm resets do not reset
424+
debug logic, but it does happen on some devices.
425+
"""
426+
self._invalidate_cache()
427+
428+
def reset(self):
429+
"""! @brief Hardware reset.
430+
431+
Pre- and post-reset notifications are sent.
432+
433+
This method can be called before the DebugPort is connected.
434+
"""
435+
self.session.notify(Target.Event.PRE_RESET, self)
411436
self.probe.reset()
437+
self.session.notify(Target.Event.POST_RESET, self)
412438

413439
def assert_reset(self, asserted):
414-
self._cached_dp_select = None
415-
if asserted:
416-
for ap in self.aps.values():
417-
ap.reset_did_occur()
440+
"""! @brief Assert or deassert the hardware reset signal.
441+
442+
A pre-reset notification is sent before asserting reset, whereas a post-reset notification is sent
443+
after deasserting reset.
444+
445+
This method can be called before the DebugPort is connected.
446+
447+
@param self This object.
448+
@param asserted True if nRESET is to be driven low; False will drive nRESET high.
449+
"""
450+
is_asserted = self.is_reset_asserted()
451+
if asserted and not is_asserted:
452+
self.session.notify(Target.Event.PRE_RESET, self)
453+
418454
self.probe.assert_reset(asserted)
419455

456+
if not asserted and is_asserted:
457+
self.session.notify(Target.Event.POST_RESET, self)
458+
420459
def is_reset_asserted(self):
460+
"""! @brief Returns the current state of the nRESET signal.
461+
462+
This method can be called before the DebugPort is initalized.
463+
464+
@retval True Reset is asserted; nRESET is low.
465+
@retval False Reset is not asserted; nRESET is high.
466+
"""
421467
return self.probe.is_reset_asserted()
422468

423469
def set_clock(self, frequency):
470+
"""! @brief Change the wire protocol's clock frequency.
471+
@param self This object.
472+
@param frequency New wire protocol frequency in Hertz.
473+
"""
424474
self.probe.set_clock(frequency)
425475

426476
def _write_dp_select(self, mask, value):
@@ -677,7 +727,7 @@ def _handle_error(self, error, num):
677727
self.write_reg(DP_ABORT, ABORT_DAPABORT)
678728

679729
def clear_sticky_err(self):
680-
self._cached_dp_select = None
730+
self._invalidate_cache()
681731
mode = self.probe.wire_protocol
682732
if mode == DebugProbe.Protocol.SWD:
683733
self.write_reg(DP_ABORT, ABORT_ORUNERRCLR | ABORT_WDERRCLR | ABORT_STKERRCLR | ABORT_STKCMPCLR)

pyocd/probe/jlink_probe.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from ..core import (exceptions, memory_interface)
2525
from ..core.plugin import Plugin
2626
from ..core.options import OptionInfo
27+
from ..core.target import Target
2728

2829
LOG = logging.getLogger(__name__)
2930

@@ -177,13 +178,19 @@ def open(self):
177178
self._default_protocol = DebugProbe.Protocol.SWD
178179
else:
179180
self._default_protocol = DebugProbe.Protocol.JTAG
181+
182+
# Subscribe to reset events.
183+
self.session.subscribe(self._reset_did_occur, (Target.Event.PRE_RESET, Target.Event.POST_RESET))
180184
except JLinkException as exc:
181185
six.raise_from(self._convert_exception(exc), exc)
182186

183187
def close(self):
184188
try:
185189
self._link.close()
186190
self._is_open = False
191+
192+
# Unsubscribe from reset events.
193+
self.session.unsubscribe(self._reset_did_occur, (Target.Event.PRE_RESET, Target.Event.POST_RESET))
187194
except JLinkException as exc:
188195
six.raise_from(self._convert_exception(exc), exc)
189196

@@ -359,6 +366,9 @@ def _invalidate_cached_registers(self):
359366
# Invalidate cached DP SELECT register.
360367
self._dp_select = -1
361368

369+
def _reset_did_occur(self, notification):
370+
self._invalidate_cached_registers()
371+
362372
@staticmethod
363373
def _convert_exception(exc):
364374
if isinstance(exc, JLinkException):

pyocd/target/family/target_psoc6.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ class CortexM_PSoC6(CortexM):
3333
VTBASE_CM4 = None
3434

3535
def reset(self, reset_type=None):
36-
self.session.notify(Target.Event.PRE_RESET, self)
36+
if reset_type is not Target.ResetType.HW:
37+
self.session.notify(Target.Event.PRE_RESET, self)
3738
self._run_token += 1
3839
if reset_type is Target.ResetType.HW:
3940
self._ap.dp.reset()
@@ -69,7 +70,8 @@ def reset(self, reset_type=None):
6970

7071
sleep(0.01)
7172

72-
self.session.notify(Target.Event.POST_RESET, self)
73+
if reset_type is not Target.ResetType.HW:
74+
self.session.notify(Target.Event.POST_RESET, self)
7375

7476
def wait_halted(self):
7577
with Timeout(5.0) as t_o:
@@ -186,7 +188,8 @@ def skip_reset_and_halt(self, value):
186188
self._skip_reset_and_halt = value
187189

188190
def reset(self, reset_type=None):
189-
self.session.notify(Target.Event.PRE_RESET, self)
191+
if reset_type is not Target.ResetType.HW:
192+
self.session.notify(Target.Event.PRE_RESET, self)
190193

191194
self._run_token += 1
192195

@@ -219,7 +222,8 @@ def reset(self, reset_type=None):
219222
except exceptions.TransferError:
220223
pass
221224

222-
self.session.notify(Target.Event.POST_RESET, self)
225+
if reset_type is not Target.ResetType.HW:
226+
self.session.notify(Target.Event.POST_RESET, self)
223227

224228
def wait_halted(self):
225229
with Timeout(5.0) as t_o:

0 commit comments

Comments
 (0)