oep-client-python 0.0.19__tar.gz → 0.0.21__tar.gz
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_python-0.0.19 → oep_client_python-0.0.21}/CHANGELOG.md +16 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/PKG-INFO +2 -2
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/README.ja.md +1 -1
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/README.md +1 -1
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/pyproject.toml +1 -1
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/__init__.py +1 -1
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/__main__.py +10 -9
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/config.py +7 -2
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/console.py +5 -3
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/endpoint.py +21 -9
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/fake.py +32 -32
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/fake_serve.py +1 -1
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/link.py +1 -1
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_capabilities.py +3 -3
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_fake_spec.py +11 -1
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/.gitignore +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/LICENSE +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/arm.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/capture.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/catalog.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/ch32_flash.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/cobs.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/core.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/decode.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/dump.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/fake_capture.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/fake_serial.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/fixture.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/frames.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/hid_stream.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/host.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/interfaces.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/message.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/names.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/registry.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/riscv.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/rp2350.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/uiapduino.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/src/oep_client/usb_stream.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_capture.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_cobs.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_config.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_fake_capture.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_fixture_targets.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_flash_console.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_hid_stream.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_interfaces.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_link_host.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_link_serial.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_session.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_target_parts.py +0 -0
- {oep_client_python-0.0.19 → oep_client_python-0.0.21}/tests/test_usb_stream.py +0 -0
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.0.21
|
|
6
|
+
- (EN) Breaking: a bind's streams carry a length each (oep-spec probe.config §1.2, freeze decision 1, missed until now): `n × (len, kind, id)`, len 3 today; the fake skips a longer one's tail and refuses one under 3 as malformed.
|
|
7
|
+
- (JA) 破壊的変更: bind のストリームの並びの各要素の前に長さを置く(oep-spec probe.config §1.2。凍結の決定 1 で漏れていた): `n × (len、kind、id)`、今の len は 3。fake は長い要素の後ろを飛ばし、3 未満は malformed で断る。
|
|
8
|
+
- (EN) `usb:X` is always a unit id, whatever its length (a 4-character unit id was read as a VID); a VID comes with its PID, `usb:VID:PID[:SERIAL]`.
|
|
9
|
+
- (JA) `usb:X` は長さによらず unit id として読む(4 文字の unit id を VID と読んでいた)。VID は PID と一緒に `usb:VID:PID[:SERIAL]` で書く。
|
|
10
|
+
- (EN) Breaking: the CLI's `name#k` is the list's instance, from 0 (oep-spec core §7.2's `name#instance`; `#0` may be left out). It was the k-th from 1.
|
|
11
|
+
- (JA) 破壊的変更: CLI の `name#k` は list の instance(0 から)にした(oep-spec core §7.2 の `name#instance`、`#0` は省ける)。1 から数えた k 番目だった。
|
|
12
|
+
- (EN) The fake probes number each interface's instance from 0 per name (core §7.2), as the probe firmware now does; they had arbitrary numbers.
|
|
13
|
+
- (JA) fake の probe はインターフェースの instance を名前ごとに 0 から振る(core §7.2)。probe の firmware も同じにした。これまでは適当な番号だった。
|
|
14
|
+
- (EN) README: `registry.INTERFACES[name]` is the public way to reach an interface's numbers by name.
|
|
15
|
+
- (JA) README: 名前からインターフェースの番号を引く公開の入口は `registry.INTERFACES[name]` と書いた。
|
|
16
|
+
|
|
17
|
+
## 0.0.20
|
|
18
|
+
- (EN) A console write's chunk fits the probe's frame: frame - 12 - the stream's prefix (the console's stream u16; none on a fixture UART). It was frame - 13 for both, one byte over on a console with 64-byte frames. Found porting it to oep-client-js.
|
|
19
|
+
- (JA) console の書き込みの 1 回の大きさが probe のフレームに収まるようにした: frame − 12 − stream の前置き(console は stream の u16、fixture UART は無し)。両方とも frame − 13 だったので、64 byte のフレームの console で 1 byte はみ出していた。oep-client-js に移すときに見つかった。
|
|
20
|
+
|
|
5
21
|
## 0.0.19
|
|
6
22
|
- (EN) The pre-freeze decisions (oep-spec docs/v1-freeze-decisions.ja.md), in the fake and the client at once:
|
|
7
23
|
- `oep.fixture.capture` is `oep.fixture.logic`; the I2C / SPI targets are the standard `oep.fixture.i2c-target` / `spi-target` (`fixture.I2cTarget` / `SpiTarget`; `esp32_targets` is gone). Their status is separate fields (`I2cStatus(state=, mode=, armed=, queued=, …)`); `read_hw` is gone, `set_stretch` is `stretch` (op 0x07).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: oep-client-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.21
|
|
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
|
|
@@ -64,7 +64,7 @@ oep-client-python X.Y.Z (until the v1 freeze every release may break the wire; t
|
|
|
64
64
|
| `config` | `oep.probe.config` (slots, binds, plan / label / idle items, get / set / save / erase, the live slot and bind state) |
|
|
65
65
|
| `capture` | `oep.fixture.logic` (revision 1, oep-spec oep-if-capture). Every segment read goes to the `Host.on_capture` callbacks as a `CaptureRecord` (the hook for run recorders; no wireskein dependency) |
|
|
66
66
|
| `decode` | decoding capture channels (I2C) |
|
|
67
|
-
| `registry` | generated from oep-spec's number table (never edited; copied again from oep-spec) |
|
|
67
|
+
| `registry` | generated from oep-spec's number table (never edited; copied again from oep-spec). The public way to reach an interface by name is `registry.INTERFACES[name]` (`.revision`, `.op`, `.tlv`, `.enum`, e.g. `INTERFACES["oep.fixture.uart"].enum["role"]`); the module-level names (`FIXTURE_UART`, ...) are the same objects |
|
|
68
68
|
| `arm` | `oep.wire.swd`, `oep.target.arm-adi`, MEM-AP, halting and calling functions on a Cortex-M |
|
|
69
69
|
| `ch32_flash` | writing a CH32 (a RAM loader, page by page) |
|
|
70
70
|
| `rp2350` | flash and reboot through the RP2350 boot ROM |
|
|
@@ -38,7 +38,7 @@ X.Y.Z(v1 の凍結までは、どのリリースも wire を壊しうるので
|
|
|
38
38
|
| `config` | `oep.probe.config`(スロット、bind、plan / label / idle の項目、get / set / save / erase、スロットと bind の今の状態) |
|
|
39
39
|
| `capture` | `oep.fixture.logic`(revision 1、oep-spec の oep-if-capture)。読んだ区画は `Host.on_capture` の callback に `CaptureRecord` で渡る(記録の受け口。wireskein には依存しない) |
|
|
40
40
|
| `decode` | キャプチャのチャネルの復号(I2C) |
|
|
41
|
-
| `registry` | oep-spec の番号の表から生成したモジュール(編集しない。oep-spec
|
|
41
|
+
| `registry` | oep-spec の番号の表から生成したモジュール(編集しない。oep-spec から写し直す)。名前からインターフェースの番号を引く公開の入口は `registry.INTERFACES[name]`(`.revision`、`.op`、`.tlv`、`.enum`。例 `INTERFACES["oep.fixture.uart"].enum["role"]`)。`FIXTURE_UART` などのモジュールの名前は同じもの |
|
|
42
42
|
| `arm` | `oep.wire.swd`、`oep.target.arm-adi`、MEM-AP、Cortex-M の停止と関数呼び出し |
|
|
43
43
|
| `ch32_flash` | CH32 の書き込み(RAM ローダー、ページ単位の書き直し) |
|
|
44
44
|
| `rp2350` | RP2350 の boot ROM 経由の flash と reboot |
|
|
@@ -42,7 +42,7 @@ oep-client-python X.Y.Z (until the v1 freeze every release may break the wire; t
|
|
|
42
42
|
| `config` | `oep.probe.config` (slots, binds, plan / label / idle items, get / set / save / erase, the live slot and bind state) |
|
|
43
43
|
| `capture` | `oep.fixture.logic` (revision 1, oep-spec oep-if-capture). Every segment read goes to the `Host.on_capture` callbacks as a `CaptureRecord` (the hook for run recorders; no wireskein dependency) |
|
|
44
44
|
| `decode` | decoding capture channels (I2C) |
|
|
45
|
-
| `registry` | generated from oep-spec's number table (never edited; copied again from oep-spec) |
|
|
45
|
+
| `registry` | generated from oep-spec's number table (never edited; copied again from oep-spec). The public way to reach an interface by name is `registry.INTERFACES[name]` (`.revision`, `.op`, `.tlv`, `.enum`, e.g. `INTERFACES["oep.fixture.uart"].enum["role"]`); the module-level names (`FIXTURE_UART`, ...) are the same objects |
|
|
46
46
|
| `arm` | `oep.wire.swd`, `oep.target.arm-adi`, MEM-AP, halting and calling functions on a Cortex-M |
|
|
47
47
|
| `ch32_flash` | writing a CH32 (a RAM loader, page by page) |
|
|
48
48
|
| `rp2350` | flash and reboot through the RP2350 boot ROM |
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "oep-client-python"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.21"
|
|
8
8
|
description = "Open Embedded Probe (OEP) v1 host: serial / USB / TCP transports, the session rules, the standard interfaces, and a fake probe"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
oep config show <probe>
|
|
5
5
|
oep config slot <probe> --name x035 --wire rvswd --pins 2,54 --attach at-boot --retry 1 --mechanism dmseq
|
|
6
6
|
oep config bind <probe> --port 1 --mode last-reset --stream slot:x035
|
|
7
|
-
oep config plan <probe> oep.fixture.uart#
|
|
7
|
+
oep config plan <probe> oep.fixture.uart#1 rx=48 tx=49 (the fn's whole plan; fn number or name#instance)
|
|
8
8
|
oep config remove <probe> bind 1 oep config save <probe> oep config erase <probe>
|
|
9
9
|
|
|
10
10
|
<probe>: a serial port, tcp://HOST:PORT or usb[:VID:PID[:SERIAL]]. A change takes the lock (owner "oep config") and
|
|
@@ -79,7 +79,7 @@ def _config_parser(sub) -> None:
|
|
|
79
79
|
bind.add_argument("--save", action="store_true")
|
|
80
80
|
plan = cs.add_parser("plan", help="the pins an interface keeps (its whole plan, kept as a setting)")
|
|
81
81
|
plan.add_argument("probe")
|
|
82
|
-
plan.add_argument("fn", help="an fn, or
|
|
82
|
+
plan.add_argument("fn", help="an fn, or name#instance (the list instance, 0 = the first; #0 may be left out, core §7.2)")
|
|
83
83
|
plan.add_argument("roles", nargs="+", help="ROLE=CHANNEL, ROLE a number or the interface's role name (rx, tx, line...)")
|
|
84
84
|
plan.add_argument("--save", action="store_true")
|
|
85
85
|
label = cs.add_parser("label", help="name a channel (shown in oep.core's describe)")
|
|
@@ -116,20 +116,21 @@ def _pins(hst, fn: int, text: str | None, wire: str) -> tuple[int, int]:
|
|
|
116
116
|
|
|
117
117
|
|
|
118
118
|
def _plan_fn(hst, spec: str) -> tuple[int, dict[str, int]]:
|
|
119
|
-
"""fn and its role names (from the registry) for `spec`: an fn number, or name[#
|
|
119
|
+
"""fn and its role names (from the registry) for `spec`: an fn number, or name[#instance] (the list's instance,
|
|
120
|
+
0-based, core §7.2)."""
|
|
120
121
|
from . import registry as reg
|
|
121
122
|
if spec.isdigit():
|
|
122
123
|
fn = int(spec)
|
|
123
124
|
name = next((e.name for e in core.list_entries(hst) if e.fn == fn), "")
|
|
124
125
|
else:
|
|
125
126
|
name, _, k = spec.partition("#")
|
|
126
|
-
|
|
127
|
-
if not
|
|
127
|
+
entries = [e for e in core.list_entries(hst, name, exact=True)]
|
|
128
|
+
if not entries:
|
|
128
129
|
raise SystemExit(f"the probe offers no {name}")
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
instance = int(k) if k else 0
|
|
131
|
+
fn = next((e.fn for e in entries if e.instance == instance), None)
|
|
132
|
+
if fn is None:
|
|
133
|
+
raise SystemExit(f"{name}: instances {sorted(e.instance for e in entries)}, not {instance}")
|
|
133
134
|
iface = reg.INTERFACES.get(name)
|
|
134
135
|
roles = dict(iface.enum.get("role", {})) if iface else {}
|
|
135
136
|
return fn, roles
|
|
@@ -111,7 +111,7 @@ class Bind:
|
|
|
111
111
|
def value(self) -> bytes:
|
|
112
112
|
v = struct.pack("<BBBB", self.port, MODE[self.mode], self.selected if self.mode == "manual" else 0,
|
|
113
113
|
len(self.streams))
|
|
114
|
-
return v + b"".join(struct.pack("<
|
|
114
|
+
return v + b"".join(struct.pack("<BBH", 3, STREAM[kind], i) for kind, i in self.streams) # len, kind, id
|
|
115
115
|
|
|
116
116
|
|
|
117
117
|
def item(it) -> bytes:
|
|
@@ -147,7 +147,12 @@ def decode(tag: int, v: bytes):
|
|
|
147
147
|
mechanism=_name(MECHANISM, mech), lock=lock, max_speed=max_speed, idle_clock=_name(IDLE_CLOCK, idle))
|
|
148
148
|
if tag == ITEM["bind"] and len(v) >= 4:
|
|
149
149
|
port, mode, selected, n = struct.unpack_from("<BBBB", v)
|
|
150
|
-
streams = [
|
|
150
|
+
streams, at = [], 4
|
|
151
|
+
for _ in range(n): # len, kind, id: a longer one's tail skipped
|
|
152
|
+
if at >= len(v) or v[at] < 3 or at + 1 + v[at] > len(v):
|
|
153
|
+
return (tag, v)
|
|
154
|
+
streams.append((_name(STREAM, v[at + 1]), struct.unpack_from("<H", v, at + 2)[0]))
|
|
155
|
+
at += 1 + v[at]
|
|
151
156
|
return Bind(port=port, mode=_name(MODE, mode), streams=streams, selected=selected)
|
|
152
157
|
return (tag, v)
|
|
153
158
|
|
|
@@ -136,10 +136,12 @@ class StreamIO:
|
|
|
136
136
|
self.lost = 0
|
|
137
137
|
|
|
138
138
|
def _limits(self) -> tuple[int, int]:
|
|
139
|
-
"""(read, write) chunk sizes that fit the probe's frame: request header 6 + session 4 + stream
|
|
140
|
-
result header 5 + start 8 +
|
|
139
|
+
"""(read, write) chunk sizes that fit the probe's frame: a write is request header 6 + session 4 + the stream's
|
|
140
|
+
prefix (the console's stream u16, none on a fixture UART) + count 2; a read's result is header 5 + start 8 +
|
|
141
|
+
flags 1. (The write was frame - 13 for both: one byte over on the console's 64-byte frames.)"""
|
|
141
142
|
frame = self.source.host.confirmed()["max_frame"]
|
|
142
|
-
|
|
143
|
+
write = frame - 12 - len(self.source._stream_prefix())
|
|
144
|
+
return max(1, min(self.MAX_READ, frame - 14)), max(1, min(self.MAX_WRITE, write))
|
|
143
145
|
|
|
144
146
|
def read(self, n: int = 512) -> bytes:
|
|
145
147
|
c = self.source.read_from(self.position, min(n, self._limits()[0]))
|
|
@@ -217,6 +217,17 @@ class Slot:
|
|
|
217
217
|
lock: tuple[int, bytes, bytes] | None # (scheme, mask, value)
|
|
218
218
|
|
|
219
219
|
|
|
220
|
+
def _bind_streams(v: bytes) -> list[int]:
|
|
221
|
+
"""Where each stream of a bind value starts (probe.config §1.2: n × (len, kind, id), len >= 3, its tail skipped)."""
|
|
222
|
+
at, out = 4, []
|
|
223
|
+
for _ in range(v[3]):
|
|
224
|
+
if at >= len(v) or v[at] < 3 or at + 1 + v[at] > len(v):
|
|
225
|
+
raise Reject(m.MALFORMED)
|
|
226
|
+
out.append(at + 1)
|
|
227
|
+
at += 1 + v[at]
|
|
228
|
+
return out
|
|
229
|
+
|
|
230
|
+
|
|
220
231
|
@dataclass(frozen=True)
|
|
221
232
|
class Bind:
|
|
222
233
|
port: int
|
|
@@ -1435,9 +1446,9 @@ class Endpoint:
|
|
|
1435
1446
|
elif tag == ITEM["slot"]:
|
|
1436
1447
|
fns.add(struct.unpack_from("<H", value, 1)[0])
|
|
1437
1448
|
elif tag == ITEM["bind"]:
|
|
1438
|
-
for
|
|
1439
|
-
if value[
|
|
1440
|
-
fns.add(struct.unpack_from("<H", value,
|
|
1449
|
+
for at in _bind_streams(value):
|
|
1450
|
+
if value[at] == BIND_STREAM["fixture_uart"]:
|
|
1451
|
+
fns.add(struct.unpack_from("<H", value, at + 1)[0])
|
|
1441
1452
|
return fns
|
|
1442
1453
|
|
|
1443
1454
|
def _apply_saved(self) -> None:
|
|
@@ -1459,9 +1470,9 @@ class Endpoint:
|
|
|
1459
1470
|
new[(tag, key)] = value[:1] + pack(struct.unpack_from("<H", value, 1)[0]) + value[3:]
|
|
1460
1471
|
elif tag == ITEM["bind"]:
|
|
1461
1472
|
v = bytearray(value)
|
|
1462
|
-
for
|
|
1463
|
-
if v[
|
|
1464
|
-
v[
|
|
1473
|
+
for at in _bind_streams(value):
|
|
1474
|
+
if v[at] == BIND_STREAM["fixture_uart"]:
|
|
1475
|
+
v[at + 1:at + 3] = pack(struct.unpack_from("<H", v, at + 1)[0])
|
|
1465
1476
|
new[(tag, key)] = bytes(v)
|
|
1466
1477
|
else:
|
|
1467
1478
|
new[(tag, key)] = value
|
|
@@ -1574,9 +1585,10 @@ class Endpoint:
|
|
|
1574
1585
|
return Slot(n, wire_fn, (swdio, swclk), attach, retry_s, max_speed, idle_clock, mech, name.decode(), lock)
|
|
1575
1586
|
|
|
1576
1587
|
def _parse_bind(self, v: bytes, slots: dict[int, Slot]) -> Bind:
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1588
|
+
if len(v) < 4:
|
|
1589
|
+
raise Reject(m.MALFORMED)
|
|
1590
|
+
port, mode, selected, n = v[:4]
|
|
1591
|
+
streams = tuple((v[at], struct.unpack_from("<H", v, at + 1)[0]) for at in _bind_streams(v))
|
|
1580
1592
|
if n == 0: # after the streams: later fields, skipped
|
|
1581
1593
|
raise Reject(m.MALFORMED)
|
|
1582
1594
|
if port not in self.serial_ports:
|
|
@@ -191,34 +191,34 @@ def p4_x035() -> FakeProbe:
|
|
|
191
191
|
_transports([(TRANSPORT["usb_serial_jtag"], 0xFF), (TRANSPORT["vendor_bulk"], 0),
|
|
192
192
|
(TRANSPORT["hid"], 1), (TRANSPORT["usb_cdc"], 2)])
|
|
193
193
|
+ (catalog.u8(CORE_OEP_PID, 1), catalog.text(CORE_CHIP, "esp32p4 v1.0"))),
|
|
194
|
-
Offered(1,
|
|
194
|
+
Offered(1, 0, "oep.wire.rvswd", (
|
|
195
195
|
catalog.channel_group(1, [(1, 2), (2, 54)]), catalog.u32(MAX_CLOCK_HZ, 5_000_000), catalog.u8(IMPLEMENTATION, 1),
|
|
196
196
|
catalog.u8(MAX_CONNECTIONS, 1))),
|
|
197
|
-
Offered(2,
|
|
197
|
+
Offered(2, 0, "oep.target.riscv-dm", (catalog.u32(FEATURES, 0b1111), catalog.u8(IMPLEMENTATION, 1),
|
|
198
198
|
catalog.u16(MAX_LENGTH, 1000))),
|
|
199
|
-
Offered(3,
|
|
200
|
-
Offered(4,
|
|
201
|
-
Offered(5,
|
|
199
|
+
Offered(3, 0, "oep.target.console", (catalog.tlv(MECHANISMS, bytes([0, 1, 2])),)),
|
|
200
|
+
Offered(4, 0, "oep.fixture.gpio", _roles({1: pins})),
|
|
201
|
+
Offered(5, 0, "oep.fixture.uart", _roles({1: pins, 2: pins}) + (
|
|
202
202
|
catalog.u32(MAX_CLOCK_HZ, 3_000_000), catalog.u8(IMPLEMENTATION, 2))),
|
|
203
|
-
Offered(6,
|
|
203
|
+
Offered(6, 1, "oep.fixture.uart", _roles({1: pins, 2: pins}) + (
|
|
204
204
|
catalog.u32(MAX_CLOCK_HZ, 3_000_000), catalog.u8(IMPLEMENTATION, 2))),
|
|
205
205
|
# the P4's PARLIO: w 1-16, one-shot / repeat / streaming
|
|
206
|
-
Offered(7,
|
|
206
|
+
Offered(7, 0, "oep.fixture.logic", _roles({k: pins for k in range(16)}) + (
|
|
207
207
|
catalog.u32(MAX_CLOCK_HZ, 20_000_000), catalog.u32(MIN_CLOCK_HZ, 1_000),
|
|
208
208
|
catalog.u16(MAX_LENGTH, 65000), catalog.u8(IMPLEMENTATION, 3))
|
|
209
209
|
+ _capture_decl(["one_shot", "repeat", "streaming"], 16, [1, 2, 4, 8, 16], 1 << 20, 8, 4096)),
|
|
210
|
-
Offered(8,
|
|
210
|
+
Offered(8, 0, "oep.fixture.i2c-target", _roles({1: pins, 2: pins}) + (
|
|
211
211
|
catalog.u16(MAX_LENGTH, 128), catalog.u32(MAX_CLOCK_HZ, 1_000_000),
|
|
212
212
|
catalog.u32(FEATURES, 0b11), catalog.u8(IMPLEMENTATION, 2))),
|
|
213
|
-
Offered(9,
|
|
213
|
+
Offered(9, 0, "oep.fixture.spi-target", _roles({1: pins, 2: pins, 3: pins, 4: pins}) + (
|
|
214
214
|
catalog.u16(MAX_LENGTH, 64), catalog.u32(MAX_CLOCK_HZ, 3_000_000), catalog.u8(IMPLEMENTATION, 2))),
|
|
215
|
-
_config(10,
|
|
215
|
+
_config(10, 0, slots_max=1),
|
|
216
216
|
# the P4's ADC1 (GPIO16-23): one ADC for all its channels, 611 Hz - 83.3 kHz in all; ESP32-style attenuations
|
|
217
|
-
Offered(11,
|
|
217
|
+
Offered(11, 0, "oep.fixture.analog", _roles({k: list(range(16, 24)) for k in range(4)}) + (
|
|
218
218
|
catalog.u32(MAX_CLOCK_HZ, 83_333), catalog.u32(MIN_CLOCK_HZ, 611), catalog.u8(IMPLEMENTATION, 3))
|
|
219
219
|
+ _analog_decl([(0, 0, 950, 0), (1, 0, 1250, 2500), (2, 0, 1750, 6000), (3, 0, 3100, 12000)], 65536)),
|
|
220
220
|
# the logic (fn 7) and the analog (fn 11) together; the ADC's 83.3 kHz is shared by its channels
|
|
221
|
-
Offered(12,
|
|
221
|
+
Offered(12, 0, "oep.fixture.capture-group", _group_decl([7, 11], [(83_333, [11])], {11: 5000})),
|
|
222
222
|
])
|
|
223
223
|
|
|
224
224
|
|
|
@@ -231,26 +231,26 @@ def esp32_v003() -> FakeProbe:
|
|
|
231
231
|
_core("3.0.0", "esp32", "0070070d9394", 40, reserved, f"{NS}.esp32-v003",
|
|
232
232
|
{16: "SWIO", 23: "NRST", 22: "DUT TX", 21: "DUT RX"},
|
|
233
233
|
_transports([(TRANSPORT["uart_bridge"], 0xFF)])),
|
|
234
|
-
Offered(1,
|
|
234
|
+
Offered(1, 0, "oep.wire.swio", (catalog.channel_group(1, [(1, 16)]), catalog.role_channels(3, [23]),
|
|
235
235
|
catalog.u8(IMPLEMENTATION, 1), catalog.u8(MAX_CONNECTIONS, 1))),
|
|
236
|
-
Offered(2,
|
|
236
|
+
Offered(2, 0, "oep.target.riscv-dm", (catalog.u32(FEATURES, 0b0111), catalog.u8(IMPLEMENTATION, 1),
|
|
237
237
|
catalog.u16(MAX_LENGTH, 40))),
|
|
238
|
-
Offered(3,
|
|
239
|
-
Offered(4,
|
|
240
|
-
Offered(5,
|
|
238
|
+
Offered(3, 0, "oep.target.console", (catalog.tlv(MECHANISMS, bytes([0, 1, 2])),)),
|
|
239
|
+
Offered(4, 0, "oep.fixture.gpio", _roles({1: wired + [23]})),
|
|
240
|
+
Offered(5, 0, "oep.fixture.uart", _roles({1: wired, 2: wired}) + (
|
|
241
241
|
catalog.u32(MAX_CLOCK_HZ, 115_200), catalog.u8(IMPLEMENTATION, 2))),
|
|
242
242
|
# the classic ESP32's GPIO sampler: a byte a sample (w 8), one-shot
|
|
243
|
-
Offered(6,
|
|
243
|
+
Offered(6, 0, "oep.fixture.logic", _roles({k: wired for k in range(4)}) + (
|
|
244
244
|
catalog.u32(MAX_CLOCK_HZ, 2_000_000), catalog.u32(MIN_CLOCK_HZ, 400_000), catalog.u8(IMPLEMENTATION, 1))
|
|
245
245
|
+ _capture_decl(["one_shot"], 8, [8], 65536, 1, 480)),
|
|
246
|
-
Offered(7,
|
|
246
|
+
Offered(7, 0, "oep.fixture.i2c-target", _roles({1: wired, 2: wired}) + (
|
|
247
247
|
catalog.u16(MAX_LENGTH, 16), catalog.u32(MAX_CLOCK_HZ, 100_000), catalog.u8(IMPLEMENTATION, 2))),
|
|
248
248
|
# Two fixed pin sets (an example of channel_group; GPIO23 is the DUT's NRST on this jig).
|
|
249
|
-
Offered(8,
|
|
249
|
+
Offered(8, 0, "oep.fixture.spi-target", (
|
|
250
250
|
catalog.channel_group(1, [(1, 18), (2, 19), (3, 5), (4, 4)]),
|
|
251
251
|
catalog.channel_group(2, [(1, 14), (2, 13), (3, 27), (4, 26)]),
|
|
252
252
|
catalog.u16(MAX_LENGTH, 32), catalog.u32(MAX_CLOCK_HZ, 3_000_000), catalog.u8(IMPLEMENTATION, 2))),
|
|
253
|
-
_config(9,
|
|
253
|
+
_config(9, 0, slots_max=1, modes=0b011, storage=1024),
|
|
254
254
|
])
|
|
255
255
|
|
|
256
256
|
|
|
@@ -264,17 +264,17 @@ def p4_bench() -> FakeProbe:
|
|
|
264
264
|
{2: "A SWDIO", 3: "A SWCLK", 4: "B SWDIO", 5: "B SWCLK", 6: "C SWDIO", 7: "C SWCLK"},
|
|
265
265
|
_transports([(TRANSPORT["usb_serial_jtag"], 0xFF), (TRANSPORT["vendor_bulk"], 0),
|
|
266
266
|
(TRANSPORT["hid"], 1), (TRANSPORT["usb_cdc"], 2)]) + (catalog.u8(CORE_OEP_PID, 1),)),
|
|
267
|
-
Offered(1,
|
|
267
|
+
Offered(1, 0, "oep.wire.rvswd", (
|
|
268
268
|
catalog.channel_group(1, [(1, 2), (2, 3)]), catalog.channel_group(2, [(1, 4), (2, 5)]),
|
|
269
269
|
catalog.channel_group(3, [(1, 6), (2, 7)]), catalog.u32(MAX_CLOCK_HZ, 5_000_000),
|
|
270
270
|
catalog.u8(IMPLEMENTATION, 1), catalog.u8(MAX_CONNECTIONS, 2))),
|
|
271
|
-
Offered(2,
|
|
271
|
+
Offered(2, 0, "oep.target.riscv-dm", (catalog.u32(FEATURES, 0b1111), catalog.u8(IMPLEMENTATION, 1),
|
|
272
272
|
catalog.u16(MAX_LENGTH, 1000))),
|
|
273
|
-
Offered(3,
|
|
274
|
-
Offered(4,
|
|
275
|
-
Offered(5,
|
|
273
|
+
Offered(3, 0, "oep.target.console", (catalog.tlv(MECHANISMS, bytes([0, 1, 2])),)),
|
|
274
|
+
Offered(4, 0, "oep.fixture.gpio", _roles({1: pins})),
|
|
275
|
+
Offered(5, 0, "oep.fixture.uart", _roles({1: pins, 2: pins}) + (
|
|
276
276
|
catalog.u32(MAX_CLOCK_HZ, 3_000_000), catalog.u8(IMPLEMENTATION, 2))),
|
|
277
|
-
_config(6,
|
|
277
|
+
_config(6, 0, slots_max=4),
|
|
278
278
|
])
|
|
279
279
|
|
|
280
280
|
|
|
@@ -287,13 +287,13 @@ def rp2350_pins() -> FakeProbe:
|
|
|
287
287
|
return FakeProbe("rp2350-pins", 1024, [
|
|
288
288
|
_core("3.0.0", "rp2350", "e66138935f2b1f2c", 30, reserved, "",
|
|
289
289
|
{}, _transports([(TRANSPORT["usb_cdc"], 0)])),
|
|
290
|
-
Offered(1,
|
|
290
|
+
Offered(1, 0, "oep.wire.rvswd", _roles({1: pins, 2: pins, 3: pins}) + (
|
|
291
291
|
catalog.u32(MAX_CLOCK_HZ, 5_000_000), catalog.u8(IMPLEMENTATION, 1), catalog.u8(MAX_CONNECTIONS, 1))),
|
|
292
|
-
Offered(2,
|
|
292
|
+
Offered(2, 0, "oep.target.riscv-dm", (catalog.u32(FEATURES, 0b1111), catalog.u8(IMPLEMENTATION, 1),
|
|
293
293
|
catalog.u16(MAX_LENGTH, 1000))),
|
|
294
|
-
Offered(3,
|
|
295
|
-
Offered(4,
|
|
296
|
-
Offered(5,
|
|
294
|
+
Offered(3, 0, "oep.target.console", (catalog.tlv(MECHANISMS, bytes([0, 1, 2])),)),
|
|
295
|
+
Offered(4, 0, "oep.fixture.gpio", _roles({1: pins})),
|
|
296
|
+
Offered(5, 0, "oep.fixture.uart", _roles({1: pins, 2: pins}) + (
|
|
297
297
|
catalog.u32(MAX_CLOCK_HZ, 3_000_000), catalog.u8(IMPLEMENTATION, 2))),
|
|
298
298
|
])
|
|
299
299
|
|
|
@@ -83,7 +83,7 @@ def build(a: argparse.Namespace) -> endpoint.Endpoint:
|
|
|
83
83
|
0, 0, mech, len(raw)) + raw + b"\x00" # no speed ceiling, rests high, no lock
|
|
84
84
|
items.append(bytes([_ITEM["slot"], len(value)]) + value)
|
|
85
85
|
if a.bind:
|
|
86
|
-
streams = b"".join(struct.pack("<
|
|
86
|
+
streams = b"".join(struct.pack("<BBH", 3, reg.PROBE_CONFIG.enum["bind_stream"]["slot_console"], n)
|
|
87
87
|
for n in range(len(a.slot)))
|
|
88
88
|
value = struct.pack("<BBBB", a.port_index, _MODES[a.bind.replace("-", "_")], 0, len(a.slot)) + streams
|
|
89
89
|
items.append(bytes([_ITEM["bind"], len(value)]) + value)
|
|
@@ -446,7 +446,7 @@ def open_host(target: str, timeout: float = 3.0):
|
|
|
446
446
|
lk.transport = "tcp"
|
|
447
447
|
elif target == "usb" or target.startswith("usb:"):
|
|
448
448
|
parts = target.split(":")[1:]
|
|
449
|
-
if len(parts) == 1
|
|
449
|
+
if len(parts) == 1: # usb:UNIT_ID (any length: a VID comes with its PID)
|
|
450
450
|
vid, pid = find_usb(parts[0])
|
|
451
451
|
return open_usb_host(vid, pid, parts[0], timeout)
|
|
452
452
|
vid = int(parts[0], 16) if parts else USB_VID
|
|
@@ -103,8 +103,8 @@ def test_p4_follows_the_agreed_names():
|
|
|
103
103
|
by_name = {o.entry.name: o for o in caps.offers}
|
|
104
104
|
assert len(caps.offers) == 13
|
|
105
105
|
assert caps.requests["list"] == 1 and caps.requests["describe"] == 13
|
|
106
|
-
# the debug port: wire, riscv-dm and console are
|
|
107
|
-
assert {by_name[n].entry.instance for n in ("oep.wire.rvswd", "oep.target.riscv-dm", "oep.target.console")} == {
|
|
106
|
+
# the debug port: wire, riscv-dm and console are the first instance of each
|
|
107
|
+
assert {by_name[n].entry.instance for n in ("oep.wire.rvswd", "oep.target.riscv-dm", "oep.target.console")} == {0}
|
|
108
108
|
assert by_name["oep.wire.rvswd"].description.groups[1] == [(1, 2), (2, 54)]
|
|
109
109
|
i2c = by_name["oep.fixture.i2c-target"]
|
|
110
110
|
assert i2c.description.roles[1] == i2c.description.roles[2]
|
|
@@ -153,7 +153,7 @@ def test_unknown_interfaces_are_shown_raw():
|
|
|
153
153
|
def test_text_and_json_outputs():
|
|
154
154
|
caps = dump.collect(fake.p4_x035().call)
|
|
155
155
|
text = dump.to_text(caps)
|
|
156
|
-
assert "instance
|
|
156
|
+
assert "instance 0" in text and "oep.fixture.i2c-target" in text
|
|
157
157
|
assert "features: preloaded tx, clock stretching" in text
|
|
158
158
|
data = json.loads(dump.to_json(caps))
|
|
159
159
|
assert data["max_frame"] == 1024 and len(data["interfaces"]) == 13
|
|
@@ -59,7 +59,7 @@ def slot_item(n, wire, pair, attach=ATTACH["at_boot"], retry=1, mech=2, name=Non
|
|
|
59
59
|
|
|
60
60
|
def bind_item(port, mode, streams, selected=0):
|
|
61
61
|
return m.tlv(ITEM["bind"], struct.pack("<BBBB", port, mode, selected, len(streams))
|
|
62
|
-
+ b"".join(struct.pack("<
|
|
62
|
+
+ b"".join(struct.pack("<BBH", 3, k, i) for k, i in streams))
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
def describe(ep, fn):
|
|
@@ -204,6 +204,16 @@ def test_slot_names_are_url_safe(name):
|
|
|
204
204
|
|
|
205
205
|
# ---- binds and the serial port ---------------------------------------------------------------------------
|
|
206
206
|
|
|
207
|
+
def test_bind_streams_carry_a_len_and_a_longer_ones_tail_is_skipped():
|
|
208
|
+
ep, h = bench()
|
|
209
|
+
head = struct.pack("<BBBB", 0, MODE["mixed"], 0, 1)
|
|
210
|
+
longer = m.tlv(ITEM["bind"], head + struct.pack("<BBHH", 5, KIND["slot_console"], 0, 0xBEEF))
|
|
211
|
+
assert h.raw(6, 0x02, slot_item(0, 1, ep.pairs[1][0]) + longer).succeeded
|
|
212
|
+
assert ep.binds[0].streams == ((KIND["slot_console"], 0),)
|
|
213
|
+
short = m.tlv(ITEM["bind"], head + struct.pack("<BBH", 2, KIND["slot_console"], 0))
|
|
214
|
+
assert h.raw(6, 0x02, short).detail == m.MALFORMED
|
|
215
|
+
|
|
216
|
+
|
|
207
217
|
def framed(req):
|
|
208
218
|
return cobs.frame(req.pack())
|
|
209
219
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|