Skip to content

Commit c4571d9

Browse files
authored
Merge pull request #1018 from flit/refactor/reconnect
Reconnect support
2 parents 38c2fdd + 44d1868 commit c4571d9

File tree

10 files changed

+116
-56
lines changed

10 files changed

+116
-56
lines changed

pyocd/commands/commander.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
from ..core.helpers import ConnectHelper
2323
from ..core import (exceptions, session)
24+
from ..probe.shared_probe_proxy import SharedDebugProbeProxy
2425
from ..utility.cmdline import convert_session_options
2526
from ..commands.repl import (PyocdRepl, ToolExitException)
2627
from ..commands.execution_context import CommandExecutionContext
@@ -160,25 +161,33 @@ def connect(self):
160161
connect_mode = 'attach'
161162

162163
# Connect to board.
163-
self.session = ConnectHelper.session_with_chosen_probe(
164+
probe = ConnectHelper.choose_probe(
164165
blocking=(not self.args.no_wait),
166+
unique_id=self.args.unique_id,
167+
)
168+
if probe is None:
169+
self.exit_code = 3
170+
return False
171+
172+
# Create a proxy so the probe can be shared between the session and a possible probe server.
173+
probe_proxy = SharedDebugProbeProxy(probe)
174+
175+
# Create the session.
176+
self.session = session.Session(probe_proxy,
165177
project_dir=self.args.project_dir,
166178
config_file=self.args.config,
167179
user_script=self.args.script,
168180
no_config=self.args.no_config,
169181
pack=self.args.pack,
170-
unique_id=self.args.unique_id,
171182
target_override=self.args.target_override,
172183
connect_mode=connect_mode,
173184
frequency=self.args.frequency,
174185
options=options,
175186
option_defaults=dict(
176187
auto_unlock=False,
177188
resume_on_disconnect=False,
178-
))
179-
if self.session is None:
180-
self.exit_code = 3
181-
return False
189+
)
190+
)
182191

183192
if not self._post_connect():
184193
self.exit_code = 4

pyocd/commands/commands.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,8 +1253,7 @@ class InitDpCommand(CommandBase):
12531253
}
12541254

12551255
def execute(self):
1256-
self.context.target.dp.init()
1257-
self.context.target.dp.power_up_debug()
1256+
self.context.target.dp.connect()
12581257

