Merge pull request #2355 from hathach/update-hil-rp2040

Update hil test for rp2040 on self-hosted PI4
This commit is contained in:
Ha Thach
2023-11-29 15:34:05 +07:00
committed by GitHub
6 changed files with 139 additions and 97 deletions

View File

@@ -105,15 +105,14 @@ jobs:
# --------------------------------------- # ---------------------------------------
# Hardware in the loop (HIL) # Hardware in the loop (HIL)
# Current self-hosted instance is running on an RPI4 with # Current self-hosted instance is running on an RPI4.
# - pico + pico-probe connected via USB # For attached hardware checkout hil_pi4.json
# - pico-probe is /dev/ttyACM0
# --------------------------------------- # ---------------------------------------
hw-rp2040-test: hw-rp2040-test:
# run only with hathach's commit due to limited resource on RPI4 # run only with hathach's commit due to limited resource on RPI4
if: github.repository_owner == 'hathach' if: github.repository_owner == 'hathach'
needs: build-arm needs: build-arm
runs-on: [self-hosted, Linux, ARM64, rp2040] runs-on: [self-hosted, rp2040, hardware-in-the-loop]
steps: steps:
- name: Clean workspace - name: Clean workspace
@@ -122,43 +121,16 @@ jobs:
rm -rf "${{ github.workspace }}" rm -rf "${{ github.workspace }}"
mkdir -p "${{ github.workspace }}" mkdir -p "${{ github.workspace }}"
- name: Checkout test/hil
uses: actions/checkout@v3
with:
sparse-checkout: test/hil
- name: Download rp2040 Artifacts - name: Download rp2040 Artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
name: rp2040 name: rp2040
- name: Create flash.sh - name: Test on actual hardware (hardware in the loop)
run: | run: |
echo > flash.sh 'cmdout=$(openocd -f "interface/cmsis-dap.cfg" -f "target/rp2040.cfg" -c "adapter speed 5000" -c "program $1 reset exit")' python3 test/hil/hil_test.py hil_pi4.json
echo >> flash.sh 'if (( $? )) ; then echo $cmdout ; fi'
chmod +x flash.sh
- name: Test cdc_dual_ports
run: |
./flash.sh cdc_dual_ports.elf
while (! ([ -e /dev/ttyACM1 ] && [ -e /dev/ttyACM2 ])) && [ $SECONDS -le 10 ]; do :; done
test -e /dev/ttyACM1 && echo "ttyACM1 exists"
test -e /dev/ttyACM2 && echo "ttyACM2 exists"
- name: Test cdc_msc
run: |
./flash.sh cdc_msc.elf
readme='/media/pi/TinyUSB MSC/README.TXT'
while (! ([ -e /dev/ttyACM1 ] && [ -f "$readme" ])) && [ $SECONDS -le 10 ]; do :; done
test -e /dev/ttyACM1 && echo "ttyACM1 exists"
test -f "$readme" && echo "$readme exists"
cat "$readme"
- name: Test dfu
run: |
./flash.sh dfu.elf
while (! (dfu-util -l | grep "Found DFU")) && [ $SECONDS -le 10 ]; do :; done
dfu-util -d cafe -a 0 -U dfu0
dfu-util -d cafe -a 1 -U dfu1
grep "TinyUSB DFU! - Partition 0" dfu0
grep "TinyUSB DFU! - Partition 1" dfu1
- name: Test dfu_runtime
run: |
./flash.sh dfu_runtime.elf
while (! (dfu-util -l | grep "Found Runtime")) && [ $SECONDS -le 10 ]; do :; done

View File

