Parallax Propeller2 USB driver - the MashUp

Characterization Report (tickets 4060 / 5040)
Login

Characterization Report (tickets 4060 / 5040)

Board / port: PNut-Term-TS P81svj44
Dogfood host: DM 40 / DP 41
Dogfood device: DM 48 / DP 49 (ganged)
Sysclk baseline: 200 MHz (baud X = $CF5C)
Architecture: usb_host = PHY cog only; usb_client = sole Spin protocol engine
Date: 2026-08-01 (updated)

Scope vs status

Layer Status Notes
Thin PHY + protocol engine Done 1000–1070, 0150 Fixed; live
Control enum multipacket Done GET_DESC n=18 mps0=8, status ACK
Device peer + BOT Restructured Per-class device cogs (not kitchen-sink)
usb_app facade Done bind_hid / bind_cdc / bind_msc (not all-at-once default)
Concurrent load + SOF Done per-class LOAD (one device cog at a time on gang)
Topology switch API Done Serial Host ch1/ch2 open+enum probe
Real SSD multi-sector Open 2030 — host path ready; needs media on Serial Host
Hub multi-device enum Open 1090 — addr tokens work; hub class not automated
Clock sweep to failure Done real ladder 140-300 MHz, honest scoring (4030 CLOSED 2026-08-09)
Class-faithful multi-pair emus Partial shared min peer plane
Isochronous + UAC1 audio Done 1180/2110 CLOSED 2026-08-12; loopback acceptance (below)

Live dogfood evidence

Log: harness/logs/headless_260801-002613.logper-class device cogs

PHASE HID: kbd.start cog → enum → bind_hid → kbd_poll key0=$04
           LOAD HID ok=40 fail=0 sof_delta=42
PHASE CDC: cdc.start cog → enum → bind_cdc → ser_write hs=$D2 ser_read b0=$43
PHASE MSC: msc.start cog → enum → bind_msc → disk_open blocks=1024
Architecture: one host Force-1 cog; each emu owns its device cog (sequential on gang)

Concurrent load (4010 / 1110)

Metric Value (live)
Iterations 40
Classes exercised HID poll, CDC bulk, MSC presence
SOF enabled during load; sof_delta ≈ iteration count with 1 ms yield
Failures some NAKs expected under tight poll

Topology (4000 / 1130)

Topo Pins Live
Dogfood 40/41 host + 48/49 device full stack green
Serial Host CH1 34/35 EN 33 open ok; enum needs stick
Serial Host CH2 38/39 EN 37 open ok; enum needs stick

Footprint (2060 / 4050)

Hub RAM (UH_TOTAL_BYTES @ version 4 at measurement time): ctrl+toggles+ scratch + class ws. (Layout is v7 since 1180: control block 20 longs, dual-bus toggle tables, and USB_AUDIO adds a 6,208 B ISO workspace — current totals per usb_hub_ram.spin2 / docs/design/hub-ram-map.md.)

Build defines Selftest .bin bytes Hub total (all-on)
USB_HID only 20978 scales HID 1024
USB_MSC only 20880 scales MSC 8192
USB_CDC only 20572 scales CDC 2048
HID+MSC+CDC 23044 11840 live

Exclusive builds drop unused class objects and hub workspaces (#IFDEF).

Clock / baud (4030)

Sysclk Baud X Notes
300→50 MHz ladder via char.clock_ladder_run 10 legal / 1 illegal (48) logged live
200 MHz $CF5C live baseline; matches ladder
≤48 MHz invalid host rejects

CLOSED 2026-08-09 with a real clkset ladder (per-step VBUS cycles, hub + 3 real devices, honest scoring - a step fails if a compiled class leaf never binds): 165-300 MHz PASS full stack, 160 MARGINAL (card-reader config read intermittent), 140/150 FAIL (same read; enum+HID+CDC alone work to 140). Sub-160 limit is CPU-bound Spin host loops, not USB timing. The TX gap self-scales as 3/4 bit period above the ~168 MHz breakpoint (4040). Guidance: keep sysclk above 180 MHz; 200 default. Note: the sub-168 gap-0 branch has never run the hostile-content canary (2087 finding postdates the sweep), and the ladder predates the 1140/1150/1160/1170 engine rework - re-proven at 200 and 300 MHz only. 0190 (2026-08-11) then found and fixed two PHY bugs at exactly this margin: the RX ACK trailed two mailbox writes (blowing the device's 16-18 bit-time window through hub latency at 200 MHz - USB-3-fallback sticks went silent/wedged behind hubs) and SOFEN bursts starved the SOF grid into device suspend. Post-fix: the FULL dual-bus suite (canaries both buses, interleave, hot-plug) passes at 200 AND 300 MHz.

Isochronous audio (1180 / 2110, CLOSED 2026-08-12)

Device: C-Media CM108 adapter ($0D8C:$0014) behind the hub; validation rig: loopback jumper headphone→mic, making the stack its own frequency counter and level meter (harness/top_audio_accept.spin2, RESULT PASS):

Measure Result
Capture rate exactly 48,000 Hz and 44,100 Hz (16.16 fractional path)
Tone matrix 500/1k/2k/4k Hz each confirmed at its own Goertzel bin
Volume/mute effective (CM108 coarse steps, master channel)
60 s playback under load 0 underruns while the 2088 job cog copied files at 251 KB/s
First-hardware run 9 clean tones by ear, 169k frames sustained 1000/s (top_tone_keyboard_example)

Campaign findings (fixed in the same pass): interrupt-IN polls carried the bulk NAK budget (~64 ms/idle poll → ~1 ms); ISO receiver lacked the own-EOP gate; frame guards taxed both buses with the ISO reservation (27 KB/s disk → audio-bus-only); aud_read clip-discard punched DSP-shredding discontinuities (now whole-frames-only). Known characteristic: the CM108 mic is synchronous-mode ISO and loses SOF lock under jitter from a degraded bus peer (~99.3 % held with healthy peers); adaptive-mode playback immune.

Error injection (3060 Fixed live; 4040 boundary still Open)

Log: harness/logs/headless_260801-001823.log

Mode Host Evidence
NAK fail hs=$5A INJ NAK line
STALL fail hs=$1E stall++
DROP fail hs=$0 timeout
CRC fail after DATA $C3 crc++
recover bulk_in ok clear_inject

inj_fail=0 (all expect_fail + recover). Sustained inject+load failure-boundary sweep still Open (4040).

Sniffer (3070)

Path Live
sample_host_z(app.line_z) during bulk/SOF samples=32, act_mask has J
spare pair start(46) start ok (quiet without jumper)

MSC media (2030) — parked

Path Result
Dogfood BOT CAPACITY green (1024×512)
Real thumb/SD in hub on Serial Host Adequate media even if slow
USB kbd/mouse dongle Useful for HID host later; same hub enum gate
Blocker GET_DESC@0 / hub class-9 port power+reset (1090) not green

Parked until root/hub leaf enum works. Do not claim 2030 Fixed on dogfood BOT alone.

Structural rules

Build

cd harness
pnut-ts -d -I ../src -D USB_HID -D USB_MSC -D USB_CDC top_selftest.spin2
pnut-term-ts --headless -r top_selftest.bin -p P81svj44 -b 2000000 --end-marker "DEBUG_END_SESSION"