diff options
| author | HiFiPhile <[email protected]> | 2025-04-18 11:13:15 +0200 |
|---|---|---|
| committer | HiFiPhile <[email protected]> | 2025-04-18 11:13:15 +0200 |
| commit | b6abc9022a4beae98cc206d8fbeefb4700deef2f (patch) | |
| tree | bdd12bb710caa9d161edc1488a37911de0931a3d /test/hil | |
| parent | 713410997326269b4072dce906933f8f44fd0efe (diff) | |
| parent | 865e3488f99209c57b73953428dccf2bc666f0be (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.json | 33 | ||||
| -rw-r--r-- | test/hil/hil_ci_set_matrix.py | 44 | ||||
| -rwxr-xr-x[-rw-r--r--] | test/hil/hil_test.py | 468 | ||||
| -rw-r--r-- | test/hil/requirements.txt | 2 | ||||
| -rw-r--r-- | test/hil/rpi.json | 82 | ||||
| -rw-r--r-- | test/hil/tinyusb.json | 250 |
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" + } + } + ] +} |
