update AGENTS.md and claude hil skill

This commit is contained in:
hathach
2026-04-28 22:18:21 +07:00
parent 81641e35c6
commit da9d36fa4c
3 changed files with 192 additions and 406 deletions

View File

@ -1,58 +0,0 @@
# hil
Run Hardware-in-the-Loop (HIL) tests on physical boards.
## Arguments
- $ARGUMENTS: Optional flags (e.g. board name, extra args). If empty, runs all boards with default config.
## Instructions
1. Parse $ARGUMENTS:
- If $ARGUMENTS contains `-b BOARD_NAME`, run for that specific board only.
- If $ARGUMENTS is empty or has no `-b`, run for all boards in the config.
- Pass through any other flags (e.g. `-v` for verbose, `-r N` for retry count) directly to the command.
2. Determine whether to run **locally** or **remotely via SSH**:
- **Local**: boards are attached to this machine (default when `local.json` is used)
- **Remote (`ssh ci.lan`)**: boards are attached to the CI machine (when `tinyusb.json` is used)
3. **Local execution** (boards attached to this machine):
```bash
python test/hil/hil_test.py -b BOARD_NAME -B examples $HIL_CONFIG $EXTRA_ARGS
```
4. **Remote execution** (boards attached to `ci.lan`):
Only copy the minimal files needed (firmware binaries + test script + config), then run remotely.
```bash
REMOTE=ci.lan
REMOTE_DIR=/tmp/tinyusb-hil
# Create remote working directory
ssh $REMOTE "rm -rf $REMOTE_DIR && mkdir -p $REMOTE_DIR/test/hil"
# Copy HIL test script and its dependency
scp test/hil/hil_test.py test/hil/pymtp.py test/hil/tinyusb.json $REMOTE:$REMOTE_DIR/test/hil/
# Copy only the firmware binaries for the target board(s)
# For a specific board:
scp -r examples/cmake-build-$BOARD_NAME $REMOTE:$REMOTE_DIR/examples/
# Or for all boards that have been built:
# for dir in examples/cmake-build-*/; do scp -r "$dir" $REMOTE:$REMOTE_DIR/examples/; done
# Run the test remotely
ssh $REMOTE "cd $REMOTE_DIR && python3 test/hil/hil_test.py -b $BOARD_NAME -B examples tinyusb.json $EXTRA_ARGS"
```
Note: The remote machine (`ci.lan`) must have:
- Python 3 with `pyserial` installed (`pip install pyserial`)
- Flasher tools: `JLinkExe`, `openocd`, etc. as needed by the board
- USB access to the boards (udev rules configured)
5. Use a timeout of at least 20 minutes (600000ms). HIL tests take 2-5 minutes. NEVER cancel early.
6. After the test completes:
- Show the test output to the user.
- Summarize pass/fail results per board.
- If there are failures, suggest re-running with `-v` flag for verbose output to help debug.

View File

