Skip to content

Commit 9c9ebb3

Browse files
authored
Merge pull request #2355 from hathach/update-hil-rp2040
Update hil test for rp2040 on self-hosted PI4
2 parents 390c109 + b8d1acd commit 9c9ebb3

File tree

6 files changed

+139
-97
lines changed

6 files changed

+139
-97
lines changed

.github/workflows/cmake_arm.yml

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,14 @@ jobs:
105105
106106
# ---------------------------------------
107107
# Hardware in the loop (HIL)
108-
# Current self-hosted instance is running on an RPI4 with
109-
# - pico + pico-probe connected via USB
110-
# - pico-probe is /dev/ttyACM0
108+
# Current self-hosted instance is running on an RPI4.
109+
# For attached hardware checkout hil_pi4.json
111110
# ---------------------------------------
112111
hw-rp2040-test:
113112
# run only with hathach's commit due to limited resource on RPI4
114113
if: github.repository_owner == 'hathach'
115114
needs: build-arm
116-
runs-on: [self-hosted, Linux, ARM64, rp2040]
115+
runs-on: [self-hosted, rp2040, hardware-in-the-loop]
117116

118117
steps:
119118
- name: Clean workspace
@@ -122,43 +121,16 @@ jobs:
122121
rm -rf "${{ github.workspace }}"
123122
mkdir -p "${{ github.workspace }}"
124123
124+
- name: Checkout test/hil
125+
uses: actions/checkout@v3
126+
with:
127+
sparse-checkout: test/hil
128+
125129
- name: Download rp2040 Artifacts
126130
uses: actions/download-artifact@v3
127131
with:
128132
name: rp2040
129133

130-
- name: Create flash.sh
131-
run: |
132-
echo > flash.sh 'cmdout=$(openocd -f "interface/cmsis-dap.cfg" -f "target/rp2040.cfg" -c "adapter speed 5000" -c "program $1 reset exit")'
133-
echo >> flash.sh 'if (( $? )) ; then echo $cmdout ; fi'
134-
chmod +x flash.sh
135-
136-
- name: Test cdc_dual_ports
137-
run: |
138-
./flash.sh cdc_dual_ports.elf
139-
while (! ([ -e /dev/ttyACM1 ] && [ -e /dev/ttyACM2 ])) && [ $SECONDS -le 10 ]; do :; done
140-
test -e /dev/ttyACM1 && echo "ttyACM1 exists"
141-
test -e /dev/ttyACM2 && echo "ttyACM2 exists"
142-
143-
- name: Test cdc_msc
144-
run: |
145-
./flash.sh cdc_msc.elf
146-
readme='/media/pi/TinyUSB MSC/README.TXT'
147-
while (! ([ -e /dev/ttyACM1 ] && [ -f "$readme" ])) && [ $SECONDS -le 10 ]; do :; done
148-
test -e /dev/ttyACM1 && echo "ttyACM1 exists"
149-
test -f "$readme" && echo "$readme exists"
150-
cat "$readme"
151-
152-
- name: Test dfu
153-
run: |
154-
./flash.sh dfu.elf
155-
while (! (dfu-util -l | grep "Found DFU")) && [ $SECONDS -le 10 ]; do :; done
156-
dfu-util -d cafe -a 0 -U dfu0
157-
dfu-util -d cafe -a 1 -U dfu1
158-
grep "TinyUSB DFU! - Partition 0" dfu0
159-
grep "TinyUSB DFU! - Partition 1" dfu1
160-
161-
- name: Test dfu_runtime
134+
- name: Test on actual hardware (hardware in the loop)
162135
run: |
163-
./flash.sh dfu_runtime.elf
164-
while (! (dfu-util -l | grep "Found Runtime")) && [ $SECONDS -le 10 ]; do :; done
136+
python3 test/hil/hil_test.py hil_pi4.json

hw/bsp/family_support.cmake

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,22 @@ function(family_flash_stlink TARGET)
395395
endfunction()
396396

397397