@@ -395,6 +395,22 @@ function(family_flash_stlink TARGET)
endfunction() endfunction()
# Add flash openocd target
function(family_flash_openocd TARGET CLI_OPTIONS)
if (NOT DEFINED OPENOCD)
set(OPENOCD openocd)
endif ()
separate_arguments(CLI_OPTIONS_LIST UNIX_COMMAND ${CLI_OPTIONS})
# note skip verify since it has issue with rp2040
add_custom_target(${TARGET}-openocd
DEPENDS ${TARGET}
COMMAND ${OPENOCD} ${CLI_OPTIONS_LIST} -c "program $<TARGET_FILE:${TARGET}> reset exit"
VERBATIM
)
endfunction()
# Add flash pycod target # Add flash pycod target
function(family_flash_pyocd TARGET) function(family_flash_pyocd TARGET)
if (NOT DEFINED PYOC) if (NOT DEFINED PYOC)

View File

@@ -12,6 +12,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/pico_sdk_import.cmake)
# include basic family CMake functionality # include basic family CMake functionality
set(FAMILY_MCUS RP2040) set(FAMILY_MCUS RP2040)
set(JLINK_DEVICE rp2040_m0_0) set(JLINK_DEVICE rp2040_m0_0)
set(OPENOCD_OPTION "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\"")
include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake) include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
@@ -158,6 +159,7 @@ function(family_configure_target TARGET RTOS)
pico_enable_stdio_uart(${TARGET} 1) pico_enable_stdio_uart(${TARGET} 1)
target_link_libraries(${TARGET} PUBLIC pico_stdlib pico_bootsel_via_double_reset tinyusb_board${RTOS_SUFFIX} tinyusb_additions) target_link_libraries(${TARGET} PUBLIC pico_stdlib pico_bootsel_via_double_reset tinyusb_board${RTOS_SUFFIX} tinyusb_additions)
family_flash_openocd(${TARGET} ${OPENOCD_OPTION})
family_flash_jlink(${TARGET}) family_flash_jlink(${TARGET})
endfunction() endfunction()

11
test/hil/hil_pi4.json Normal file
View File

@@ -0,0 +1,11 @@
{
"boards": [
{
"name": "raspberry_pi_pico",
"uid": "E6614C311B764A37",
"debugger": "openocd",
"debugger_sn": "E6614103E72C1D2F",
"debugger_args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\""
}
]
}

View File