@ -0,0 +1,75 @@
---
name: hil
description: Use when running TinyUSB Hardware-in-the-Loop (HIL) tests on physical boards, debugging HIL failures, or copying firmware to the ci.lan test rig. Covers local execution and remote execution over SSH, config selection, and debugging tips.
---
# Hardware-in-the-Loop (HIL) Testing
Run TinyUSB HIL tests against real boards. Two execution modes — **local** (boards attached to this machine) and **remote** (boards attached to `ci.lan`, reached over SSH). Default to **local** unless the user specifies `remote`. Do not auto-detect.
## Prerequisites
- Examples must already be built for the target board(s). See AGENTS.md "Build" section, Option 2 (all examples for a board), which produces `examples/cmake-build-BOARD_NAME/`.
- `-B examples` tells `hil_test.py` that `examples/` is the parent folder containing the per-board build outputs.
## Choosing arguments
Infer from the user's request:
- **Mode:** `local` (default) or `remote`. Only switch to `remote` if the user explicitly says so or names `ci.lan`.
- **Board:** if the user names a specific board, pass `-b BOARD_NAME`. Otherwise run all boards in the config.
- **Pass-through flags:** `-v` (verbose), `-r N` (retry count), etc. — pass through unchanged.
Config file follows from mode:
- **Local** → `local.json`
- **Remote** → `tinyusb.json`
## Local execution
Boards attached to this machine:
```bash
python test/hil/hil_test.py -b BOARD_NAME -B examples local.json $EXTRA_ARGS
# or for all boards in the config:
python test/hil/hil_test.py -B examples local.json $EXTRA_ARGS
```
## Remote execution (ci.lan)
Copy only the minimal files needed (firmware binaries + test script + config), then run remotely:
```bash
REMOTE=ci.lan
REMOTE_DIR=/tmp/tinyusb-hil
# Create remote working directory
ssh $REMOTE "rm -rf $REMOTE_DIR && mkdir -p $REMOTE_DIR/test/hil"
# Copy HIL test script and its dependency
scp test/hil/hil_test.py test/hil/pymtp.py test/hil/tinyusb.json $REMOTE:$REMOTE_DIR/test/hil/
# Copy firmware binaries
# Specific board:
scp -r examples/cmake-build-$BOARD_NAME $REMOTE:$REMOTE_DIR/examples/
# Or all built boards:
# for dir in examples/cmake-build-*/; do scp -r "$dir" $REMOTE:$REMOTE_DIR/examples/; done
# Run the test remotely
ssh $REMOTE "cd $REMOTE_DIR && python3 test/hil/hil_test.py -b $BOARD_NAME -B examples tinyusb.json $EXTRA_ARGS"
```
The remote machine (`ci.lan`) must have:
- Python 3 with `pyserial` installed (`pip install pyserial`)
- Flasher tools: `JLinkExe`, `openocd`, etc. as needed by the board
- USB access to the boards (udev rules configured)
## Timing
HIL runs take 2-5 minutes. Use a timeout of at least 20 minutes (600000 ms). NEVER cancel early.
## Reporting results
After the test completes:
- Show the test output to the user.
- Summarize pass/fail per board.
- On failure, suggest re-running with `-v` for verbose output. If `-v` isn't enough, temporarily add debug prints to `test/hil/hil_test.py` to pinpoint the issue.

465
AGENTS.md
View File

