Files
tinyusb/AGENTS.md
2026-04-28 22:18:21 +07:00

8.3 KiB

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.

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.
  • Formatting/lint: clang-format (.clang-format), codespell (.codespellrc), run pre-commit run --all-files before submitting.

Bootstrap

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)
. $HOME/code/esp-idf/export.sh                     # Espressif only: before any build/flash/monitor

Build

Single example (CMake+Ninja, recommended, 1-3 s):

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):

cd examples && mkdir -p build && cd build
cmake -DBOARD=raspberry_pi_pico -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel .. && cmake --build .

Single example with Make:

cd examples/device/cdc_msc && make BOARD=raspberry_pi_pico all

Espressif (only ESP-IDF examples like cdc_msc_freertos):

. $HOME/code/esp-idf/export.sh
cd examples/device/cdc_msc_freertos
idf.py -DBOARD=espressif_s3_devkitc build

Build options (CMake -D… / Make …=…):

  • Debug: CMAKE_BUILD_TYPE=Debug / DEBUG=1
  • Logging: LOG=2 (add LOGGER=rtt for RTT)
  • Root hub port: RHPORT_DEVICE=1
  • Speed: RHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED

Flash

ninja cdc_msc-jlink     | make BOARD=… flash-jlink        # JLink
ninja cdc_msc-openocd   | make BOARD=… flash-openocd      # OpenOCD
ninja cdc_msc-uf2       | make BOARD=… all uf2            # UF2
ninja -t targets                                          # list CMake targets
idf.py -DBOARD=… flash|monitor                            # Espressif (after export.sh)

GDB Debugging

Look up JLINK_DEVICE / OPENOCD_OPTION in hw/bsp/*/boards/*/board.cmake.

JLink — Terminal 1:

JLinkGDBServer -device stm32h743xi -if SWD -speed 4000 -port 2331 -swoport 2332 -telnetport 2333 -nogui

OpenOCD — Terminal 1:

openocd -f interface/stlink.cfg -f target/stm32h7x.cfg      # or interface/jlink.cfg
# rp2040/rp2350 via CMSIS-DAP:
openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000"

Terminal 2 — connect GDB (JLink :2331, OpenOCD :3333):

arm-none-eabi-gdb /tmp/build/firmware.elf
(gdb) target remote :2331
(gdb) monitor reset halt
(gdb) load
(gdb) break main         # optional, to stop at entry
(gdb) continue

RTT logging: build with LOG=2 LOGGER=rtt, flash, then run JLinkGDBServer with -RTTTelnetPort 19021, and in another terminal JLinkRTTClient (pipe to tee rtt.log or use timeout 20s JLinkRTTClient > rtt.log for non-interactive capture).

Testing

Unit (Ceedling, Unity+CMock, ~4 s):

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) for the full procedure (local vs remote mode, config selection, SSH copy steps, debugging tips). Requires pre-built examples (Build Option 2).

Documentation

pip install -r docs/requirements.txt
cd docs && sphinx-build -b html . _build        # ~2.5 s

Code Size Metrics

Verify size impact before committing.

Single-board (iterative, ~30 s):

rm -rf cmake-build
python3 tools/build.py -b raspberry_pi_pico --target all --target tinyusb_metrics
python3 tools/metrics.py combine -j -m -f tinyusb/src cmake-build/cmake-build-*/metrics.json

Compare vs master: run the above on master, mv metrics.json metrics_master.json, switch branch, rebuild, then:

python3 tools/metrics.py compare -m -f tinyusb/src metrics_master.json metrics.json

Full CI (all arm-gcc families, 2-4 min):

rm -rf cmake-build
FAMILIES=$(python3 .github/workflows/ci_set_matrix.py | python3 -c "import sys,json;d=json.load(sys.stdin);print(' '.join(d.get('arm-gcc',[])))")
python3 tools/build.py --one-first --target all --target tinyusb_metrics $FAMILIES
python3 tools/metrics.py combine -j -m -f tinyusb/src cmake-build/cmake-build-*/metrics.json

Static Analysis (PVS-Studio)

Requires compile_commands.json (CMake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON).

pvs-studio-analyzer analyze \
  -f examples/cmake-build-raspberry_pi_pico/compile_commands.json \
  -R .PVS-Studio/.pvsconfig [-S path/to/file.c ...] \
  -o pvs-report.log -j12 --dump-files \
  --misra-c-version 2023 --misra-cpp-version 2008 --use-old-parser
plog-converter -a GA:1,2 -t errorfile pvs-report.log     # view results

Add -S <file> (repeatable) to restrict to specific sources. ~10-30 s.

Validation After Changes

  1. pre-commit run --all-files — format, spell, unit tests (10-15 s).
  2. Build at least one board's full example set (Build Option 2) 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

Do not commit automatically — leave changes for maintainer review.

  1. Bump version at top of tools/make_release.py.
  2. Run python3 tools/make_release.py to refresh: src/tusb_option.h, repository.yml, library.json, sonar-project.properties, docs/reference/boards.rst, hw/bsp/BoardPresets.json.
  3. Changelog docs/info/changelog.rst:
    • git log <last-tag>..HEAD --oneline for commit list.
    • Read merged PRs for context (gh pr view, or github MCP tools).
    • Follow existing format: version + ====== underline, italic date, sections (General, API Changes, DCD & HCD, Device Stack, Host Stack, Testing), RST inline code for symbols.
  4. Validate: ceedling test:all, build cdc_msc for stm32f407disco, review git diff --stat.
  5. Leave unstaged. Maintainer commits Bump version to X.Y.Z, then: git tag -a vX.Y.Z -m "Release X.Y.Z" && git push origin <branch> vX.Y.Z. Create GitHub release from tag.

References

  • MCU reference manuals, datasheets, schematics: $HOME/Documents/Calibre Library.
  • 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_config.h, tools/get_deps.py, tools/build.py, test/unit-test/project.yml.

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.