398+
# Add flash openocd target
399+
function(family_flash_openocd TARGET CLI_OPTIONS)
400+
if (NOT DEFINED OPENOCD)
401+
set(OPENOCD openocd)
402+
endif ()
403+
404+
separate_arguments(CLI_OPTIONS_LIST UNIX_COMMAND ${CLI_OPTIONS})
405+
406+
# note skip verify since it has issue with rp2040
407+
add_custom_target(${TARGET}-openocd
408+
DEPENDS ${TARGET}
409+
COMMAND ${OPENOCD} ${CLI_OPTIONS_LIST} -c "program $<TARGET_FILE:${TARGET}> reset exit"
410+
VERBATIM
411+
)
412+
endfunction()
413+
398414
# Add flash pycod target
399415
function(family_flash_pyocd TARGET)
400416
if (NOT DEFINED PYOC)

hw/bsp/rp2040/family.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/pico_sdk_import.cmake)
1212
# include basic family CMake functionality
1313
set(FAMILY_MCUS RP2040)
1414
set(JLINK_DEVICE rp2040_m0_0)
15+
set(OPENOCD_OPTION "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\"")
1516

1617
include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
1718

@@ -158,6 +159,7 @@ function(family_configure_target TARGET RTOS)
158159
pico_enable_stdio_uart(${TARGET} 1)
159160
target_link_libraries(${TARGET} PUBLIC pico_stdlib pico_bootsel_via_double_reset tinyusb_board${RTOS_SUFFIX} tinyusb_additions)
160161

162+
family_flash_openocd(${TARGET} ${OPENOCD_OPTION})
161163
family_flash_jlink(${TARGET})
162164
endfunction()
163165

test/hil/hil_pi4.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"boards": [
3+
{
4+
"name": "raspberry_pi_pico",
5+
"uid": "E6614C311B764A37",
6+
"debugger": "openocd",
7+
"debugger_sn": "E6614103E72C1D2F",
8+
"debugger_args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\""
9+
}
10+
]
11+
}

test/hil/hil_test.py

Lines changed: 99 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,66 @@
3232
import subprocess
3333
import json
3434

35+
ENUM_TIMEOUT = 10
3536

36-
def get_serial_dev(id, product, ifnum):
37+
38+
def get_serial_dev(id, vendor_str, product_str, ifnum):
3739
# get usb serial by id
38-
return f'/dev/serial/by-id/usb-TinyUSB_{product}_{id}-if{ifnum:02d}'
40+
return f'/dev/serial/by-id/usb-{vendor_str}_{product_str}_{id}-if{ifnum:02d}'
3941

4042

41-
def get_disk_dev(id, lun):
43+
# Currently not used, left as reference
44+
def get_disk_dev(id, vendor_str, lun):
4245
# get usb disk by id
43-
return f'/dev/disk/by-id/usb-TinyUSB_Mass_Storage_{id}-0:{lun}'
46+
return f'/dev/disk/by-id/usb-{vendor_str}_Mass_Storage_{id}-0:{lun}'
47+
48+
49+
def get_hid_dev(id, vendor_str, product_str, event):
50+
return f'/dev/input/by-id/usb-{vendor_str}_{product_str}_{id}-{event}'
51+
52+
53+
def open_serial_dev(port):
54+
timeout = ENUM_TIMEOUT
55+
ser = None
56+
while timeout:
57+
if os.path.exists(port):
58+
try:
59+
# slight delay since kernel may occupy the port briefly
60+
time.sleep(0.2)
61+
ser = serial.Serial(port, timeout=1)
62+
break
63+
except serial.SerialException:
64+
pass
65+
time.sleep(0.8)
66+
timeout = timeout - 1
67+
assert timeout, 'Device not available or Cannot open port'
68+
return ser
69+
70+
71+
def read_disk_file(id, fname):
72+
# on different self-hosted, the mount point is different
73+
file_list = [
74+
f'/media/blkUSB_{id[-8:]}.02/{fname}',
75+
f'/media/{os.getenv("USER")}/TinyUSB MSC/{fname}'
76+
]
77+
timeout = ENUM_TIMEOUT
78+
while timeout:
79+
for file in file_list:
80+
if os.path.isfile(file):
81+
with open(file, 'rb') as f:
82+
data = f.read()
83+
return data
4484