@ -1,428 +1,197 @@
# TinyUSB Agent Instructions
TinyUSB is an open-source cross-platform USB Host/Device stack for embedded systems, designed to be memory-safe with no
dynamic allocation and thread-safe with all interrupt events deferred to non-ISR task functions.
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).
Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected
information that does not match the info here.
Reference these instructions first; fall back to search/bash only when reality diverges.
## Shared Ground Rules
- Keep TinyUSB memory-safe: avoid dynamic allocation, defer ISR work to task context, and follow C99 with two-space indentation/no tabs.
- Match file organization: core stack under `src`, MCU/BSP support in `hw/{mcu,bsp}`, examples under `examples/{device,host,dual}`, docs in `docs`, tests under `test/{unit-test,fuzz,hil}`.
- Use descriptive snake_case for helpers, reserve `tud_`/`tuh_` for public APIs, `TU_` for macros, and keep headers self-contained with `#if CFG_TUSB_MCU` guards where needed.
- Prefer `.clang-format` for C/C++ formatting, run `pre-commit run --all-files` before submitting, and document board/HIL coverage when applicable.
- Commit in imperative mood, keep changes scoped, and supply PRs with linked issues plus test/build evidence.
## Behavioral Guidelines
Bias toward caution over speed. For trivial tasks, use judgment.
## Bootstrap and Build Setup
- **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.
- Install ARM GCC toolchain: `sudo apt-get update && sudo apt-get install -y gcc-arm-none-eabi`
- Fetch core dependencies: `python3 tools/get_deps.py` -- takes <1 second. NEVER CANCEL.
- For specific board families: `python3 tools/get_deps.py FAMILY_NAME` (e.g., rp2040, stm32f4), or
`python3 tools/get_deps.py -b BOARD_NAME`
- Dependencies are cached in `lib/` and `hw/mcu/` directories
- For **Espressif** boards, initialize the ESP-IDF environment before any build/flash/monitor command:
`. $HOME/code/esp-idf/export.sh`
## Ground Rules
## Build Examples
- **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.
Choose ONE of these approaches:
**Option 1: Individual Example with CMake and Ninja (RECOMMENDED)**
## Bootstrap
```bash
cd examples/device/cdc_msc
mkdir -p build && cd build
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):
```bash
cd examples/device/cdc_msc && mkdir -p build && cd build
cmake -DBOARD=raspberry_pi_pico -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel ..
cmake --build .
```
-- takes 1-2 seconds. NEVER CANCEL. Set timeout to 5+ minutes.
**Option 2: All Examples for a Board**
different folder than Option 1
All examples for a board (15-20 s; some objcopy failures are non-critical):
```bash
cd examples/
mkdir -p build && cd build
cmake -DBOARD=raspberry_pi_pico -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel ..
cmake --build .
cd examples && mkdir -p build && cd build
cmake -DBOARD=raspberry_pi_pico -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel .. && cmake --build .
```
-- takes 15-20 seconds, may have some objcopy failures that are non-critical. NEVER CANCEL. Set timeout to 30+ minutes.
**Option 3: Individual Example with Make**
Single example with Make:
```bash
cd examples/device/cdc_msc
make BOARD=raspberry_pi_pico all
cd examples/device/cdc_msc && make BOARD=raspberry_pi_pico all
```
-- takes 2-3 seconds. NEVER CANCEL. Set timeout to 5+ minutes.
**Option 4: Espressif Example with ESP-IDF**
Only ESP-IDF-enabled examples are supported for Espressif boards. Use FreeRTOS examples such as `examples/device/cdc_msc_freertos`
that contain `idf_component_register()` support.
Espressif (only ESP-IDF examples like `cdc_msc_freertos`):
```bash
. $HOME/code/esp-idf/export.sh
cd examples/device/cdc_msc_freertos
idf.py -DBOARD=espressif_s3_devkitc build
```
Use `-DBOARD=...` with any supported board under `hw/bsp/espressif/boards/`. NEVER CANCEL. Set timeout to 10+ minutes.
**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
## Build Options
- **Debug build**:
- CMake: `-DCMAKE_BUILD_TYPE=Debug`
- Make: `DEBUG=1`
- **With logging**:
- CMake: `-DLOG=2`
- Make: `LOG=2`
- **With RTT logger**:
- CMake: `-DLOG=2 -DLOGGER=rtt`
- Make: `LOG=2 LOGGER=rtt`
- **RootHub port selection**:
- CMake: `-DRHPORT_DEVICE=1`
- Make: `RHPORT_DEVICE=1`
- **Port speed**:
- CMake: `-DRHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED`
- Make: `RHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED`
## Flashing and Deployment
- **Flash with JLink**:
- CMake: `ninja cdc_msc-jlink`
- Make: `make BOARD=raspberry_pi_pico flash-jlink`
- **Flash with OpenOCD**:
- CMake: `ninja cdc_msc-openocd`
- Make: `make BOARD=raspberry_pi_pico flash-openocd`
- **Generate UF2**:
- CMake: `ninja cdc_msc-uf2`
- Make: `make BOARD=raspberry_pi_pico all uf2`
- **List all targets** (CMake/Ninja): `ninja -t targets`
- **Espressif flash**:
- Run `. $HOME/code/esp-idf/export.sh`
- `cd examples/device/cdc_msc_freertos`
- `idf.py -DBOARD=espressif_s3_devkitc flash`
- **Espressif serial monitor / chip log output**:
- Run `. $HOME/code/esp-idf/export.sh`
- `cd examples/device/cdc_msc_freertos`
- `idf.py -DBOARD=espressif_s3_devkitc monitor`
```bash
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 the board's `JLINK_DEVICE` and `OPENOCD_OPTION` from `hw/bsp/*/boards/*/board.cmake` (or `board.mk`).
Look up `JLINK_DEVICE` / `OPENOCD_OPTION` in `hw/bsp/*/boards/*/board.cmake`.
### JLinkGDBServer
**Terminal 1 start the GDB server:**
**JLink — Terminal 1:**
```bash
JLinkGDBServer -device stm32h743xi -if SWD -speed 4000 \
-port 2331 -swoport 2332 -telnetport 2333 -nogui
JLinkGDBServer -device stm32h743xi -if SWD -speed 4000 -port 2331 -swoport 2332 -telnetport 2333 -nogui
```
**Terminal 2 connect GDB:**
**OpenOCD — Terminal 1:**
```bash
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):
```bash
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
```
To break on entry instead of running immediately:
**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):**
```bash
(gdb) monitor reset halt
(gdb) load
(gdb) break main
(gdb) continue
sudo gem install ceedling
cd test/unit-test && ceedling test:all # or ceedling test:test_fifo
```
### OpenOCD
**Terminal 1 start the GDB server:**
```bash
openocd -f interface/stlink.cfg -f target/stm32h7x.cfg
# or with J-Link probe:
openocd -f interface/jlink.cfg -f target/stm32h7x.cfg
```
For **rp2040/rp2350** with a CMSIS-DAP probe (e.g. Picoprobe, debugprobe):
```bash
openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 5000"
# or for rp2350:
openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "adapter speed 5000"
```
For boards that define `OPENOCD_OPTION` in `board.cmake`, use those options directly:
```bash
openocd $(cat hw/bsp/FAMILY/boards/BOARD/board.cmake | grep OPENOCD_OPTION | ...)
```
**Terminal 2 connect GDB (OpenOCD default port is 3333):**
```bash
arm-none-eabi-gdb /tmp/build/firmware.elf
(gdb) target remote :3333
(gdb) monitor reset halt
(gdb) load
(gdb) continue
```
### RTT Logging with JLinkGDBServer
- Build with RTT logging enabled (example):
`cd examples/device/cdc_msc && make BOARD=stm32h743eval LOG=2 LOGGER=rtt all`
- Flash with J-Link:
`cd examples/device/cdc_msc && make BOARD=stm32h743eval LOG=2 LOGGER=rtt flash-jlink`
- Launch GDB server with RTT port (keep this running in terminal 1):
`JLinkGDBServer -device stm32h743xi -if SWD -speed 4000 -port 2331 -swoport 2332 -telnetport 2333 -RTTTelnetPort 19021 -nogui`
- Read RTT output (terminal 2):
`JLinkRTTClient`
- Capture RTT to file (optional):
`JLinkRTTClient | tee rtt.log`
- For non-interactive capture:
`timeout 20s JLinkRTTClient > rtt.log`
## Unit Testing
- Install Ceedling: `sudo gem install ceedling`
- Run all unit tests: `cd test/unit-test && ceedling` or `cd test/unit-test && ceedling test:all` -- takes 4 seconds.
NEVER CANCEL. Set timeout to 10+ minutes.
- Run specific test: `cd test/unit-test && ceedling test:test_fifo`
- Tests use Unity framework with CMock for mocking
## Hardware-in-the-Loop (HIL) Testing
- `-B examples` means `examples` is the parent folder that contains multi-board build outputs such as `examples/cmake-build-BOARD_NAME/...`
- Select config file before running HIL tests:
- if GitHub Actions self-hosted runner service is running, use `tinyusb.json`
- otherwise use `local.json`
- example:
`HIL_CONFIG=$( (systemctl list-units --type=service --state=running 2>/dev/null; systemctl --user list-units --type=service --state=running 2>/dev/null) | grep -q 'actions\.runner' && echo tinyusb.json || echo local.json )`
- Run tests on actual hardware, one of following ways:
- test a specific board `python test/hil/hil_test.py -b BOARD_NAME -B examples $HIL_CONFIG`
- test all boards in config `python test/hil/hil_test.py -B examples $HIL_CONFIG`
- In case of error, enabled verbose mode with `-v` flag for detailed logs. Also try to observe script output, and try to
modify hil_test.py (temporarily) to add more debug prints to pinpoint the issue.
- Requires pre-built (all) examples for target boards (see Build Examples section 2)
take 2-5 minutes. NEVER CANCEL. Set timeout to 20+ minutes.
**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
- Install requirements: `pip install -r docs/requirements.txt`
- Build docs: `cd docs && sphinx-build -b html . _build` -- takes 2-3 seconds. NEVER CANCEL. Set timeout to 10+ minutes.
```bash
pip install -r docs/requirements.txt
cd docs && sphinx-build -b html . _build # ~2.5 s
```
## Code Size Metrics
Generate and compare code size metrics to evaluate the impact of changes. This is the most common workflow
when making code changes use it to verify size impact before committing.
**Quick single-board metrics (preferred for iterative development):**
Verify size impact before committing.
**Single-board (iterative, ~30 s):**
```bash
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
```
This builds all examples for one board and produces `metrics.json` + `metrics.md`. Takes ~30 seconds.
NEVER CANCEL. Set timeout to 10+ minutes.
**Comparing with master (before/after workflow):**
1. On master: build and save baseline
```bash
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
mv metrics.json metrics_master.json
```
2. Switch to your branch: rebuild
```bash
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
```
3. Compare: `python3 tools/metrics.py compare -m -f tinyusb/src metrics_master.json metrics.json`
Produces `metrics_compare.md` showing size differences.
**Full CI metrics (all arm-gcc families, for thorough validation):**
**Compare vs master:** run the above on master, `mv metrics.json metrics_master.json`, switch branch, rebuild, then:
```bash
python3 tools/metrics.py compare -m -f tinyusb/src metrics_master.json metrics.json
```
**Full CI (all arm-gcc families, 2-4 min):**
```bash
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',[])))")
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
```
Builds the first board of each family. Takes 2-4 minutes. NEVER CANCEL. Set timeout to 10+ minutes.
## Static Analysis (PVS-Studio)
## Code Quality and Validation
Requires `compile_commands.json` (CMake `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`).
- Format code: `clang-format -i path/to/file.c` (uses `.clang-format` config)
- Check spelling: `pip install codespell && codespell` (uses `.codespellrc` config)
- Pre-commit hooks validate unit tests and code quality automatically
## Static Analysis with PVS-Studio
- **Analyze whole project**:
```bash
pvs-studio-analyzer analyze -f examples/cmake-build-raspberry_pi_pico/compile_commands.json -R .PVS-Studio/.pvsconfig -o pvs-report.log -j12 --dump-files --misra-cpp-version 2008 --misra-c-version 2023 --use-old-parser
```
- **Analyze specific source files**:
```bash
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-cpp-version 2008 --misra-c-version 2023 --use-old-parser
```
- **Multiple specific files**:
```bash
pvs-studio-analyzer analyze -f examples/cmake-build-raspberry_pi_pico/compile_commands.json -R .PVS-Studio/.pvsconfig -S src/file1.c -S src/file2.c -o pvs-report.log -j12 --dump-files --misra-cpp-version 2008 --misra-c-version 2023 --use-old-parser
```
- Requires `compile_commands.json` in the build directory (generated by CMake with `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`)
- Use `-f` option to specify path to `compile_commands.json`
- Use `-R .PVS-Studio/.pvsconfig` to specify rule configuration file
- Use `-j12` for parallel analysis with 12 threads
- `--dump-files` saves preprocessed files for debugging
- `--misra-c-version 2023` enables MISRA C:2023 checks
- `--misra-cpp-version 2008` enables MISRA C++:2008 checks
- `--use-old-parser` uses legacy parser for compatibility
- Analysis takes ~10-30 seconds depending on project size. Set timeout to 5+ minutes.
- View results: `plog-converter -a GA:1,2 -t errorfile pvs-report.log` or open in PVS-Studio GUI
## Validation Checklist
### ALWAYS Run These After Making Changes
1. **Pre-commit validation** (RECOMMENDED): `pre-commit run --all-files`
- Install pre-commit: `pip install pre-commit && pre-commit install`
- Runs all quality checks, unit tests, spell checking, and formatting
- Takes 10-15 seconds. NEVER CANCEL. Set timeout to 15+ minutes.
2. **Build validation**: Build at least one board with all example that exercises your changes, see Build Examples
section (option 2)
3. Run unit tests relevant to touched modules; add fuzz/HIL coverage when modifying parsers or protocol state machines.
### Manual Testing Scenarios
- **Device examples**: Cannot be fully tested without real hardware, but must build successfully
- **Unit tests**: Exercise core stack functionality - ALL tests must pass
- **Build system**: Must be able to build examples for multiple board families
### Board Selection for Testing
- **STM32F4**: `stm32f407disco` - no external SDK required, good for testing
- **RP2040**: `raspberry_pi_pico` - requires Pico SDK, commonly used
- **Other families**: Check `hw/bsp/FAMILY/boards/` for available boards
## Release Instructions
**DO NOT commit files automatically - only modify files and let the maintainer review before committing.**
1. Bump the release version variable at the top of `tools/make_release.py`.
2. Execute `python3 tools/make_release.py` to refresh:
- `src/tusb_option.h` (version defines)
- `repository.yml` (version mapping)
- `library.json` (PlatformIO version)
- `sonar-project.properties` (SonarQube version)
- `docs/reference/boards.rst` (generated board documentation)
- `hw/bsp/BoardPresets.json` (CMake presets)
3. Generate release notes for `docs/info/changelog.rst`:
- Get commit list: `git log <last-release-tag>..HEAD --oneline`
- **Visit GitHub PRs** for merged pull requests to understand context and gather details
- Use GitHub tools to search/read PRs: `github-mcp-server-list_pull_requests`, `github-mcp-server-pull_request_read`
- Extract key changes, API modifications, bug fixes, and new features from PR descriptions
- Add new changelog entry following the existing format:
- Version heading with equals underline (e.g., `0.20.0` followed by `======`)
- Release date in italics (e.g., `*November 19, 2024*`)
- Major sections: General, API Changes, Controller Driver (DCD & HCD), Device Stack, Host Stack, Testing
- Use bullet lists with descriptive categorization
- Reference function names, config macros, and file paths using RST inline code (double backticks)
- Include meaningful descriptions, not just commit messages
4. **Validation before commit**:
- Run unit tests: `cd test/unit-test && ceedling test:all`
- Build at least one example: `cd examples/device/cdc_msc && make BOARD=stm32f407disco all`
- Verify changed files look correct: `git diff --stat`
5. **Leave files unstaged** for maintainer to review, modify if needed, and commit with message: `Bump version to X.Y.Z`
6. **After maintainer commits**: Create annotated tag with `git tag -a vX.Y.Z -m "Release X.Y.Z"`
7. Push commit and tag: `git push origin <branch> && git push origin vX.Y.Z`
8. Create GitHub release from the tag with changelog content
## Repository Structure Quick Reference
```
├── src/ # Core TinyUSB stack
│ ├── class/ # USB device classes (CDC, HID, MSC, Audio, etc.)
│ ├── portable/ # MCU-specific drivers (organized by vendor)
│ ├── device/ # USB device stack core
│ ├── host/ # USB host stack core
│ └── common/ # Shared utilities (FIFO, etc.)
├── examples/ # Example applications
│ ├── device/ # Device examples (cdc_msc, hid_generic, etc.)
│ ├── host/ # Host examples
│ └── dual/ # Dual-role examples
├── hw/bsp/ # Board Support Packages
│ └── FAMILY/boards/ # Board-specific configurations
├── test/unit-test/ # Unit tests using Ceedling
├── tools/ # Build and utility scripts
└── docs/ # Sphinx documentation
```bash
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
```
#### Build Time Reference
- **Dependency fetch**: <1 second
- **Single example build**: 1-3 seconds
- **Unit tests**: ~4 seconds
- **Documentation build**: ~2.5 seconds
- **Full board examples**: 15-20 seconds
- **Toolchain installation**: 2-5 minutes (one-time)
Add `-S <file>` (repeatable) to restrict to specific sources. ~10-30 s.
#### Key Files to Know
- `tools/get_deps.py`: Manages dependencies for MCU families
- `tools/build.py`: Builds multiple examples, supports make/cmake
- `src/tusb.h`: Main TinyUSB header file
- `src/tusb_config.h`: Configuration template
- `examples/device/cdc_msc/`: Most commonly used example for testing
- `test/unit-test/project.yml`: Ceedling test configuration
## Validation After Changes
#### MCU Reference Manuals and Datasheets
- Look in `$HOME/Documents/Calibre Library` for all MCU reference manuals, datasheets and board schematics.
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.
#### Debugging Build Issues
- **Missing compiler**: Install `gcc-arm-none-eabi` package
- **Missing dependencies**: Run `python3 tools/get_deps.py FAMILY`
- **Board not found**: Check `hw/bsp/FAMILY/boards/` for valid board names
- **objcopy errors**: Often non-critical in full builds, try individual example builds
**Boards good for local testing:**
- `stm32f407disco` — no external SDK
- `raspberry_pi_pico` — Pico SDK required
- Others: see `hw/bsp/FAMILY/boards/`
#### Working with USB Device Classes
- **CDC (Serial)**: `src/class/cdc/` - Virtual serial port
- **HID**: `src/class/hid/` - Human Interface Device (keyboard, mouse, etc.)
- **MSC**: `src/class/msc/` - Mass Storage Class (USB drive)
- **Audio**: `src/class/audio/` - USB Audio Class
- Each class has device (`*_device.c`) and host (`*_host.c`) implementations
Device examples need real hardware to validate runtime behavior; must at least build.
#### MCU Family Support
- **STM32**: Largest support (F0, F1, F2, F3, F4, F7, G0, G4, H7, L4, U5, etc.)
- **Raspberry Pi**: RP2040, RP2350 with PIO-USB host support
- **NXP**: iMXRT, Kinetis, LPC families
- **Microchip**: SAM D/E/G/L families
- Check `hw/bsp/` for complete list and `docs/reference/boards.rst` for details
## Release
### Code Style Guidelines
**Do not commit automatically — leave changes for maintainer review.**
#### General Coding Standards
- Use C99 standard
- Memory-safe: no dynamic allocation
- Thread-safe: defer all interrupt events to non-ISR task functions
- 2-space indentation, no tabs
- Use snake_case for variables/functions
- Use UPPER_CASE for macros and constants
- Follow existing variable naming patterns in files you're modifying
- Include proper header comments with MIT license
- Add descriptive comments for non-obvious functions
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.
#### Best Practices
- When including headers, group in order: C stdlib, tusb common, drivers, classes
- Always check return values from functions that can fail
- Use TU_ASSERT() for error checking with return statements
- Follow the existing code patterns in the files you're modifying
## References
Remember: TinyUSB is designed for embedded systems - builds are fast, tests are focused, and the codebase is optimized for resource-constrained environments.
- 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.