@@ -32,21 +32,66 @@ import serial
import subprocess import subprocess
import json import json
ENUM_TIMEOUT = 10
def get_serial_dev(id, product, ifnum):
def get_serial_dev(id, vendor_str, product_str, ifnum):
# get usb serial by id # get usb serial by id
return f'/dev/serial/by-id/usb-TinyUSB_{product}_{id}-if{ifnum:02d}' return f'/dev/serial/by-id/usb-{vendor_str}_{product_str}_{id}-if{ifnum:02d}'
def get_disk_dev(id, lun): # Currently not used, left as reference
def get_disk_dev(id, vendor_str, lun):
# get usb disk by id # get usb disk by id
return f'/dev/disk/by-id/usb-TinyUSB_Mass_Storage_{id}-0:{lun}' return f'/dev/disk/by-id/usb-{vendor_str}_Mass_Storage_{id}-0:{lun}'
def get_hid_dev(id, product, event): def get_hid_dev(id, vendor_str, product_str, event):
return f'/dev/input/by-id/usb-TinyUSB_{product}_{id}-{event}' return f'/dev/input/by-id/usb-{vendor_str}_{product_str}_{id}-{event}'
def open_serial_dev(port):
timeout = ENUM_TIMEOUT
ser = None
while timeout:
if os.path.exists(port):
try:
# slight delay since kernel may occupy the port briefly
time.sleep(0.2)
ser = serial.Serial(port, timeout=1)
break
except serial.SerialException:
pass
time.sleep(0.8)
timeout = timeout - 1
assert timeout, 'Device not available or Cannot open port'
return ser
def read_disk_file(id, fname):
# on different self-hosted, the mount point is different
file_list = [
f'/media/blkUSB_{id[-8:]}.02/{fname}',
f'/media/{os.getenv("USER")}/TinyUSB MSC/{fname}'
]
timeout = ENUM_TIMEOUT
while timeout:
for file in file_list:
if os.path.isfile(file):
with open(file, 'rb') as f:
data = f.read()
return data
time.sleep(1)
timeout = timeout - 1
assert timeout, 'Device not available'
return None
# -------------------------------------------------------------
# Flash with debugger
# -------------------------------------------------------------
def flash_jlink(sn, dev, firmware): def flash_jlink(sn, dev, firmware):
script = ['halt', 'r', f'loadfile {firmware}', 'r', 'go', 'exit'] script = ['halt', 'r', f'loadfile {firmware}', 'r', 'go', 'exit']
f = open('flash.jlink', 'w') f = open('flash.jlink', 'w')
@@ -59,31 +104,29 @@ def flash_jlink(sn, dev, firmware):
assert ret.returncode == 0, 'Flash failed\n' + stdout assert ret.returncode == 0, 'Flash failed\n' + stdout
def flash_openocd(sn, args, firmware):
ret = subprocess.run(f'openocd {args} -c "program {firmware} reset exit"',
shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout = ret.stdout.decode()
assert ret.returncode == 0, 'Flash failed\n' + stdout
# -------------------------------------------------------------
# Tests
# -------------------------------------------------------------
def test_board_test(id): def test_board_test(id):
# Dummy test # Dummy test
pass pass
def test_cdc_dual_ports(id): def test_cdc_dual_ports(id):
port1 = get_serial_dev(id, "TinyUSB_Device", 0) port1 = get_serial_dev(id, 'TinyUSB', "TinyUSB_Device", 0)
port2 = get_serial_dev(id, "TinyUSB_Device", 2) port2 = get_serial_dev(id, 'TinyUSB', "TinyUSB_Device", 2)
# Wait device enum ser1 = open_serial_dev(port1)
timeout = 10 ser2 = open_serial_dev(port2)
while timeout:
if os.path.exists(port1) and os.path.exists(port2):
break
time.sleep(1)
timeout = timeout - 1
assert timeout, 'Device not available'
# Echo test # Echo test
ser1 = serial.Serial(port1)
ser2 = serial.Serial(port2)
ser1.timeout = 1
ser2.timeout = 1
str1 = b"test_no1" str1 = b"test_no1"
ser1.write(str1) ser1.write(str1)
ser1.flush() ser1.flush()
@@ -98,32 +141,17 @@ def test_cdc_dual_ports(id):
def test_cdc_msc(id): def test_cdc_msc(id):
port = get_serial_dev(id, "TinyUSB_Device", 0)
file = f'/media/blkUSB_{id[-8:]}.02/README.TXT'
# Wait device enum
timeout = 10
while timeout:
if os.path.exists(port) and os.path.isfile(file):
break
time.sleep(1)
timeout = timeout - 1
assert timeout, 'Device not available'
# Echo test # Echo test
ser1 = serial.Serial(port) port = get_serial_dev(id, 'TinyUSB', "TinyUSB_Device", 0)
ser = open_serial_dev(port)
ser1.timeout = 1
str = b"test_str" str = b"test_str"
ser1.write(str) ser.write(str)
ser1.flush() ser.flush()
assert ser1.read(100) == str, 'CDC wrong data' assert ser.read(100) == str, 'CDC wrong data'
# Block test # Block test
f = open(file, 'rb') data = read_disk_file(id, 'README.TXT')
data = f.read()
readme = \ readme = \
b"This is tinyusb's MassStorage Class demo.\r\n\r\n\ b"This is tinyusb's MassStorage Class demo.\r\n\r\n\
If you find any bugs or get any questions, feel free to file an\r\n\ If you find any bugs or get any questions, feel free to file an\r\n\
@@ -131,9 +159,10 @@ issue at github.com/hathach/tinyusb"
assert data == readme, 'MSC wrong data' assert data == readme, 'MSC wrong data'
def test_dfu(id): def test_dfu(id):
# Wait device enum # Wait device enum
timeout = 10 timeout = ENUM_TIMEOUT
while timeout: while timeout:
ret = subprocess.run(f'dfu-util -l', ret = subprocess.run(f'dfu-util -l',
shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
@@ -172,7 +201,7 @@ def test_dfu(id):
def test_dfu_runtime(id): def test_dfu_runtime(id):
# Wait device enum # Wait device enum
timeout = 10 timeout = ENUM_TIMEOUT
while timeout: while timeout:
ret = subprocess.run(f'dfu-util -l', ret = subprocess.run(f'dfu-util -l',
shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
@@ -186,11 +215,11 @@ def test_dfu_runtime(id):
def test_hid_boot_interface(id): def test_hid_boot_interface(id):
kbd = get_hid_dev(id, 'TinyUSB_Device', 'event-kbd') kbd = get_hid_dev(id, 'TinyUSB', 'TinyUSB_Device', 'event-kbd')
mouse1 = get_hid_dev(id, 'TinyUSB_Device', 'if01-event-mouse') mouse1 = get_hid_dev(id, 'TinyUSB', 'TinyUSB_Device', 'if01-event-mouse')
mouse2 = get_hid_dev(id, 'TinyUSB_Device', 'if01-mouse') mouse2 = get_hid_dev(id, 'TinyUSB', 'TinyUSB_Device', 'if01-mouse')
# Wait device enum # Wait device enum
timeout = 10 timeout = ENUM_TIMEOUT
while timeout: while timeout:
if os.path.exists(kbd) and os.path.exists(mouse1) and os.path.exists(mouse2): if os.path.exists(kbd) and os.path.exists(mouse1) and os.path.exists(mouse2):
break break
@@ -211,11 +240,13 @@ if __name__ == '__main__':
# all possible tests, board_test is last to disable board's usb # all possible tests, board_test is last to disable board's usb
all_tests = [ all_tests = [
'cdc_dual_ports', 'cdc_msc', 'dfu', 'dfu_runtime', 'hid_boot_interface', 'board_test' 'cdc_dual_ports', 'cdc_msc', 'dfu', 'dfu_runtime', 'hid_boot_interface',
'board_test'
] ]
for board in config['boards']: for board in config['boards']:
print(f'Testing board:{board["name"]}') print(f'Testing board:{board["name"]}')
debugger = board['debugger'].lower()
# default to all tests # default to all tests
if 'tests' in board: if 'tests' in board:
@@ -230,18 +261,27 @@ if __name__ == '__main__':
test_list.remove(skip) test_list.remove(skip)
for test in test_list: for test in test_list:
mk_elf = f'examples/device/{test}/_build/{board["name"]}/{test}.elf' # cmake, make, download from artifacts
cmake_elf = f'cmake-build/cmake-build-{board["name"]}/device/{test}/{test}.elf' elf_list = [
if os.path.isfile(cmake_elf): f'cmake-build/cmake-build-{board["name"]}/device/{test}/{test}.elf',
elf = cmake_elf f'examples/device/{test}/_build/{board["name"]}/{test}.elf',
elif os.path.isfile(mk_elf): f'{test}.elf'
elf = mk_elf ]
else:
elf = None
for e in elf_list:
if os.path.isfile(e):
elf = e
break
if elf is None:
print(f'Cannot find firmware file for {test}') print(f'Cannot find firmware file for {test}')
sys.exit(-1) sys.exit(-1)
if board['debugger'].lower() == 'jlink': if debugger == 'jlink':
flash_jlink(board['debugger_sn'], board['cpu'], elf) flash_jlink(board['debugger_sn'], board['cpu'], elf)
elif debugger == 'openocd':
flash_openocd(board['debugger_sn'], board['debugger_args'], elf)
else: else:
# ToDo # ToDo
pass pass

View File

@@ -11,3 +11,4 @@ attch
endianess endianess
pris pris
busses busses
ser