85+
time.sleep(1)
86+
timeout = timeout - 1
4587

46-
def get_hid_dev(id, product, event):
47-
return f'/dev/input/by-id/usb-TinyUSB_{product}_{id}-{event}'
88+
assert timeout, 'Device not available'
89+
return None
4890

4991

92+
# -------------------------------------------------------------
93+
# Flash with debugger
94+
# -------------------------------------------------------------
5095
def flash_jlink(sn, dev, firmware):
5196
script = ['halt', 'r', f'loadfile {firmware}', 'r', 'go', 'exit']
5297
f = open('flash.jlink', 'w')
@@ -59,31 +104,29 @@ def flash_jlink(sn, dev, firmware):
59104
assert ret.returncode == 0, 'Flash failed\n' + stdout
60105

61106

107+
def flash_openocd(sn, args, firmware):
108+
ret = subprocess.run(f'openocd {args} -c "program {firmware} reset exit"',
109+
shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
110+
stdout = ret.stdout.decode()
111+
assert ret.returncode == 0, 'Flash failed\n' + stdout
112+
113+
114+
# -------------------------------------------------------------
115+
# Tests
116+
# -------------------------------------------------------------
62117
def test_board_test(id):
63118
# Dummy test
64119
pass
65120

66-
def test_cdc_dual_ports(id):
67-
port1 = get_serial_dev(id, "TinyUSB_Device", 0)
68-
port2 = get_serial_dev(id, "TinyUSB_Device", 2)
69121

70-
# Wait device enum
71-
timeout = 10
72-
while timeout:
73-
if os.path.exists(port1) and os.path.exists(port2):
74-
break
75-
time.sleep(1)
76-
timeout = timeout - 1
122+
def test_cdc_dual_ports(id):
123+
port1 = get_serial_dev(id, 'TinyUSB', "TinyUSB_Device", 0)
124+
port2 = get_serial_dev(id, 'TinyUSB', "TinyUSB_Device", 2)
77125

78-
assert timeout, 'Device not available'
126+
ser1 = open_serial_dev(port1)
127+
ser2 = open_serial_dev(port2)
79128

80129
# Echo test
81-
ser1 = serial.Serial(port1)
82-
ser2 = serial.Serial(port2)
83-
84-
ser1.timeout = 1
85-
ser2.timeout = 1
86-
87130
str1 = b"test_no1"
88131
ser1.write(str1)
89132
ser1.flush()
@@ -98,42 +141,28 @@ def test_cdc_dual_ports(id):
98141

99142

100143
def test_cdc_msc(id):
101-
port = get_serial_dev(id, "TinyUSB_Device", 0)
102-
file = f'/media/blkUSB_{id[-8:]}.02/README.TXT'
103-
# Wait device enum
104-
timeout = 10
105-
while timeout:
106-
if os.path.exists(port) and os.path.isfile(file):
107-
break
108-
time.sleep(1)
109-
timeout = timeout - 1
110-
111-
assert timeout, 'Device not available'
112-
113144
# Echo test
114-
ser1 = serial.Serial(port)
115-
116-
ser1.timeout = 1
145+
port = get_serial_dev(id, 'TinyUSB', "TinyUSB_Device", 0)
146+
ser = open_serial_dev(port)
117147

118148
str = b"test_str"
119-
ser1.write(str)
120-
ser1.flush()
121-
assert ser1.read(100) == str, 'CDC wrong data'
149+
ser.write(str)
150+
ser.flush()
151+
assert ser.read(100) == str, 'CDC wrong data'
122152

123153
# Block test
124-
f = open(file, 'rb')
125-
data = f.read()
126-
154+
data = read_disk_file(id, 'README.TXT')
127155
readme = \
128156
b"This is tinyusb's MassStorage Class demo.\r\n\r\n\
129157
If you find any bugs or get any questions, feel free to file an\r\n\
130158
issue at github.com/hathach/tinyusb"
131159

132160
assert data == readme, 'MSC wrong data'
133161

162+
134163
def test_dfu(id):
135164
# Wait device enum
136-
timeout = 10
165+
timeout = ENUM_TIMEOUT
137166
while timeout:
138167
ret = subprocess.run(f'dfu-util -l',
139168
shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
@@ -172,7 +201,7 @@ def test_dfu(id):
172201

173202
def test_dfu_runtime(id):
174203
# Wait device enum
175-
timeout = 10
204+
timeout = ENUM_TIMEOUT
176205
while timeout:
177206
ret = subprocess.run(f'dfu-util -l',
178207
shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
@@ -186,11 +215,11 @@ def test_dfu_runtime(id):
186215

187216

188217
def test_hid_boot_interface(id):
189-
kbd = get_hid_dev(id, 'TinyUSB_Device', 'event-kbd')
190-
mouse1 = get_hid_dev(id, 'TinyUSB_Device', 'if01-event-mouse')
191-
mouse2 = get_hid_dev(id, 'TinyUSB_Device', 'if01-mouse')
218+
kbd = get_hid_dev(id, 'TinyUSB', 'TinyUSB_Device', 'event-kbd')
219+
mouse1 = get_hid_dev(id, 'TinyUSB', 'TinyUSB_Device', 'if01-event-mouse')
220+
mouse2 = get_hid_dev(id, 'TinyUSB', 'TinyUSB_Device', 'if01-mouse')
192221
# Wait device enum
193-
timeout = 10
222+
timeout = ENUM_TIMEOUT
194223
while timeout:
195224
if os.path.exists(kbd) and os.path.exists(mouse1) and os.path.exists(mouse2):
196225
break
@@ -211,11 +240,13 @@ def test_hid_boot_interface(id):
211240

212241
# all possible tests, board_test is last to disable board's usb
213242
all_tests = [
214-
'cdc_dual_ports', 'cdc_msc', 'dfu', 'dfu_runtime', 'hid_boot_interface', 'board_test'
243+
'cdc_dual_ports', 'cdc_msc', 'dfu', 'dfu_runtime', 'hid_boot_interface',
244+
'board_test'
215245
]
216246

217247
for board in config['boards']:
218248
print(f'Testing board:{board["name"]}')
249+
debugger = board['debugger'].lower()
219250

220251
# default to all tests
221252
if 'tests' in board:
@@ -230,18 +261,27 @@ def test_hid_boot_interface(id):
230261
test_list.remove(skip)
231262

232263
for test in test_list:
233-
mk_elf = f'examples/device/{test}/_build/{board["name"]}/{test}.elf'
234-
cmake_elf = f'cmake-build/cmake-build-{board["name"]}/device/{test}/{test}.elf'
235-
if os.path.isfile(cmake_elf):
236-
elf = cmake_elf
237-
elif os.path.isfile(mk_elf):
238-
elf = mk_elf
239-
else:
264+
# cmake, make, download from artifacts
265+
elf_list = [
266+
f'cmake-build/cmake-build-{board["name"]}/device/{test}/{test}.elf',
267+
f'examples/device/{test}/_build/{board["name"]}/{test}.elf',
268+
f'{test}.elf'
269+
]
270+
271+
elf = None
272+
for e in elf_list:
273+
if os.path.isfile(e):
274+
elf = e
275+
break
276+
277+
if elf is None:
240278
print(f'Cannot find firmware file for {test}')
241279
sys.exit(-1)
242280

243-
if board['debugger'].lower() == 'jlink':
281+
if debugger == 'jlink':
244282
flash_jlink(board['debugger_sn'], board['cpu'], elf)
283+
elif debugger == 'openocd':
284+
flash_openocd(board['debugger_sn'], board['debugger_args'], elf)
245285
else:
246286
# ToDo
247287
pass

tools/codespell/ignore-words.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ attch
1111
endianess
1212
pris
1313
busses
14+
ser

0 commit comments

Comments
 (0)