oep-client-python 0.0.32__py3-none-any.whl → 0.0.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oep_client/__init__.py +1 -1
- oep_client/__main__.py +16 -0
- oep_client/config.py +47 -0
- oep_client/endpoint.py +84 -17
- oep_client/registry.py +7 -7
- oep_client/riscv.py +30 -2
- oep_client/virtual_attach_model.py +3 -2
- oep_client/virtual_bench.py +7 -3
- oep_client/virtual_oep_declaration_model.py +3 -3
- oep_client/virtual_registered_ops_model.py +1 -1
- oep_client/virtual_scan_options_model.py +1 -1
- {oep_client_python-0.0.32.dist-info → oep_client_python-0.0.34.dist-info}/METADATA +1 -1
- {oep_client_python-0.0.32.dist-info → oep_client_python-0.0.34.dist-info}/RECORD +16 -16
- {oep_client_python-0.0.32.dist-info → oep_client_python-0.0.34.dist-info}/WHEEL +0 -0
- {oep_client_python-0.0.32.dist-info → oep_client_python-0.0.34.dist-info}/entry_points.txt +0 -0
- {oep_client_python-0.0.32.dist-info → oep_client_python-0.0.34.dist-info}/licenses/LICENSE +0 -0
oep_client/__init__.py
CHANGED
oep_client/__main__.py
CHANGED
|
@@ -353,6 +353,18 @@ def _config_parser(sub) -> None:
|
|
|
353
353
|
idle.add_argument("channel", type=int)
|
|
354
354
|
idle.add_argument("mode", choices=sorted(config.IDLE))
|
|
355
355
|
idle.add_argument("--save", action="store_true")
|
|
356
|
+
rest = cs.add_parser("rvswd-rest", help="how rvswd rests a port's SWCLK: auto (the probe decides from the target_id), "
|
|
357
|
+
"or high / low fixed (the automatic choice off; debug §3.1)")
|
|
358
|
+
rest.add_argument("probe")
|
|
359
|
+
rest.add_argument("channel", type=int, help="the port's SWCLK channel")
|
|
360
|
+
rest.add_argument("mode", choices=sorted(config.REST_MODE))
|
|
361
|
+
rest.add_argument("--save", action="store_true")
|
|
362
|
+
hold = cs.add_parser("wire-hold", help="whether a debug wire's lines stay held at their rest after a close "
|
|
363
|
+
"(on, the default) or go free at once (off; debug §2)")
|
|
364
|
+
hold.add_argument("probe")
|
|
365
|
+
hold.add_argument("channel", type=int, help="the port's SWDIO channel")
|
|
366
|
+
hold.add_argument("mode", choices=sorted(config.HOLD_MODE))
|
|
367
|
+
hold.add_argument("--save", action="store_true")
|
|
356
368
|
dis = cs.add_parser("disable", help="channels the probe never drives or sets up (not on this board)")
|
|
357
369
|
dis.add_argument("probe")
|
|
358
370
|
dis.add_argument("channels", type=int, nargs="+")
|
|
@@ -435,6 +447,10 @@ def _config(args) -> int:
|
|
|
435
447
|
_change(hst, cfg, [config.Label(channel=args.channel, text=args.text)], args.save)
|
|
436
448
|
elif args.action == "idle":
|
|
437
449
|
_change(hst, cfg, [config.Idle(channel=args.channel, mode=args.mode)], args.save)
|
|
450
|
+
elif args.action == "wire-hold":
|
|
451
|
+
_change(hst, cfg, [config.WireHold(channel=args.channel, mode=args.mode)], args.save)
|
|
452
|
+
elif args.action == "rvswd-rest":
|
|
453
|
+
_change(hst, cfg, [config.RvswdRest(channel=args.channel, mode=args.mode)], args.save)
|
|
438
454
|
elif args.action == "disable":
|
|
439
455
|
_change(hst, cfg, [config.Disable(channel=ch) for ch in args.channels], args.save)
|
|
440
456
|
elif args.action == "remove":
|
oep_client/config.py
CHANGED
|
@@ -36,6 +36,8 @@ NAMES = tuple(_CFG.names["setting"]) # the standard i
|
|
|
36
36
|
ITEM = {k.replace("-", "_"): i + 1 for i, k in enumerate(NAMES)}
|
|
37
37
|
DESCRIBE = _CFG.tlv["describe"]
|
|
38
38
|
IDLE = {k.replace("_", "-"): v for k, v in _CFG.enum["idle_mode"].items()}
|
|
39
|
+
REST_MODE = dict(_CFG.enum["rvswd_rest_mode"]) # rvswd-rest: auto / high / low (debug §3.1)
|
|
40
|
+
HOLD_MODE = dict(_CFG.enum["wire_hold_mode"]) # wire-hold: off / on (debug §2)
|
|
39
41
|
STORAGE_STATE = {v: k for k, v in _CFG.enum["storage_state"].items()}
|
|
40
42
|
UNREADABLE = {v: k for k, v in _CFG.enum["storage_unreadable"].items()}
|
|
41
43
|
FORM = _CFG.enum["setting_form"]
|
|
@@ -140,6 +142,47 @@ class Passthrough:
|
|
|
140
142
|
return struct.pack("<HHIB", self.fn, self.rx, self.baud, self.format)
|
|
141
143
|
|
|
142
144
|
|
|
145
|
+
@dataclass
|
|
146
|
+
class RvswdRest:
|
|
147
|
+
"""How an rvswd attach without idle_clock rests the SWCLK on `channel` (the port's SWCLK; debug §3.1): auto - the
|
|
148
|
+
probe's choice from the target_id it reads (also with no item) - or high / low fixed, the automatic choice off for
|
|
149
|
+
that port. An attach's idle_clock TLV still decides."""
|
|
150
|
+
channel: int
|
|
151
|
+
mode: str = "auto"
|
|
152
|
+
NAME = "rvswd-rest"
|
|
153
|
+
|
|
154
|
+
def key(self) -> int:
|
|
155
|
+
return self.channel
|
|
156
|
+
|
|
157
|
+
def value(self) -> bytes:
|
|
158
|
+
return struct.pack("<H", self.key()) + self.data()
|
|
159
|
+
|
|
160
|
+
def data(self) -> bytes:
|
|
161
|
+
if self.mode not in REST_MODE:
|
|
162
|
+
raise ValueError(f"rvswd-rest mode {self.mode!r}: one of {', '.join(REST_MODE)}")
|
|
163
|
+
return bytes([REST_MODE[self.mode]])
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass
|
|
167
|
+
class WireHold:
|
|
168
|
+
"""Whether a debug wire's lines on the port whose SWDIO is `channel` stay held at their rest after its connection
|
|
169
|
+
closes (debug §2): on - also with no item - or off (back to the free state at once)."""
|
|
170
|
+
channel: int
|
|
171
|
+
mode: str = "on"
|
|
172
|
+
NAME = "wire-hold"
|
|
173
|
+
|
|
174
|
+
def key(self) -> int:
|
|
175
|
+
return self.channel
|
|
176
|
+
|
|
177
|
+
def value(self) -> bytes:
|
|
178
|
+
return struct.pack("<H", self.key()) + self.data()
|
|
179
|
+
|
|
180
|
+
def data(self) -> bytes:
|
|
181
|
+
if self.mode not in HOLD_MODE:
|
|
182
|
+
raise ValueError(f"wire-hold mode {self.mode!r}: one of {', '.join(HOLD_MODE)}")
|
|
183
|
+
return bytes([HOLD_MODE[self.mode]])
|
|
184
|
+
|
|
185
|
+
|
|
143
186
|
@dataclass(kw_only=True)
|
|
144
187
|
class ConsolePassthrough:
|
|
145
188
|
"""Serial port `port` carries the target's debug console - read by the probe itself over `wire_fn` on (swdio, swclk)
|
|
@@ -293,6 +336,10 @@ def decode(name: str, key: int, d: bytes, setting: Setting | None = None, netifs
|
|
|
293
336
|
try:
|
|
294
337
|
if name == "label":
|
|
295
338
|
return Label(channel=key, text=d.decode("utf-8", "replace"))
|
|
339
|
+
if name == "wire-hold" and len(d) == 1 and d[0] in HOLD_MODE.values():
|
|
340
|
+
return WireHold(channel=key, mode={v: k for k, v in HOLD_MODE.items()}[d[0]])
|
|
341
|
+
if name == "rvswd-rest" and len(d) == 1 and d[0] in REST_MODE.values():
|
|
342
|
+
return RvswdRest(channel=key, mode={v: k for k, v in REST_MODE.items()}[d[0]])
|
|
296
343
|
if name == "idle" and len(d) == 1:
|
|
297
344
|
return Idle(channel=key, mode=_name(IDLE, d[0]))
|
|
298
345
|
if name == "disable" and not d:
|
oep_client/endpoint.py
CHANGED
|
@@ -117,7 +117,9 @@ RESET_MODE = _RV.enum["reset_mode"]
|
|
|
117
117
|
READ_FROM = reg.COMMON.enum["read_from"] # position, oldest, now, last_mark (common §1.2)
|
|
118
118
|
STEP_LEFT = _RV.tlv["step_answer"]["step_left"] # step's answer: the hart could not be halted again (§4.2)
|
|
119
119
|
ITEM = {k.replace("-", "_"): k for k in cfgmod.NAMES} # the standard items by name (probe.config §1, core §7.7)
|
|
120
|
-
CHANNEL_ITEMS = (ITEM["label"], ITEM["idle"], ITEM["disable"]) #
|
|
120
|
+
CHANNEL_ITEMS = (ITEM["label"], ITEM["idle"], ITEM["disable"], ITEM["rvswd_rest"], ITEM["wire_hold"]) # keyed by a channel
|
|
121
|
+
HOLD_MODE = cfgmod.HOLD_MODE
|
|
122
|
+
REST_MODE = cfgmod.REST_MODE
|
|
121
123
|
IDLE_MODE = _CFG.enum["idle_mode"]
|
|
122
124
|
CFG_DESCRIBE = cfgmod.DESCRIBE
|
|
123
125
|
SETTING_FORM = _CFG.enum["setting_form"]
|
|
@@ -137,7 +139,18 @@ I2C_FEATURES, SPI_FEATURES = _I2C.enum["features"], _SPI.enum["features"]
|
|
|
137
139
|
TARGET_ROLES = {"oep.fixture.gpio": {1}, "oep.fixture.uart": {1, 2}, _I2C.name: set(_I2C.enum["role"].values()),
|
|
138
140
|
_SPI.name: set(_SPI.enum["role"].values())} # the plan roles each fixture takes
|
|
139
141
|
UART_FORMAT_MASK = 0x1F # the defined format bits (fixture §2)
|
|
140
|
-
_T_SCAN, _T_ATTACH, _T_ATTACH_ANSWER = (
|
|
142
|
+
_T_SCAN, _T_ATTACH, _T_ATTACH_ANSWER, _T_CONNECTIONS_ANSWER = (
|
|
143
|
+
reg.WIRE_RVSWD.tlv[k] for k in ("scan", "attach", "attach_answer", "connections_answer"))
|
|
144
|
+
_IDLE_CLOCK = reg.WIRE_RVSWD.enum["idle_clock"]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def rvswd_rest(target_id: int | None) -> int:
|
|
148
|
+
"""The rest an rvswd attach without idle_clock uses (debug §3.1): low for a family in the registry's
|
|
149
|
+
idle_low_family (target_id >> target_id_family.shift), high otherwise and when no target_id was read."""
|
|
150
|
+
if target_id is None:
|
|
151
|
+
return _IDLE_CLOCK["high"]
|
|
152
|
+
family = target_id >> reg.WIRE_RVSWD.enum["target_id_family"]["shift"]
|
|
153
|
+
return _IDLE_CLOCK["low"] if family in reg.WIRE_RVSWD.enum["idle_low_family"].values() else _IDLE_CLOCK["high"]
|
|
141
154
|
_WIRE_INDEX = reg.WIRE_RVSWD.tlv["unsupported_payload"]["index"] # scan's candidate index (debug §1)
|
|
142
155
|
ATTACH_METHOD = reg.WIRE_RVSWD.enum["attach_method"]
|
|
143
156
|
GPIO_MODE_MAX = max(_GPIO.enum["mode"].values()) # 6: the modes revision 1 defines are 0-6 (fixture §1)
|
|
@@ -315,6 +328,7 @@ class VirtualTarget:
|
|
|
315
328
|
havereset: bool = True
|
|
316
329
|
resume_misses: int = 0 # resumes that do not take (status state, dpc unchanged)
|
|
317
330
|
present: bool = True # something answers on this pin pair (scan, attach)
|
|
331
|
+
needs_drive: bool = False # loses its halt once the probe stops driving the line (a CH32L103)
|
|
318
332
|
target_id: int | None = None # the dmi_7f target_id attach reports (None: none)
|
|
319
333
|
# run(pc, regs) -> (stopped, dpc, elapsed_us); default: halts 0x10 past the start
|
|
320
334
|
run_hook: Callable | None = None
|
|
@@ -542,7 +556,7 @@ class Connection:
|
|
|
542
556
|
|
|
543
557
|
|
|
544
558
|
ITEM_SIZES = {ITEM["idle"]: 1, ITEM["disable"]: 0, ITEM["passthrough"]: 9, # the data of a fixed length (probe.config §1)
|
|
545
|
-
ITEM["console_passthrough"]: 13, ITEM["ipv4"]: 14}
|
|
559
|
+
ITEM["console_passthrough"]: 13, ITEM["ipv4"]: 14, ITEM["rvswd_rest"]: 1, ITEM["wire_hold"]: 1}
|
|
546
560
|
PASSTHROUGHS = (ITEM["passthrough"], ITEM["console_passthrough"])
|
|
547
561
|
|
|
548
562
|
|
|
@@ -772,6 +786,7 @@ class Endpoint:
|
|
|
772
786
|
self.session_ports: set[int] = set() # the transports the lock holder's requests came on (probe.config §1.1)
|
|
773
787
|
self.passthrough_out: dict[int, bytearray] = {} # a serial port's pass-through bytes not sent yet
|
|
774
788
|
self.resident: dict[int, bool] = {} # console_passthrough's own connection per port: attached (config §1.2)
|
|
789
|
+
self.holds: dict[tuple[int, tuple[int, int]], int] = {} # (wire fn, pair) -> rest: lines held after a close (debug §2)
|
|
775
790
|
self.last: int | None = None # S: the id that holds or last held the lock (the resend table's, §5.2)
|
|
776
791
|
self.owner: bytes | None = None # the holder's owner, while the lock is held (core §6.4)
|
|
777
792
|
self.lease_ms = self.lease_default_ms
|
|
@@ -1549,7 +1564,7 @@ class Endpoint:
|
|
|
1549
1564
|
connections closed (an unknown `cid`: none)."""
|
|
1550
1565
|
gone = [c for c in (list(self.conns) if cid is None else [cid]) if c in self.conns]
|
|
1551
1566
|
for c in gone:
|
|
1552
|
-
self._close_conn(c)
|
|
1567
|
+
self._close_conn(c, lost=True)
|
|
1553
1568
|
return gone
|
|
1554
1569
|
|
|
1555
1570
|
# ---- core -----------------------------------------------------------------------------------
|
|
@@ -2293,8 +2308,11 @@ class Endpoint:
|
|
|
2293
2308
|
if tg.havereset:
|
|
2294
2309
|
tg.havereset, flags = False, flags | ATTACH_FLAGS["havereset_acked"]
|
|
2295
2310
|
c = self.conns[cid]
|
|
2296
|
-
if
|
|
2297
|
-
|
|
2311
|
+
if rvswd: # the host's TLV, the setting, else from target_id (§3.1)
|
|
2312
|
+
setting = self.config.get((ITEM["rvswd_rest"], pair[1]), bytes([REST_MODE["auto"]]))[0]
|
|
2313
|
+
c.idle_clock = (idle_clock if idle_clock is not None
|
|
2314
|
+
else _IDLE_CLOCK["high"] if setting == REST_MODE["high"]
|
|
2315
|
+
else _IDLE_CLOCK["low"] if setting == REST_MODE["low"] else rvswd_rest(c.tid))
|
|
2298
2316
|
if reset is not None and tg.reset_line not in (None, channel):
|
|
2299
2317
|
if method == 1: # a line that resets nothing: halted where it ran
|
|
2300
2318
|
tg.halted, tg.dpc = True, tg.reset_vector + 0x2f8
|
|
@@ -2308,6 +2326,8 @@ class Endpoint:
|
|
|
2308
2326
|
if tg.halted and method == 1: # bit3: the probe saw hart 0 halted (§3)
|
|
2309
2327
|
flags |= ATTACH_FLAGS["halted"]
|
|
2310
2328
|
tail += m.tlv(_T_ATTACH_ANSWER["dpc"], struct.pack("<I", tg.dpc))
|
|
2329
|
+
if rvswd: # the rest in use, always (§3.1)
|
|
2330
|
+
tail += m.tlv(_T_ATTACH_ANSWER["idle_clock"], bytes([c.idle_clock]))
|
|
2311
2331
|
return self._answer(struct.pack("<IIBI", cid, tg.dmstatus(), flags, c.speed) + tail)
|
|
2312
2332
|
if op == O["detach"]:
|
|
2313
2333
|
cid = t.take("I")
|
|
@@ -2317,28 +2337,40 @@ class Endpoint:
|
|
|
2317
2337
|
raise wrong_kind() # another wire's connection
|
|
2318
2338
|
self._close_conn(cid)
|
|
2319
2339
|
return m.COMPLETED, m.SUCCESS, b""
|
|
2340
|
+
if op == O["holds"]: # lock-free, paged by first (debug §2.2)
|
|
2341
|
+
first = t.take("B")
|
|
2342
|
+
t.tail()
|
|
2343
|
+
rows = [struct.pack("<HHB", *pair, rest) for pair, rest in self.held_ports(fn)]
|
|
2344
|
+
return m.COMPLETED, m.SUCCESS, self._paged(rows, first)
|
|
2320
2345
|
if op == O["connections"]: # lock-free, paged by first (debug §2.1)
|
|
2321
2346
|
first = t.take("B")
|
|
2322
2347
|
t.tail()
|
|
2323
2348
|
mine = sorted((c.order, cid) for cid, c in self.conns.items() if c.fn == fn)
|
|
2324
|
-
rows = []
|
|
2349
|
+
rows, rests = [], []
|
|
2325
2350
|
for _, cid in mine:
|
|
2326
2351
|
c = self.conns[cid]
|
|
2327
2352
|
tid = b"" if c.tid is None else struct.pack("<I", c.tid)
|
|
2328
2353
|
scheme = TARGET_ID_SCHEME["dmi_7f"] if tid else 0
|
|
2329
2354
|
rows.append(struct.pack("<IHHIBB", cid, *c.pair, c.speed, scheme, len(tid)) + tid)
|
|
2330
|
-
|
|
2355
|
+
rests.append(c.idle_clock)
|
|
2356
|
+
# rvswd: TLV idle_clock (entry_index, rest) for every entry of the page (§2.1, §3.1)
|
|
2357
|
+
return m.COMPLETED, m.SUCCESS, self._paged(rows, first, rests if rvswd else None)
|
|
2331
2358
|
return m.REJECTED, m.UNKNOWN_OPERATION, b""
|
|
2332
2359
|
|
|
2333
|
-
def _paged(self, rows: list[bytes], first: int) -> bytes:
|
|
2334
|
-
"""more(u8) count(u8) the rows from `first` that fit the frame (connections, streams: core §2.3's lists)
|
|
2360
|
+
def _paged(self, rows: list[bytes], first: int, rests: list[int] | None = None) -> bytes:
|
|
2361
|
+
"""more(u8) count(u8) the rows from `first` that fit the frame (connections, streams: core §2.3's lists);
|
|
2362
|
+
with `rests` (rvswd's connections), each row's TLV idle_clock (entry_index, rest) after them, counted in."""
|
|
2335
2363
|
budget = self.probe.max_frame - m.RESULT_HEADER - 2
|
|
2336
2364
|
out: list[bytes] = []
|
|
2337
|
-
|
|
2338
|
-
|
|
2365
|
+
tail: list[bytes] = []
|
|
2366
|
+
for i, row in enumerate(rows[first:]):
|
|
2367
|
+
extra = b"" if rests is None else m.tlv(_T_CONNECTIONS_ANSWER["idle_clock"], bytes([len(out), rests[first + i]]))
|
|
2368
|
+
if out and sum(map(len, out + tail)) + len(row) + len(extra) > budget:
|
|
2339
2369
|
break
|
|
2340
2370
|
out.append(row)
|
|
2341
|
-
|
|
2371
|
+
if extra:
|
|
2372
|
+
tail.append(extra)
|
|
2373
|
+
return bytes([int(first + len(out) < len(rows)), len(out)]) + b"".join(out) + b"".join(tail)
|
|
2342
2374
|
|
|
2343
2375
|
def _attach_methods(self, fn: int) -> set[int]:
|
|
2344
2376
|
"""method 0, and 1 when the wire declares features bit0 attach_halt (debug §3)."""
|
|
@@ -2385,14 +2417,45 @@ class Endpoint:
|
|
|
2385
2417
|
self.conns[cid] = Connection(fn, pair, self._order, speed, tid)
|
|
2386
2418
|
return cid
|
|
2387
2419
|
|
|
2388
|
-
def _close_conn(self, cid: int) -> None:
|
|
2389
|
-
"""The connection goes, its console streams first (common §2)
|
|
2420
|
+
def _close_conn(self, cid: int, lost: bool = False) -> None:
|
|
2421
|
+
"""The connection goes, its console streams first (common §2). Its lines stay held at its rest unless the line
|
|
2422
|
+
was lost, the port's wire-hold is off or a pin has an idle / disable item (debug §2); else to their idle state,
|
|
2423
|
+
and a target that needs the drive loses its halt."""
|
|
2390
2424
|
for sid in [k for k, st in self.streams.items() if st.conn == cid]:
|
|
2391
2425
|
self._close_stream(sid)
|
|
2392
2426
|
gone = self.conns.pop(cid, None)
|
|
2393
2427
|
self.resources.pop(cid, None)
|
|
2394
|
-
if gone is
|
|
2395
|
-
|
|
2428
|
+
if gone is None:
|
|
2429
|
+
return
|
|
2430
|
+
if not lost and self._may_hold(gone.pair):
|
|
2431
|
+
self.holds[(gone.fn, gone.pair)] = gone.idle_clock if self.names[gone.fn] == "oep.wire.rvswd" else 0
|
|
2432
|
+
return
|
|
2433
|
+
self._park(gone.pair)
|
|
2434
|
+
tg = self.targets.get((gone.fn, gone.pair))
|
|
2435
|
+
if tg is not None and tg.needs_drive:
|
|
2436
|
+
tg.halted = False # its debug module dropped the link: the hart runs
|
|
2437
|
+
|
|
2438
|
+
def _may_hold(self, pair: tuple[int, int]) -> bool:
|
|
2439
|
+
"""debug §2: wire-hold on (no item: on) for the port's SWDIO, and no idle / disable item on its pins."""
|
|
2440
|
+
mode = self.config.get((ITEM["wire_hold"], pair[0]), bytes([HOLD_MODE["on"]]))[0]
|
|
2441
|
+
pins = [p for p in pair if p != 0xFFFF]
|
|
2442
|
+
return (mode == HOLD_MODE["on"] and not any((ITEM["idle"], p) in self.config for p in pins)
|
|
2443
|
+
and not any(p in self.disabled for p in pins))
|
|
2444
|
+
|
|
2445
|
+
def held_ports(self, fn: int) -> list[tuple[tuple[int, int], int]]:
|
|
2446
|
+
"""The ports wire `fn` holds now (debug §2.2): a hold ends once its pins are taken (a plan, a connection), its
|
|
2447
|
+
wire-hold goes off or an idle / disable item lands on them; an ended hold's pins go to their free state."""
|
|
2448
|
+
busy = {a[2] for a in self.plan if not self._listens(a[0])} | {p for c in self.conns.values() for p in c.pair}
|
|
2449
|
+
for key in list(self.holds):
|
|
2450
|
+
hfn, pair = key
|
|
2451
|
+
if any(p in busy for p in pair if p != 0xFFFF) or not self._may_hold(pair):
|
|
2452
|
+
del self.holds[key]
|
|
2453
|
+
if not any(p in busy for p in pair if p != 0xFFFF):
|
|
2454
|
+
self._park(pair)
|
|
2455
|
+
tg = self.targets.get(key)
|
|
2456
|
+
if tg is not None and tg.needs_drive:
|
|
2457
|
+
tg.halted = False
|
|
2458
|
+
return [(pair, rest) for (hfn, pair), rest in self.holds.items() if hfn == fn]
|
|
2396
2459
|
|
|
2397
2460
|
def _cleanup_failed(self, cid: int, done: int, payload: bytes) -> tuple[int, int, bytes]:
|
|
2398
2461
|
"""A block op whose restore of DATA0 / DATA1 failed: partial when done > 0 (all words included), else failed,
|
|
@@ -3275,6 +3338,10 @@ class Endpoint:
|
|
|
3275
3338
|
return True
|
|
3276
3339
|
if mode in (IDLE_MODE["pull_up"], IDLE_MODE["pull_down"]) and mode in self.no_pull.get(key, ()):
|
|
3277
3340
|
return True
|
|
3341
|
+
if name == ITEM["rvswd_rest"] and d[0] not in REST_MODE.values():
|
|
3342
|
+
return True # a mode the definition leaves unused (§1)
|
|
3343
|
+
if name == ITEM["wire_hold"] and d[0] not in HOLD_MODE.values():
|
|
3344
|
+
return True
|
|
3278
3345
|
return False
|
|
3279
3346
|
|
|
3280
3347
|
def _shown(self, name: str, d: bytes) -> bytes:
|
oep_client/registry.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
from types import SimpleNamespace as _NS
|
|
5
5
|
|
|
6
|
-
REGISTRY_HASH = "
|
|
6
|
+
REGISTRY_HASH = "32b9f952338089a5"
|
|
7
7
|
SCHEMA = 1
|
|
8
8
|
PROTOCOL_REVISION = 1
|
|
9
9
|
CONFIRM_REQUEST_MAGIC = 'OEP?'
|
|
@@ -41,15 +41,15 @@ PROBE_LINK = _NS(name="oep.probe.link", revision=1, target=None, op={"source": 0
|
|
|
41
41
|
tlv={}, event={}, enum={"port_speed_step": {"try": 0x00, "commit": 0x01, "revert": 0x02}, "port_state": {"boot": 0x00, "trying": 0x01, "committed": 0x02, "recovery_pending": 0x03}}, own={},
|
|
42
42
|
line_names={}, names={})
|
|
43
43
|
INTERFACES["oep.probe.link"] = PROBE_LINK
|
|
44
|
-
WIRE_RVSWD = _NS(name="oep.wire.rvswd", revision=1, target="RISC-V targets whose debug module is reached over the two-wire RVSWD line", op={"scan": 0x10, "attach": 0x11, "detach": 0x12, "connections": 0x13}, lock_free={0x13},
|
|
45
|
-
tlv={"describe": {"max_connections": 0x40}, "scan": {"max_speed": 0x01, "idle_clock": 0x03}, "attach": {"idle_clock": 0x03, "reset": 0x04}, "attach_answer": {"target_id": 0x01, "dpc": 0x02}, "unavailable_payload": {"index": 0x40}, "unsupported_payload": {"index": 0x40}}, event={}, enum={"scan_kind": {"riscv_dm": 0x01}, "attach_flags": {"havereset_acked": 0x01, "halted": 0x08}, "features": {"attach_halt": 0x01}, "attach_method": {"run": 0x00, "halt": 0x01}, "pin_role": {"swdio": 0x01, "swclk": 0x02, "reset": 0x03}, "idle_clock": {"high": 0x00, "low": 0x01}}, own={},
|
|
44
|
+
WIRE_RVSWD = _NS(name="oep.wire.rvswd", revision=1, target="RISC-V targets whose debug module is reached over the two-wire RVSWD line", op={"scan": 0x10, "attach": 0x11, "detach": 0x12, "connections": 0x13, "holds": 0x14}, lock_free={0x13, 0x14},
|
|
45
|
+
tlv={"describe": {"max_connections": 0x40}, "scan": {"max_speed": 0x01, "idle_clock": 0x03}, "attach": {"idle_clock": 0x03, "reset": 0x04}, "attach_answer": {"target_id": 0x01, "dpc": 0x02, "idle_clock": 0x03}, "connections_answer": {"idle_clock": 0x02}, "unavailable_payload": {"index": 0x40}, "unsupported_payload": {"index": 0x40}}, event={}, enum={"scan_kind": {"riscv_dm": 0x01}, "attach_flags": {"havereset_acked": 0x01, "halted": 0x08}, "features": {"attach_halt": 0x01}, "attach_method": {"run": 0x00, "halt": 0x01}, "pin_role": {"swdio": 0x01, "swclk": 0x02, "reset": 0x03}, "idle_clock": {"high": 0x00, "low": 0x01}, "target_id_family": {"shift": 0x14}, "idle_low_family": {"ch32l103": 0x103}}, own={},
|
|
46
46
|
line_names={}, names={})
|
|
47
47
|
INTERFACES["oep.wire.rvswd"] = WIRE_RVSWD
|
|
48
|
-
WIRE_SWIO = _NS(name="oep.wire.swio", revision=1, target="RISC-V targets whose debug module is reached over the one-wire SWIO line", op={"scan": 0x10, "attach": 0x11, "detach": 0x12, "connections": 0x13}, lock_free={0x13},
|
|
48
|
+
WIRE_SWIO = _NS(name="oep.wire.swio", revision=1, target="RISC-V targets whose debug module is reached over the one-wire SWIO line", op={"scan": 0x10, "attach": 0x11, "detach": 0x12, "connections": 0x13, "holds": 0x14}, lock_free={0x13, 0x14},
|
|
49
49
|
tlv={"describe": {"max_connections": 0x40}, "scan": {"max_speed": 0x01}, "attach": {"reset": 0x04}, "attach_answer": {"target_id": 0x01, "dpc": 0x02}, "unavailable_payload": {"index": 0x40}, "unsupported_payload": {"index": 0x40}}, event={}, enum={"scan_kind": {"riscv_dm": 0x01}, "attach_flags": {"havereset_acked": 0x01, "halted": 0x08}, "features": {"attach_halt": 0x01}, "attach_method": {"run": 0x00, "halt": 0x01}, "pin_role": {"swdio": 0x01, "reset": 0x03}}, own={},
|
|
50
50
|
line_names={}, names={})
|
|
51
51
|
INTERFACES["oep.wire.swio"] = WIRE_SWIO
|
|
52
|
-
WIRE_SWD = _NS(name="oep.wire.swd", revision=1, target="ARM targets with an SWD debug port", op={"scan": 0x10, "attach": 0x11, "detach": 0x12, "connections": 0x13}, lock_free={0x13},
|
|
52
|
+
WIRE_SWD = _NS(name="oep.wire.swd", revision=1, target="ARM targets with an SWD debug port", op={"scan": 0x10, "attach": 0x11, "detach": 0x12, "connections": 0x13, "holds": 0x14}, lock_free={0x13, 0x14},
|
|
53
53
|
tlv={"describe": {"max_connections": 0x40}, "scan": {"max_speed": 0x01, "targetsel": 0x02}, "attach": {"targetsel": 0x02, "reset": 0x04}, "attach_answer": {"target_id": 0x01}, "connections_answer": {"selected_target": 0x01}, "unavailable_payload": {"index": 0x40}, "unsupported_payload": {"index": 0x40}}, event={}, enum={"scan_kind": {"arm_adi": 0x02}, "attach_flags": {"dormant_woken": 0x04}, "features": {}, "attach_method": {"run": 0x00}, "pin_role": {"swdio": 0x01, "swclk": 0x02, "reset": 0x03}}, own={},
|
|
54
54
|
line_names={}, names={})
|
|
55
55
|
INTERFACES["oep.wire.swd"] = WIRE_SWD
|
|
@@ -94,6 +94,6 @@ FIXTURE_CAPTURE_GROUP = _NS(name="oep.fixture.capture-group", revision=1, target
|
|
|
94
94
|
line_names={}, names={})
|
|
95
95
|
INTERFACES["oep.fixture.capture-group"] = FIXTURE_CAPTURE_GROUP
|
|
96
96
|
PROBE_CONFIG = _NS(name="oep.probe.config", revision=1, target=None, op={"get": 0x10, "set": 0x11, "save": 0x12, "erase": 0x13, "unset": 0x14, "state": 0x15, "network": 0x16}, lock_free={0x10, 0x15, 0x16},
|
|
97
|
-
tlv={"describe": {"storage": 0x40, "setting": 0x41, "netif": 0x42}}, event={}, enum={"idle_mode": {"hi_z": 0x00, "pull_up": 0x01, "pull_down": 0x02, "output_low": 0x03, "output_high": 0x04}, "ipv4_mode": {"dhcp": 0x00, "static": 0x01}, "network_state": {"off": 0x00, "connecting": 0x01, "up": 0x02, "waiting": 0x03}, "network_reason": {"none": 0x00, "not_found": 0x01, "auth": 0x02, "no_address": 0x03, "other": 0x04}, "setting_form": {"named": 0x00, "bytes": 0x01, "text": 0x02, "u32": 0x03, "ipv4": 0x04, "bool": 0x05}, "setting_flag": {"secret": 0x01, "restart": 0x02}, "storage_state": {"none": 0x00, "applied": 0x01, "unreadable": 0x02, "partial": 0x03}, "storage_unreadable": {"none": 0x00, "form": 0x01, "item": 0x02, "capacity": 0x03}}, own={},
|
|
98
|
-
line_names={'nrst': 'the target reset line (Host only)', 'power_hi': 'target power on when high (Host only)', 'power_lo': 'target power on when low (Host only)'}, names={'setting': {'label': 'key channel; text 1-32 bytes', 'idle': 'key channel; mode(u8)', 'disable': 'key channel; no data', 'passthrough': 'key port; fn(u16) rx(u16) baud(u32) format(u8)', 'console-passthrough': 'key port; wire_fn(u16) swdio(u16) swclk(u16) max_speed(u32) console_fn(u16) mechanism(u8)', 'wifi': 'key entry; ssid_len(u8) ssid pass_len(u8) passphrase; passphrase write-only', 'ipv4': 'key netif; mode(u8) address(4) prefix(u8) gateway(4) dns(4)'}, 'netif': {'wifi': 'Wi-Fi station', 'ethernet': 'wired Ethernet'}})
|
|
97
|
+
tlv={"describe": {"storage": 0x40, "setting": 0x41, "netif": 0x42}}, event={}, enum={"idle_mode": {"hi_z": 0x00, "pull_up": 0x01, "pull_down": 0x02, "output_low": 0x03, "output_high": 0x04}, "rvswd_rest_mode": {"auto": 0x00, "high": 0x01, "low": 0x02}, "wire_hold_mode": {"off": 0x00, "on": 0x01}, "ipv4_mode": {"dhcp": 0x00, "static": 0x01}, "network_state": {"off": 0x00, "connecting": 0x01, "up": 0x02, "waiting": 0x03}, "network_reason": {"none": 0x00, "not_found": 0x01, "auth": 0x02, "no_address": 0x03, "other": 0x04}, "setting_form": {"named": 0x00, "bytes": 0x01, "text": 0x02, "u32": 0x03, "ipv4": 0x04, "bool": 0x05}, "setting_flag": {"secret": 0x01, "restart": 0x02}, "storage_state": {"none": 0x00, "applied": 0x01, "unreadable": 0x02, "partial": 0x03}, "storage_unreadable": {"none": 0x00, "form": 0x01, "item": 0x02, "capacity": 0x03}}, own={},
|
|
98
|
+
line_names={'swdio': 'the debug data line (2 wires: rvswd, swd) (Host only)', 'swclk': 'the debug clock line (2 wires: rvswd, swd) (Host only)', 'swio': 'the one debug wire (swio) (Host only)', 'rx': 'the fixture UART receive, wired to the target TX (Host only)', 'tx': 'the fixture UART transmit, wired to the target RX (Host only)', 'nrst': 'the target reset line (Host only)', 'power_hi': 'target power on when high (Host only)', 'power_lo': 'target power on when low (Host only)'}, names={'setting': {'label': 'key channel; text 1-32 bytes', 'idle': 'key channel; mode(u8)', 'disable': 'key channel; no data', 'passthrough': 'key port; fn(u16) rx(u16) baud(u32) format(u8)', 'console-passthrough': 'key port; wire_fn(u16) swdio(u16) swclk(u16) max_speed(u32) console_fn(u16) mechanism(u8)', 'wifi': 'key entry; ssid_len(u8) ssid pass_len(u8) passphrase; passphrase write-only', 'ipv4': 'key netif; mode(u8) address(4) prefix(u8) gateway(4) dns(4)', 'rvswd-rest': "key channel (the port's SWCLK); mode(u8): rvswd_rest_mode", 'wire-hold': "key channel (the port's SWDIO); mode(u8): wire_hold_mode"}, 'netif': {'wifi': 'Wi-Fi station', 'ethernet': 'wired Ethernet'}})
|
|
99
99
|
INTERFACES["oep.probe.config"] = PROBE_CONFIG
|
oep_client/riscv.py
CHANGED
|
@@ -188,7 +188,7 @@ class WireBase(Interface):
|
|
|
188
188
|
"""oep.wire.<link>: scan / attach / detach / connections (oep-if-debug §0-§2). The shared part; each link's attach
|
|
189
189
|
takes its own arguments. A failed scan, attach or detach is completed failed with `status(u8) [TLV]` (common §3).
|
|
190
190
|
attach names its pins in the fixed part (§1): `pins` None takes the wire's one candidate pair (candidate_pairs)."""
|
|
191
|
-
SCAN, ATTACH, DETACH, CONNECTIONS = (_WIRE.op[k] for k in ("scan", "attach", "detach", "connections"))
|
|
191
|
+
SCAN, ATTACH, DETACH, CONNECTIONS, HOLDS = (_WIRE.op[k] for k in ("scan", "attach", "detach", "connections", "holds"))
|
|
192
192
|
REVISION = 1
|
|
193
193
|
TAG_RESET = _WIRE.tlv["attach"]["reset"] # channel(u16) hold_ms(u16), critical: attach under reset (§3)
|
|
194
194
|
TAG_SCAN_MAX_SPEED = _WIRE.tlv["scan"]["max_speed"]
|
|
@@ -197,6 +197,10 @@ class WireBase(Interface):
|
|
|
197
197
|
ONE_WIRE = False # swio: swclk is 0xFFFF (§3)
|
|
198
198
|
DEFAULT_MAX_SPEED = 1_000_000 # when the wire declares no max_clock_hz either
|
|
199
199
|
|
|
200
|
+
def _is_rvswd(self) -> bool:
|
|
201
|
+
"""The rest TLVs are rvswd's (debug §3.1); the name as opened, else the class's."""
|
|
202
|
+
return getattr(self, "name", self.NAME) == "oep.wire.rvswd"
|
|
203
|
+
|
|
200
204
|
def _description(self) -> catalog.Description:
|
|
201
205
|
return catalog.decode_description(b"".join(catalog.tlv(t, v) for t, v in describe(self.host, self.fn)))
|
|
202
206
|
|
|
@@ -295,6 +299,19 @@ class WireBase(Interface):
|
|
|
295
299
|
"""Close the connection and its streams (oep-if-debug §2)."""
|
|
296
300
|
self._call(self.DETACH, struct.pack("<I", conn))
|
|
297
301
|
|
|
302
|
+
def holds(self) -> list[tuple[tuple[int, int], int]]:
|
|
303
|
+
"""The ports this wire holds at their rest after a close (oep-if-debug §2.2, lock-free; paged like connections):
|
|
304
|
+
[((swdio, swclk), rest)] - rest: rvswd's idle_clock value (0 high, 1 low), 0 on other wires. Not connections."""
|
|
305
|
+
out: list[tuple[tuple[int, int], int]] = []
|
|
306
|
+
while True:
|
|
307
|
+
rd = m.Reader(self._call(self.HOLDS, bytes([len(out)]), locked=False).payload)
|
|
308
|
+
more, count = rd.take("BB")
|
|
309
|
+
page = [((d, c), r) for d, c, r in (rd.take("HHB") for _ in range(count))]
|
|
310
|
+
rd.tail()
|
|
311
|
+
out += page
|
|
312
|
+
if not more or not count or len(out) > 0xFF:
|
|
313
|
+
return out
|
|
314
|
+
|
|
298
315
|
def connections(self) -> list["ConnectionInfo"]:
|
|
299
316
|
"""The wire's live connections, in the order they were made (oep-if-debug §2.1, lock-free; paged: the request
|
|
300
317
|
is first(u8), the answer more count entries [TLV], followed until more is 0). swd's selected_target TLVs
|
|
@@ -307,17 +324,22 @@ class WireBase(Interface):
|
|
|
307
324
|
for _ in range(count):
|
|
308
325
|
conn, swdio, swclk, speed, scheme, n = rd.take("IHHIBB")
|
|
309
326
|
tid = rd.bytes(n)
|
|
310
|
-
page.append([conn, (swdio, swclk), speed, (scheme, tid) if scheme else None, None])
|
|
327
|
+
page.append([conn, (swdio, swclk), speed, (scheme, tid) if scheme else None, None, None])
|
|
311
328
|
for tag, value in m.split_tlvs(rd.rest()):
|
|
312
329
|
# entry_index(u8) targetsel(u32), repeatable; bit 7 in an answer: an unknown tag (core §2.2, §2.3)
|
|
313
330
|
if tag == _SELECTED_TARGET and len(value) == 5 and value[0] < len(page):
|
|
314
331
|
page[value[0]][4] = struct.unpack_from("<I", value, 1)[0]
|
|
332
|
+
# rvswd: entry_index(u8) idle_clock(u8) for every entry (debug §2.1, §3.1)
|
|
333
|
+
if self._is_rvswd() and tag == _REST and len(value) == 2 and value[0] < len(page):
|
|
334
|
+
page[value[0]][5] = _REST_NAMES.get(value[1])
|
|
315
335
|
out += [ConnectionInfo(*row) for row in page]
|
|
316
336
|
if not more or not count or len(out) > 0xFF:
|
|
317
337
|
return out
|
|
318
338
|
|
|
319
339
|
|
|
320
340
|
_SELECTED_TARGET = reg.WIRE_SWD.tlv["connections_answer"]["selected_target"]
|
|
341
|
+
_REST = reg.WIRE_RVSWD.tlv["connections_answer"]["idle_clock"]
|
|
342
|
+
_REST_NAMES = {v: k for k, v in reg.WIRE_RVSWD.enum["idle_clock"].items()}
|
|
321
343
|
|
|
322
344
|
|
|
323
345
|
@dataclass(frozen=True)
|
|
@@ -329,6 +351,7 @@ class ConnectionInfo:
|
|
|
329
351
|
speed_hz: int
|
|
330
352
|
target_id: tuple[int, bytes] | None
|
|
331
353
|
targetsel: int | None = None
|
|
354
|
+
idle_clock: str | None = None # rvswd: "high" / "low", the rest the connection uses (debug §3.1)
|
|
332
355
|
|
|
333
356
|
|
|
334
357
|
class Wire(WireBase):
|
|
@@ -342,6 +365,7 @@ class Wire(WireBase):
|
|
|
342
365
|
SCHEME_DMI_7F = reg.COMMON.enum["target_id_scheme"]["dmi_7f"] # the u32 at DMI 0x7F (oep-if-debug §3)
|
|
343
366
|
|
|
344
367
|
TAG_DPC = reg.WIRE_RVSWD.tlv["attach_answer"]["dpc"]
|
|
368
|
+
TAG_REST = reg.WIRE_RVSWD.tlv["attach_answer"]["idle_clock"]
|
|
345
369
|
|
|
346
370
|
def __init__(self, hst: h.Host, name: str = "oep.wire.rvswd"):
|
|
347
371
|
super().__init__(hst, name)
|
|
@@ -397,6 +421,10 @@ class Wire(WireBase):
|
|
|
397
421
|
dpc = tail.get(self.TAG_DPC)
|
|
398
422
|
# dpc is a u32: a value of another length is broken, not used (core §2.3)
|
|
399
423
|
self.dpc = struct.unpack("<I", dpc)[0] if self.halted and dpc is not None and len(dpc) == 4 else None
|
|
424
|
+
# rvswd: the rest the connection uses, the host's or the probe's choice from target_id (debug §3.1)
|
|
425
|
+
rest = tail.get(self.TAG_REST) if self._is_rvswd() else None
|
|
426
|
+
names = {v: k for k, v in self.IDLE_CLOCK.items()}
|
|
427
|
+
self.idle_clock = names.get(rest[0]) if rest is not None and len(rest) == 1 else None
|
|
400
428
|
return conn, status
|
|
401
429
|
|
|
402
430
|
def attach_under_reset(self, channel: int, hold_ms: int = 20, max_speed: int | None = None,
|
|
@@ -36,7 +36,7 @@ class Attachments(Dependencies):
|
|
|
36
36
|
return bytes(page)
|
|
37
37
|
def describe(self,fn):
|
|
38
38
|
if fn==4:return (tlv(DC['ops'],pack_ops(set(COP.values()))),*(tlv(CD['mechanism'],name.encode()) for name in self.mechanisms))
|
|
39
|
-
rows=[tlv(DC['ops'],pack_ops({WOP['attach'],WOP['detach'],WOP['connections']})),tlv(WT['describe']['max_connections'],b'\x02'),tlv(DC['min_clock_hz'],struct.pack('<I',1000))]
|
|
39
|
+
rows=[tlv(DC['ops'],pack_ops({WOP['attach'],WOP['detach'],WOP['connections'],WOP['holds']})),tlv(WT['describe']['max_connections'],b'\x02'),tlv(DC['min_clock_hz'],struct.pack('<I',1000))]
|
|
40
40
|
for index,pair in enumerate(self.pairs[fn]):
|
|
41
41
|
members=[(WE['pin_role']['swdio'],pair[0])] if fn==2 else [(WE['pin_role']['swdio'],pair[0]),(WE['pin_role']['swclk'],pair[1])]
|
|
42
42
|
rows.append(tlv(DC['channel_group'],bytes((index,len(members)))+b''.join(struct.pack('<BH',*row) for row in members)))
|
|
@@ -79,6 +79,7 @@ class Attachments(Dependencies):
|
|
|
79
79
|
def dispatch_request(self,fn,op,payload,tags):
|
|
80
80
|
if fn<=3:
|
|
81
81
|
if op==WOP['connections']:return self.connection_page(fn,payload[0])
|
|
82
|
+
if op==WOP['holds']:return b'\0\0'+extension() # no port held in this model (debug §2.2)
|
|
82
83
|
if op==WOP['detach']:
|
|
83
84
|
rid=struct.unpack('<I',payload)[0];self.resource(rid,'connection')
|
|
84
85
|
if self.connections[rid]['fn']!=fn:raise Reject(R['unavailable'],cause('wrong_state'))
|
|
@@ -131,7 +132,7 @@ class AttachModel(DependencyModel):
|
|
|
131
132
|
if len(request)>=6 and struct.unpack_from('<H',request,3)[0]==4:
|
|
132
133
|
e.fixed={COP['open']:5,COP['read']:15,COP['marks']:8,COP['clear']:4,COP['mark']:5,COP['write']:6,COP['close']:4,COP['streams']:2};e.free={COP['read'],COP['marks'],COP['streams']}
|
|
133
134
|
if request[5]==COP['write'] and len(request)>=16:e.fixed[COP['write']]=6+struct.unpack_from('<H',request,14)[0]
|
|
134
|
-
else:e.fixed={WOP['attach']:9,WOP['detach']:4,WOP['connections']:1};e.free={WOP['connections']}
|
|
135
|
+
else:e.fixed={WOP['attach']:9,WOP['detach']:4,WOP['connections']:1,WOP['holds']:1};e.free={WOP['connections'],WOP['holds']}
|
|
135
136
|
try:return self.ep.handle(request)
|
|
136
137
|
finally:e.fixed,e.free=fixed,free
|
|
137
138
|
def restart_fixture(self):
|
oep_client/virtual_bench.py
CHANGED
|
@@ -262,7 +262,7 @@ def _setting(name: str, keys: int, form: int = 0, max_len: int = 0, flags: int =
|
|
|
262
262
|
|
|
263
263
|
|
|
264
264
|
def _config(fn: int, instance: int, storage: int = 4096, *, channels: int = 64, ports: int = 4, netifs=(), wifi_entries: int = 4,
|
|
265
|
-
own=()) -> Offered:
|
|
265
|
+
own=(), rvswd: bool = False, hold: bool = True) -> Offered:
|
|
266
266
|
"""oep.probe.config's declarations (probe.config §4): the standard items label / idle / disable (keys: the
|
|
267
267
|
channels), passthrough / console-passthrough (keys: the transports), with `netifs` (names) wifi / ipv4 and the
|
|
268
268
|
network op; `own`: the probe's own items as (name, form, keys, max_len, flags) (§1.6). storage 0 = none - no storage
|
|
@@ -274,6 +274,10 @@ def _config(fn: int, instance: int, storage: int = 4096, *, channels: int = 64,
|
|
|
274
274
|
if "wifi" in netifs:
|
|
275
275
|
settings.append(_setting("wifi", wifi_entries))
|
|
276
276
|
settings.append(_setting("ipv4", len(netifs) + 1))
|
|
277
|
+
if rvswd: # its automatic rest can be turned off per port (debug §3.1)
|
|
278
|
+
settings.append(_setting("rvswd-rest", channels))
|
|
279
|
+
if hold: # a debug wire's hold after a close, per port (debug §2)
|
|
280
|
+
settings.append(_setting("wire-hold", channels))
|
|
277
281
|
settings += [_setting(name, keys, form, max_len, flags) for name, form, keys, max_len, flags in own]
|
|
278
282
|
return Offered(fn, instance, "oep.probe.config", ops_of("oep.probe.config", *drop)
|
|
279
283
|
+ ((catalog.u32(_CFG["storage"], storage),) if storage else ()) + tuple(settings)
|
|
@@ -388,7 +392,7 @@ def p4_x035() -> VirtualProbe:
|
|
|
388
392
|
max_stretch_us=100_000), # stretch
|
|
389
393
|
Offered(9, 0, "oep.fixture.spi-target", _roles({1: pins, 2: pins, 3: pins, 4: pins})
|
|
390
394
|
+ _spi_decl(max_length=64, max_hz=3_000_000, features=0b1, queue_depth=8)), # LSB first
|
|
391
|
-
_config(10, 0, channels=55, ports=4, netifs=("ethernet",)),
|
|
395
|
+
_config(10, 0, channels=55, ports=4, netifs=("ethernet",), rvswd=True),
|
|
392
396
|
# the P4's ADC1 (GPIO16-23): one ADC for all its channels, 611 Hz - 83.3 kHz in all; ESP32-style attenuations
|
|
393
397
|
Offered(11, 0, "oep.fixture.analog", _roles({k: list(range(16, 24)) for k in range(4)})
|
|
394
398
|
+ _analog_decl([(0, 0, 950), (1, 0, 1250), (2, 0, 1750), (3, 0, 3100)], 65536, 611, 83_333),
|
|
@@ -465,7 +469,7 @@ def p4_bench() -> VirtualProbe:
|
|
|
465
469
|
_console(3),
|
|
466
470
|
_gpio(4, pins),
|
|
467
471
|
_uart(5, 0, pins, 3_000_000),
|
|
468
|
-
_config(6, 0, channels=55, ports=4),
|
|
472
|
+
_config(6, 0, channels=55, ports=4, rvswd=True),
|
|
469
473
|
_link(7),
|
|
470
474
|
_plan(8),
|
|
471
475
|
_restart(9),
|
|
@@ -24,7 +24,7 @@ class OepDeclarations(Resources):
|
|
|
24
24
|
|
|
25
25
|
def describe(self, fn):
|
|
26
26
|
if fn==4:return (tlv(DC['ops'],pack_ops(set(COP.values()))),tlv(CD['mechanism'],b'dmdata'))
|
|
27
|
-
rows=[tlv(DC['ops'],pack_ops({WOP['attach'],WOP['detach'],WOP['connections']}))]
|
|
27
|
+
rows=[tlv(DC['ops'],pack_ops({WOP['attach'],WOP['detach'],WOP['connections'],WOP['holds']}))]
|
|
28
28
|
if fn==1:rows.append(tlv(WT['describe']['max_connections'],b'\x02'))
|
|
29
29
|
if fn==2:rows.append(tlv(DC['min_clock_hz'],struct.pack('<I',1000000)))
|
|
30
30
|
role=WE['pin_role']
|
|
@@ -35,7 +35,7 @@ class OepDeclarations(Resources):
|
|
|
35
35
|
return tuple(rows)
|
|
36
36
|
|
|
37
37
|
def dispatch(self, fn, op, payload):
|
|
38
|
-
if (fn!=4 and op
|
|
38
|
+
if (fn!=4 and op in (WOP['connections'],WOP['holds'])) or (fn==4 and op==COP['streams']):return b'\0\0'+extension()
|
|
39
39
|
raise Reject(R['no_resource']) # declarations only: no simulated attach or target work
|
|
40
40
|
|
|
41
41
|
|
|
@@ -50,6 +50,6 @@ class OepDeclarationModel(ResourceModel):
|
|
|
50
50
|
if len(request)>=6:
|
|
51
51
|
fn=struct.unpack_from('<H',request,3)[0]
|
|
52
52
|
if fn==4:e.fixed={COP['open']:5,COP['read']:15,COP['marks']:8,COP['clear']:4,COP['mark']:5,COP['write']:6,COP['close']:4,COP['streams']:2};e.free={COP['read'],COP['marks'],COP['streams']}
|
|
53
|
-
else:e.fixed={WOP['attach']:9,WOP['detach']:4,WOP['connections']:1};e.free={WOP['connections']}
|
|
53
|
+
else:e.fixed={WOP['attach']:9,WOP['detach']:4,WOP['connections']:1,WOP['holds']:1};e.free={WOP['connections'],WOP['holds']}
|
|
54
54
|
try:return super().handle(request)
|
|
55
55
|
finally:e.fixed,e.free=fixed,free
|
|
@@ -8,7 +8,7 @@ from . import registry as _reg
|
|
|
8
8
|
# Each interface's mandatory ops, by name (looked up in the registry: never literal numbers).
|
|
9
9
|
_MANDATORY={
|
|
10
10
|
'oep.probe.plan':('plan_apply','plan_release'),'oep.probe.restart':('restart',),'oep.probe.link':('source','sink'),
|
|
11
|
-
**{w:('attach','detach','connections') for w in ('oep.wire.rvswd','oep.wire.swio','oep.wire.swd')},
|
|
11
|
+
**{w:('attach','detach','connections','holds') for w in ('oep.wire.rvswd','oep.wire.swio','oep.wire.swd')},
|
|
12
12
|
'oep.target.riscv-dm':('dmi',),'oep.target.arm-adi':('transfer',),
|
|
13
13
|
'oep.target.console':('open','read','close','streams'),
|
|
14
14
|
'oep.fixture.gpio':('set','read'),'oep.fixture.uart':('configure','read','status'),
|
|
@@ -14,7 +14,7 @@ KIND = _reg.WIRE_RVSWD.enum["scan_kind"] | _reg.WIRE_SWD.enum["scan_kind"]
|
|
|
14
14
|
|
|
15
15
|
class ScanOptions(WireOptions):
|
|
16
16
|
def __init__(self,ep):super().__init__(ep);self.scan_limit=2;self.identities={};self.scan_live=set();self.scan_events=[]
|
|
17
|
-
def describe(self,fn):return tuple(tlv(DC['ops'],pack_ops({WOP['scan'],WOP['attach'],WOP['detach'],WOP['connections']})) if fn<=3 and row[0]==DC['ops'] else row for row in super().describe(fn))
|
|
17
|
+
def describe(self,fn):return tuple(tlv(DC['ops'],pack_ops({WOP['scan'],WOP['attach'],WOP['detach'],WOP['connections'],WOP['holds']})) if fn<=3 and row[0]==DC['ops'] else row for row in super().describe(fn))
|
|
18
18
|
def request_tags(self,fn,op,data):
|
|
19
19
|
if fn>3 or op!=WOP['scan']:return super().request_tags(fn,op,data)
|
|
20
20
|
known={SC['max_speed']:4}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: oep-client-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.34
|
|
4
4
|
Summary: Open Embedded Probe (OEP) v1 host: serial / USB / TCP transports, the session rules, the standard interfaces, and a virtual bench
|
|
5
5
|
Project-URL: Homepage, https://github.com/Open-Embedded-Probe/oep-client-python
|
|
6
6
|
Project-URL: Repository, https://github.com/Open-Embedded-Probe/oep-client-python
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
oep_client/__init__.py,sha256=
|
|
2
|
-
oep_client/__main__.py,sha256=
|
|
1
|
+
oep_client/__init__.py,sha256=WV8YLSlZH0QElGgErRZN3FX_Ujze1gxUJA5gNUbc0M4,164
|
|
2
|
+
oep_client/__main__.py,sha256=7NYC9SZWd7M17YhOpjj15nu7_7-mEF_cQMfXJVkg53c,33449
|
|
3
3
|
oep_client/arm.py,sha256=SszSLae8zErD4T-a1uQ6s6o3Lz7pe2SR8d3Qy-wi5rY,15633
|
|
4
4
|
oep_client/capture.py,sha256=AFyJ-_DRb3ztYlvmFCDh9siNaKrwChOx8xvoNH6CKsI,48884
|
|
5
5
|
oep_client/catalog.py,sha256=WkaksNn9cFvkTRnvtX4I9g1AvkFoldEiNQaKiyPQ7_E,10772
|
|
6
6
|
oep_client/ch32_flash.py,sha256=lY_HKxf2foSfhNda91lcWefgRnY9ebTljsg4zJEj-Ks,12885
|
|
7
7
|
oep_client/cobs.py,sha256=RbdPrqgzD3SukxOIXX9m2dZpVTud8xAYYEhm3rcW2nA,2513
|
|
8
|
-
oep_client/config.py,sha256=
|
|
8
|
+
oep_client/config.py,sha256=1X66HIrT9WCvj7MjhPDET-jIrNyaQU1AM-wGq8BHpp8,30487
|
|
9
9
|
oep_client/conformance.py,sha256=phKkP_vT_8b9MyQiJU0p7JoCnkrPbPdkolcEqaD61TI,62859
|
|
10
10
|
oep_client/conformance_allocator.py,sha256=83xXiJ7c-6-M86615Kvq6yDD0sf0Z6lIOKDtbYcKeyM,6274
|
|
11
11
|
oep_client/conformance_allocator_sample.py,sha256=GGv43xbDNSPjQ8_flNXe-QbkLOr-NCbPpWPOfbD_wH8,879
|
|
@@ -178,7 +178,7 @@ oep_client/core.py,sha256=VPtgaVe2K5-HJMi72DDbIpIirATAXjcJSKautKUFgOM,22677
|
|
|
178
178
|
oep_client/decode.py,sha256=6pTFVBT0MLAKedZLVKyd5yT8hjrRw4fdDjnoDiE9XRs,2380
|
|
179
179
|
oep_client/discovery.py,sha256=OS6c4Qa8N32zbOHmw75e1y6ff3SQrL0uLtyPi7BWKUA,20187
|
|
180
180
|
oep_client/dump.py,sha256=31v4Hg27Het3cu3joCm6ttTWxQHSvvNryyGo8pwHXO4,12788
|
|
181
|
-
oep_client/endpoint.py,sha256=
|
|
181
|
+
oep_client/endpoint.py,sha256=Fqo5nSsWoqRulnwVujMhpNn4Uux57oZiuLrTKx5kcbk,229246
|
|
182
182
|
oep_client/fixture.py,sha256=GbAPXAk9GQ0qG6STL-rY4-EqXftmKfNKVI2xZZUiFzA,18108
|
|
183
183
|
oep_client/frames.py,sha256=OrvoNugGB4ewfmILhwBiJfI9sQ09i-RzO47LESVbWXw,4800
|
|
184
184
|
oep_client/hardware.py,sha256=RboxkRxkQJ3DbNJEXyyiOFYlLFKGxrlcSwHho8zFC1U,30256
|
|
@@ -192,8 +192,8 @@ oep_client/message.py,sha256=-WgnHcmnhJ6pozYvZP_FS8dr0ZMg2Wp9rTdRReGvogU,13116
|
|
|
192
192
|
oep_client/names.py,sha256=ohrRvQNURraYyJ-BtjHRwxGt_GLq-t8IO0aOji9Dhe0,3531
|
|
193
193
|
oep_client/pins.py,sha256=YVM0JXjCKcMMMWChtWsQoSKU4-OTOnDbZjo_mo_5hD0,32574
|
|
194
194
|
oep_client/pytest_conformance.py,sha256=-bcJU2E1UE4xjQ7HZjaIgtp0l65cIibBTNFnaW2tews,2929
|
|
195
|
-
oep_client/registry.py,sha256=
|
|
196
|
-
oep_client/riscv.py,sha256=
|
|
195
|
+
oep_client/registry.py,sha256=8ynarYyjC54IuD45PgXAzxIkpEs9ly-QAX2uZWIk1qI,16869
|
|
196
|
+
oep_client/riscv.py,sha256=_98qwqnxUIj9vitWIz6iJfJKsT63FNsQH9eZrZnqB4g,59939
|
|
197
197
|
oep_client/rp2350.py,sha256=7OMyj270k3AY1e3-ljicPwOjf9jOlokbT6QNkA0b0yY,4227
|
|
198
198
|
oep_client/speed_record.py,sha256=6THUKbN7gQKpfIdUO2551P8mXiAFqbFDky72Xbki7L0,7229
|
|
199
199
|
oep_client/targets.py,sha256=zFekSr76NVO7tzdgyQsLDHLWRvkgylfwvGNTz_hQGf8,5041
|
|
@@ -202,8 +202,8 @@ oep_client/usb_stream.py,sha256=pFbQexircX88eV8MZG7ECfUtRXCboBr-4-dtahvF6ro,1135
|
|
|
202
202
|
oep_client/virtual_allocator_model.py,sha256=XBrbjG2Zupf0P28gjUhQVDw6UzO2jQXsYZ1E1qAIKc8,1570
|
|
203
203
|
oep_client/virtual_analog_model.py,sha256=iV5pyJ1sy37D6VMy4qVmgzsmq4gLwEPMcdOGXPIin1Q,13872
|
|
204
204
|
oep_client/virtual_arm_adi_model.py,sha256=xlqhjr_QCF6MNVOnVelTanlNWpmaCJ5uWXf6V9WkqUM,8121
|
|
205
|
-
oep_client/virtual_attach_model.py,sha256=
|
|
206
|
-
oep_client/virtual_bench.py,sha256=
|
|
205
|
+
oep_client/virtual_attach_model.py,sha256=YhGYz4X2IcdBMsBb0vjkV2UWUst6tSVieZHUwQZ89gA,10433
|
|
206
|
+
oep_client/virtual_bench.py,sha256=6wJNW5odjS5UpeLfUTOIgKfWM_aXdPcqIrtTHGnZbFI,36137
|
|
207
207
|
oep_client/virtual_bench_capture.py,sha256=hIIOUOqKcSxBnsCb9rcyzYFqSsBmuic7sAkF7zsBAIs,52208
|
|
208
208
|
oep_client/virtual_bench_mdns.py,sha256=cnJNBKg5zTduXUcH-wqN_ZBDGl7DyL-tL3CBn2jro0E,13305
|
|
209
209
|
oep_client/virtual_bench_serial.py,sha256=EvbdOYeGrM6CRti2b90I0PzpmmherfoJRaAg4v7Iq9Y,13205
|
|
@@ -238,12 +238,12 @@ oep_client/virtual_logic_model.py,sha256=_a4z_BLYWaoPS2o4NqWiNEElFBWI2pkCc0psf4h
|
|
|
238
238
|
oep_client/virtual_mailbox_model.py,sha256=VNjwg-_E_djPwpys95RIWg26_DctS6uqo16Kg5i5ckA,2996
|
|
239
239
|
oep_client/virtual_names.py,sha256=p72ME65R9Jy56xmVYnVCtapK3Id8iFpAdI5xLe2tNZ0,1449
|
|
240
240
|
oep_client/virtual_notification_model.py,sha256=iUYcQidlUq2DJ6emd5VXwuPMRw2bOKuSa0A_fc2_OVM,2673
|
|
241
|
-
oep_client/virtual_oep_declaration_model.py,sha256=
|
|
241
|
+
oep_client/virtual_oep_declaration_model.py,sha256=LrhcsEAnG-hwyrXaio9owpOEZj6oC2gGK5HIl9kwwPk,2951
|
|
242
242
|
oep_client/virtual_outcome_model.py,sha256=cmMiM9XqhqCZ523YShkWFDKAn6NPMM6hnWNxqBGPxVc,3704
|
|
243
243
|
oep_client/virtual_pipeline_model.py,sha256=3XHOQAB1pm5wOxfVBtIK60L_N_d-fUpSgVX0S_5-V7E,4063
|
|
244
244
|
oep_client/virtual_port_speed_execution_model.py,sha256=vgUSHHmMNFC1UL6s1S_0t9wJg_dDUYXTJ7NXqM5lPfg,1676
|
|
245
245
|
oep_client/virtual_port_speed_model.py,sha256=1gI0tlN4ObTm3B1Ys1BsEJ02c6jYAqfIbaFFbVmn7cE,8680
|
|
246
|
-
oep_client/virtual_registered_ops_model.py,sha256=
|
|
246
|
+
oep_client/virtual_registered_ops_model.py,sha256=MRYX08hhfsY1IH3MHzdWz9uEiTN5OQBTU08fuEWUWVc,2730
|
|
247
247
|
oep_client/virtual_reset_model.py,sha256=mfIMxArHv7LRZjC9tjYkVJ0flYuZlOVrJChWmgiNh1Y,4303
|
|
248
248
|
oep_client/virtual_resource_model.py,sha256=XTYxvgIz371EQJ4-vKs3arqSf4kV8nNYKgFjFmRc6dk,3151
|
|
249
249
|
oep_client/virtual_restart_model.py,sha256=ZDHo4MCF1EuP1eo0utKqCZUOHTl6cIgFO8FOQqEqUGM,4651
|
|
@@ -252,7 +252,7 @@ oep_client/virtual_retry_model.py,sha256=2Vf7Seic2FX7Mwf3UR4UHBGJYHuqpKIaxgMmURy
|
|
|
252
252
|
oep_client/virtual_route_model.py,sha256=k3bWMKakWzDNnoaqK80LKWK5RTU2q1eYhAK_OSlgOLY,7299
|
|
253
253
|
oep_client/virtual_run_model.py,sha256=qYFb1vmhbJFnoM00Tq1Kj0x3T8GVlY5Nb4rsBxGG3Nw,6464
|
|
254
254
|
oep_client/virtual_scan_model.py,sha256=9Q5h4hRfFdPzEKwjgIPFa0vl5OYax08AVa4JJDsnMeI,4330
|
|
255
|
-
oep_client/virtual_scan_options_model.py,sha256=
|
|
255
|
+
oep_client/virtual_scan_options_model.py,sha256=_aFpSL5wXzicZSflniaov5G3NABeTqstEE8yLmbnIk4,6214
|
|
256
256
|
oep_client/virtual_selected_metadata_model.py,sha256=uKUy73sAqVHt1xPuY3LgjYbq-BMLZ_jnU70VYGfDo9g,934
|
|
257
257
|
oep_client/virtual_spi_model.py,sha256=uKcA0bG1v4tMCm8IFOmlCzC9CN3d2MFPSUf4wP0jS88,10258
|
|
258
258
|
oep_client/virtual_step_model.py,sha256=g-CfAloAktuf3oB9eR8wB291Pnh0leezaP1Drqrbr_Q,4200
|
|
@@ -261,8 +261,8 @@ oep_client/virtual_tlv_request_model.py,sha256=gp-eiN784NnloQBANG_4TnM6oUCmrj046
|
|
|
261
261
|
oep_client/virtual_uart_model.py,sha256=Sd_MWLjl1-6_bemsCuI2at9zpVJRBehFQgZ6By97PdQ,9386
|
|
262
262
|
oep_client/virtual_wire_options_model.py,sha256=1QNF16hRWmdFt0J7Zd1WKZdEc07vns6osMQLetMBlXw,9206
|
|
263
263
|
oep_client/virtual_writer_lease_model.py,sha256=E1MAQsGuShqONVdIeLubun7Cop0URNUSk8wicPicbt4,1728
|
|
264
|
-
oep_client_python-0.0.
|
|
265
|
-
oep_client_python-0.0.
|
|
266
|
-
oep_client_python-0.0.
|
|
267
|
-
oep_client_python-0.0.
|
|
268
|
-
oep_client_python-0.0.
|
|
264
|
+
oep_client_python-0.0.34.dist-info/METADATA,sha256=epM_SIcto4RoFOoLl3JP7ZRYwMi5lf-XBvU9n2Ufzi4,52711
|
|
265
|
+
oep_client_python-0.0.34.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
266
|
+
oep_client_python-0.0.34.dist-info/entry_points.txt,sha256=XEt39Jz7oQyU9Hfg_tV6eDGofNxWYrjSfslxKP-fh70,135
|
|
267
|
+
oep_client_python-0.0.34.dist-info/licenses/LICENSE,sha256=ntlalztb3L1OaNAxmQdHlubu5fVhjMc9oRZi3PeQhXg,1076
|
|
268
|
+
oep_client_python-0.0.34.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|