# TinyUSB Agent Instructions TinyUSB is a cross-platform USB Host/Device stack for embedded systems: memory-safe (no dynamic allocation) and thread-safe (ISR events deferred to task context). Reference these instructions first; fall back to search/bash only when reality diverges. ## Behavioral Guidelines Bias toward caution over speed. For trivial tasks, use judgment. - **Think first** — state assumptions; ask if unclear; present alternatives instead of picking silently. - **Simplicity** — no features, abstractions, flexibility, or error handling beyond what was asked. If 200 lines could be 50, rewrite. - **Surgical changes** — touch only what the task requires; match existing style; don't refactor working code; mention unrelated dead code rather than deleting it. Remove only orphans *your* changes created. - **Goal-driven** — turn tasks into verifiable goals ("write failing test, make it pass"). For multi-step work, state a brief `step → verify` plan. - **Worktrees** — default to a git worktree for any branch or multi-step work; never switch the shared primary checkout's branch. Before creating a branch or worktree, inspect the active branch and existing worktrees. If the target branch is already checked out, or the target changes already exist in an active checkout, ask the user whether to reuse that checkout before creating another branch or worktree. Sessions run concurrently: switching the primary checkout mid-flight disrupts other sessions and can silently point a review, build, or commit at the wrong diff. Only trivial one-shot fixes may skip this. Standard location: `.worktrees/` at the repo root (gitignored), e.g. `git worktree add .worktrees/my-branch -b my-branch`. In a new worktree, symlink the dependency dirs (`lib/*`, `hw/mcu/*`, `tools/linkermap` — the keys of `deps_all` in `tools/get_deps.py`) to the primary checkout instead of re-cloning them; only if the branch needs a different dep revision, replace that one symlink with a real dir and run `get_deps.py` for it. ## Ground Rules - **Language/style:** C99, 2-space indent (no tabs), snake_case helpers, `UPPER_CASE` macros. Public APIs use `tud_`/`tuh_`; macros use `TU_`. Headers self-contained with `#if CFG_TUSB_MCU` guards. - **Safety:** no dynamic allocation; defer ISR work to task context; use `TU_ASSERT()` for error checks; always check return values; include order: C stdlib → tusb common → drivers → classes. - **Layout:** `src/` core, `hw/{mcu,bsp}/` MCU+BSP, `examples/{device,host,dual}/`, `test/{unit-test,fuzz,hil}/`, `docs/`, `tools/`. - **Commits/PRs:** imperative mood, scoped changes, link issues, include test/build evidence. After opening a PR, drive it to green: address automated review comments (Copilot/Codex/Claude) and fix failing CI, pushing follow-ups until checks pass and threads resolve. Useful: `gh pr checks --watch`, `gh pr view --comments`. - **Deferred work:** work that is worth doing but is a *separate scope* from the current PR — it deserves its own PR, written by a different session. Write it as a **handoff** with the `superpowers:writing-plans` skill, one doc per follow-up, in `docs/superpowers/followup/pr-.md` (the PR it was split out of, so the origin stays traceable). Say what is already established (with citations/measurements), what remains, and why it was split out. Delete the doc when its PR lands. Never bundle unrelated follow-ups into one file. - **Formatting/lint:** `clang-format` (`.clang-format`), `codespell` (`.codespellrc`); run `pre-commit run --all-files` before submitting. ## Bootstrap ```bash sudo apt-get install -y gcc-arm-none-eabi # ARM toolchain (2-5 min, one-time) python3 tools/get_deps.py [FAMILY|-b BOARD] # fetch deps into lib/, hw/mcu/ (<1 s) . "$IDF_PATH/export.sh" # Espressif only: before any build/flash/monitor (IDF_PATH set per host) ``` ## Build Single example (CMake+Ninja, recommended, 1-3 s): ```bash cd examples/device/cdc_msc && mkdir -p build && cd build cmake -DBOARD=raspberry_pi_pico -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel .. && cmake --build . ``` All examples for a board (15-20 s; some objcopy failures are non-critical). The build dir **must** be `cmake-build-` — HIL tests expect that exact name: ```bash cd examples cmake -B cmake-build-raspberry_pi_pico -DBOARD=raspberry_pi_pico -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel . && cmake --build cmake-build-raspberry_pi_pico ``` - **Make:** `cd examples/device/cdc_msc && make BOARD=raspberry_pi_pico all` - **Espressif** (ESP-IDF examples only, e.g. `cdc_msc_freertos`): after `export.sh`, `cd examples/device/cdc_msc_freertos && idf.py -DBOARD=espressif_s3_devkitc build` - **Options** (CMake `-D…` / Make `…=…`): `CMAKE_BUILD_TYPE=Debug`/`DEBUG=1`; `LOG=2` (`LOGGER=rtt` for RTT); `RHPORT_DEVICE=1`; `RHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED` ## Flash ```bash ninja cdc_msc-jlink # CMake; Make: make BOARD= flash-jlink ninja cdc_msc-openocd # CMake; Make: make BOARD= flash-openocd ninja cdc_msc-uf2 # CMake; Make: make BOARD= all uf2 ninja -t targets # list CMake targets ``` Espressif (after `export.sh`): `idf.py -DBOARD= flash` / `… monitor`. ## GDB Debugging Look up `JLINK_DEVICE` / `OPENOCD_OPTION` in `hw/bsp/*/boards/*/board.cmake` (CMake) or `board.mk` (Make). Terminal 1 — start a gdbserver: ```bash JLinkGDBServer -device stm32h743xi -if SWD -speed 4000 -port 2331 -nogui # JLink → :2331 openocd -f interface/stlink.cfg -f target/stm32h7x.cfg # OpenOCD → :3333 openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000" # rp2040/rp2350 ``` Terminal 2 — connect (``: 2331 JLink, 3333 OpenOCD): ```bash arm-none-eabi-gdb build/your_app.elf (gdb) target remote : # then: monitor reset halt → load → continue ``` **RTT:** build `LOG=2 LOGGER=rtt`; capture/console via the `rtt` skill (`.claude/skills/rtt/SKILL.md`). ## Testing **Unit (Ceedling, Unity+CMock, ~4 s):** ```bash sudo gem install ceedling cd test/unit-test && ceedling test:all # or ceedling test:test_fifo ``` **HIL (2-5 min):** invoke the `hil` skill (`.claude/skills/hil/SKILL.md`) — local vs remote mode, config selection, SSH copy steps, debugging. Requires pre-built examples (Build → "All examples for a board"). ## Documentation Sphinx docs in `docs/` (`.rst`, or `.md` via MyST). Use the `build-doc` skill (`.claude/skills/build-doc/SKILL.md`) to build/preview locally and regenerate auto-generated files (`tools/gen_doc.py` + `tools/gen_presets.py`) after adding a board or dependency. ## Code Size Metrics Verify size impact before committing with the `code-size` skill (`.claude/skills/code-size/SKILL.md`) — it wraps `tools/metrics_compare_base.py` for the base-vs-branch worktree + build + compare. Scopes: single example (`-e device/cdc_msc -b `, add `--bloaty`), all examples on a board (`-b `), or all arm-gcc CI families (`--ci`). Reports land in `cmake-metrics//metrics_compare.md` (and `_combined/` for `--ci`). ## Static Analysis (PVS-Studio) Use the `pvs` skill (`.claude/skills/pvs/SKILL.md`) — it builds the examples with an exported `compile_commands.json` and runs SAST + MISRA C:2023/C++:2008 for a board, emitting readable + SARIF output (~10-30 s). The examples build exports `compile_commands.json` by default. ## Validation After Changes 1. `pre-commit run --all-files` — format, spell, unit tests, HIL suites (~55 s; the HIL hooks deliberately exercise real timeouts and hangs). 2. Build at least one board's full example set (Build → "All examples for a board") for modules you touched. 3. Run relevant unit tests; add fuzz/HIL coverage for parsers or protocol state machines. **Boards good for local testing:** - `stm32f407disco` — no external SDK - `raspberry_pi_pico` — Pico SDK required - Others: see `hw/bsp/FAMILY/boards/` Device examples need real hardware to validate runtime behavior; must at least build. ## Release Cutting a release — version bump, regenerated files, the per-release changelog, validation, and the maintainer's commit/tag/GitHub-release — is handled by the `make-release` skill (`.claude/skills/make-release/SKILL.md`). ## References - MCU reference manuals, datasheets, schematics: before answering register/bitfield/pinout/errata/timing questions from memory or the web — or changing a specific dcd/hcd driver — use the `read-doc` skill (`.claude/skills/read-doc/SKILL.md`) to cross-check against the maintainer's document library; tell the user if the needed document is missing (skill no-ops if the library is absent). Never search the library tree directly — the skill owns its location and search. - Linux kernel behaviour (usbfs, usbtest, sysfs attributes, device locks, D state): never infer it from symptoms — read the source for the *running* version. It refutes as often as it confirms: it has killed two plausible dcd theories and corrected a recovery skill's own attribute list. ```bash V=$(uname -r | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+') # on the rig: ssh ci.lan uname -r curl -fsSL "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/drivers/usb/core/sysfs.c?h=v$V" ``` - Supported MCUs/boards: `hw/bsp/` and `docs/reference/boards.rst`. - USB classes: `src/class/{cdc,hid,msc,audio,…}/` — each has `*_device.c` and `*_host.c`. - Key files: `src/tusb.h`, `src/tusb_option.h`, `tools/get_deps.py`, `tools/build.py`, `test/unit-test/project.yml` (each example carries its own `src/tusb_config.h`). ## Common Build Issues - Missing compiler → install `gcc-arm-none-eabi`. - Missing deps → `python3 tools/get_deps.py FAMILY`. - Unknown board → check `hw/bsp/FAMILY/boards/`. - `objcopy` errors in full builds are often non-critical; retry the single example.