summaryrefslogtreecommitdiff
path: root/test/hil
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2025-04-18 11:13:15 +0200
committerHiFiPhile <[email protected]>2025-04-18 11:13:15 +0200
commitb6abc9022a4beae98cc206d8fbeefb4700deef2f (patch)
treebdd12bb710caa9d161edc1488a37911de0931a3d /test/hil
parent713410997326269b4072dce906933f8f44fd0efe (diff)
parent865e3488f99209c57b73953428dccf2bc666f0be (diff)
Merge remote-tracking branch 'upstream/master' into hcd-template-comments
Signed-off-by: HiFiPhile <[email protected]>
Diffstat (limited to 'test/hil')
-rw-r--r--test/hil/hfp.json33
-rw-r--r--test/hil/hil_ci_set_matrix.py44
-rwxr-xr-x[-rw-r--r--]test/hil/hil_test.py468
-rw-r--r--test/hil/requirements.txt2
-rw-r--r--test/hil/rpi.json82
-rw-r--r--test/hil/tinyusb.json250
6 files changed, 656 insertions, 223 deletions
diff --git a/test/hil/hfp.json b/test/hil/hfp.json
index e79a3cfc9..8ba7a8f44 100644
--- a/test/hil/hfp.json
+++ b/test/hil/hfp.json
@@ -3,23 +3,38 @@
{
"name": "stm32l412nucleo",
"uid": "41003B000E504E5457323020",
- "flasher": "jlink",
- "flasher_sn": "774470029",
- "flasher_args": "-device STM32L412KB"
+ "tests": {
+ "device": true, "host": false, "dual": false
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "774470029",
+ "args": "-device STM32L412KB"
+ }
},
{
"name": "stm32f746disco",
"uid": "210041000C51343237303334",
- "flasher": "jlink",
- "flasher_sn": "770935966",
- "flasher_args": "-device STM32F746NG"
+ "tests": {
+ "device": true, "host": false, "dual": false
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "770935966",
+ "args": "-device STM32F746NG"
+ }
},
{
"name": "lpcxpresso43s67",
"uid": "08F000044528BAAA8D858F58C50700F5",
- "flasher": "jlink",
- "flasher_sn": "728973776",
- "flasher_args": "-device LPC43S67_M4"
+ "tests": {
+ "device": true, "host": false, "dual": false
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "728973776",
+ "args": "-device LPC43S67_M4"
+ }
}
]
}
diff --git a/test/hil/hil_ci_set_matrix.py b/test/hil/hil_ci_set_matrix.py
new file mode 100644
index 000000000..67ce2abb8
--- /dev/null
+++ b/test/hil/hil_ci_set_matrix.py
@@ -0,0 +1,44 @@
+import argparse
+import json
+import os
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('config_file', help='Configuration JSON file')
+ args = parser.parse_args()
+
+ config_file = args.config_file
+
+ # if config file is not found, try to find it in the same directory as this script
+ if not os.path.exists(config_file):
+ config_file = os.path.join(os.path.dirname(__file__), config_file)
+ with open(config_file) as f:
+ config = json.load(f)
+
+ matrix = {
+ 'arm-gcc': [],
+ 'esp-idf': []
+ }
+ for board in config['boards']:
+ name = board['name']
+ flasher = board['flasher']
+ if flasher['name'] == 'esptool':
+ toolchain = 'esp-idf'
+ else:
+ toolchain = 'arm-gcc'
+
+ if 'build' in board and 'flags_on' in board['build']:
+ for f in board['build']['flags_on']:
+ if f == '':
+ matrix[toolchain].append(f'-b {name}')
+ else:
+ matrix[toolchain].append(f'-b {name} -f1 {f.replace(" ", " -f1 ")}')
+ else:
+ matrix[toolchain].append(f'-b {name}')
+
+ print(json.dumps(matrix))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py
index 486f0d2eb..8b89de66c 100644..100755
--- a/test/hil/hil_test.py
+++ b/test/hil/hil_test.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python3
#
# The MIT License (MIT)
#
@@ -27,6 +28,7 @@
import argparse
import os
+import re
import sys
import time
import serial
@@ -38,6 +40,37 @@ import fs
ENUM_TIMEOUT = 30
+STATUS_OK = "\033[32mOK\033[0m"
+STATUS_FAILED = "\033[31mFailed\033[0m"
+STATUS_SKIPPED = "\033[33mSkipped\033[0m"
+
+verbose = False
+
+WCH_RISCV_CONTENT = """
+adapter driver wlinke
+adapter speed 6000
+transport select sdi
+
+wlink_set_address 0x00000000
+set _CHIPNAME wch_riscv
+sdi newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001
+
+set _TARGETNAME $_CHIPNAME.cpu
+
+target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME
+$_TARGETNAME.0 configure -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1
+set _FLASHNAME $_CHIPNAME.flash
+
+flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0
+
+echo "Ready for Remote Connections"
+"""
+
+# -------------------------------------------------------------
+# Path
+# -------------------------------------------------------------
+OPENCOD_ADI_PATH = f'{os.getenv("HOME")}/app/openocd_adi'
+TINYUSB_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# get usb serial by id
def get_serial_dev(id, vendor_str, product_str, ifnum):
@@ -48,7 +81,7 @@ def get_serial_dev(id, vendor_str, product_str, ifnum):
return f'/dev/serial/by-id/usb-{vendor_str}_{product_str}_{id}-if{ifnum:02d}'
else:
# just use id: mostly for cp210x/ftdi flasher
- pattern = f'/dev/serial/by-id/usb-*_{id}-if{ifnum:02d}*'
+ pattern = f'/dev/serial/by-id/usb-*_{id}-if*'
port_list = glob.glob(pattern)
return port_list[0]
@@ -65,20 +98,18 @@ def get_hid_dev(id, vendor_str, product_str, event):
def open_serial_dev(port):
timeout = ENUM_TIMEOUT
ser = None
- while timeout:
+ while timeout > 0:
if os.path.exists(port):
try:
- # slight delay since kernel may occupy the port briefly
- time.sleep(0.5)
- timeout = timeout - 0.5
- ser = serial.Serial(port, timeout=5)
+ ser = serial.Serial(port, baudrate=115200, timeout=5)
break
except serial.SerialException:
+ print(f'serial {port} not reaady {timeout} sec')
pass
- time.sleep(0.5)
- timeout = timeout - 0.5
+ time.sleep(0.1)
+ timeout -= 0.1
- assert timeout, f'Cannot open port f{port}' if os.path.exists(port) else f'Port {port} not existed'
+ assert timeout > 0, f'Cannot open port f{port}' if os.path.exists(port) else f'Port {port} not existed'
return ser
@@ -86,7 +117,7 @@ def read_disk_file(uid, lun, fname):
# open_fs("fat://{dev}) require 'pip install pyfatfs'
dev = get_disk_dev(uid, 'TinyUSB', lun)
timeout = ENUM_TIMEOUT
- while timeout:
+ while timeout > 0:
if os.path.exists(dev):
fat = fs.open_fs(f'fat://{dev}?read_only=true')
try:
@@ -99,16 +130,15 @@ def read_disk_file(uid, lun, fname):
time.sleep(1)
timeout -= 1
- assert timeout, f'Storage {dev} not existed'
+ assert timeout > 0, f'Storage {dev} not existed'
return None
# -------------------------------------------------------------
# Flashing firmware
# -------------------------------------------------------------
-def run_cmd(cmd):
- #print(cmd)
- r = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+def run_cmd(cmd, cwd=None):
+ r = subprocess.run(cmd, cwd=cwd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if r.returncode != 0:
title = f'COMMAND FAILED: {cmd}'
print()
@@ -119,123 +149,255 @@ def run_cmd(cmd):
else:
print(title)
print(r.stdout.decode("utf-8"))
+ elif verbose:
+ print(cmd)
+ print(r.stdout.decode("utf-8"))
return r
def flash_jlink(board, firmware):
+ flasher = board['flasher']
script = ['halt', 'r', f'loadfile {firmware}.elf', 'r', 'go', 'exit']
f_jlink = f'{board["name"]}_{os.path.basename(firmware)}.jlink'
with open(f_jlink, 'w') as f:
f.writelines(f'{s}\n' for s in script)
- ret = run_cmd(f'JLinkExe -USB {board["flasher_sn"]} {board["flasher_args"]} -if swd -JTAGConf -1,-1 -speed auto -NoGui 1 -ExitOnError 1 -CommandFile {f_jlink}')
+ ret = run_cmd(f'JLinkExe -USB {flasher["uid"]} {flasher["args"]} -if swd -JTAGConf -1,-1 -speed auto -NoGui 1 -ExitOnError 1 -CommandFile {f_jlink}')
os.remove(f_jlink)
return ret
-def flash_stlink(board, firmware):
- ret = run_cmd(f'STM32_Programmer_CLI --connect port=swd sn={board["flasher_sn"]} --write {firmware}.elf --go')
+def reset_jlink(board):
+ flasher = board['flasher']
+ script = ['halt', 'r', 'go', 'exit']
+ f_jlink = f'{board["name"]}_reset.jlink'
+ if not os.path.exists(f_jlink):
+ with open(f_jlink, 'w') as f:
+ f.writelines(f'{s}\n' for s in script)
+ ret = run_cmd(f'JLinkExe -USB {flasher["uid"]} {flasher["args"]} -if swd -JTAGConf -1,-1 -speed auto -NoGui 1 -ExitOnError 1 -CommandFile {f_jlink}')
return ret
+def flash_stlink(board, firmware):
+ flasher = board['flasher']
+ return run_cmd(f'STM32_Programmer_CLI --connect port=swd sn={flasher["uid"]} --write {firmware}.elf --go')
+
+
+def reset_stlink(board):
+ flasher = board['flasher']
+ return run_cmd(f'STM32_Programmer_CLI --connect port=swd sn={flasher["uid"]} --rst --go')
+
def flash_stflash(board, firmware):
- ret = run_cmd(f'st-flash --serial {board["flasher_sn"]} write {firmware}.bin 0x8000000')
+ flasher = board['flasher']
+ ret = run_cmd(f'st-flash --serial {flasher["uid"]} write {firmware}.bin 0x8000000')
return ret
+def reset_stflash(board):
+ flasher = board['flasher']
+ return subprocess.CompletedProcess(args=['dummy'], returncode=0)
+
+
def flash_openocd(board, firmware):
- ret = run_cmd(f'openocd -c "adapter serial {board["flasher_sn"]}" {board["flasher_args"]} -c "program {firmware}.elf reset exit"')
+ flasher = board['flasher']
+ ret = run_cmd(f'openocd -c "tcl_port disabled" -c "gdb_port disabled" -c "adapter serial {flasher["uid"]}" '
+ f'{flasher["args"]} -c "init; halt; program {firmware}.elf verify; reset; exit"')
return ret
-def flash_openocd_wch(board, firmware):
- # Content of the wch-riscv.cfg file
- cfg_content = """
-adapter driver wlinke
-adapter speed 6000
-transport select sdi
+def reset_openocd(board):
+ flasher = board['flasher']
+ ret = run_cmd(f'openocd -c "tcl_port disabled" -c "gdb_port disabled" -c "adapter serial {flasher["uid"]}" '
+ f'{flasher["args"]} -c "init; reset run; exit"')
+ return ret
-wlink_set_address 0x00000000
-set _CHIPNAME wch_riscv
-sdi newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001
-set _TARGETNAME $_CHIPNAME.cpu
+def flash_openocd_wch(board, firmware):
+ flasher = board['flasher']
+ f_wch = f"wch-riscv_{board['uid']}.cfg"
+ if not os.path.exists(f_wch):
+ with open(f_wch, 'w') as file:
+ file.write(WCH_RISCV_CONTENT)
-target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME
-$_TARGETNAME.0 configure -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1
-set _FLASHNAME $_CHIPNAME.flash
+ ret = run_cmd(f'openocd_wch -c "adapter serial {flasher["uid"]}" -f {f_wch} '
+ f'-c "program {firmware}.elf reset exit"')
+ return ret
-flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0
-echo "Ready for Remote Connections"
-"""
+def reset_openocd_wch(board):
+ flasher = board['flasher']
f_wch = f"wch-riscv_{board['uid']}.cfg"
if not os.path.exists(f_wch):
with open(f_wch, 'w') as file:
- file.write(cfg_content)
+ file.write(WCH_RISCV_CONTENT)
- ret = run_cmd(f'openocd_wch -c "adapter serial {board["flasher_sn"]}" -f {f_wch} -c "program {firmware}.elf reset exit"')
+ ret = run_cmd(f'openocd_wch -c "adapter serial {flasher["uid"]}" -f {f_wch} -c "program reset exit"')
+ return ret
+
+
+def flash_openocd_adi(board, firmware):
+ flasher = board['flasher']
+ ret = run_cmd(f'{OPENCOD_ADI_PATH}/src/openocd -c "adapter serial {flasher["uid"]}" -s {OPENCOD_ADI_PATH}/tcl '
+ f'{flasher["args"]} -c "program {firmware}.elf reset exit"')
+ return ret
+
+
+def reset_openocd_adi(board):
+ flasher = board['flasher']
+ ret = run_cmd(f'{OPENCOD_ADI_PATH}/src/openocd -c "adapter serial {flasher["uid"]}" -s {OPENCOD_ADI_PATH}/tcl '
+ f'{flasher["args"]} -c "program reset exit"')
return ret
def flash_wlink_rs(board, firmware):
+ flasher = board['flasher']
# wlink use index for probe selection and lacking usb serial support
ret = run_cmd(f'wlink flash {firmware}.elf')
return ret
+def reset_wlink_rs(board):
+ flasher = board['flasher']
+ # wlink use index for probe selection and lacking usb serial support
+ ret = run_cmd(f'wlink reset')
+ return ret
+
+
def flash_esptool(board, firmware):
- port = get_serial_dev(board["flasher_sn"], None, None, 0)
- dir = os.path.dirname(f'{firmware}.bin')
- with open(f'{dir}/config.env') as f:
- IDF_TARGET = json.load(f)['IDF_TARGET']
- with open(f'{dir}/flash_args') as f:
+ flasher = board['flasher']
+ port = get_serial_dev(flasher["uid"], None, None, 0)
+ fw_dir = os.path.dirname(f'{firmware}.bin')
+ with open(f'{fw_dir}/config.env') as f:
+ idf_target = json.load(f)['IDF_TARGET']
+ with open(f'{fw_dir}/flash_args') as f:
flash_args = f.read().strip().replace('\n', ' ')
- command = (f'esptool.py --chip {IDF_TARGET} -p {port} {board["flasher_args"]} '
+ command = (f'esptool.py --chip {idf_target} -p {port} {flasher["args"]} '
f'--before=default_reset --after=hard_reset write_flash {flash_args}')
- ret = subprocess.run(command, shell=True, cwd=dir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ ret = run_cmd(command, cwd=fw_dir)
+ return ret
+
+
+def reset_esptool(board):
+ flasher = board['flasher']
+ return subprocess.CompletedProcess(args=['dummy'], returncode=0)
+
+
+def flash_uniflash(board, firmware):
+ flasher = board['flasher']
+ ret = run_cmd(f'dslite.sh {flasher["args"]} -f {firmware}.hex')
return ret
+def reset_uniflash(board):
+ flasher = board['flasher']
+ return subprocess.CompletedProcess(args=['dummy'], returncode=0)
+
+
# -------------------------------------------------------------
-# Tests
+# Tests: dual
# -------------------------------------------------------------
-def test_board_test(board):
+def test_dual_host_info_to_device_cdc(board):
+ uid = board['uid']
+ declared_devs = [f'{d["vid_pid"]}_{d["serial"]}' for d in board['tests']['dev_attached']]
+ port = get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 0)
+ ser = open_serial_dev(port)
+
+ # read from cdc, first line should contain vid/pid and serial
+ data = ser.read(10000)
+ ser.close()
+ if len(data) == 0:
+ assert False, 'No data from device'
+ lines = data.decode('utf-8', errors='ignore').splitlines()
+
+ enum_dev_sn = []
+ for l in lines:
+ vid_pid_sn = re.search(r'ID ([0-9a-fA-F]+):([0-9a-fA-F]+) SN (\w+)', l)
+ if vid_pid_sn:
+ print(f'\r\n {l} ', end='')
+ enum_dev_sn.append(f'{vid_pid_sn.group(1)}_{vid_pid_sn.group(2)}_{vid_pid_sn.group(3)}')
+
+ if set(declared_devs) != set(enum_dev_sn):
+ failed_msg = f'Expected {declared_devs}, Enumerated {enum_dev_sn}'
+ print('\n'.join(lines))
+ assert False, failed_msg
+ return 0
+
+
+# -------------------------------------------------------------
+# Tests: host
+# -------------------------------------------------------------
+def test_host_device_info(board):
+ flasher = board['flasher']
+ declared_devs = [f'{d["vid_pid"]}_{d["serial"]}' for d in board['tests']['dev_attached']]
+
+ port = get_serial_dev(flasher["uid"], None, None, 0)
+ ser = open_serial_dev(port)
+
+ # reset device since we can miss the first line
+ ret = globals()[f'reset_{flasher["name"].lower()}'](board)
+ assert ret.returncode == 0, 'Failed to reset device'
+
+ data = ser.read(10000)
+ ser.close()
+ if len(data) == 0:
+ assert False, 'No data from device'
+ lines = data.decode('utf-8', errors='ignore').splitlines()
+
+ enum_dev_sn = []
+ for l in lines:
+ vid_pid_sn = re.search(r'ID ([0-9a-fA-F]+):([0-9a-fA-F]+) SN (\w+)', l)
+ if vid_pid_sn:
+ print(f'\r\n {l} ', end='')
+ enum_dev_sn.append(f'{vid_pid_sn.group(1)}_{vid_pid_sn.group(2)}_{vid_pid_sn.group(3)}')
+
+ if set(declared_devs) != set(enum_dev_sn):
+ failed_msg = f'Expected {declared_devs}, Enumerated {enum_dev_sn}'
+ print('\n'.join(lines))
+ assert False, failed_msg
+ return 0
+
+
+# -------------------------------------------------------------
+# Tests: device
+# -------------------------------------------------------------
+def test_device_board_test(board):
# Dummy test
pass
-def test_cdc_dual_ports(board):
+def test_device_cdc_dual_ports(board):
uid = board['uid']
- port1 = get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 0)
- port2 = get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 2)
-
- ser1 = open_serial_dev(port1)
- ser2 = open_serial_dev(port2)
+ port = [
+ get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 0),
+ get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 2)
+ ]
+ ser = [open_serial_dev(p) for p in port]
- # Echo test
- str1 = b"test_no1"
- ser1.write(str1)
- ser1.flush()
- assert ser1.read(100) == str1.lower(), 'Port1 wrong data'
- assert ser2.read(100) == str1.upper(), 'Port2 wrong data'
-
- str2 = b"test_no2"
- ser2.write(str2)
- ser2.flush()
- assert ser1.read(100) == str2.lower(), 'Port1 wrong data'
- assert ser2.read(100) == str2.upper(), 'Port2 wrong data'
+ str_test = [ b"test_no1", b"test_no2" ]
+ # Echo test write to each port and read back
+ for i in range(len(str_test)):
+ s = str_test[i]
+ l = len(s)
+ ser[i].write(s)
+ ser[i].flush()
+ rd = [ ser[i].read(l) for i in range(len(ser)) ]
+ assert rd[0] == s.lower(), f'Port1 wrong data: expected {s.lower()} was {rd[0]}'
+ assert rd[1] == s.upper(), f'Port2 wrong data: expected {s.upper()} was {rd[1]}'
+ ser[0].close()
+ ser[1].close()
-def test_cdc_msc(board):
+def test_device_cdc_msc(board):
uid = board['uid']
# Echo test
port = get_serial_dev(uid, 'TinyUSB', "TinyUSB_Device", 0)
ser = open_serial_dev(port)
- str = b"test_str"
- ser.write(str)
+ test_str = b"test_str"
+ ser.write(test_str)
ser.flush()
- assert ser.read(100) == str, 'CDC wrong data'
+ rd_str = ser.read(len(test_str))
+ ser.close()
+ assert rd_str == test_str, f'CDC wrong data: expected: {test_str} was {rd_str}'
# Block test
data = read_disk_file(uid,0,'README.TXT')
@@ -247,25 +409,24 @@ issue at github.com/hathach/tinyusb"
assert data == readme, 'MSC wrong data'
-def test_cdc_msc_freertos(board):
- test_cdc_msc(board)
+def test_device_cdc_msc_freertos(board):
+ test_device_cdc_msc(board)
-def test_dfu(board):
+def test_device_dfu(board):
uid = board['uid']
# Wait device enum
timeout = ENUM_TIMEOUT
- while timeout:
- ret = subprocess.run(f'dfu-util -l',
- shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ while timeout > 0:
+ ret = run_cmd(f'dfu-util -l')
stdout = ret.stdout.decode()
if f'serial="{uid}"' in stdout and 'Found DFU: [cafe:4000]' in stdout:
break
time.sleep(1)
timeout = timeout - 1
- assert timeout, 'Device not available'
+ assert timeout > 0, 'Device not available'
f_dfu0 = f'dfu0_{uid}'
f_dfu1 = f'dfu1_{uid}'
@@ -293,40 +454,39 @@ def test_dfu(board):
os.remove(f_dfu1)
-def test_dfu_runtime(board):
+def test_device_dfu_runtime(board):
uid = board['uid']
# Wait device enum
timeout = ENUM_TIMEOUT
- while timeout:
- ret = subprocess.run(f'dfu-util -l',
- shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ while timeout > 0:
+ ret = run_cmd(f'dfu-util -l')
stdout = ret.stdout.decode()
if f'serial="{uid}"' in stdout and 'Found Runtime: [cafe:4000]' in stdout:
break
time.sleep(1)
timeout = timeout - 1
- assert timeout, 'Device not available'
+ assert timeout > 0, 'Device not available'
-def test_hid_boot_interface(board):
+def test_device_hid_boot_interface(board):
uid = board['uid']
kbd = get_hid_dev(uid, 'TinyUSB', 'TinyUSB_Device', 'event-kbd')
mouse1 = get_hid_dev(uid, 'TinyUSB', 'TinyUSB_Device', 'if01-event-mouse')
mouse2 = get_hid_dev(uid, 'TinyUSB', 'TinyUSB_Device', 'if01-mouse')
# Wait device enum
timeout = ENUM_TIMEOUT
- while timeout:
+ while timeout > 0:
if os.path.exists(kbd) and os.path.exists(mouse1) and os.path.exists(mouse2):
break
time.sleep(1)
timeout = timeout - 1
- assert timeout, 'HID device not available'
+ assert timeout > 0, 'HID device not available'
-def test_hid_composite_freertos(id):
+def test_device_hid_composite_freertos(id):
# TODO implement later
pass
@@ -334,66 +494,100 @@ def test_hid_composite_freertos(id):
# -------------------------------------------------------------
# Main
# -------------------------------------------------------------
-# all possible tests: board_test is added last to disable board's usb
-all_tests = [
- 'cdc_dual_ports',
- 'cdc_msc',
- 'dfu',
- 'cdc_msc_freertos', # dont test 2 cdc_msc next to each other, since they have same vid/pid. Can be confused by host
- 'dfu_runtime',
- 'hid_boot_interface',
- 'board_test'
+# device tests
+# note don't test 2 examples with cdc or 2 msc next to each other
+device_tests = [
+ 'device/cdc_dual_ports',
+ 'device/dfu',
+ 'device/cdc_msc',
+ 'device/dfu_runtime',
+ 'device/cdc_msc_freertos',
+ 'device/hid_boot_interface',
+]
+
+dual_tests = [
+ 'dual/host_info_to_device_cdc',
+]
+
+host_test = [
+ 'host/device_info',
]
def test_board(board):
name = board['name']
- flasher = board['flasher'].lower()
+ flasher = board['flasher']
# default to all tests
+ test_list = []
+
if 'tests' in board:
- test_list = board['tests'] + ['board_test']
- else:
- test_list = list(all_tests)
+ board_tests = board['tests']
+ if 'device' in board_tests and board_tests['device'] == True:
+ test_list += list(device_tests)
+ if 'dual' in board_tests and board_tests['dual'] == True:
+ test_list += dual_tests
+ if 'host' in board_tests and board_tests['host'] == True:
+ test_list += host_test
+ if 'only' in board_tests:
+ test_list = board_tests['only']
+ if 'skip' in board_tests:
+ for skip in board_tests['skip']:
+ if skip in test_list:
+ test_list.remove(skip)
+ print(f'{name:25} {skip:30} ... Skip')
- # remove skip_tests
- if 'tests_skip' in board:
- for skip in board['tests_skip']:
- if skip in test_list:
- test_list.remove(skip)
+ # board_test is added last to disable board's usb
+ test_list.append('device/board_test')
err_count = 0
- for test in test_list:
- fw_dir = f'cmake-build/cmake-build-{name}/device/{test}'
- if not os.path.exists(fw_dir):
- fw_dir = f'examples/cmake-build-{name}/device/{test}'
- fw_name = f'{fw_dir}/{test}'
- print(f'{name:30} {test:20} ... ', end='')
+ flags_on_list = [""]
+ if 'build' in board and 'flags_on' in board['build']:
+ flags_on_list = board['build']['flags_on']
- if not os.path.exists(fw_dir):
- print('Skip')
- continue
+ for f1 in flags_on_list:
+ f1_str = ""
+ if f1 != "":
+ f1_str = '-f1_' + f1.replace(' ', '_')
+ for test in test_list:
+ fw_dir = f'{TINYUSB_ROOT}/cmake-build/cmake-build-{name}{f1_str}/{test}'
+ if not os.path.exists(fw_dir):
+ fw_dir = f'{TINYUSB_ROOT}/examples/cmake-build-{name}{f1_str}/{test}'
+ fw_name = f'{fw_dir}/{os.path.basename(test)}'
+ print(f'{name+f1_str:40} {test:30} ... ', end='')
- # flash firmware. It may fail randomly, retry a few times
- for i in range(3):
- ret = globals()[f'flash_{flasher}'](board, fw_name)
- if ret.returncode == 0:
- break
- else:
- print(f'Flashing failed, retry {i+1}')
- time.sleep(1)
+ if not os.path.exists(fw_dir) or not (os.path.exists(f'{fw_name}.elf') or os.path.exists(f'{fw_name}.bin')):
+ print('Skip (no binary)')
+ continue
- if ret.returncode == 0:
- try:
- ret = globals()[f'test_{test}'](board)
- print('OK')
- except Exception as e:
+ if verbose:
+ print(f'Flashing {fw_name}.elf')
+
+ # flash firmware. It may fail randomly, retry a few times
+ max_rety = 2
+ for i in range(max_rety):
+ ret = globals()[f'flash_{flasher["name"].lower()}'](board, fw_name)
+ if ret.returncode == 0:
+ try:
+ globals()[f'test_{test.replace("/", "_")}'](board)
+ print('OK')
+ break
+ except Exception as e:
+ if i == max_rety - 1:
+ err_count += 1
+ print(STATUS_FAILED)
+ print(f' {e}')
+ else:
+ print()
+ print(f' Test failed: {e}, retry {i+2}/{max_rety}')
+ time.sleep(1)
+ else:
+ print(f'Flashing failed, retry {i+2}/{max_rety}')
+ time.sleep(1)
+
+ if ret.returncode != 0:
err_count += 1
- print('Failed')
- print(f' {e}')
- else:
- err_count += 1
- print('Flash failed')
+ print(f'Flash {STATUS_FAILED}')
return err_count
@@ -402,13 +596,19 @@ def main():
"""
Hardware test on specified boards
"""
+ global verbose
+
+ duration = time.time()
+
parser = argparse.ArgumentParser()
parser.add_argument('config_file', help='Configuration JSON file')
parser.add_argument('-b', '--board', action='append', default=[], help='Boards to test, all if not specified')
+ parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')
args = parser.parse_args()
config_file = args.config_file
boards = args.board
+ verbose = args.verbose
# if config file is not found, try to find it in the same directory as this script
if not os.path.exists(config_file):
@@ -421,11 +621,15 @@ def main():
else:
config_boards = [e for e in config['boards'] if e['name'] in boards]
- err_count_list = 0
with Pool(processes=os.cpu_count()) as pool:
- err_count_list = pool.map(test_board, config_boards)
+ err_count = sum(pool.map(test_board, config_boards))
- sys.exit(sum(err_count_list))
+ duration = time.time() - duration
+ print()
+ print("-" * 30)
+ print(f'Total failed: {err_count} in {duration:.1f}s')
+ print("-" * 30)
+ sys.exit(err_count)
if __name__ == '__main__':
diff --git a/test/hil/requirements.txt b/test/hil/requirements.txt
new file mode 100644
index 000000000..c33980c9d
--- /dev/null
+++ b/test/hil/requirements.txt
@@ -0,0 +1,2 @@
+fs
+pyfatfs
diff --git a/test/hil/rpi.json b/test/hil/rpi.json
deleted file mode 100644
index 688ea3822..000000000
--- a/test/hil/rpi.json
+++ /dev/null
@@ -1,82 +0,0 @@
-{
- "boards": [
- {
- "name": "feather_nrf52840_express",
- "uid": "1F0479CD0F764471",
- "flasher": "jlink",
- "flasher_sn": "000682804350",
- "flasher_args": "-device nrf52840_xxaa"
- },
- {
- "name": "itsybitsy_m4",
- "uid": "D784B28C5338533335202020FF044726",
- "flasher": "openocd",
- "flasher_sn": "E6614C311B597D32",
- "flasher_args": "-f interface/cmsis-dap.cfg -f target/atsame5x.cfg -c \"adapter speed 5000\""
- },
- {
- "name": "lpcxpresso11u37",
- "uid": "17121919",
- "flasher": "jlink",
- "flasher_sn": "000724441579",
- "flasher_args": "-device LPC11U37/401"
- },
- {
- "name": "ra4m1_ek",
- "uid": "152E163038303131393346E46F26574B",
- "tests_skip": ["cdc_msc", "cdc_msc_freertos"],
- "comment": "MSC is slow to enumerated #2602",
- "flasher": "jlink",
- "flasher_sn": "000831174392",
- "flasher_args": "-device R7FA4M1AB"
- },
- {
- "name": "raspberry_pi_pico",
- "uid": "E6614C311B764A37",
- "flasher": "openocd",
- "flasher_sn": "E6614103E72C1D2F",
- "flasher_args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\""
- },
- {
- "name": "stm32f072disco",
- "uid": "3A001A001357364230353532",
- "flasher": "jlink",
- "flasher_sn": "779541626",
- "flasher_args": "-device stm32f072rb"
- },
- {
- "name": "stm32g0b1nucleo",
- "uid": "4D0038000450434E37343120",
- "flasher": "openocd",
- "flasher_sn": "066FFF495087534867063844",
- "flasher_args": "-f interface/stlink.cfg -f target/stm32g0x.cfg"
- },
- {
- "name": "nanoch32v203",
- "uid": "CDAB277B0FBC03E339E339E3",
- "flasher": "openocd_wch",
- "flasher_sn": "EBCA8F0670AF",
- "flasher_args": ""
- }
- ],
- "boards-skip": [
- {
- "name": "metro_m7_1011",
- "uid": "9CE8715DD71137363E00005002004200",
- "flasher": "jlink",
- "flasher_sn": "000611000000",
- "flasher_args": "-device MIMXRT1011xxx5A",
- "comment": "not running reliably in bulk with other boards, probably power, flashing etc .."
- },
- {
- "name": "espressif_s3_devkitm",
- "uid": "84F703C084E4",
- "tests": [
- "cdc_msc_freertos", "hid_composite_freertos"
- ],
- "flasher": "esptool",
- "flasher_sn": "3ea619acd1cdeb11a0a0b806e93fd3f1",
- "flasher_args": "-b 921600"
- }
- ]
-}
diff --git a/test/hil/tinyusb.json b/test/hil/tinyusb.json
new file mode 100644
index 000000000..a9460bf9d
--- /dev/null
+++ b/test/hil/tinyusb.json
@@ -0,0 +1,250 @@
+{
+ "boards": [
+ {
+ "name": "espressif_p4_function_ev",
+ "uid": "6055F9F98715",
+ "build" : {
+ "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE CFG_TUH_DWC2_DMA_ENABLE"]
+ },
+ "tests": {
+ "only": ["device/cdc_msc_freertos", "device/hid_composite_freertos", "host/device_info"],
+ "dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2002427"}]
+ },
+ "flasher": {
+ "name": "esptool",
+ "uid": "4ea4f48f6bc3ee11bbb9d00f9e1b1c54",
+ "args": "-b 1500000"
+ },
+ "comment": "Use TS3USB30 mux to test both device and host"
+ },
+ {
+ "name": "espressif_s3_devkitm",
+ "uid": "84F703C084E4",
+ "build" : {
+ "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"]
+ },
+ "tests": {
+ "only": ["device/cdc_msc_freertos", "device/hid_composite_freertos"]
+ },
+ "flasher": {
+ "name": "esptool",
+ "uid": "3ea619acd1cdeb11a0a0b806e93fd3f1",
+ "args": "-b 1500000"
+ }
+ },
+ {
+ "name": "feather_nrf52840_express",
+ "uid": "1F0479CD0F764471",
+ "tests": {
+ "device": true, "host": false, "dual": false
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "000682804350",
+ "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",
+ "tests": {
+ "device": true, "host": false, "dual": true,
+ "dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2002130"}]
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "123456",
+ "args": "-device ATSAMD51J19"
+ }
+ },
+ {
+ "name": "mimxrt1064_evk",
+ "uid": "BAE96FB95AFA6DBB8F00005002001200",
+ "tests": {
+ "device": true, "host": true, "dual": true,
+ "dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2023299"}]
+ },
+ "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,
+ "skip": ["device/cdc_msc", "device/cdc_msc_freertos"]
+ },
+ "comment": "MSC is slow to enumerated #2602",
+ "flasher": {
+ "name": "jlink",
+ "uid": "000831174392",
+ "args": "-device R7FA4M1AB"
+ }
+ },
+ {
+ "name": "raspberry_pi_pico",
+ "uid": "E6614C311B764A37",
+ "build" : {
+ "flags_on": ["CFG_TUH_RPI_PIO_USB"]
+ },
+ "tests": {
+ "device": true, "host": true, "dual": true,
+ "dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2002470"}]
+ },
+ "flasher": {
+ "name": "openocd",
+ "uid": "E6614103E72C1D2F",
+ "args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\""
+ }
+ },
+ {
+ "name": "raspberry_pi_pico2",
+ "uid": "560AE75E1C7152C9",
+ "build" : {
+ "flags_on": ["CFG_TUH_RPI_PIO_USB"]
+ },
+ "tests": {
+ "device": true, "host": true, "dual": true,
+ "dev_attached": [{"vid_pid": "1a86_55d4", "serial": "533D004242"}]
+ },
+ "flasher": {
+ "name": "openocd",
+ "uid": "E6633861A3978538",
+ "args": "-f interface/cmsis-dap.cfg -f target/rp2350.cfg -c \"adapter speed 5000\""
+ }
+ },
+ {
+ "name": "stm32f072disco",
+ "uid": "3A001A001357364230353532",
+ "flasher": {
+ "name": "jlink",
+ "uid": "779541626",
+ "args": "-device stm32f072rb"
+ }
+ },
+ {
+ "name": "stm32f723disco",
+ "uid": "460029001951373031313335",
+ "build" : {
+ "flags_on": ["", "CFG_TUH_DWC2_DMA_ENABLE"]
+ },
+ "tests": {
+ "device": true, "host": true, "dual": false,
+ "dev_attached": [{"vid_pid": "1a86_55d4", "serial": "52D2003414"}]
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "000776606156",
+ "args": "-device stm32f723ie"
+ },
+ "comment": "Device port0 FS (slave only), Host port1 HS with DMA"
+ },
+ {
+ "name": "stm32h743nucleo",
+ "uid": "110018000951383432343236",
+ "build" : {
+ "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"]
+ },
+ "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"
+ }
+ }
+ ],
+ "boards-skip": [
+ {
+ "name": "stm32f769disco",
+ "uid": "21002F000F51363531383437",
+ "build" : {
+ "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"]
+ },
+ "tests": {
+ "device": true, "host": false, "dual": false
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "000778170924",
+ "args": "-device stm32f769ni"
+ }
+ },
+ {
+ "name": "mimxrt1015_evk",
+ "uid": "DC28F865D2111D228D00B0543A70463C",
+ "tests": {
+ "device": true, "host": false, "dual": false
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "000726284213",
+ "args": "-device MIMXRT1015DAF5A"
+ }
+ },
+ {
+ "name": "nanoch32v203",
+ "uid": "CDAB277B0FBC03E339E339E3",
+ "tests": {
+ "device": true, "host": false, "dual": false
+ },
+ "flasher": {
+ "name": "openocd_wch",
+ "uid": "EBCA8F0670AF",
+ "args": ""
+ }
+ },
+ {
+ "name": "stm32f407disco",
+ "uid": "30001A000647313332353735",
+ "tests": {
+ "device": true, "host": false, "dual": false
+ },
+ "flasher": {
+ "name": "jlink",
+ "uid": "000773661813",
+ "args": "-device stm32f407vg"
+ }
+ }
+ ]
+}