initial commit

This commit is contained in:
shell committed 2026-10-11 21:48:59 +08:00
commit b44ce7bd77
2716 files changed
+559005

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
{
"boards": [
{
"name": "stm32l412nucleo",
"uid": "41003B000E504E5457323020",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "774470029",
"args": "-device STM32L412KB"
}
},
{
"name": "stm32f746disco",
"uid": "210041000C51343237303334",
"variant": [
{ "name": "stm32f746disco", "flags": "" },
{ "name": "stm32f746disco-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "770935966",
"args": "-device STM32F746NG"
}
},
{
"name": "lpcxpresso43s67",
"uid": "08F000044528BAAA8D858F58C50700F5",
"tests": {
"device": true, "host": false, "dual": false
},
"flasher": {
"name": "jlink",
"uid": "728973776",
"args": "-device LPC43S67_M4"
}
}
]
}
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/env bash
# Run HIL test remotely on ci.lan
# Usage: test/hil/hil_ci.sh [-b BOARD] [-t TEST] [extra hil_test.py args...]
# Example:
# test/hil/hil_ci.sh -b stm32f723disco
# test/hil/hil_ci.sh -b stm32f723disco -t host/cdc_msc_hid -r 1
#
# Env overrides: REMOTE, REMOTE_DIR, CONFIG (path to HIL config json),
# ROOT_DIR (tinyusb checkout to test; defaults to the script's own checkout).
set -euo pipefail
REMOTE=${REMOTE:-ci.lan}
REMOTE_DIR=${REMOTE_DIR:-/tmp/tinyusb-hil}
ROOT_DIR=${ROOT_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}
CONFIG=${CONFIG:-$ROOT_DIR/test/hil/tinyusb.json}
[[ -f "$ROOT_DIR/test/hil/hil_test.py" && -d "$ROOT_DIR/examples" ]] || {
echo "error: $ROOT_DIR does not look like a tinyusb checkout" >&2
exit 1
}
# Parse -b BOARD from arguments to know which build to copy
BOARD=""
ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
-b)
[[ $# -ge 2 ]] || { echo "error: -b requires a BOARD argument" >&2; exit 1; }
BOARD="$2"
ARGS+=("$1" "$2")
shift 2
;;
*)
ARGS+=("$1")
shift
;;
esac
done
# Setup remote directory. Use `bash -s` + heredoc so REMOTE_DIR (user-overridable)
# is passed as a positional parameter and never reinterpreted by the remote shell.
echo "==> Setting up remote $REMOTE:$REMOTE_DIR"
ssh "$REMOTE" bash -s -- "$REMOTE_DIR" <<'REMOTE'
set -e
rm -rf -- "$1"
mkdir -p -- "$1/test/hil" "$1/examples"
REMOTE
# Copy HIL test script and config
echo "==> Copying test scripts"
scp -q "$ROOT_DIR/test/hil/hil_test.py" \
"$ROOT_DIR/test/hil/pymtp.py" \
"$CONFIG" \
"$REMOTE:$REMOTE_DIR/test/hil/"
# Copy only firmware binaries (elf/bin/hex) plus esptool metadata
# (config.env + flash_args needed by the esptool flasher), preserving structure
copy_board_binaries() {
local src="$1"
rsync -a --prune-empty-dirs \
--include='*/' --include='*.elf' --include='*.bin' --include='*.hex' \
--include='config.env' --include='flash_args' \
--exclude='*' \
"$src" "$REMOTE:$REMOTE_DIR/examples/"
}
if [ -n "$BOARD" ]; then
# Copy the board's build dir plus its variant dirs. Variant names come from
# $CONFIG (they are not required to be prefixed with the board name); the
# cmake-build-<BOARD>-* glob is kept as a fallback for ad-hoc local builds.
# Collect only dirs that actually exist, deduplicated.
declare -A SEEN_DIRS=()
BUILD_DIRS=()
add_build_dir() {
[[ -d "$1" && -z "${SEEN_DIRS[$1]:-}" ]] || return 0
SEEN_DIRS[$1]=1
BUILD_DIRS+=("$1")
}
shopt -s nullglob
for d in "$ROOT_DIR"/examples/cmake-build-"$BOARD" "$ROOT_DIR"/examples/cmake-build-"$BOARD"-*; do
add_build_dir "$d"
done
shopt -u nullglob
while IFS= read -r v; do
add_build_dir "$ROOT_DIR/examples/cmake-build-$v"
done < <(python3 -c '
import json, sys
cfg = json.load(open(sys.argv[1]))
for b in cfg.get("boards", []):
if b["name"] == sys.argv[2]:
for v in b.get("variant") or []:
print(v["name"])
' "$CONFIG" "$BOARD")
if [ ${#BUILD_DIRS[@]} -eq 0 ]; then
echo "Error: no build directory found for $BOARD under $ROOT_DIR/examples/"
echo "Build first with: cd examples && cmake --preset $BOARD && cmake --build --preset $BOARD"
exit 1
fi
echo "==> Copying binaries for $BOARD (${#BUILD_DIRS[@]} build dir(s))"
for d in "${BUILD_DIRS[@]}"; do
copy_board_binaries "$d"
done
else
echo "==> Copying all built binaries"
# Use `%/` parameter expansion to strip the trailing slash from the glob —
# rsync needs the bare dir name so the per-board cmake-build-<BOARD>/ subdir
# is preserved on the remote (hil_test.py looks up binaries by that path).
for dir in "$ROOT_DIR"/examples/cmake-build-*/; do
[ -d "$dir" ] && copy_board_binaries "${dir%/}"
done
fi
# Run test. Use `bash -s` so REMOTE_DIR + ARGS reach the remote shell as positional
# parameters; quoting and metacharacters in args are preserved.
CONFIG_BASENAME="$(basename "$CONFIG")"
echo "==> Running HIL test on $REMOTE"
rc=0
ssh "$REMOTE" bash -s -- "$REMOTE_DIR" "${ARGS[@]}" "test/hil/$CONFIG_BASENAME" <<'REMOTE' || rc=$?
cd -- "$1"
shift
# Flasher CLIs live in the user bin dirs on ci.lan (esptool/idf in ~/.local/bin,
# STM32CubeProgrammer's STM32_Programmer_CLI in ~/bin); the non-interactive shell
# subprocess used for flashing doesn't source profile/rc, so add them explicitly.
export PATH="$HOME/.local/bin:$HOME/bin:$PATH"
python3 -u test/hil/hil_test.py -B examples "$@"
REMOTE
# Copy the generated report back to the local checkout (best-effort; the run's
# exit code is preserved regardless of whether a report was produced).
scp -q "$REMOTE:$REMOTE_DIR/hil_report.md" "$ROOT_DIR/hil_report.md" \
&& echo "==> Report copied to $ROOT_DIR/hil_report.md" \
|| echo "==> warning: no hil_report.md copied back" >&2
exit $rc
+75
View File
@@ -0,0 +1,75 @@
import argparse
import json
import os
def _resolve_config_path(config_file):
if os.path.exists(config_file):
return config_file
script_relative = os.path.join(os.path.dirname(__file__), config_file)
if os.path.exists(script_relative):
return script_relative
raise FileNotFoundError(f'Config file not found: {config_file}')
def main():
parser = argparse.ArgumentParser()
parser.add_argument('config_files', nargs='+', help='Configuration JSON file(s)')
args = parser.parse_args()
# Toolchain buckets must match the toolchains instantiated by the hil-build
# job in .github/workflows/build.yml. Keep all keys present (even if empty)
# so `fromJSON(hil_json)[toolchain]` always resolves to a list.
matrix = {
'arm-gcc': [],
'riscv-gcc': [],
'esp-idf': []
}
seen = {toolchain: set() for toolchain in matrix}
def append_build_arg(toolchain, build_arg):
if build_arg not in seen[toolchain]:
seen[toolchain].add(build_arg)
matrix[toolchain].append(build_arg)
for config_file in args.config_files:
with open(_resolve_config_path(config_file)) as f:
config = json.load(f)
for board in config['boards']:
name = board['name']
flasher = board['flasher']
# esptool boards must build under esp-idf; others default to arm-gcc
# but may opt into another bucket via an explicit "toolchain" field
# (e.g. RISC-V boards like ch32v20x need "riscv-gcc").
if flasher['name'] == 'esptool':
toolchain = 'esp-idf'
else:
toolchain = board.get('toolchain', 'arm-gcc')
build_board = f'-b {name}'
if 'build' in board and 'args' in board['build']:
build_board += ' ' + ' '.join(f'-D{a}' for a in board['build']['args'])
# Each variant builds into cmake-build-<variant.name> with its own cmake
# -D defines and raw CFLAGS. No 'variant' -> a single build named after
# the board.
variants = board.get('variant') or [{'name': name, 'flags': ''}]
for v in variants:
arg = build_board
if v['name'] != name:
arg += f' --build-name {v["name"]}'
for d in v.get('defines', []):
arg += f' -D{d}'
for tok in v.get('flags', '').split():
arg += f' --cflag={tok}'
append_build_arg(toolchain, arg)
print(json.dumps(matrix))
if __name__ == '__main__':
main()
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+9
View File
@@ -0,0 +1,9 @@
# System packages (install separately):
# sudo apt install mtools libmtp9 alsa-utils iperf
# mtools - read_disk_file (device/cdc_msc, device/msc_dual_lun)
# libmtp9 - pymtp ctypes load (device/mtp); Debian 13 uses libmtp9t64
# alsa-utils - arecord (device/audio_test_freertos)
# iperf - throughput tests (device/net_lwip_*)
hidapi
pyserial
esptool
+533
View File
@@ -0,0 +1,533 @@
{
"boards": [
{
"name": "ek_tm4c123gxl",
"uid": "010105186C60A110",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "lm4flash",
"uid": "0E205D19",
"args": "-v"
}
},
{
"name": "espressif_p4_function_ev",
"uid": "6055F9F98715",
"variant": [
{ "name": "espressif_p4_function_ev", "flags": "" },
{ "name": "espressif_p4_function_ev-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"tests": {
"only": [
"device/cdc_msc_freertos",
"device/hid_composite_freertos",
"device/audio_test_freertos",
"host/device_info",
"host/msc_file_explorer_freertos"
],
"dev_attached": [
{
"vid_pid": "1a86_55d4",
"serial": "52D2002427",
"is_cdc": true
},
{
"vid_pid": "21c4_0cc7",
"serial": "900058944CB80A53",
"is_msc": true,
"block_size": 512,
"block_count": 60620800,
"msc_inquiry": "Lexar USB Flash Drive PMAP"
}
]
},
"flasher": {
"name": "esptool",
"uid": "4ea4f48f6bc3ee11bbb9d00f9e1b1c54",
"args": "-b 1500000",
"comment": "use --force for ESP32-P4 v0.1"
},
"comment": "Use TS3USB30 mux to test both device and host"
},
{
"name": "espressif_s3_devkitm",
"uid": "84F703C084E4",
"variant": [
{ "name": "espressif_s3_devkitm", "flags": "" },
{ "name": "espressif_s3_devkitm-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"tests": {
"only": [
"device/cdc_msc_freertos",
"device/hid_composite_freertos",
"device/audio_test_freertos",
"host/device_info",
"host/msc_file_explorer_freertos"
],
"dev_attached": [
{
"vid_pid": "1a86_55d4",
"serial": "52D2005402",
"is_cdc": true
},
{
"vid_pid": "048d_04d2",
"serial": "\u0409",
"is_msc": true,
"block_size": 512,
"block_count": 30720000,
"msc_inquiry": "General UDisk 5.00",
"comment": "General UDisk reports iSerialNumber=U+0409"
}
]
},
"flasher": {
"name": "esptool",
"uid": "3ea619acd1cdeb11a0a0b806e93fd3f1",
"args": "-b 1500000"
},
"comment": "Use TS3USB30 mux to test both device and host"
},
{
"name": "feather_nrf52840_express",
"uid": "1F0479CD0F764471",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "681295394",
"args": "-device nrf52840_xxaa"
}
},
{
"name": "max32666fthr",
"uid": "0C81464124010B20FF0A08CC2C",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "openocd_adi",
"uid": "E6614C311B597D32",
"args": "-f interface/cmsis-dap.cfg -f target/max32665.cfg"
}
},
{
"name": "metro_m4_express",
"uid": "9995AD485337433231202020FF100A34",
"build": {
"args": [
"MAX3421_HOST=1"
]
},
"tests": {
"device": true,
"host": false,
"dual": true,
"skip": ["device/audio_test_freertos"],
"dev_attached": [
{
"vid_pid": "067b_2303",
"serial": "0",
"is_cdc": true
}
],
"comment": "pl23x; audio_test_freertos skipped: samd51 iso-IN capture fails (arecord EIO)"
},
"flasher": {
"name": "jlink",
"uid": "123456",
"args": "-device ATSAMD51J19"
}
},
{
"name": "mimxrt1015_evk",
"uid": "DC28F865D2111D228D00B0543A70463C",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "000726284213",
"args": "-device MIMXRT1015DAF5A"
}
},
{
"name": "mimxrt1064_evk",
"uid": "BAE96FB95AFA6DBB8F00005002001200",
"tests": {
"device": true,
"host": true,
"dual": true,
"dev_attached": [
{
"vid_pid": "10c4_ea60",
"serial": "0001",
"is_cdc": true,
"comment": "cp2102"
},
{
"vid_pid": "21c4_0cc7",
"serial": "900058874D871F66",
"is_msc": true,
"block_size": 512,
"block_count": 60620800,
"msc_inquiry": "Lexar USB Flash Drive PMAP"
}
]
},
"flasher": {
"name": "jlink",
"uid": "000725299165",
"args": "-device MIMXRT1064xxx6A"
}
},
{
"name": "lpcxpresso11u37",
"uid": "17121919",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "000724441579",
"args": "-device LPC11U37/401"
}
},
{
"name": "ra4m1_ek",
"uid": "152E163038303131393346E46F26574B",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "000831174392",
"args": "-device R7FA4M1AB"
}
},
{
"name": "raspberry_pi_pico",
"uid": "E6614C311B764A37",
"variant": [
{ "name": "raspberry_pi_pico", "flags": "-DCFG_TUH_RPI_PIO_USB=1" }
],
"tests": {
"device": true,
"host": true,
"dual": true,
"dev_attached": [
{
"vid_pid": "1a86_7523",
"serial": "0",
"is_cdc": true,
"comment": "ch34x"
},
{
"vid_pid": "048d_04d2",
"serial": "\u0409",
"is_msc": true,
"block_size": 512,
"block_count": 30720000,
"msc_inquiry": "General UDisk 5.00",
"comment": "General UDisk reports iSerialNumber=U+0409"
}
]
},
"flasher": {
"name": "openocd",
"uid": "E6614103E72C1D2F",
"args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\""
}
},
{
"name": "raspberry_pi_pico_w",
"uid": "E6614864D35DAE36",
"tests": {
"device": false,
"host": true,
"dual": false,
"dev_attached": [
{
"vid_pid": "1a86_55d4",
"serial": "52D2002694",
"is_cdc": true
},
{
"vid_pid": "2008_2018",
"serial": "O20070925A002746",
"is_msc": true,
"block_size": 512,
"block_count": 4124152,
"msc_inquiry": "USB2.0 Flash Disk 2.10"
}
]
},
"flasher": {
"name": "openocd",
"uid": "E6633861A3819D38",
"args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\""
},
"comment": "Test native host"
},
{
"name": "raspberry_pi_pico2",
"uid": "560AE75E1C7152C9",
"tests": {
"device": false,
"host": true,
"dual": false,
"dev_attached": [
{
"vid_pid": "0951_1603",
"serial": "820000000000000045B46338",
"is_msc": true,
"block_size": 512,
"block_count": 3987456,
"msc_inquiry": "Kingston DataTraveler 2.0 1.00"
}
]
},
"flasher": {
"name": "openocd",
"uid": "E6633861A3978538",
"args": "-f interface/cmsis-dap.cfg -f target/rp2350.cfg -c \"adapter speed 5000\""
}
},
{
"name": "adafruit_fruit_jam",
"uid": "2B0DC7A45781189E",
"tests": {
"device": true,
"host": true,
"dual": true,
"dev_attached": [
{
"vid_pid": "0403_6001",
"serial": "0",
"is_cdc": true
},
{
"vid_pid": "058f_6387",
"serial": "A8BEE062633D",
"is_msc": true,
"block_size": 512,
"block_count": 7639040,
"msc_inquiry": "Generic Flash Disk 8.07"
}
]
},
"flasher": {
"name": "openocd",
"uid": "E663AC91D3359B38",
"args": "-f interface/cmsis-dap.cfg -f target/rp2350.cfg -c \"adapter speed 5000\""
}
},
{
"name": "stm32f072disco",
"uid": "3A001A001357364230353532",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "779541626",
"args": "-device stm32f072rb"
},
"comment": "2x16 access scheme with 1KB USB SRAM"
},
{
"name": "stm32f407disco",
"uid": "30001A000647313332353735",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "000773661813",
"args": "-device stm32f407vg"
}
},
{
"name": "stm32f723disco",
"uid": "460029001951373031313335",
"variant": [
{ "name": "stm32f723disco", "flags": "" },
{ "name": "stm32f723disco-DMA", "flags": "-DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"tests": {
"device": true,
"host": true,
"dual": false,
"dev_attached": [
{
"vid_pid": "1a86_55d4",
"serial": "52D2003414",
"is_cdc": true
},
{
"vid_pid": "21c4_0cc7",
"serial": "90005893730A1A63",
"is_msc": true,
"block_size": 512,
"block_count": 60620800,
"msc_inquiry": "Lexar USB Flash Drive PMAP"
}
]
},
"flasher": {
"name": "jlink",
"uid": "000776606156",
"args": "-device stm32f723ie"
},
"comment": "Device port0 FS (slave only), Host port1 HS with DMA"
},
{
"name": "stm32h743nucleo",
"uid": "110018000951383432343236",
"variant": [
{ "name": "stm32h743nucleo", "flags": "" },
{ "name": "stm32h743nucleo-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "openocd",
"uid": "004C00343137510F39383538",
"args": "-f interface/stlink.cfg -f target/stm32h7x.cfg"
}
},
{
"name": "stm32g0b1nucleo",
"uid": "4D0038000450434E37343120",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "openocd",
"uid": "066FFF495087534867063844",
"args": "-f interface/stlink.cfg -f target/stm32g0x.cfg"
},
"comment": "32-bit scheme, 2KB USB SRAM"
},
{
"name": "stm32l476disco",
"uid": "3C0050001150334258343920",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "777632258",
"args": "-device STM32L476VG"
}
},
{
"name": "stm32u083nucleo",
"uid": "300044000D5036394E373620",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "stlink",
"uid": "0668FF575457657187061314"
}
},
{
"name": "nanoch32v203",
"uid": "CDAB277B0FBC03E339E339E3",
"toolchain": "riscv-gcc",
"variant": [
{"name": "nanoch32v203-fsdev", "defines": ["RHPORT_DEVICE=0"]},
{"name": "nanoch32v203-usbfs", "defines": ["RHPORT_DEVICE=1"]}
],
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "openocd_wch",
"uid": "EBCA8F0670AF",
"args": ""
}
},
{
"name": "ch32v103r_r1_1v0",
"uid": "CDAB3E8749BC54EF0F410025",
"toolchain": "riscv-gcc",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "openocd_wch",
"uid": "BC4954081051",
"args": ""
}
},
{
"name": "ch582m_evt",
"uid": "D443627B5450",
"toolchain": "riscv-gcc",
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "openocd_wch",
"uid": "7FD88F0604B5",
"args": ""
}
}
],
"boards-skip": [
{
"name": "stm32f769disco",
"uid": "21002F000F51363531383437",
"variant": [
{ "name": "stm32f769disco", "flags": "" },
{ "name": "stm32f769disco-DMA", "flags": "-DCFG_TUD_DWC2_DMA_ENABLE=1 -DCFG_TUH_DWC2_DMA_ENABLE=1" }
],
"tests": {
"device": true,
"host": false,
"dual": false
},
"flasher": {
"name": "jlink",
"uid": "000778170924",
"args": "-device stm32f769ni"
}
}
]
}