mirror of
https://github.com/hathach/tinyusb.git
synced 2026-08-24 14:02:53 +00:00
Merge pull request #3837 from hathach/hil-setup-writeup
docs: add hardware-in-the-loop rig reference
This commit is contained in:
@ -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.
|
||||
|
||||
@ -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).
|
||||
|
||||
|
||||
BIN
docs/assets/hil/cable-xh254.jpg
Normal file
BIN
docs/assets/hil/cable-xh254.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/assets/hil/leaf-hub.jpg
Normal file
BIN
docs/assets/hil/leaf-hub.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
BIN
docs/assets/hil/pcie-card.jpg
Normal file
BIN
docs/assets/hil/pcie-card.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
BIN
docs/assets/hil/storage-box.jpg
Normal file
BIN
docs/assets/hil/storage-box.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 173 KiB |
@ -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 -------------------------------------------------
|
||||
|
||||
351
docs/reference/hardware-in-the-loop.md
Normal file
351
docs/reference/hardware-in-the-loop.md
Normal file
@ -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 <vmid> --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 <vmid>` 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 <bdf> 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 <board>
|
||||
```
|
||||
|
||||
## 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 <family> # 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 <board> && cmake --build --preset <board>
|
||||
cd .. && python3 test/hil/hil_test.py -B examples test/hil/local.json
|
||||
```
|
||||
45
docs/reference/hil_boards.md
Normal file
45
docs/reference/hil_boards.md
Normal file
@ -0,0 +1,45 @@
|
||||
<!-- Generated by tools/gen_doc.py - do not edit. -->
|
||||
|
||||
### 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 | | |
|
||||
@ -14,4 +14,5 @@ Complete reference documentation for TinyUSB APIs, configuration, and supported
|
||||
dependencies
|
||||
concurrency
|
||||
device_issues
|
||||
hardware-in-the-loop
|
||||
glossary
|
||||
|
||||
@ -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 = ["<!-- Generated by tools/gen_doc.py - do not edit. -->", ""]
|
||||
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()
|
||||
|
||||
@ -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()
|
||||
|
||||
Reference in New Issue
Block a user