summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-07-23 13:38:13 +0700
committerhathach <[email protected]>2026-07-24 14:55:59 +0700
commit9c6c0390a0b163c8ea64145f759118795c45b47f (patch)
tree23c7be1f3a296fed5f726defeaa4b504db8fa5f9
parent21bbcb5bbff18454979a1dc9aaa210e938a004b5 (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.md30
-rw-r--r--.claude/skills/hil/SKILL.md10
-rw-r--r--.claude/skills/target-debug/SKILL.md47
-rw-r--r--.claude/skills/usb-sniffer/SKILL.md12
-rw-r--r--.claude/skills/usbmon/SKILL.md24
-rw-r--r--.claude/skills/usbtest/SKILL.md12
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** —