diff options
| author | hathach <[email protected]> | 2026-07-23 13:38:13 +0700 |
|---|---|---|
| committer | hathach <[email protected]> | 2026-07-24 14:55:59 +0700 |
| commit | 9c6c0390a0b163c8ea64145f759118795c45b47f (patch) | |
| tree | 23c7be1f3a296fed5f726defeaa4b504db8fa5f9 | |
| parent | 21bbcb5bbff18454979a1dc9aaa210e938a004b5 (diff) | |
docs(skills): formatting feedback — agent skill table, probe bullets, aligned columns
- target-debugger: skill list is now a table referencing skills by name only
(path pattern stated once).
- target-debug: probe-mapping run-on paragraph split into bullets; drop the
GDB Ninth-Edition caveat (calibre now holds the Tenth-Edition PDF, id 2264).
- Align markdown table columns across target-debug, usb-sniffer, usbmon, hil,
usbtest and the agent (7 tables); tables with paragraph-length cells left
unpadded (usbmon symptom map, usbtest case map).
| -rw-r--r-- | .claude/agents/target-debugger.md | 30 | ||||
| -rw-r--r-- | .claude/skills/hil/SKILL.md | 10 | ||||
| -rw-r--r-- | .claude/skills/target-debug/SKILL.md | 47 | ||||
| -rw-r--r-- | .claude/skills/usb-sniffer/SKILL.md | 12 | ||||
| -rw-r--r-- | .claude/skills/usbmon/SKILL.md | 24 | ||||
| -rw-r--r-- | .claude/skills/usbtest/SKILL.md | 12 |
6 files changed, 65 insertions, 70 deletions
diff --git a/.claude/agents/target-debugger.md b/.claude/agents/target-debugger.md index 4d874e38c..78110b179 100644 --- a/.claude/agents/target-debugger.md +++ b/.claude/agents/target-debugger.md @@ -8,26 +8,18 @@ You debug one failing USB behavior on one physical board until you can name the mechanism — or report exactly what you ruled out. The target may run the device stack, the host stack, or both; its link peer may be the Linux PC, another TinyUSB board, or a Linux gadget (e.g. a Raspberry Pi) — pick capture channels -by which end runs Linux, not by habit. These repo skills are your source of -truth; read the relevant SKILL.md BEFORE acting: +by which end runs Linux, not by habit. These repo skills (each at +`.claude/skills/<name>/SKILL.md`) are your source of truth; read the relevant +one BEFORE acting: -- `.claude/skills/target-debug/SKILL.md` — your primary playbook: technique - choice by intrusiveness, channel choice by link topology, capture recipes, - breakpoint/watchpoint budget and cost model, vector catch + fault autopsy, - SWO trace, GDB autopsy, all rig warnings. -- `.claude/skills/hil/SKILL.md` — host/config selection, board lock protocol, - `hil_test.py` invocation. -- `.claude/skills/usbmon/SKILL.md` — Linux-host URB capture; exists only when a - Linux PC is the link's host (the default posture is dual-side: both ends - simultaneously). -- `.claude/skills/usb-sniffer/SKILL.md` — wire-level capture with the hardware - tap: when the host can't see the bus (device never enumerates, pre-URB - failures), when usbmon and target logs disagree — the wire arbitrates — or - when TinyUSB is the host and no end has usbmon. -- `.claude/skills/usb-kernel-debug/SKILL.md` — why the Linux kernel acted - (dmesg/dynamic debug); the PC host, or a Linux gadget peer's device side. -- `.claude/skills/usb-kernel-recover/SKILL.md` — only when the DUT or fixture - wedges the rig PC's Linux host stack. +| Skill | Use for | +|--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| target-debug | primary playbook — technique choice by intrusiveness, channel choice by link topology, capture recipes, bp/wp budget + cost model, vector catch + fault autopsy, SWO trace, GDB autopsy, rig warnings | +| hil | host/config selection, board lock protocol, `hil_test.py` invocation | +| usbmon | Linux-host URB capture; only when a Linux PC is the link's host (default posture: dual-side, both ends simultaneously) | +| usb-sniffer | wire-level capture (hardware tap): host can't see the bus, usbmon vs target logs disagree, or TinyUSB is the host (no usbmon anywhere) | +| usb-kernel-debug | why the Linux kernel acted (dmesg/dynamic debug); PC host or a Linux gadget peer's device side | +| usb-kernel-recover | only when the DUT or fixture wedges the rig PC's Linux host stack | ## The loop (deliberately serial — no fan-out) diff --git a/.claude/skills/hil/SKILL.md b/.claude/skills/hil/SKILL.md index 7b76bf1b8..2aeaa10af 100644 --- a/.claude/skills/hil/SKILL.md +++ b/.claude/skills/hil/SKILL.md @@ -7,11 +7,11 @@ description: Use when running TinyUSB Hardware-in-the-Loop (HIL) tests on physic Run TinyUSB HIL tests on real boards. **Run `hostname` first** — it tells you which host you are on, which determines the default config and whether remote mode is possible. -| Host | Local config | Remote (SSH → ci.lan)? | -|------|--------------|------------------------| -| `htpc` (dev PC) | `test/hil/local.json` | yes (large pool, `test/hil/tinyusb.json`) | -| `ci` (the rig) | `test/hil/tinyusb.json` (large pool) | no — can't SSH to htpc, and boards are already local | -| `hifiphile` (external rig) | `test/hil/hfp.json` | no outbound SSH to htpc/ci; SSH-reachable FROM both | +| Host | Local config | Remote (SSH → ci.lan)? | +|----------------------------|--------------------------------------|------------------------------------------------------| +| `htpc` (dev PC) | `test/hil/local.json` | yes (large pool, `test/hil/tinyusb.json`) | +| `ci` (the rig) | `test/hil/tinyusb.json` (large pool) | no — can't SSH to htpc, and boards are already local | +| `hifiphile` (external rig) | `test/hil/hfp.json` | no outbound SSH to htpc/ci; SSH-reachable FROM both | Default to **local**. Use **remote** only when on `htpc` and the user says `remote`/`ci.lan`. Never attempt remote on `ci`. diff --git a/.claude/skills/target-debug/SKILL.md b/.claude/skills/target-debug/SKILL.md index d0ceef2a9..a89162ed6 100644 --- a/.claude/skills/target-debug/SKILL.md +++ b/.claude/skills/target-debug/SKILL.md @@ -10,12 +10,12 @@ stack (`hcd_*`/`tuh_*`), or both. Its link peer is not always a Linux PC: a TinyUSB host may face another TinyUSB board or a Linux gadget (e.g. a Raspberry Pi). Pick capture channels by which end runs Linux, not by habit: -| Skill | Answers | Exists when | -|---|---|---| -| `usbmon` | what the Linux host exchanged (URBs) | a Linux PC is the link's host | +| Skill | Answers | Exists when | +|--------------------|----------------------------------------------------|---------------------------------------------------| +| `usbmon` | what the Linux host exchanged (URBs) | a Linux PC is the link's host | | `usb-kernel-debug` | why the Linux kernel acted (dmesg / dynamic debug) | Linux on either end: PC host or Linux gadget peer | -| **`target-debug`** | **what the target did** (logs, driver state, PC) | always — either role, needs a debug probe | -| `usb-sniffer` | what crossed the wire (PIDs, handshakes, resets) | hardware tap cabled in — role-agnostic | +| **`target-debug`** | **what the target did** (logs, driver state, PC) | always — either role, needs a debug probe | +| `usb-sniffer` | what crossed the wire (PIDs, handshakes, resets) | hardware tap cabled in — role-agnostic | For enumeration/transfer bugs the default posture is **dual-side capture** — both ends simultaneously: usbmon + a target @@ -35,12 +35,15 @@ python3 test/hil/board_lock.py release <board> ``` Board → probe mapping: `test/hil/tinyusb.json` — `flasher.name` is the probe -family, `flasher.uid` the **probe serial** (many identical probes on the rig: -J-Link needs `-SelectEmuBySN <uid>` / GDB server `-select usb=<uid>`; OpenOCD -`-c 'adapter serial <uid>'`). `JLINK_DEVICE` / `OPENOCD_OPTION` come from -`hw/bsp/<family>/boards/<board>/board.cmake` (or `board.mk`); find the family -with `ls -d hw/bsp/*/boards/<board>`. Run on the host that owns the probe — -config is `test/hil/tinyusb.json` on ci, `local.json` on htpc (`hil` skill). +family, `flasher.uid` the **probe serial** (many identical probes on the rig): + +- Select the probe by serial: J-Link `-SelectEmuBySN <uid>`, its GDB server + `-select usb=<uid>`, OpenOCD `-c 'adapter serial <uid>'`. +- `JLINK_DEVICE` / `OPENOCD_OPTION`: from + `hw/bsp/<family>/boards/<board>/board.cmake` (or `board.mk`); family via + `ls -d hw/bsp/*/boards/<board>`. +- Run on the host that owns the probe — config `test/hil/tinyusb.json` on ci, + `local.json` on htpc (`hil` skill). ## Pick the least intrusive technique that can answer the question @@ -48,16 +51,16 @@ Observation can mask the bug — the ch32v307 Heisenbug changed behavior under logging *and* under the debugger. If the bug disappears when instrumented, that IS a finding (timing-sensitive): move down in intrusiveness, not up. -| Technique | Intrusiveness | Reach for it when | -|---|---|---| -| PC-sampling | none — no halt, no code change | core wedged/spinning somewhere unknown (rusb2 FRDY) | -| SWO exception trace / hw PC-sample | none — needs SWO pin wired | ISR ordering/timing with zero code change | -| Vector catch | none until a fault fires | crash-shaped wedges — autopsy AT the faulting pc | -| RAM ring-buffer | ~tens of cycles per event | ISR ordering/timing bugs (musb babble) | -| TU_LOG (RTT) | µs per line | logic bugs that survive logging (J-Link or OpenOCD rtt) | -| TU_LOG (UART) | ms per line — blocking write | same, when no debug-probe RTT path | -| dprintf / conditional breakpoint | halt+resume per hit (~ms) | low-rate probes post-wedge; never ISR-rate events | -| GDB halt / breakpoints | stops USB service entirely | post-mortem state autopsy once wedged | +| Technique | Intrusiveness | Reach for it when | +|------------------------------------|--------------------------------|---------------------------------------------------------| +| PC-sampling | none — no halt, no code change | core wedged/spinning somewhere unknown (rusb2 FRDY) | +| SWO exception trace / hw PC-sample | none — needs SWO pin wired | ISR ordering/timing with zero code change | +| Vector catch | none until a fault fires | crash-shaped wedges — autopsy AT the faulting pc | +| RAM ring-buffer | ~tens of cycles per event | ISR ordering/timing bugs (musb babble) | +| TU_LOG (RTT) | µs per line | logic bugs that survive logging (J-Link or OpenOCD rtt) | +| TU_LOG (UART) | ms per line — blocking write | same, when no debug-probe RTT path | +| dprintf / conditional breakpoint | halt+resume per hit (~ms) | low-rate probes post-wedge; never ISR-rate events | +| GDB halt / breakpoints | stops USB service entirely | post-mortem state autopsy once wedged | ## TU_LOG capture @@ -293,7 +296,7 @@ the wire itself: `usb-sniffer` skill (hardware tap, PID-level). - J-Link (UM08001): <https://kb.segger.com/UM08001_J-Link_/_J-Trace_User_Guide> — flash breakpoints, RTT, SWO, monitor mode, Commander. - OpenOCD: <https://openocd.org/doc/html/index.html> — `rtt`, `bp`/`wp`, `cortex_m vector_catch`/`maskisr`, `itm`/`tpiu`. - "Debugging with GDB" (§5.1 = break/watch/dprintf): Tenth Edition (GDB 18) - via calibre/`read-doc` — NOT the 2002 Ninth-Edition txt also there — or + via calibre/`read-doc`, or `curl -sL -o /tmp/gdb.pdf https://sourceware.org/gdb/current/onlinedocs/gdb.pdf` (the HTML mirror blocks fetchers). Installed `arm-none-eabi-gdb` `help <cmd>` is authoritative here. diff --git a/.claude/skills/usb-sniffer/SKILL.md b/.claude/skills/usb-sniffer/SKILL.md index a20aecf05..8d070709f 100644 --- a/.claude/skills/usb-sniffer/SKILL.md +++ b/.claude/skills/usb-sniffer/SKILL.md @@ -7,12 +7,12 @@ description: Use when you need wire-level USB evidence that host-side capture ca Extends the debugging trio with the layer below URBs: -| Skill | Answers | -|---|---| -| `usbmon` | what a Linux PC host exchanged (URBs) | -| `usb-kernel-debug` | why the Linux kernel acted (dmesg / dynamic debug) | -| `target-debug` | what the TinyUSB target did (device or host role) | -| **`usb-sniffer`** | **what actually crossed D+/D-** (PIDs, handshakes, resets, timing) | +| Skill | Answers | +|--------------------|--------------------------------------------------------------------| +| `usbmon` | what a Linux PC host exchanged (URBs) | +| `usb-kernel-debug` | why the Linux kernel acted (dmesg / dynamic debug) | +| `target-debug` | what the TinyUSB target did (device or host role) | +| **`usb-sniffer`** | **what actually crossed D+/D-** (PIDs, handshakes, resets, timing) | Reach for it when usbmon can't see (device never binds, pre-enumeration failures), can't be trusted (URB completed but did the wire really ACK?), or diff --git a/.claude/skills/usbmon/SKILL.md b/.claude/skills/usbmon/SKILL.md index a2e7c1ac1..490c79e7f 100644 --- a/.claude/skills/usbmon/SKILL.md +++ b/.claude/skills/usbmon/SKILL.md @@ -29,18 +29,18 @@ tshark -r cap.pcapng -Y 'usb.device_address==26' # filter to one device ## Filter (`-Y '<expr>'`) -| Goal | Expression | -|---|---| -| One device / endpoint | `usb.device_address==26` / `usb.endpoint_address==0x81` | -| IN (to host) / OUT (from host) | `usb.endpoint_address.direction==1` / `==0` | -| Submit / Complete event | `usb.urb_type=='S'` / `=='C'` (char literal: single quotes) | -| Control / bulk / interrupt / iso | `usb.transfer_type==2` / `3` / `1` / `0` | -| Only transfers carrying data | `usb.data_len>0` | -| GET_DESCRIPTOR / SET_ADDRESS / SET_CONFIGURATION | `usb.setup.bRequest==6` / `5` / `9` | -| SET_INTERFACE / CLEAR_FEATURE (clear-halt) | `usb.setup.bRequest==11` / `1` | -| Descriptor type DEVICE/CONFIG/STRING/HID-report | `usb.bDescriptorType==1` / `2` / `3` / `0x22` | -| Class / vendor requests | `usb.bmRequestType.type!=0` | -| STALLs / errors | `usb.urb_status!=0 && usb.urb_status!=-115` | +| Goal | Expression | +|--------------------------------------------------|-------------------------------------------------------------| +| One device / endpoint | `usb.device_address==26` / `usb.endpoint_address==0x81` | +| IN (to host) / OUT (from host) | `usb.endpoint_address.direction==1` / `==0` | +| Submit / Complete event | `usb.urb_type=='S'` / `=='C'` (char literal: single quotes) | +| Control / bulk / interrupt / iso | `usb.transfer_type==2` / `3` / `1` / `0` | +| Only transfers carrying data | `usb.data_len>0` | +| GET_DESCRIPTOR / SET_ADDRESS / SET_CONFIGURATION | `usb.setup.bRequest==6` / `5` / `9` | +| SET_INTERFACE / CLEAR_FEATURE (clear-halt) | `usb.setup.bRequest==11` / `1` | +| Descriptor type DEVICE/CONFIG/STRING/HID-report | `usb.bDescriptorType==1` / `2` / `3` / `0x22` | +| Class / vendor requests | `usb.bmRequestType.type!=0` | +| STALLs / errors | `usb.urb_status!=0 && usb.urb_status!=-115` | Combine with `&&` — e.g. one endpoint's data: `usb.endpoint_address==0x02 && usb.data_len>0`. diff --git a/.claude/skills/usbtest/SKILL.md b/.claude/skills/usbtest/SKILL.md index 850781d6e..76a01839c 100644 --- a/.claude/skills/usbtest/SKILL.md +++ b/.claude/skills/usbtest/SKILL.md @@ -80,12 +80,12 @@ python3 test/hil/usbtest.py --serial <uid> --keep-binding --tests 29 # one case ## Debug ladder (escalate in order) -| errno | Meaning | -|---|---| -| 110 | timeout — endpoint NAKing forever / device wedged | -| 32 | EPIPE — unexpected STALL | -| 5 | EIO — iso packet errors (check `dmesg`: "N errors out of M") | -| 71 | EPROTO — device answered wrong / too slow (after HC retries) | +| errno | Meaning | +|-------|--------------------------------------------------------------| +| 110 | timeout — endpoint NAKing forever / device wedged | +| 32 | EPIPE — unexpected STALL | +| 5 | EIO — iso packet errors (check `dmesg`: "N errors out of M") | +| 71 | EPROTO — device answered wrong / too slow (after HC retries) | 1. `usbtest.py` per-case output + its captured `dmesg` (`TEST n` markers bracket each case). 2. **usbmon** (`usbmon` skill): URB-level ground truth. **It cannot show data toggles or NAKs** — |