12591258
class MakeApCommand(CommandBase):
12601259
INFO = {

pyocd/coresight/coresight_target.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def create_init_sequence(self):
7575
seq = CallSequence(
7676
('load_svd', self.load_svd),
7777
('pre_connect', self.pre_connect),
78-
('dp_init', self.dp.init_sequence),
78+
('dp_init', self.dp.create_connect_sequence),
7979
('create_discoverer', self.create_discoverer),
8080
('discovery', lambda : self._discoverer.discover()),
8181
('check_for_cores', self.check_for_cores),

pyocd/coresight/dap.py

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,15 @@ def idr(self):
126126
"""! @brief DPIDR instance containing values read from the DP IDR register."""
127127
return self._idr
128128

129+
def _get_protocol(self, protocol):
130+
# Convert protocol from setting if not passed as parameter.
131+
if protocol is None:
132+
protocol_name = self._session.options.get('dap_protocol').strip().lower()
133+
protocol = DebugProbe.PROTOCOL_NAME_MAP[protocol_name]
134+
if protocol not in self._probe.supported_wire_protocols:
135+
raise exceptions.DebugError("requested wire protocol %s not supported by the debug probe" % protocol.name)
136+
return protocol
137+
129138
def connect(self, protocol=None):
130139
"""! @brief Establish a connection to the DP.
131140
@@ -138,27 +147,52 @@ def connect(self, protocol=None):
138147
@exception DebugError
139148
@exception TransferError
140149
"""
141-
protocol_name = self._session.options.get('dap_protocol').strip().lower()
142-
send_swj = self._session.options.get('dap_swj_enable') \
143-
and (DebugProbe.Capability.SWJ_SEQUENCE in self._probe.capabilities)
144-
use_dormant = self._session.options.get('dap_swj_use_dormant')
150+
try:
151+
self._probe.lock()
145152

146-
# Convert protocol from setting if not passed as parameter.
147-
if protocol is None:
148-
protocol = DebugProbe.PROTOCOL_NAME_MAP[protocol_name]
149-
if protocol not in self._probe.supported_wire_protocols:
150-
raise exceptions.DebugError("requested wire protocol %s not supported by the debug probe" % protocol.name)
151-
if protocol != DebugProbe.Protocol.DEFAULT:
153+
# Determine the requested wire protocol.
154+
protocol = self._get_protocol(protocol)
155+
156+
# If this is not None then the probe is already connected.
157+
current_wire_protocol = self._probe.wire_protocol
158+
already_connected = current_wire_protocol is not None
159+
160+
if already_connected:
161+
self._check_protocol(current_wire_protocol, protocol)
162+
else:
163+
self._connect_probe(protocol)
164+
165+
protocol = self._probe.wire_protocol
166+
self._connect_dp(protocol)
167+
finally:
168+
self._probe.unlock()
169+
170+
def _check_protocol(self, current_wire_protocol, protocol):
171+
# Warn about mismatched current and requested wire protocols.
172+
if (protocol is not current_wire_protocol) and (protocol is not DebugProbe.Protocol.DEFAULT):
173+
LOG.warning("Cannot use %s; already connected with %s", protocol.name, current_wire_protocol.name)
174+
else:
175+
LOG.debug("Already connected with %s", current_wire_protocol.name)
176+
177+
def _connect_probe(self, protocol):
178+
# Debug log with the selected protocol.
179+
if protocol is not DebugProbe.Protocol.DEFAULT:
152180
LOG.debug("Using %s wire protocol", protocol.name)
153-
181+
154182
# Connect using the selected protocol.
155183
self._probe.connect(protocol)
156184

157185
# Log the actual protocol if selected was default.
158-
if protocol == DebugProbe.Protocol.DEFAULT:
186+
if protocol is DebugProbe.Protocol.DEFAULT:
159187
protocol = self._probe.wire_protocol
160188
LOG.debug("Default wire protocol selected; using %s", protocol.name)
161189

190+
def _connect_dp(self, protocol):
191+
# Get SWJ settings.
192+
use_dormant = self._session.options.get('dap_swj_use_dormant')
193+
send_swj = self._session.options.get('dap_swj_enable') \
194+
and (DebugProbe.Capability.SWJ_SEQUENCE in self._probe.capabilities)
195+
162196
# Create object to send SWJ sequences.
163197
swj = SWJSequenceSender(self._probe, use_dormant)
164198

@@ -207,6 +241,13 @@ class DebugPort(object):
207241
"""! @brief Represents the Arm Debug Interface (ADI) Debug Port (DP)."""
208242

209243
def __init__(self, probe, target):
244+
"""! @brief Constructor.
245+
@param self The DebugPort object.
246+
@param probe The @ref pyocd.probe.debug_probe.DebugProbe "DebugProbe" object. The probe is assumed to not
247+
have been opened yet.
248+
@param target An instance of @ref pyocd.core.soc_target.SoCTarget "SoCTarget". Assumed to not have been
249+
fully initialized.
250+
"""
210251
self._probe = probe
211252
self.target = target
212253
self._session = target.session
@@ -219,6 +260,8 @@ def __init__(self, probe, target):
219260
self._probe_managed_ap_select = False
220261
self._probe_managed_dpbanksel = False
221262
self._probe_supports_dpbanksel = False
263+
self._have_probe_capabilities = False
264+
self._did_check_version = False
222265

223266
# DPv3 attributes
224267
self._is_dpv3 = False
@@ -268,24 +311,24 @@ def unlock(self):
268311
"""! @brief Unlock the DP."""
269312
self.probe.unlock()
270313

271-
def init(self, protocol=None):
314+
def connect(self, protocol=None):
272315
"""! @brief Connect to the target.
273316
274317
This method causes the debug probe to connect using the selected wire protocol. The probe
275318
must have already been opened prior to this call.
276319
277-
Unlike init_sequence(), this method is intended to be used when manually constructing a
278-
DebugPort instance. It simply calls init_sequence() and invokes the returned call sequence.
320+
Unlike create_connect_sequence(), this method is intended to be used when manually constructing a
321+
DebugPort instance. It simply calls create_connect_sequence() and invokes the returned call sequence.
279322
280323
@param self
281324
@param protocol One of the @ref pyocd.probe.debug_probe.DebugProbe.Protocol
282325
"DebugProbe.Protocol" enums. If not provided, will default to the `protocol` setting.
283326
"""
284327
self._protocol = protocol
285-
self.init_sequence().invoke()
328+
self.create_connect_sequence().invoke()
286329

287-
def init_sequence(self):
288-
"""! @brief Init task to connect to the target.
330+
def create_connect_sequence(self):
331+
"""! @brief Returns call sequence to connect to the target.
289332
290333
Returns a @ref pyocd.utility.sequence.CallSequence CallSequence that will connect to the
291334
DP, power up debug and the system, check the DP version to identify whether the target uses
@@ -294,21 +337,36 @@ def init_sequence(self):
294337
The probe must have already been opened prior to this method being called.
295338
296339
@param self
340+
@return @ref pyocd.utility.sequence.CallSequence CallSequence
297341
"""
298-
return CallSequence(
299-
('get_probe_capabilities', self._get_probe_capabilities),
342+
seq = [
343+
('lock_probe', self.probe.lock),
344+
]
345+
if not self._have_probe_capabilities:
346+
seq += [
347+
('get_probe_capabilities', self._get_probe_capabilities),
348+
]
349+
seq += [
300350
('connect', self._connect),
301351
('clear_sticky_err', self.clear_sticky_err),
302352
('power_up_debug', self.power_up_debug),
303-
('check_version', self._check_version),
304-
)
353+
]
354+
if not self._did_check_version:
355+
seq += [
356+
('check_version', self._check_version),
357+
]
358+
seq += [
359+
('unlock_probe', self.probe.unlock),
360+
]
361+
return CallSequence(*seq)
305362

306363
def _get_probe_capabilities(self):
307364
"""! @brief Examine the probe's capabilities."""
308365
caps = self._probe.capabilities
309366
self._probe_managed_ap_select = (DebugProbe.Capability.MANAGED_AP_SELECTION in caps)
310367
self._probe_managed_dpbanksel = (DebugProbe.Capability.MANAGED_DPBANKSEL in caps)
311368
self._probe_supports_dpbanksel = (DebugProbe.Capability.BANKED_DP_REGISTERS in caps)
369+
self._have_probe_capabilities = True
312370

313371
def _connect(self):
314372
# Attempt to connect.
@@ -347,6 +405,8 @@ def _check_version(self):
347405
else:
348406
LOG.warning("DPv3 has no valid base address")
349407

408+
self._did_check_version = True
409+
350410
def flush(self):
351411
try:
352412
self.probe.flush()

pyocd/probe/debug_probe.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ def wire_protocol(self):
142142
"""! @brief Currently selected wire protocol.
143143
144144
If the probe is not open and connected, i.e., open() and connect() have not been called,
145-
then this property will be None.
145+
then this property will be None. If a value other than None is returned, then the probe
146+
has been connected successfully.
146147
"""
147148
raise NotImplementedError()
148149

@@ -188,6 +189,8 @@ def lock(self):
188189
189190
This lock is recursive, so locking multiple times from a single thread is acceptable as long
190191
as the thread unlocks the same number of times.
192+
193+
This method does not return until the calling thread has ownership of the lock.
191194
"""
192195
self._lock.acquire()
193196

pyocd/probe/shared_probe_proxy.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ def __init__(self, probe):
3333
self._probe = probe
3434
self._open_count = 0
3535
self._connect_count = 0
36-
self._active_protocol = None
3736

3837
@property
3938
def session(self):
@@ -73,9 +72,7 @@ def disconnect(self):
7372
self._connect_count -= 1
7473

7574
def swj_sequence(self, length, bits):
76-
# Only the first connected client can perform SWJ sequences.
77-
if self._connect_count == 1:
78-
self._probe.swj_sequence(length, bits)
75+
self._probe.swj_sequence(length, bits)
7976

8077
def __getattr__(self, name):
8178
"""! @brief Redirect to underlying probe object methods."""

pyocd/probe/swj.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ def select_protocol(self, protocol):
4343
self._switch_to_swd()
4444
elif protocol == DebugProbe.Protocol.JTAG:
4545
self._switch_to_jtag()
46+
else:
47+
assert False, "unhandled protocol %s in SWJSequenceSender" % protocol
4648

4749
def _switch_to_swd(self):
4850
"""! @brief Send SWJ sequence to select SWD."""

pyocd/target/builtin/target_CC3220SF.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,7 @@ def init(self):
9999
time.sleep(1.3)
100100

101101
# reconnect to the board
102-
self.target.dp.init()
103-
self.target.dp.power_up_debug()
102+
self.target.dp.connect()
104103

105104
self.target.halt()
106105
self.target.reset_and_halt()

pyocd/target/builtin/target_s5js100.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,7 @@ def reset(self, reset_type=None):
232232
sleep(0.1)
233233
except exceptions.TransferError:
234234
self.flush()
235-
self._ap.dp.init()
236-
self._ap.dp.power_up_debug()
235+
self._ap.dp.connect()
237236
sleep(0.01)
238237
else:
239238
raise exceptions.TimeoutError("Timeout waiting for reset")
@@ -271,8 +270,7 @@ def get_state(self):
271270
# LOG.info("s5js100.get_state dhcsr 0x%x", dhcsr)
272271
except exceptions.TransferError:
273272
# LOG.info("s5js100.get_state read fail dhcsr..try more")
274-
self._ap.dp.init()
275-
self._ap.dp.power_up_debug()
273+
self._ap.dp.connect()
276274
dhcsr = self.read_memory(CortexM.DHCSR)
277275
# LOG.info("fail s5js100.get_state dhcsr 0x%x", dhcsr)
278276

pyocd/target/family/target_psoc6.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ def reset(self, reset_type=None):
3939
if reset_type is Target.ResetType.HW:
4040
self._ap.dp.reset()
4141
sleep(0.5)
42-
self._ap.dp.init()
43-
self._ap.dp.power_up_debug()
42+
self._ap.dp.connect()
4443
self.fpb.enable()
4544
else:
4645
if reset_type is Target.ResetType.SW_VECTRESET:
@@ -63,8 +62,7 @@ def reset(self, reset_type=None):
6362
except exceptions.TransferError:
6463
self.flush()
6564
try:
66-
self._ap.dp.init()
67-
self._ap.dp.power_up_debug()
65+
self._ap.dp.connect()
6866
except exceptions.TransferError:
6967
self.flush()
7068

@@ -213,8 +211,7 @@ def reset(self, reset_type=None):
213211
with Timeout(5.0) as t_o:
214212
while t_o.check():
215213
try:
216-
self._ap.dp.init()
217-
self._ap.dp.power_up_debug()
214+
self._ap.dp.connect()
218215
dhcsr_reg = self.read32(CortexM.DHCSR)
219216
if (dhcsr_reg & CortexM.S_RESET_ST) == 0:
220217
break
@@ -241,8 +238,7 @@ def reinit_dap(self):
241238
with Timeout(2.0) as t_o:
242239
while t_o.check():
243240
try:
244-
self._ap.dp.init()
245-
self._ap.dp.power_up_debug()
241+
self._ap.dp.connect()
246242
self.flush()
247243
break
248244
except exceptions.TransferError:
@@ -255,8 +251,7 @@ def acquire(self):
255251
with Timeout(self.acquire_timeout) as t_o:
256252
while t_o.check():
257253
try:
258-
self._ap.dp.init()
259-
self._ap.dp.power_up_debug()
254+
self._ap.dp.connect()
260255
# self.write32(self.IPC2_DATA_ADDR, 0)
261256
self.write32(self.TEST_MODE_ADDR, self.TEST_MODE_VALUE)
262257
self.flush()
@@ -315,8 +310,7 @@ def reset_and_halt(self, reset_type=None):
315310
with Timeout(self.acquire_timeout) as t_o:
316311
while t_o.check():
317312
try:
318-
self._ap.dp.init()
319-
self._ap.dp.power_up_debug()
313+
self._ap.dp.connect()
320314
self.halt()
321315
self.wait_halted()
322316
self.write_core_register('xpsr', CortexM.XPSR_THUMB)
@@ -384,8 +378,7 @@ def reset(self, reset_type=None):
384378
with Timeout(self._acquire_timeout) as t_o:
385379
while t_o.check():
386380
try:
387-
self._ap.dp.init()
388-
self._ap.dp.power_up_debug()
381+
self._ap.dp.connect()
389382
break
390383
except exceptions.TransferError:
391384
pass

0 commit comments

Comments
 (0)