From 872b4fbdc3c607b522211839a6b3b82e18310a1b Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 20 Aug 2026 14:30:59 +0700 Subject: docs: add hardware-in-the-loop rig reference Document the ci and hfp HIL rigs in enough detail to reproduce one: bill of materials with photos, BIOS/IOMMU and vfio-pci passthrough on the Proxmox host, the Renesas uPD720201 firmware install, the guest software and permissions, the one-hub-per-root-port USB topology rule and the per-box split of probe and DUT hubs, how CI drives the rigs, and the operational gotchas. The attached-board table is generated from test/hil/tinyusb.json and test/hil/hfp.json by tools/gen_doc.py into docs/reference/hil_boards.md, which the page includes. Sphinx excludes that partial so it is not also built as an orphan document. Also exclude docs/superpowers/ from the Sphinx build: it holds internal plans, specs and handoffs rather than published documentation, and since nothing references them from a toctree each emitted "document isn't included in any toctree" -- 26 warnings in total, so build_doc.py -W could never pass. It now does. --- .claude/skills/build-doc/SKILL.md | 8 +- .claude/skills/make-release/SKILL.md | 2 +- docs/assets/hil/cable-xh254.jpg | Bin 0 -> 13306 bytes docs/assets/hil/leaf-hub.jpg | Bin 0 -> 94557 bytes docs/assets/hil/pcie-card.jpg | Bin 0 -> 80079 bytes docs/assets/hil/storage-box.jpg | Bin 0 -> 177136 bytes docs/conf.py | 4 +- docs/reference/hardware-in-the-loop.md | 351 +++++++++++++++++++++++++++++++++ docs/reference/hil_boards.md | 45 +++++ docs/reference/index.rst | 1 + tools/gen_doc.py | 52 +++++ tools/make_release.py | 1 + 12 files changed, 459 insertions(+), 5 deletions(-) create mode 100644 docs/assets/hil/cable-xh254.jpg create mode 100644 docs/assets/hil/leaf-hub.jpg create mode 100644 docs/assets/hil/pcie-card.jpg create mode 100644 docs/assets/hil/storage-box.jpg create mode 100644 docs/reference/hardware-in-the-loop.md create mode 100644 docs/reference/hil_boards.md diff --git a/.claude/skills/build-doc/SKILL.md b/.claude/skills/build-doc/SKILL.md index 73544b18a..d57beb664 100644 --- a/.claude/skills/build-doc/SKILL.md +++ b/.claude/skills/build-doc/SKILL.md @@ -1,6 +1,6 @@ --- name: build-doc -description: Use when building, previewing, or testing the TinyUSB Sphinx docs locally (docs/ → HTML), chasing Sphinx warnings, understanding how example READMEs get into the docs, or regenerating the auto-generated reference files after adding a board or dependency (boards.rst, dependencies.rst, BoardPresets.json, CMakePresets.json). +description: Use when building, previewing, or testing the TinyUSB Sphinx docs locally (docs/ → HTML), chasing Sphinx warnings, understanding how example READMEs get into the docs, or regenerating the auto-generated reference files after adding a board, a dependency, or a HIL rig board (boards.rst, dependencies.rst, hil_boards.md, BoardPresets.json, CMakePresets.json). --- # Build TinyUSB Docs @@ -19,14 +19,16 @@ python3 tools/build_doc.py -o # build docs/_build/ and open it ## Regenerate after adding a board or dependency -Run from the repo root; `docs/reference/*.rst` and the preset JSONs are **generated** — don't hand-edit. +Run from the repo root; `docs/reference/*.rst`, `docs/reference/hil_boards.md` and the preset JSONs are **generated** — don't hand-edit. | Added | Run | |---|---| | Board (`hw/bsp/FAMILY/boards/`) | `python3 tools/gen_doc.py` + `python3 tools/gen_presets.py` | | Dependency (edited `tools/get_deps.py`) | `python3 tools/gen_doc.py` | +| HIL board roster (`test/hil/tinyusb.json`, `hfp.json`) | `python3 tools/gen_doc.py` | -- `gen_doc.py` → `docs/reference/boards.rst` + `dependencies.rst`. Needs `pandas` + `tabulate` (not in `requirements.txt`) — `pip install pandas tabulate` if it errors. +- `gen_doc.py` → `docs/reference/boards.rst` + `dependencies.rst` + `hil_boards.md` (the roster partial included by `hardware-in-the-loop.md`). Needs `pandas` + `tabulate` (not in `requirements.txt`) — `pip install pandas tabulate` if it errors. +- `gen_doc.py` rewrites all three files whichever one you came for; revert any unrelated churn in `boards.rst`/`dependencies.rst` before committing. - `gen_presets.py` → `hw/bsp/BoardPresets.json` + per-example `CMakePresets.json`. Then rebuild and `git diff` the regenerated files; commit them with the board/dep change. diff --git a/.claude/skills/make-release/SKILL.md b/.claude/skills/make-release/SKILL.md index 3e53595e1..47d06d219 100644 --- a/.claude/skills/make-release/SKILL.md +++ b/.claude/skills/make-release/SKILL.md @@ -13,7 +13,7 @@ description: Use when cutting a new TinyUSB release — version bump, regenerate # set version = 'X.Y.Z' in tools/make_release.py, then FROM REPO ROOT: python3 tools/make_release.py ``` -Refreshes `tusb_option.h`, `repository.yml`, `library.json`, `sonar-project.properties`, and (via gen_doc/gen_presets) `docs/reference/{boards,dependencies}.rst` + preset JSONs (presets/docs change only if boards/deps changed). +Refreshes `tusb_option.h`, `repository.yml`, `library.json`, `sonar-project.properties`, and (via gen_doc/gen_presets) `docs/reference/{boards,dependencies}.rst`, `docs/reference/hil_boards.md` + preset JSONs (they change only if boards, deps or the HIL rosters did). Gotchas: `gen_doc` needs `pandas`+`tabulate` (not in requirements) → `pip install pandas tabulate`; `boards.rst` lands with no trailing newline → let pre-commit fix it (step 3). diff --git a/docs/assets/hil/cable-xh254.jpg b/docs/assets/hil/cable-xh254.jpg new file mode 100644 index 000000000..1f8ec8a07 Binary files /dev/null and b/docs/assets/hil/cable-xh254.jpg differ diff --git a/docs/assets/hil/leaf-hub.jpg b/docs/assets/hil/leaf-hub.jpg new file mode 100644 index 000000000..3557047e6 Binary files /dev/null and b/docs/assets/hil/leaf-hub.jpg differ diff --git a/docs/assets/hil/pcie-card.jpg b/docs/assets/hil/pcie-card.jpg new file mode 100644 index 000000000..4563bb9f7 Binary files /dev/null and b/docs/assets/hil/pcie-card.jpg differ diff --git a/docs/assets/hil/storage-box.jpg b/docs/assets/hil/storage-box.jpg new file mode 100644 index 000000000..03f389ab0 Binary files /dev/null and b/docs/assets/hil/storage-box.jpg differ diff --git a/docs/conf.py b/docs/conf.py index c6d04bff5..ea3de0c44 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -31,7 +31,9 @@ extensions = [ templates_path = ['_templates'] -exclude_patterns = ['_build'] +# 'superpowers' holds internal plans/specs/handoffs (see CLAUDE.md), not published docs. +# 'reference/hil_boards.md' is a generated partial that hardware-in-the-loop.md includes. +exclude_patterns = ['_build', 'superpowers', 'reference/hil_boards.md'] # -- Options for HTML output ------------------------------------------------- diff --git a/docs/reference/hardware-in-the-loop.md b/docs/reference/hardware-in-the-loop.md new file mode 100644 index 000000000..48c362f4f --- /dev/null +++ b/docs/reference/hardware-in-the-loop.md @@ -0,0 +1,351 @@ +# Hardware in the Loop (HIL) + +Every pull request that touches code builds the examples and runs them on real silicon +before it can merge. This page documents the rigs that do it, in enough detail to +reproduce one. + +Two rigs run the CI matrix: + +| Rig | Config | Runner labels | +|-------|-------------------------|---------------------------------------------------------| +| `ci` | `test/hil/tinyusb.json` | `self-hosted`, `X64`, `hathach`, `hardware-in-the-loop` | +| `hfp` | `test/hil/hfp.json` | `self-hosted`, `Linux`, `X64`, `hifiphile` | + +`ci` is hathach's rig and is what the rest of this page describes. `hfp` is a similar VM +with a uPD720201 card, hosted by hifiphile. + +## Bill of materials + +| Part | Used on `ci` | Notes | +|-----------------|----------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------| +| Host PC | Ryzen 9 3900X, MSI MAG B550M MORTAR WIFI, 32 GB | Any x86 with a working IOMMU | +| USB controllers | 4 × Renesas uPD720201 (`1912:0014` rev 03) on one PCIe card | [SSU SU-U3244-12U][aio-card]: four controllers behind an on-board PCIe switch, 12 ports | +| Leaf hubs | [MCS-92M 7-port USB 2.0 hub board][hub-board] | XH2.54 headers instead of Type-A: sturdier under handling and far tidier to route | +| Cables | XH2.54 → [Type-C][cable-c] / [micro-B][cable-micro] pigtails | Hub-end pin order: `+`, `D−`, `D+`, `−` | +| Debug probes | J-Link, ST-Link, RP2040 debug probe (CMSIS-DAP), WCH-Link, TI ICDI, ESP USB-JTAG | One per board — see Attached boards below | +| USB fixtures | Per host-capable board: one USB-serial adapter and one USB flash drive | Only for boards that run host/dual tests — see below | + +[aio-card]: https://item.taobao.com/item.htm?id=990655153501 +[hub-board]: https://item.taobao.com/item.htm?id=556123792554 +[cable-c]: https://item.taobao.com/item.htm?id=826743445229 +[cable-micro]: https://item.taobao.com/item.htm?id=591895354552 + +```{figure} ../assets/hil/pcie-card.jpg +:alt: Four-controller USB PCIe card +:width: 360px + +One card, four uPD720201 controllers behind a PCIe switch. +``` + +```{figure} ../assets/hil/leaf-hub.jpg +:alt: MCS-92M leaf hub board +:width: 360px + +One leaf hub: power in, upstream to a root port, seven XH2.54 ports out. +``` + +```{figure} ../assets/hil/cable-xh254.jpg +:alt: XH2.54 to USB-C pigtail +:width: 240px + +Hub-end XH2.54, board-end USB — Type-C shown, micro-B is the same cable. +``` + +## Proxmox host + +### 1. BIOS + +Enable SVM (or VT-x/VT-d), IOMMU, and *Above 4G decoding*. + +### 2. Kernel command line + +In `/etc/default/grub`, then `update-grub`: + +``` +GRUB_CMDLINE_LINUX_DEFAULT="quiet iommu=pt pcie_acs_override=downstream,multifunction" +``` + +`pcie_acs_override` is required because the card's four controllers sit behind its own +PCIe switch, and that switch does not advertise ACS. Without the override all four land +in one IOMMU group and none can be passed through individually. It relaxes DMA isolation +between them — fine on a dedicated test rig, not on a shared host. Note it is a +Proxmox-kernel patch, not mainline: a stock kernel ignores it silently. + +### 3. Bind the controllers to vfio-pci at boot + +`/etc/modules`: + +``` +vfio +vfio_iommu_type1 +vfio_pci +``` + +`/etc/modprobe.d/vfio.conf`: + +``` +options vfio-pci ids=1912:0014 +softdep xhci_pci pre: vfio-pci +softdep xhci_pci_renesas pre: vfio-pci +``` + +Bind at boot, ahead of the host's xhci driver — do not rely on Proxmox's late binding. +If the host ever owns these ports, the constant failed enumerations from the boards keep +udev busy past 120 s, `udevadm settle` times out inside `ifupdown2-pre`, +`networking.service` is cancelled, and the host comes up with no network. + +Then `update-initramfs -u -k all`, reboot, and check: + +```bash +lspci -nnk -d 1912:0014 | grep -i 'kernel driver' # vfio-pci +``` + +### 4. Pass the controllers to the VM + +One `hostpci` entry per controller, not per card — take the BDFs from +`lspci -nn -d 1912:0014`: + +```bash +qm set --machine q35 --cpu host \ + --hostpci0 0000:07:00,pcie=1 --hostpci1 0000:08:00,pcie=1 \ + --hostpci2 0000:09:00,pcie=1 --hostpci3 0000:0a:00,pcie=1 +``` + +`qm config ` should then list all four. + +## Guest + +Debian 13, 16 vCPU, 18 GB RAM. + +### Renesas firmware + +The controllers' ROM firmware is not reliable under HIL churn: Address Device fails with +`unexpected setup address command completion code 0x11`, and the controller eventually +dies outright (`xHCI host controller not responding, assume dead`). Install Renesas +firmware 2.0.2.6, which the kernel loads into the controller at boot. + +Do this on the kernel that *binds* the controllers — with passthrough that is the guest, +not the Proxmox host. + +1. Download 2.0.2.6 from [station-drivers][fw-dl]. It arrives as `k2026fwup1.exe`, a + Windows self-extracting installer of 1,895,424 bytes. Verify the firmware it contains, + not the installer — the md5 in the next step is the one that matters. +2. Unpack it — despite the name, the firmware inside is called `UPDATE.mem`: + + ```bash + 7z x k2026fwup1.exe -oupd # or: cabextract -d upd k2026fwup1.exe + md5sum upd/UPDATE.mem # 11b49c68a400564b704c6ef17a0e6c0a, 13012 bytes + ``` + +3. Install it under the name the kernel looks for, and rebuild the initramfs + (`xhci-pci-renesas` lives there): + + ```bash + sudo install -m 644 upd/UPDATE.mem /lib/firmware/renesas_usb_fw.mem + sudo update-initramfs -u -k all + sudo reboot + ``` + +4. Confirm the controller is running it. The first check is the one + `test/hil/usbtest.py` gates its own battery on — anything lower and it refuses to + run, failing that board's `usbtest` cell: + + ```bash + sudo setpci -s 0x6c.l # whole dword, must be >= 00202609 + dmesg | grep 'hcc params' # 0x014051cf = firmware loaded, 0x014050cf = ROM fallback + ``` + +The kernel reloads the firmware on every power cycle, so the file must stay installed — +that is what the initramfs step is for. The uPD720202 (`1912:0015`) takes the same +firmware and the same check. + +A one-off `soft lockup` warning in `renesas_fw_download_image` while the firmware is +written is expected — it busy-waits over PCI config space for ~30 s. + +[fw-dl]: https://www.station-drivers.com/index.php?option=com_remository&Itemid=353&func=fileinfo&id=1348&lang=en + +### Software + +| Purpose | What `ci` uses | +|-----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------| +| Build | `cmake`, `ninja-build`, and a toolchain per family: `gcc-arm-none-eabi`, a RISC-V GCC, ESP-IDF | +| Flashing | Five tools, one per `Flasher` value — see below | +| Test harness | `pip install -r test/hil/requirements.txt` — hidapi, pyserial, esptool | +| Host-side test tools | `dfu-util`, `mtools`, `libmtp9`, `libmtp-runtime`, `alsa-utils` (apt) — the DFU, MSC, MTP and audio tests shell out to these | +| USB inspection and recovery | `pciutils` (the `usbtest` firmware gate), `uhubctl` (apt), `tshark` for usbmon capture, `testusb` from the kernel's `tools/usb/testusb.c` | + +The `Flasher` column in Attached boards names one of five values; only the ones your own +boards use have to be installed. The mapping is not always guessable: + +| `Flasher` | Binary | +|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------| +| `jlink` | `JLinkExe`, from the SEGGER J-Link software | +| `stlink` | `STM32_Programmer_CLI`, from STM32CubeProgrammer — **not** `st-flash` | +| `openocd` | [`hathach/openocd`][openocd-fork] branch `tinyusb` — one build merging the Raspberry Pi (RP2350), WCH and Analog Devices (MAX32) forks, none upstream | +| `esptool` | `esptool` (pip) | +| `lm4flash` | `lm4flash` (apt) | + +[openocd-fork]: https://github.com/hathach/openocd/tree/tinyusb + +### Permissions and tools + +```bash +sudo cp tools/88-tinyusb.rules /etc/udev/rules.d/ +sudo udevadm control --reload-rules && sudo udevadm trigger +# the groups 88-tinyusb.rules assigns; skip any the distro does not have +# (`wireshark` only exists once wireshark-common is installed) +for g in adm dialout plugdev users wireshark; do + getent group "$g" >/dev/null && sudo usermod -aG "$g" "$USER" +done +``` + +Add the vendor rules for the probes you use (J-Link, picotool). `uhubctl` needs one too +and no package ships it — without it every port toggle wants root: + +``` +# /etc/udev/rules.d/52-uhubctl.rules - root hubs, plus each hub vendor in the rig +SUBSYSTEM=="usb", ATTR{idVendor}=="1d6b", MODE="0664", GROUP="plugdev" +SUBSYSTEM=="usb", ATTR{idVendor}=="1a40", MODE="0664", GROUP="plugdev" +SUBSYSTEM=="usb", ATTR{idVendor}=="045b", MODE="0664", GROUP="plugdev" +``` + +Flasher CLIs and toolchains must be reachable from *non-interactive* shells — neither the +Actions runner nor `hil_ci.sh` sources a login profile. Keep them in `~/.local/bin` and +`~/bin` (symlinks are fine) and add both to the runner's `.path`. + +`pciutils` and passwordless sudo are hard requirements, not conveniences: +`test/hil/usbtest.py` shells out as `sudo -n` for `setpci`, `modprobe`, `dmesg` and +`testusb`, and exits outright if it cannot read the host controller's firmware version. +`helper/hil_pool_check.py` gates recovery on the same `sudo -n` plus +`.claude/skills/usb-kernel-recover/scripts/usb_recover.sh` being present; without both it +cannot re-authorize a wedged probe's port and files the board `flash-failed` instead. + +The `usbtest` battery additionally needs `testusb` built from the kernel tools and +`CONFIG_USB_TEST=m` available. + +## USB topology + +**One 7-port hub per uPD720201 root port. Never chain hubs.** + +Each controller presents four root ports (on both its USB 2 and USB 3 root hubs); the +card brings 12 of those 16 out to connectors. Hang exactly one leaf hub on a root port. + +Boards are grouped into storage boxes, each holding **two** leaf hubs: one carries only +debug probes, the other only the boards under test. Keeping them apart is what makes +recovery tractable — a DUT re-enumerates constantly and can wedge its hub, while the +probes stay on a bus that never moves, so the probe you need to reset a hung board is +still there when you reach for it. + +```{figure} ../assets/hil/storage-box.jpg +:alt: A storage box of boards, probes and two leaf hubs +:width: 800px + +One box: boards, their probes, and the two leaf hubs serving them. +``` + +Boards that run **host** or **dual** tests additionally need a USB peripheral plugged +into the board's *own* USB port — a USB-serial adapter and/or a flash drive for the host +stack to enumerate. Ten `ci` boards have these, recorded as `dev_attached` in the rig +config and matched by exact VID:PID and serial, so a substitute part means updating the +config. The two Espressif +boards also use a TS3USB30 mux to drive device and host tests through one connector. + +Why the rule matters: + +- **Bandwidth.** Every leaf hub gets its own 480 Mbit uplink to the controller. Chaining + puts a second hub's whole subtree behind one of those uplinks, and the `usbtest` + battery saturates whatever it is given. +- **Blast radius.** A board that wedges its hub costs seven ports, not the rig. +- **Scheduling.** `hil_test.py` budgets flashing and `usbtest` concurrency per host + controller (`test/hil/helper/hil_lock.py`: `FLASH_PARALLEL`, `USBTEST_PARALLEL`), which + only means anything when a controller's set of devices is fixed. + +Bus numbers are *not* stable across reboots or recabling, so nothing in the harness +addresses a board by bus path. Boards are identified by the MCU's unique ID and probes by +their serial, both recorded in the rig config — which is why every HIL board must +implement `board_get_unique_id()`. + +## Attached boards + +Roles come from each board's `tests` entry; `Flasher` is the tool that programs it. +Both files are the source of truth — this table is generated from them. + +```{include} hil_boards.md +``` + +## How CI runs the tests + +1. `hil-build` and `hil-build-esp` build the examples on GitHub-hosted runners and upload + the binaries as artifacts. +2. `hil-tinyusb` runs on the self-hosted rigs, downloads those artifacts and calls + `test/hil/hil_test.py`, which flashes each board and runs its tests. Espressif boards + run in `hil-tinyusb-esp`, gated on the slower ESP-IDF build, and `hil-hfp-iar` builds + with IAR inside the job. +3. On pull requests, `test/hil/helper/hil_select.py` narrows the run to the boards a diff + can affect, falling open to the full matrix when it cannot tell. +4. Each board is arbitrated by a kernel flock in `/tmp/tinyusb-hil-locks/`, so interactive + work and CI can share the rig without colliding. +5. Each rig job uploads its report as an artifact; `pr_comment.yml` downloads them and + posts the combined tables onto the pull request. + +From a development PC, the same run can be driven remotely. `REMOTE` and `CONFIG` +default to `ci`, so point them at your own: + +```bash +REMOTE=myrig.lan CONFIG=$PWD/test/hil/local.json bash test/hil/hil_ci.sh -b +``` + +## Gotchas + +- **The Renesas firmware is not optional.** On ROM firmware these controllers fail Address + Device and eventually die under test churn. +- **Port power is logical only.** `uhubctl` "off" on these controllers drops D+/D− but leaves + VBUS hot — boards stay powered and running. Real per-port power switching needs the + controller's PPON pins wired to load switches, which the card omits. +- **Use `uhubctl -S` on root ports.** Without it, uhubctl writes sysfs `disable`, which + takes the root hub's lock — and if anything in that subtree is in D state it blocks + there, leaving the whole bus untouchable. `-S` forces the libusb path instead, which is + why `usb_recover.sh root-cycle` uses it. Resetting the board through its debug probe is + the surer cure, but a wedged *probe* has none, so the port-side drop is the only lever + left there. +- **Park firmware must busy-spin, never `wfe`/`wfi`.** A parked core in a low-power state + can make SWD unreachable and leave the board needing recovery. +- **Most "7-port" hubs are two 4-port hubs in series.** Commodity 7-port hubs commonly + cascade two controllers internally — three ports on the first, four behind a second. + `lsusb -t` tells you which you bought: a single-tier hub appears as one device with + seven ports, a cascaded one shows a hub inside a hub. Every hub on `ci` sits directly + under a root port and reports `maxchild=7`. +- **Size the hub supplies.** Boards take VBUS from the leaf hub, so a seven-board hub on + an undersized supply browns out under load. + +## A minimal rig + +None of the above is a prerequisite. The VM, the uPD720201 cards and the leaf hubs are +what let one machine hold 27 boards and recover them unattended — the harness itself runs +fine against boards plugged straight into a development PC's own USB ports, on whatever +xHCI that PC already has. All it takes is the boards, their debug probes, and a +`test/hil/local.json` describing them in the same shape as `tinyusb.json`. + +Host-side prerequisites, beyond a cross toolchain: + +```bash +python3 tools/get_deps.py # MCU SDKs for your boards +pip install -r test/hil/requirements.txt # hidapi, pyserial, esptool +sudo apt install cmake ninja-build uhubctl \ + dfu-util mtools libmtp9 libmtp-runtime alsa-utils +``` + +`cmake` and `ninja-build` are needed by any run and `uhubctl` by recovery; the rest only +by the tests that shell out to them, so dropping one just fails the DFU, MSC, MTP or audio +cells on an otherwise healthy rig. `test/hil/requirements.txt` names those at the top, +along with `iperf` for the `device/net_lwip_*` tests, which are off in the default matrix. + +Only two of this page's host-controller concerns carry over. `test/hil/usbtest.py` refuses +a DUT behind a MosChip MCS9990 (`9710:9990`) outright, and it applies the Renesas firmware +check only when the DUT really is behind a uPD720201/02 — on a stock Intel or AMD xHCI +there is nothing to install, and `pciutils` is only needed for that check. + +```bash +cd examples && cmake --preset && cmake --build --preset +cd .. && python3 test/hil/hil_test.py -B examples test/hil/local.json +``` diff --git a/docs/reference/hil_boards.md b/docs/reference/hil_boards.md new file mode 100644 index 000000000..e8f364646 --- /dev/null +++ b/docs/reference/hil_boards.md @@ -0,0 +1,45 @@ + + +### ci rig + +27 boards, from `test/hil/tinyusb.json`. + +| Board | Roles | Flasher | Variants | Note | +|--------------------------|--------------------|-----------|--------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| frdm_k64f | host | jlink | | | +| ek_tm4c123gxl | device | lm4flash | | | +| espressif_p4_function_ev | device, host | esptool | espressif_p4_function_ev, espressif_p4_function_ev-DMA | Use TS3USB30 mux to test both device and host | +| espressif_s3_devkitm | device, host | esptool | espressif_s3_devkitm, espressif_s3_devkitm-DMA | Use TS3USB30 mux to test both device and host | +| feather_nrf52840_express | device | jlink | | | +| max32666fthr | device | openocd | | | +| metro_m4_express | device, dual | jlink | | pl23x; audio_test_freertos skipped: samd51 iso-IN capture fails (arecord EIO) | +| lpcxpresso11u37 | device | jlink | | | +| lpcxpresso55s28 | device | jlink | | | +| ra4m1_ek | device | jlink | | | +| raspberry_pi_pico | device, host, dual | openocd | raspberry_pi_pico | | +| raspberry_pi_pico_w | host | openocd | | Test native host | +| raspberry_pi_pico2 | host | openocd | | | +| adafruit_fruit_jam | device, host, dual | openocd | | | +| stm32f072disco | device | jlink | | 2x16 access scheme with 1KB USB SRAM | +| stm32f407disco | device | jlink | | | +| stm32f723disco | device, host | jlink | stm32f723disco, stm32f723disco-DMA | Device port0 FS (slave only), Host port1 HS with DMA | +| stm32h743nucleo | device | stlink | stm32h743nucleo, stm32h743nucleo-DMA | | +| stm32g0b1nucleo | device | stlink | | 32-bit scheme, 2KB USB SRAM | +| stm32l476disco | device | jlink | | | +| stm32u083nucleo | device | stlink | | | +| nanoch32v203 | device | openocd | nanoch32v203-fsdev, nanoch32v203-usbfs | | +| ch32v103r_r1_1v0 | device | openocd | | | +| ch32v307v_r1_1v0 | device | openocd | ch32v307v_r1_1v0-usbhs, ch32v307v_r1_1v0-usbfs | | +| ch582m_evt | device | openocd | | | +| mimxrt1064_evk | device, host, dual | jlink | | | +| nrf54lm20dk | device | jlink | | board new to HIL: audio_test_freertos never reaches dcd_init (FreeRTOS itself runs; cdc_msc_freertos and usbtest pass) - example-level issue on nRF54L, fix separately | + +### hfp rig + +3 boards, from `test/hil/hfp.json`. + +| Board | Roles | Flasher | Variants | Note | +|-----------------|---------|-----------|------------------------------------|--------| +| stm32l412nucleo | device | stlink | | | +| stm32f746disco | device | stlink | stm32f746disco, stm32f746disco-DMA | | +| lpcxpresso43s67 | device | jlink | | | diff --git a/docs/reference/index.rst b/docs/reference/index.rst index c66ce618f..85fd767ea 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -14,4 +14,5 @@ Complete reference documentation for TinyUSB APIs, configuration, and supported dependencies concurrency device_issues + hardware-in-the-loop glossary diff --git a/tools/gen_doc.py b/tools/gen_doc.py index 3920531d5..41a60c0b6 100755 --- a/tools/gen_doc.py +++ b/tools/gen_doc.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import json import re import pandas as pd from tabulate import tabulate @@ -109,9 +110,60 @@ Following boards are supported""" f.write(tabulate(df, headers="keys", tablefmt='rst')) +# ----------------------------------------- +# HIL rig rosters +# ----------------------------------------- +def hil_cell(text): + """A '|' in free-form roster text would silently split the markdown row.""" + return ' '.join((text or '').split()).replace('|', '\\|') + + +def hil_rows(boards): + rows = [] + for b in boards: + tests = b.get('tests', {}) + if 'only' in tests: + roles = sorted({t.split('/')[0] for t in tests['only']}) + else: + roles = [r for r in ('device', 'host', 'dual') if tests.get(r)] + rows.append([ + b['name'], + ', '.join(roles), + b.get('flasher', {}).get('name', ''), + hil_cell(', '.join(v['name'] for v in b.get('variant') or [])), + hil_cell(b.get('comment') or tests.get('comment')), + ]) + return rows + + +def gen_hil_boards_doc(): + tinyusb = json.loads((Path(TOP) / "test/hil/tinyusb.json").read_text()) + hfp = json.loads((Path(TOP) / "test/hil/hfp.json").read_text()) + sections = [ + ("ci rig", "test/hil/tinyusb.json", tinyusb.get('boards', [])), + ("hfp rig", "test/hil/hfp.json", hfp.get('boards', [])), + ] + headers = ['Board', 'Roles', 'Flasher', 'Variants', 'Note'] + + out = ["", ""] + for title, src, boards in sections: + if not boards: + continue + out.append(f"### {title}") + out.append("") + out.append(f"{len(boards)} boards, from `{src}`.") + out.append("") + out.append(tabulate(hil_rows(boards), headers=headers, tablefmt='github')) + out.append("") + + hil_md = Path(TOP) / "docs/reference/hil_boards.md" + hil_md.write_text('\n'.join(out)) + + # ----------------------------------------- # Main # ----------------------------------------- if __name__ == "__main__": gen_deps_doc() gen_boards_doc() + gen_hil_boards_doc() diff --git a/tools/make_release.py b/tools/make_release.py index 65226834f..ec4755f34 100755 --- a/tools/make_release.py +++ b/tools/make_release.py @@ -59,6 +59,7 @@ with open(f_sonar_properties, 'w') as f: # gen docs gen_doc.gen_deps_doc() gen_doc.gen_boards_doc() +gen_doc.gen_hil_boards_doc() # gen presets gen_presets.main() -- cgit v1.3.1