oep-client-python 0.0.5__py3-none-any.whl → 0.0.7__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 +6 -1
- oep_client/capture.py +7 -5
- oep_client/config.py +12 -7
- oep_client/endpoint.py +28 -10
- oep_client/fake.py +2 -2
- oep_client/fake_serve.py +2 -2
- oep_client/interfaces.py +3 -4
- oep_client/registry.py +5 -5
- oep_client/riscv.py +31 -12
- {oep_client_python-0.0.5.dist-info → oep_client_python-0.0.7.dist-info}/METADATA +1 -1
- {oep_client_python-0.0.5.dist-info → oep_client_python-0.0.7.dist-info}/RECORD +15 -15
- {oep_client_python-0.0.5.dist-info → oep_client_python-0.0.7.dist-info}/WHEEL +0 -0
- {oep_client_python-0.0.5.dist-info → oep_client_python-0.0.7.dist-info}/entry_points.txt +0 -0
- {oep_client_python-0.0.5.dist-info → oep_client_python-0.0.7.dist-info}/licenses/LICENSE +0 -0
oep_client/__init__.py
CHANGED
oep_client/__main__.py
CHANGED
|
@@ -64,6 +64,10 @@ def _config_parser(sub) -> None:
|
|
|
64
64
|
slot.add_argument("--attach", choices=sorted(config.ATTACH), default="host")
|
|
65
65
|
slot.add_argument("--retry", type=int, default=0, help="at-boot: try again every N s while absent (0: never)")
|
|
66
66
|
slot.add_argument("--mechanism", choices=sorted(config.MECHANISM), default="dmseq")
|
|
67
|
+
slot.add_argument("--max-speed", type=int, default=0,
|
|
68
|
+
help="the line's ceiling in Hz for the probe's own attach (0: none); the target's, e.g. 1000000")
|
|
69
|
+
slot.add_argument("--idle-clock", choices=sorted(config.IDLE_CLOCK), default="high",
|
|
70
|
+
help="rvswd: SWCLK while the line rests (the target's: low on CH32L103 / V203)")
|
|
67
71
|
slot.add_argument("--lock", help="MASK:VALUE (hex u32) the target_id (WCH DMI 0x7F) must match, e.g. ffffff0f:035e0600")
|
|
68
72
|
slot.add_argument("--save", action="store_true", help="save after the change")
|
|
69
73
|
bind = cs.add_parser("bind", help="what a serial port carries")
|
|
@@ -183,7 +187,7 @@ def _config(args) -> int:
|
|
|
183
187
|
mask, _, value = args.lock.partition(":")
|
|
184
188
|
lock = (1, struct.pack("<I", int(mask, 16)), struct.pack("<I", int(value, 16)))
|
|
185
189
|
it = config.Slot(args.slot, fn, _pins(hst, fn, args.pins, args.wire), args.name, args.attach, args.retry,
|
|
186
|
-
args.mechanism, lock)
|
|
190
|
+
args.mechanism, lock, args.max_speed, args.idle_clock)
|
|
187
191
|
_change(hst, cfg, [it], args.save)
|
|
188
192
|
elif args.action == "bind":
|
|
189
193
|
slots = {it.name: it.slot for it in cfg.items() if isinstance(it, config.Slot)}
|
|
@@ -250,6 +254,7 @@ def _show(hst, cfg, as_json: bool) -> int:
|
|
|
250
254
|
s = by_slot.get(it.slot)
|
|
251
255
|
pins = f"{it.pins[0]}" if it.pins[1] == 0xFFFF else f"{it.pins[0]},{it.pins[1]}"
|
|
252
256
|
retry = f" retry {it.retry_s} s" if it.attach == "at-boot" else ""
|
|
257
|
+
retry += (f" max {it.max_speed} Hz" if it.max_speed else "") + (" idle-low" if it.idle_clock == "low" else "")
|
|
253
258
|
lock = (f" lock {int.from_bytes(it.lock[1], 'little'):08x}:{int.from_bytes(it.lock[2], 'little'):08x}"
|
|
254
259
|
if it.lock else "")
|
|
255
260
|
live = ""
|
oep_client/capture.py
CHANGED
|
@@ -347,18 +347,20 @@ class LogicCapture(Interface):
|
|
|
347
347
|
return [(data[(i * c.width + bit0) >> 3] >> ((i * c.width + bit0) & 7)) & 1 for i in range(n)]
|
|
348
348
|
|
|
349
349
|
def to_sr(self, path: str, data: bytes, samples: int, names: list[str] | None = None) -> None:
|
|
350
|
-
"""A sigrok session file:
|
|
350
|
+
"""A sigrok session file: bit k of each sample = channel k, one byte per sample up to 8 channels, two (little
|
|
351
|
+
endian, sigrok's unitsize 2) up to 16."""
|
|
351
352
|
c = self.config
|
|
352
353
|
n_ch = len(c.positions)
|
|
353
|
-
if n_ch
|
|
354
|
-
|
|
354
|
+
unit = 1 if n_ch <= 8 else 2
|
|
355
|
+
if n_ch > 16:
|
|
356
|
+
raise ValueError("to_sr writes up to 16 channels")
|
|
355
357
|
names = names or [f"D{k}" for k in range(n_ch)]
|
|
356
358
|
chans = [self.channel(data, k, samples) for k in range(n_ch)]
|
|
357
|
-
out =
|
|
359
|
+
out = b"".join(sum(chans[k][i] << k for k in range(n_ch)).to_bytes(unit, "little") for i in range(samples))
|
|
358
360
|
rate = c.rate.numerator // c.rate.denominator
|
|
359
361
|
meta = ["[global]", "sigrok version=0.5.2", "", "[device 1]", "capturefile=logic-1",
|
|
360
362
|
f"total probes={n_ch}", f"samplerate={rate} Hz", "total analog=0"]
|
|
361
|
-
meta += [f"probe{k + 1}={nm}" for k, nm in enumerate(names)] + ["unitsize=
|
|
363
|
+
meta += [f"probe{k + 1}={nm}" for k, nm in enumerate(names)] + [f"unitsize={unit}", ""]
|
|
362
364
|
with zipfile.ZipFile(path, "w") as z:
|
|
363
365
|
z.writestr("version", "2")
|
|
364
366
|
z.writestr("metadata", "\n".join(meta))
|
oep_client/config.py
CHANGED
|
@@ -29,6 +29,7 @@ MECHANISM = {k: v for k, v in reg.TARGET_CONSOLE.enum["mechanism"].items()}
|
|
|
29
29
|
SLOT_STATE = {v: k.replace("_", "-") for k, v in _CFG.enum["slot_state"].items()}
|
|
30
30
|
BIND_FLOW = {v: k for k, v in _CFG.enum["bind_flow"].items()}
|
|
31
31
|
IDLE = {k.replace("_", "-"): v for k, v in _CFG.enum["idle_mode"].items()}
|
|
32
|
+
IDLE_CLOCK = dict(reg.WIRE_RVSWD.enum["idle_clock"]) # a slot's idle_clock (oep-if-debug §3)
|
|
32
33
|
STORAGE_STATE = {v: k for k, v in _CFG.enum["storage_state"].items()}
|
|
33
34
|
|
|
34
35
|
|
|
@@ -79,12 +80,15 @@ class Slot:
|
|
|
79
80
|
retry_s: int = 0 # at-boot: try again every retry_s while the target is not there (0: never)
|
|
80
81
|
mechanism: str = "dmseq" # sdi, dmdata, dmseq
|
|
81
82
|
lock: tuple[int, bytes, bytes] | None = None
|
|
83
|
+
max_speed: int = 0 # the line's ceiling in Hz for the probe's own attach (0: none) - the target's
|
|
84
|
+
idle_clock: str = "high" # rvswd: SWCLK while the line rests, high / low - the target's (oep-if-debug §3)
|
|
82
85
|
TAG = ITEM["slot"]
|
|
83
86
|
|
|
84
87
|
def value(self) -> bytes:
|
|
85
88
|
name = self.name.encode()
|
|
86
|
-
v = struct.pack("<
|
|
87
|
-
self.retry_s if self.attach == "at-boot" else 0,
|
|
89
|
+
v = struct.pack("<BHHHBHIBBB", self.slot, self.wire_fn, *self.pins, ATTACH[self.attach],
|
|
90
|
+
self.retry_s if self.attach == "at-boot" else 0, self.max_speed, IDLE_CLOCK[self.idle_clock],
|
|
91
|
+
MECHANISM[self.mechanism], len(name)) + name
|
|
88
92
|
if self.lock is None:
|
|
89
93
|
return v + b"\x00"
|
|
90
94
|
scheme, mask, value = self.lock
|
|
@@ -127,15 +131,16 @@ def decode(tag: int, v: bytes):
|
|
|
127
131
|
return Label(struct.unpack_from("<H", v)[0], v[2:].decode("utf-8", "replace"))
|
|
128
132
|
if tag == ITEM["idle"] and len(v) == 3:
|
|
129
133
|
return Idle(struct.unpack_from("<H", v)[0], _name(IDLE, v[2]))
|
|
130
|
-
if tag == ITEM["slot"] and len(v) >=
|
|
131
|
-
n, wire_fn, swdio, swclk, attach, retry_s, mech, name_len = struct.unpack_from("<
|
|
132
|
-
name = v[
|
|
133
|
-
rest = v[
|
|
134
|
+
if tag == ITEM["slot"] and len(v) >= 18:
|
|
135
|
+
n, wire_fn, swdio, swclk, attach, retry_s, max_speed, idle, mech, name_len = struct.unpack_from("<BHHHBHIBBB", v)
|
|
136
|
+
name = v[17:17 + name_len].decode("ascii", "replace")
|
|
137
|
+
rest = v[17 + name_len:]
|
|
134
138
|
lock = None
|
|
135
139
|
if rest and rest[0]:
|
|
136
140
|
half = (len(rest) - 1) // 2
|
|
137
141
|
lock = (rest[0], rest[1:1 + half], rest[1 + half:])
|
|
138
|
-
return Slot(n, wire_fn, (swdio, swclk), name, _name(ATTACH, attach), retry_s, _name(MECHANISM, mech), lock
|
|
142
|
+
return Slot(n, wire_fn, (swdio, swclk), name, _name(ATTACH, attach), retry_s, _name(MECHANISM, mech), lock,
|
|
143
|
+
max_speed, _name(IDLE_CLOCK, idle))
|
|
139
144
|
if tag == ITEM["bind"] and len(v) >= 4:
|
|
140
145
|
port, mode, selected, n = struct.unpack_from("<BBBB", v)
|
|
141
146
|
streams = [(_name(STREAM, v[4 + 3 * k]), struct.unpack_from("<H", v, 5 + 3 * k)[0]) for k in range(n)]
|
oep_client/endpoint.py
CHANGED
|
@@ -60,6 +60,7 @@ WIRES = ("oep.wire.rvswd", "oep.wire.swio")
|
|
|
60
60
|
OWNER = reg.CORE.tlv["open"]["owner"]
|
|
61
61
|
SLOT_NAME = re.compile(r"[a-z0-9_-]{1,32}")
|
|
62
62
|
NO_SLOT, NEVER = 0xFF, 0xFFFFFFFF
|
|
63
|
+
PIN_ROLE_RESET = reg.WIRE_RVSWD.enum["pin_role"]["reset"] # the channels attach_under_reset may take
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
class Reject(Exception):
|
|
@@ -180,6 +181,7 @@ class Connection:
|
|
|
180
181
|
speed: int = 4_000_000
|
|
181
182
|
tid: int | None = None
|
|
182
183
|
users: set = field(default_factory=set) # "host" and/or ("slot", n)
|
|
184
|
+
idle_clock: int = 0 # rvswd: SWCLK while the line rests, 0 high / 1 low
|
|
183
185
|
|
|
184
186
|
|
|
185
187
|
@dataclass(frozen=True)
|
|
@@ -189,6 +191,8 @@ class Slot:
|
|
|
189
191
|
pair: tuple[int, int]
|
|
190
192
|
attach: int
|
|
191
193
|
retry_s: int
|
|
194
|
+
max_speed: int # 0: no ceiling (oep-if-debug §3: the target's, the host's to set)
|
|
195
|
+
idle_clock: int
|
|
192
196
|
mechanism: int
|
|
193
197
|
name: str
|
|
194
198
|
lock: tuple[int, bytes, bytes] | None # (scheme, mask, value)
|
|
@@ -249,9 +253,13 @@ class Endpoint:
|
|
|
249
253
|
self.serial_ports = {i for i, k in enumerate(self.transports) if k in fake.SERIAL_KINDS}
|
|
250
254
|
self.pairs: dict[int, list[tuple[int, int]]] = {} # wire fn -> allowed (swdio, swclk), declared order
|
|
251
255
|
self.max_connections: dict[int, int] = {}
|
|
256
|
+
self.reset_channels: dict[int, set[int]] = {} # wire fn -> channels attach_under_reset may take (role 3)
|
|
252
257
|
for fn, name in self.names.items():
|
|
253
258
|
if name in WIRES:
|
|
254
259
|
self.pairs[fn] = [self._group_pair(name, t) for t in self.static[fn] if t[0] == catalog.CHANNEL_GROUP]
|
|
260
|
+
self.reset_channels[fn] = {
|
|
261
|
+
c for t in self.static[fn] if t[0] == catalog.ROLE_CHANNELS and t[2] == PIN_ROLE_RESET
|
|
262
|
+
for c in catalog.bitmap_to_channels(struct.unpack_from("<H", t, 3)[0], t[5:2 + t[1]])}
|
|
255
263
|
self.max_connections[fn] = next((t[2] for t in self.static[fn] if t[0] == fake.MAX_CONNECTIONS), 1)
|
|
256
264
|
self.targets: dict[tuple[int, tuple[int, int]], FakeTarget] = {
|
|
257
265
|
(fn, p): FakeTarget() for fn in sorted(self.pairs) for p in self.pairs[fn]}
|
|
@@ -575,17 +583,18 @@ class Endpoint:
|
|
|
575
583
|
got.append(struct.unpack_from("<HBH", value))
|
|
576
584
|
elif tag & m.TAG_CRITICAL:
|
|
577
585
|
return m.REJECTED, m.UNSUPPORTED, bytes([tag])
|
|
578
|
-
self._check_plan(got)
|
|
579
586
|
named = {fn for fn, _, _ in got}
|
|
587
|
+
if named & self.plan_from_config: # the settings' plan is the settings' (core §8)
|
|
588
|
+
raise Reject(m.UNAVAILABLE)
|
|
589
|
+
self._check_plan(got)
|
|
580
590
|
self.plan = {a for a in self.plan if a[0] not in named} | set(got)
|
|
581
|
-
self.plan_from_config -= named
|
|
582
591
|
return m.COMPLETED, m.SUCCESS, b""
|
|
583
592
|
if op == m.OP_PLAN_RELEASE: # n(u8) n x fn(u16); n = 0: every fn
|
|
584
593
|
n = t.take("B")
|
|
585
594
|
fns = {t.take("H") for _ in range(n)}
|
|
586
595
|
t.tail()
|
|
587
|
-
for fn in {a[0] for a in self.plan if not fns or a[0] in fns}:
|
|
588
|
-
self._drop_plan(fn)
|
|
596
|
+
for fn in {a[0] for a in self.plan if not fns or a[0] in fns} - self.plan_from_config:
|
|
597
|
+
self._drop_plan(fn) # the settings' plans stay, n = 0 too (core §8)
|
|
589
598
|
return m.COMPLETED, m.SUCCESS, b""
|
|
590
599
|
return m.REJECTED, m.UNKNOWN_OPERATION, b""
|
|
591
600
|
|
|
@@ -657,10 +666,14 @@ class Endpoint:
|
|
|
657
666
|
if method > 1:
|
|
658
667
|
raise Reject(m.UNSUPPORTED)
|
|
659
668
|
else:
|
|
660
|
-
channel, _hold = t.take("HH")
|
|
661
|
-
if channel not in (
|
|
669
|
+
channel, _hold = t.take("HH") # no default reset line (oep-if-debug §3)
|
|
670
|
+
if channel not in self.reset_channels.get(fn, set()) or any(a[2] == channel for a in self.plan):
|
|
662
671
|
raise Reject(m.UNAVAILABLE)
|
|
663
|
-
|
|
672
|
+
rvswd = self.names[fn] == "oep.wire.rvswd"
|
|
673
|
+
got, ignored = t.tail({0x01, 0x03, 0x04} if rvswd else {0x01, 0x03})
|
|
674
|
+
if 0x04 in got and (len(got[0x04]) != 1 or got[0x04][0] > 1):
|
|
675
|
+
raise Reject(m.MALFORMED)
|
|
676
|
+
idle_clock = got[0x04][0] if 0x04 in got else 0
|
|
664
677
|
pair = self._pick_pair(fn, got)
|
|
665
678
|
tg = self.targets[(fn, pair)]
|
|
666
679
|
speed = min(4_000_000, struct.unpack("<I", got[0x01])[0]) if 0x01 in got else 4_000_000
|
|
@@ -676,6 +689,7 @@ class Endpoint:
|
|
|
676
689
|
flags |= 2
|
|
677
690
|
self.conns[cid].speed = min(self.conns[cid].speed, speed)
|
|
678
691
|
c = self.conns[cid]
|
|
692
|
+
c.idle_clock = idle_clock # an existing connection takes the new rest level
|
|
679
693
|
c.users.add("host")
|
|
680
694
|
for n, s in self.slots.items(): # a new connection for an evicted slot: a new cue
|
|
681
695
|
if s.wire_fn == fn and s.pair == pair:
|
|
@@ -1218,7 +1232,7 @@ class Endpoint:
|
|
|
1218
1232
|
|
|
1219
1233
|
def _parse_slot(self, v: bytes) -> Slot:
|
|
1220
1234
|
t = Take(v)
|
|
1221
|
-
n, wire_fn, swdio, swclk, attach, retry_s, mech, name_len = t.take("
|
|
1235
|
+
n, wire_fn, swdio, swclk, attach, retry_s, max_speed, idle_clock, mech, name_len = t.take("BHHHBHIBBB")
|
|
1222
1236
|
name = t.bytes(name_len)
|
|
1223
1237
|
scheme = t.take("B")
|
|
1224
1238
|
rest = v[t.at:]
|
|
@@ -1226,6 +1240,8 @@ class Endpoint:
|
|
|
1226
1240
|
raise Reject(m.MALFORMED)
|
|
1227
1241
|
if retry_s and attach != SLOT_ATTACH["at_boot"]:
|
|
1228
1242
|
raise Reject(m.MALFORMED)
|
|
1243
|
+
if idle_clock > 1 or (idle_clock and self.names.get(wire_fn) != "oep.wire.rvswd"):
|
|
1244
|
+
raise Reject(m.MALFORMED)
|
|
1229
1245
|
if not SLOT_NAME.fullmatch(name.decode("ascii", "replace")):
|
|
1230
1246
|
raise Reject(m.MALFORMED)
|
|
1231
1247
|
if self.names.get(wire_fn) not in WIRES or (swdio, swclk) not in self.pairs.get(wire_fn, []):
|
|
@@ -1239,7 +1255,7 @@ class Endpoint:
|
|
|
1239
1255
|
lock = (scheme, rest[:len(rest) // 2], rest[len(rest) // 2:])
|
|
1240
1256
|
elif rest:
|
|
1241
1257
|
raise Reject(m.MALFORMED)
|
|
1242
|
-
return Slot(n, wire_fn, (swdio, swclk), attach, retry_s, mech, name.decode(), lock)
|
|
1258
|
+
return Slot(n, wire_fn, (swdio, swclk), attach, retry_s, max_speed, idle_clock, mech, name.decode(), lock)
|
|
1243
1259
|
|
|
1244
1260
|
def _parse_bind(self, v: bytes, slots: dict[int, Slot]) -> Bind:
|
|
1245
1261
|
t = Take(v)
|
|
@@ -1291,7 +1307,9 @@ class Endpoint:
|
|
|
1291
1307
|
if not tg.present:
|
|
1292
1308
|
return
|
|
1293
1309
|
try:
|
|
1294
|
-
|
|
1310
|
+
speed = min(4_000_000, s.max_speed) if s.max_speed else 4_000_000 # the slot's line settings
|
|
1311
|
+
cid = self._seat(s.wire_fn, s.pair, tg, speed, evict=False) # automatic: never evicts
|
|
1312
|
+
self.conns[cid].idle_clock = s.idle_clock
|
|
1295
1313
|
except Reject:
|
|
1296
1314
|
return
|
|
1297
1315
|
tg.havereset = False
|
oep_client/fake.py
CHANGED
|
@@ -185,8 +185,8 @@ def esp32_v003() -> FakeProbe:
|
|
|
185
185
|
_core("3.0.0", "esp32-d0wd", bytes.fromhex("0070070d9394"), 40, reserved, f"{NS}.esp32-v003",
|
|
186
186
|
{16: "SWIO", 23: "NRST", 22: "DUT TX", 21: "DUT RX"},
|
|
187
187
|
_transports([(TRANSPORT["uart_bridge"], 0xFF)])),
|
|
188
|
-
Offered(1, 1, "oep.wire.swio", (catalog.channel_group(1, [(1, 16)]), catalog.
|
|
189
|
-
catalog.u8(MAX_CONNECTIONS, 1))),
|
|
188
|
+
Offered(1, 1, "oep.wire.swio", (catalog.channel_group(1, [(1, 16)]), catalog.role_channels(3, [23]),
|
|
189
|
+
catalog.u8(IMPLEMENTATION, 1), catalog.u8(MAX_CONNECTIONS, 1))),
|
|
190
190
|
Offered(2, 1, "oep.target.riscv-dm", (catalog.u32(FEATURES, 0b0111), catalog.u8(IMPLEMENTATION, 1),
|
|
191
191
|
catalog.u16(MAX_LENGTH, 40))),
|
|
192
192
|
Offered(3, 1, "oep.target.console", (catalog.tlv(MECHANISMS, bytes([0, 1, 2])),)),
|
oep_client/fake_serve.py
CHANGED
|
@@ -77,8 +77,8 @@ def build(a: argparse.Namespace) -> endpoint.Endpoint:
|
|
|
77
77
|
swdio, swclk = ep.pairs[wire][n]
|
|
78
78
|
mech = 2 if 2 in ep.mechanisms else min(ep.mechanisms)
|
|
79
79
|
raw = name.encode()
|
|
80
|
-
value = struct.pack("<
|
|
81
|
-
mech, len(raw)) + raw + b"\x00"
|
|
80
|
+
value = struct.pack("<BHHHBHIBBB", n, wire, swdio, swclk, reg.PROBE_CONFIG.enum["slot_attach"]["at_boot"], 1,
|
|
81
|
+
0, 0, mech, len(raw)) + raw + b"\x00" # no speed ceiling, rests high, no lock
|
|
82
82
|
items.append(bytes([_ITEM["slot"], len(value)]) + value)
|
|
83
83
|
if a.bind:
|
|
84
84
|
streams = b"".join(struct.pack("<BH", reg.PROBE_CONFIG.enum["bind_stream"]["slot_console"], n)
|
oep_client/interfaces.py
CHANGED
|
@@ -71,15 +71,14 @@ KNOWN: dict[str, Known] = {
|
|
|
71
71
|
0x46: ("label", _label), 0x47: ("resets on open", lambda v: "yes"),
|
|
72
72
|
0x49: ("transport", _transport), 0x4A: ("OEP VID:PID", lambda v: "yes" if v[:1] == b"\x01" else "no")}),
|
|
73
73
|
"oep.wire.rvswd": Known("scan, attach, detach over RVSWD (attach returns a connection)",
|
|
74
|
-
roles={1: "SWDIO", 2: "SWCLK"}, tags={0x40: ("max connections", lambda v: str(v[0]))}),
|
|
74
|
+
roles={1: "SWDIO", 2: "SWCLK", 3: "reset"}, tags={0x40: ("max connections", lambda v: str(v[0]))}),
|
|
75
75
|
"oep.wire.swio": Known("scan, attach, detach over SWIO, one wire (attach returns a connection)",
|
|
76
|
-
roles={1: "SWIO"}, tags={0x40: ("max connections", lambda v: str(v[0]))}),
|
|
76
|
+
roles={1: "SWIO", 3: "reset"}, tags={0x40: ("max connections", lambda v: str(v[0]))}),
|
|
77
77
|
"oep.wire.swd": Known("scan, attach, detach over ARM SWD", roles={1: "SWDIO", 2: "SWCLK"},
|
|
78
78
|
tags={0x40: ("max connections", lambda v: str(v[0]))}),
|
|
79
79
|
"oep.target.riscv-dm": Known(
|
|
80
80
|
"RISC-V Debug Module over DMI: step lists, block read/write, run until halt, halt/resume",
|
|
81
|
-
features={0: "block read/write", 1: "run until halt", 2: "reset", 3: "step"},
|
|
82
|
-
tags={0x40: ("clobbers", lambda v: ", ".join(f"0x{r:04x}" for r in struct.unpack(f"<{len(v) // 2}H", v)))}),
|
|
81
|
+
features={0: "block read/write", 1: "run until halt", 2: "reset", 3: "step"}),
|
|
83
82
|
"oep.target.arm-adi": Known("ARM Debug Interface: DP/AP transfer lists, block transfers"),
|
|
84
83
|
"oep.target.console": Known(
|
|
85
84
|
"console streams on a debug connection (position-addressed, marks)",
|
oep_client/registry.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from types import SimpleNamespace as _NS
|
|
4
4
|
|
|
5
|
-
REGISTRY_HASH = "
|
|
5
|
+
REGISTRY_HASH = "dd6d527421bb6206"
|
|
6
6
|
SCHEMA = 1
|
|
7
7
|
PROTOCOL_REVISION = 1
|
|
8
8
|
CONFIRM_REQUEST_MAGIC = 'OEP?'
|
|
@@ -22,19 +22,19 @@ TIMING = {"resync_quiet_ms": 0x32, "probe_frame_gap_ms": 0xC8, "host_frame_pause
|
|
|
22
22
|
|
|
23
23
|
INTERFACES = {}
|
|
24
24
|
CORE = _NS(name="oep.core", revision=1, op={"confirm": 0x01, "list": 0x02, "describe": 0x03, "plan_apply": 0x04, "plan_release": 0x05, "open": 0x10, "end": 0x11, "keepalive": 0x12, "lock_state": 0x13, "subscribe": 0x30, "unsubscribe": 0x32, "link_source": 0x40, "link_sink": 0x41}, lock_free={0x01, 0x02, 0x03, 0x10, 0x13, 0x40, 0x41},
|
|
25
|
-
closed_tail={0x40}, tlv={"plan_apply": {"role_assignment": 0x90}, "open": {"owner": 0x01}, "lock_state_answer": {"owner": 0x01}, "locked_payload": {"owner": 0x01}, "describe": {"firmware": 0x40, "model": 0x41, "unit_id": 0x42, "channels": 0x43, "reserved": 0x44, "profile": 0x45, "label": 0x46, "resets_on_open": 0x47, "transport": 0x49, "oep_pid": 0x4A}}, event={"heartbeat": 0x01}, enum={"transport_kind": {"uart_bridge": 0x01, "usb_cdc": 0x02, "usb_serial_jtag": 0x03, "vendor_bulk": 0x04, "hid": 0x05, "tcp": 0x06}}, own={})
|
|
25
|
+
closed_tail={0x40}, tlv={"plan_apply": {"role_assignment": 0x90}, "open": {"owner": 0x01}, "lock_state_answer": {"owner": 0x01}, "locked_payload": {"owner": 0x01}, "describe": {"firmware": 0x40, "model": 0x41, "unit_id": 0x42, "channels": 0x43, "reserved": 0x44, "profile": 0x45, "label": 0x46, "resets_on_open": 0x47, "transport": 0x49, "oep_pid": 0x4A, "plan_roles": 0x4B}}, event={"heartbeat": 0x01}, enum={"transport_kind": {"uart_bridge": 0x01, "usb_cdc": 0x02, "usb_serial_jtag": 0x03, "vendor_bulk": 0x04, "hid": 0x05, "tcp": 0x06}}, own={})
|
|
26
26
|
INTERFACES["oep.core"] = CORE
|
|
27
27
|
WIRE_RVSWD = _NS(name="oep.wire.rvswd", revision=1, op={"scan": 0x01, "attach": 0x02, "detach": 0x03, "attach_under_reset": 0x04, "connections": 0x05}, lock_free={0x05},
|
|
28
|
-
closed_tail={}, tlv={"describe": {"max_connections": 0x40}, "detach": {"force": 0x01}, "attach": {"max_speed": 0x01, "pins": 0x03}, "attach_under_reset": {"max_speed": 0x01, "pins": 0x03}, "attach_answer": {"target_id": 0x10}}, event={}, enum={"connection_users": {"host_session": 0x01, "slot": 0x02}, "target_id_scheme": {"wch_dmi_7f": 0x01}, "attach_method": {"run": 0x00, "halt": 0x01}, "pin_role": {"swdio": 0x01, "swclk": 0x02}}, own={})
|
|
28
|
+
closed_tail={}, tlv={"describe": {"max_connections": 0x40}, "detach": {"force": 0x01}, "attach": {"max_speed": 0x01, "pins": 0x03, "idle_clock": 0x04}, "attach_under_reset": {"max_speed": 0x01, "pins": 0x03, "idle_clock": 0x04}, "attach_answer": {"target_id": 0x10}}, event={}, enum={"connection_users": {"host_session": 0x01, "slot": 0x02}, "target_id_scheme": {"wch_dmi_7f": 0x01}, "attach_method": {"run": 0x00, "halt": 0x01}, "pin_role": {"swdio": 0x01, "swclk": 0x02, "reset": 0x03}, "idle_clock": {"high": 0x00, "low": 0x01}}, own={})
|
|
29
29
|
INTERFACES["oep.wire.rvswd"] = WIRE_RVSWD
|
|
30
30
|
WIRE_SWIO = _NS(name="oep.wire.swio", revision=1, op={"scan": 0x01, "attach": 0x02, "detach": 0x03, "attach_under_reset": 0x04, "connections": 0x05}, lock_free={0x05},
|
|
31
|
-
closed_tail={}, tlv={"describe": {"max_connections": 0x40}, "detach": {"force": 0x01}, "attach": {"max_speed": 0x01, "pins": 0x03}, "attach_under_reset": {"max_speed": 0x01, "pins": 0x03}, "attach_answer": {"target_id": 0x10}}, event={}, enum={"connection_users": {"host_session": 0x01, "slot": 0x02}, "target_id_scheme": {"wch_dmi_7f": 0x01}, "attach_method": {"run": 0x00, "halt": 0x01}, "pin_role": {"swdio": 0x01}}, own={})
|
|
31
|
+
closed_tail={}, tlv={"describe": {"max_connections": 0x40}, "detach": {"force": 0x01}, "attach": {"max_speed": 0x01, "pins": 0x03}, "attach_under_reset": {"max_speed": 0x01, "pins": 0x03}, "attach_answer": {"target_id": 0x10}}, event={}, enum={"connection_users": {"host_session": 0x01, "slot": 0x02}, "target_id_scheme": {"wch_dmi_7f": 0x01}, "attach_method": {"run": 0x00, "halt": 0x01}, "pin_role": {"swdio": 0x01, "reset": 0x03}}, own={})
|
|
32
32
|
INTERFACES["oep.wire.swio"] = WIRE_SWIO
|
|
33
33
|
WIRE_SWD = _NS(name="oep.wire.swd", revision=1, op={"scan": 0x01, "attach": 0x02, "detach": 0x03, "connections": 0x05}, lock_free={0x05},
|
|
34
34
|
closed_tail={}, tlv={"describe": {"max_connections": 0x40}, "detach": {"force": 0x01}, "attach": {"max_speed": 0x01, "targetsel": 0x02, "pins": 0x03}}, event={}, enum={"connection_users": {"host_session": 0x01, "slot": 0x02}, "pin_role": {"swdio": 0x01, "swclk": 0x02}}, own={})
|
|
35
35
|
INTERFACES["oep.wire.swd"] = WIRE_SWD
|
|
36
36
|
TARGET_RISCV_DM = _NS(name="oep.target.riscv-dm", revision=1, op={"dmi": 0x01, "halt": 0x02, "resume": 0x03, "reset": 0x04, "read_block": 0x05, "write_block": 0x06, "run": 0x07, "step": 0x08}, lock_free={},
|
|
37
|
-
closed_tail={}, tlv={"describe": {
|
|
37
|
+
closed_tail={}, tlv={"describe": {}, "reset": {"method": 0x01}}, event={}, enum={"dmi_step": {"write": 0x01, "read": 0x02, "poll_reads": 0x03, "wait_us": 0x04, "poll_us": 0x05}, "reset_mode": {"run": 0x00, "run_verified": 0x01, "halt_at_reset": 0x02}, "reset_method": {"probe_default": 0x00, "ndmreset": 0x01, "system_reset": 0x02}}, own={})
|
|
38
38
|
INTERFACES["oep.target.riscv-dm"] = TARGET_RISCV_DM
|
|
39
39
|
TARGET_ARM_ADI = _NS(name="oep.target.arm-adi", revision=1, op={"transfer": 0x01, "read_block": 0x02, "write_block": 0x03}, lock_free={},
|
|
40
40
|
closed_tail={}, tlv={}, event={}, enum={}, own={})
|
oep_client/riscv.py
CHANGED
|
@@ -14,8 +14,8 @@ import struct
|
|
|
14
14
|
import time
|
|
15
15
|
from dataclasses import dataclass, field
|
|
16
16
|
|
|
17
|
-
from . import host as h, message as m, registry as reg
|
|
18
|
-
from .core import Interface
|
|
17
|
+
from . import catalog, host as h, message as m, registry as reg
|
|
18
|
+
from .core import Interface, describe
|
|
19
19
|
from .fixture import Gpio
|
|
20
20
|
|
|
21
21
|
STATUS = reg.STATUS
|
|
@@ -109,7 +109,9 @@ class WireBase(Interface):
|
|
|
109
109
|
class Wire(WireBase):
|
|
110
110
|
"""oep.wire.rvswd / oep.wire.swio (CH32 debug links to a RISC-V debug module)."""
|
|
111
111
|
NAME = "oep.wire.rvswd"
|
|
112
|
-
|
|
112
|
+
ROLE_RESET = reg.WIRE_RVSWD.enum["pin_role"]["reset"]
|
|
113
|
+
TAG_IDLE_CLOCK = reg.WIRE_RVSWD.tlv["attach"]["idle_clock"]
|
|
114
|
+
IDLE_CLOCK = reg.WIRE_RVSWD.enum["idle_clock"]
|
|
113
115
|
RUN, HALT = reg.WIRE_RVSWD.enum["attach_method"]["run"], reg.WIRE_RVSWD.enum["attach_method"]["halt"]
|
|
114
116
|
TAG_TARGET_ID = reg.WIRE_RVSWD.tlv["attach_answer"]["target_id"]
|
|
115
117
|
SCHEME_WCH_DMI_7F = reg.WIRE_RVSWD.enum["target_id_scheme"]["wch_dmi_7f"]
|
|
@@ -125,12 +127,28 @@ class Wire(WireBase):
|
|
|
125
127
|
v = tail.get(self.TAG_TARGET_ID)
|
|
126
128
|
self.target_id = (v[0], bytes(v[1:])) if v else None
|
|
127
129
|
|
|
128
|
-
def
|
|
130
|
+
def _idle_tlv(self, idle_clock: str | None) -> bytes:
|
|
131
|
+
# rvswd only, critical: a probe that cannot rest the line that way must refuse (oep-if-debug §3)
|
|
132
|
+
return b"" if idle_clock is None else m.tlv(self.TAG_IDLE_CLOCK, bytes([self.IDLE_CLOCK[idle_clock]]), critical=True)
|
|
133
|
+
|
|
134
|
+
def reset_channels(self) -> list[int]:
|
|
135
|
+
"""The channels attach_under_reset may take (describe role_channels, role reset). There is no default reset
|
|
136
|
+
line: the host names one every time (oep-if-debug §3)."""
|
|
137
|
+
out = set()
|
|
138
|
+
for tag, v in describe(self.host, self.fn):
|
|
139
|
+
if tag & ~m.TAG_CRITICAL == catalog.ROLE_CHANNELS and v[0] == self.ROLE_RESET:
|
|
140
|
+
out.update(catalog.bitmap_to_channels(struct.unpack_from("<H", v, 1)[0], v[3:]))
|
|
141
|
+
return sorted(out)
|
|
142
|
+
|
|
143
|
+
def attach(self, halt: bool = True, max_speed: int | None = None, pins: tuple[int, int] | None = None,
|
|
144
|
+
idle_clock: str | None = None) -> tuple[int, int]:
|
|
129
145
|
"""-> (connection, DMSTATUS). Attaching an attached wire returns its connection as it is (self.existing).
|
|
130
146
|
self.had_reset: a pending havereset was acknowledged first (a V00x's DMSTATUS halt / run bits stay frozen
|
|
131
147
|
until then); self.speed_hz: the speed the probe chose; max_speed: a ceiling the probe must keep (critical);
|
|
132
|
-
|
|
133
|
-
|
|
148
|
+
idle_clock: "high" / "low", how rvswd rests SWCLK (critical). Both are the target's, known by the host
|
|
149
|
+
(oep-if-debug §3). self.target_id: (scheme, value) of the target's identity when the probe could read one."""
|
|
150
|
+
body = (bytes([self.HALT if halt else self.RUN]) + self._speed_tlv(max_speed) + self._pins_tlv(pins)
|
|
151
|
+
+ self._idle_tlv(idle_clock))
|
|
134
152
|
rd = m.Reader(self._call(self.ATTACH, body).payload)
|
|
135
153
|
conn, status, flags, self.speed_hz = rd.take("HIBI")
|
|
136
154
|
self.had_reset, self.existing = bool(flags & 1), bool(flags & 2)
|
|
@@ -139,12 +157,13 @@ class Wire(WireBase):
|
|
|
139
157
|
self._take_target_id(tail)
|
|
140
158
|
return conn, status
|
|
141
159
|
|
|
142
|
-
def attach_under_reset(self, channel: int
|
|
143
|
-
|
|
144
|
-
"""Hold the target in reset through `channel` (
|
|
145
|
-
halt it at once - the way back from firmware that turns the
|
|
146
|
-
|
|
147
|
-
|
|
160
|
+
def attach_under_reset(self, channel: int, hold_ms: int = 20, max_speed: int | None = None,
|
|
161
|
+
pins: tuple[int, int] | None = None, idle_clock: str | None = None) -> tuple[int, int]:
|
|
162
|
+
"""Hold the target in reset through `channel` (always named: there is no default reset line; the probe
|
|
163
|
+
allows reset_channels()), attach, release and halt it at once - the way back from firmware that turns the
|
|
164
|
+
debug pins into GPIOs. -> (connection, dpc)"""
|
|
165
|
+
body = (struct.pack("<HH", channel, hold_ms) + self._speed_tlv(max_speed) + self._pins_tlv(pins)
|
|
166
|
+
+ self._idle_tlv(idle_clock))
|
|
148
167
|
rd = m.Reader(self._call(self.ATTACH_UNDER_RESET, body).payload)
|
|
149
168
|
conn, dpc, self.speed_hz = rd.take("HII")
|
|
150
169
|
self._take_target_id(rd.tail())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: oep-client-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7
|
|
4
4
|
Summary: Open Embedded Probe (OEP) v1 host: serial / USB / TCP transports, the session rules, the standard interfaces, and a fake probe
|
|
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,36 +1,36 @@
|
|
|
1
|
-
oep_client/__init__.py,sha256=
|
|
2
|
-
oep_client/__main__.py,sha256=
|
|
1
|
+
oep_client/__init__.py,sha256=Eo4zAyMLP7Kn4ZVfKG_wGeLWVvEnMO3lQdewP4ORx6k,163
|
|
2
|
+
oep_client/__main__.py,sha256=qtVGdpAHULBtLY2SedabJf-uwDZZhMf_YmrbqRzM57U,14669
|
|
3
3
|
oep_client/arm.py,sha256=JLyUhLSPcmsk73schZcaJYoOKuHzrvnU8kPmfrEvVuo,12331
|
|
4
|
-
oep_client/capture.py,sha256=
|
|
4
|
+
oep_client/capture.py,sha256=hs8mQU1gE1P67GOivxw2C2Ur8wimWqCdhtk1nqGN71Q,18092
|
|
5
5
|
oep_client/catalog.py,sha256=EqAQnSgHFxGbCO2GQ_Ee-uheJKbBBUUpL1-0qHfNMXU,6574
|
|
6
6
|
oep_client/ch32_flash.py,sha256=Fc-6jEj15cQtMSKmfC5dmDetMXJ8hAdAuJ9d9odwrsA,12651
|
|
7
7
|
oep_client/cobs.py,sha256=hqddQI_X7rsBvBvcP_MBE3bXPyB13-Azjqp_9JfdQ1g,2288
|
|
8
|
-
oep_client/config.py,sha256=
|
|
8
|
+
oep_client/config.py,sha256=a8zlxPI0sG0Z8gwUyg9OuKbXHoB5FctCrRDHULHhxCo,9753
|
|
9
9
|
oep_client/console.py,sha256=YYcuc9Qxe3kihla8wynYbDaLVr7wBLEvswrM-cMK3Tk,6859
|
|
10
10
|
oep_client/core.py,sha256=Cyyaknjipkg6tS76dZe5GMqjmOoQjlfeP0WFtMHkJNs,10403
|
|
11
11
|
oep_client/decode.py,sha256=M-bRARrA2qGZLoJn0BdXeTb4WjL_2Hb2nk_g1IGXIw4,2382
|
|
12
12
|
oep_client/dump.py,sha256=GAnmu2abuDmGmbWcbbEfC3EUuPp5Hady9vQVNcQ-724,6743
|
|
13
|
-
oep_client/endpoint.py,sha256=
|
|
13
|
+
oep_client/endpoint.py,sha256=tRx9ZUIiFN0J9NS3OwZ89NorIV7XqmZbYeUMnvJb6bE,74082
|
|
14
14
|
oep_client/esp32_targets.py,sha256=b8tNH-nb2KC-XgNfWg5l3TO0cyhZOF-gJajaVxNVSGQ,4988
|
|
15
|
-
oep_client/fake.py,sha256=
|
|
15
|
+
oep_client/fake.py,sha256=TJpzwSCFDSco-3HCE2ZHzusMx0r0dtrD1bkGn6aos90,13136
|
|
16
16
|
oep_client/fake_serial.py,sha256=uKl0UNcifhdeykQtm8gnRRhqV7TT4vPwBFuBhbf-9BI,4504
|
|
17
|
-
oep_client/fake_serve.py,sha256=
|
|
17
|
+
oep_client/fake_serve.py,sha256=Ke_hGRiOayCyut-uYOa0PaOFzy-zlvPHGwjs4HEZkAQ,14644
|
|
18
18
|
oep_client/fixture.py,sha256=653hCD-Iz0Lfs8dT4Hdg3QyubbbOGs1_xjq_3nb8foU,5564
|
|
19
19
|
oep_client/frames.py,sha256=xjgjnXmXUqNJZ5m0h91r4Cy48WoVK1mEMW4gRg-mlMI,3991
|
|
20
20
|
oep_client/hid_stream.py,sha256=99Dsm4Io1keukuETG_UNqjBGiVYZZeb__Pv4ytaKg9k,10695
|
|
21
21
|
oep_client/host.py,sha256=Y5oqE-pw5Ja9cG1dzdy8svIEgR4Kqhki0wuyrpP_xH8,15563
|
|
22
|
-
oep_client/interfaces.py,sha256=
|
|
22
|
+
oep_client/interfaces.py,sha256=TNvof5blyLjO1Mc0OentKcMVBD_5e4riRG18UgEh2ZE,5380
|
|
23
23
|
oep_client/link.py,sha256=J9VtBWfTgNoIgUQuj8SsH9hHesQM2uEN545IoxKVS8E,19406
|
|
24
24
|
oep_client/message.py,sha256=MEM1UT6KFM7AsT56jvBLt_FJK_6SJ7O_nGAiipW0_zo,9912
|
|
25
25
|
oep_client/names.py,sha256=ztCZFWs__aR-a5G14TnJP-Q4YfLhbyrzvOVOMJuG93k,3653
|
|
26
|
-
oep_client/registry.py,sha256=
|
|
27
|
-
oep_client/riscv.py,sha256=
|
|
26
|
+
oep_client/registry.py,sha256=uO9Ilx_pPPhLD-17TosgXH9QfI11DymYNIR-EacaZOw,9984
|
|
27
|
+
oep_client/riscv.py,sha256=PNeo_HXlq08b4h083olOOrGxqejD6ggQ7QzVqkIHEMA,22621
|
|
28
28
|
oep_client/rp2350.py,sha256=7OMyj270k3AY1e3-ljicPwOjf9jOlokbT6QNkA0b0yY,4227
|
|
29
29
|
oep_client/target.py,sha256=R8BvYfFfYNhZF0MysJkNb8b69YgBlmeGXNX8ReZIe5k,1068
|
|
30
30
|
oep_client/uiapduino.py,sha256=IEPG3LbZPksi8g3wmAf6T0CRWSfYKW1_qrH_GBVQdxY,6090
|
|
31
31
|
oep_client/usb_stream.py,sha256=VJMCxsOlQCk005FcS92pqTh33fRSFBizoL__L0VbUMA,10242
|
|
32
|
-
oep_client_python-0.0.
|
|
33
|
-
oep_client_python-0.0.
|
|
34
|
-
oep_client_python-0.0.
|
|
35
|
-
oep_client_python-0.0.
|
|
36
|
-
oep_client_python-0.0.
|
|
32
|
+
oep_client_python-0.0.7.dist-info/METADATA,sha256=Shg-dYEbV4747Sl1HZlRz-AxoIdVQ2Z9LzszmjCTdnY,7368
|
|
33
|
+
oep_client_python-0.0.7.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
34
|
+
oep_client_python-0.0.7.dist-info/entry_points.txt,sha256=h6_8QX89Ww1ogALEGG5g4kspT3IOV6O51lwKqf5BWco,49
|
|
35
|
+
oep_client_python-0.0.7.dist-info/licenses/LICENSE,sha256=ntlalztb3L1OaNAxmQdHlubu5fVhjMc9oRZi3PeQhXg,1076
|
|
36
|
+
oep_client_python-0.0.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|