From 53ccfa30614a3a9eeddb27f5a1b70f02c1ef62a6 Mon Sep 17 00:00:00 2001 From: Carlos Sanchez Date: Mon, 19 Jan 2026 18:23:54 +0100 Subject: [PATCH 1/4] test: new prototypical semi-automatic test. --- CMakeLists.txt | 35 +++++++++++ src/usbd_gs_can.c | 8 +++ test/README.md | 55 +++++++++++++++++ test/test.sh | 151 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 249 insertions(+) create mode 100644 test/README.md create mode 100755 test/test.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index b87f1c4d..99a05f60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -225,6 +225,15 @@ function(add_f042_target TGTNAME) target_sources(${TGTNAME}_fw PRIVATE "src/device/device_f0.c") target_link_options(${TGTNAME}_fw BEFORE PRIVATE ${CPUFLAGS_F0}) target_link_libraries(${TGTNAME}_fw PRIVATE STM32_HAL_STM32F042x6 STM32_USB_Device_Library_STM32F042x6) + + add_target_common(${TGTNAME}_test STM32F042X6) + target_compile_options(${TGTNAME}_test_fw BEFORE PRIVATE ${CPUFLAGS_F0}) + target_compile_definitions(${TGTNAME}_test_fw PRIVATE BOARD_${TGTNAME} STM32F0 USBD_TEST) + target_sources(${TGTNAME}_test_fw PRIVATE "src/can/bxcan.c") + target_sources(${TGTNAME}_test_fw PRIVATE "src/device/device_f0.c") + target_link_options(${TGTNAME}_test_fw BEFORE PRIVATE ${CPUFLAGS_F0}) + target_link_libraries(${TGTNAME}_test_fw PRIVATE STM32_HAL_STM32F042x6 STM32_USB_Device_Library_STM32F042x6) + set_target_properties(${TGTNAME}_test_fw PROPERTIES EXCLUDE_FROM_ALL TRUE) endfunction() function(add_f072_target TGTNAME) @@ -235,6 +244,15 @@ function(add_f072_target TGTNAME) target_sources(${TGTNAME}_fw PRIVATE "src/device/device_f0.c") target_link_options(${TGTNAME}_fw BEFORE PRIVATE ${CPUFLAGS_F0}) target_link_libraries(${TGTNAME}_fw PRIVATE STM32_HAL_STM32F072xB STM32_USB_Device_Library_STM32F072xB) + + add_target_common(${TGTNAME}_test STM32F072XB) + target_compile_options(${TGTNAME}_test_fw BEFORE PRIVATE ${CPUFLAGS_F0}) + target_compile_definitions(${TGTNAME}_test_fw PRIVATE BOARD_${TGTNAME} STM32F0 USBD_TEST) + target_sources(${TGTNAME}_test_fw PRIVATE "src/can/bxcan.c") + target_sources(${TGTNAME}_test_fw PRIVATE "src/device/device_f0.c") + target_link_options(${TGTNAME}_test_fw BEFORE PRIVATE ${CPUFLAGS_F0}) + target_link_libraries(${TGTNAME}_test_fw PRIVATE STM32_HAL_STM32F072xB STM32_USB_Device_Library_STM32F072xB) + set_target_properties(${TGTNAME}_test_fw PROPERTIES EXCLUDE_FROM_ALL TRUE) endfunction() function(add_f407_target TGTNAME) @@ -245,6 +263,15 @@ function(add_f407_target TGTNAME) target_sources(${TGTNAME}_fw PRIVATE "src/device/device_f4.c") target_link_options(${TGTNAME}_fw BEFORE PRIVATE ${CPUFLAGS_F4}) target_link_libraries(${TGTNAME}_fw PRIVATE STM32_HAL_STM32F407xE STM32_USB_Device_Library_STM32F407xE) + + add_target_common(${TGTNAME}_test STM32F407XE) + target_compile_definitions(${TGTNAME}_test_fw PRIVATE BOARD_${TGTNAME} STM32F4 USBD_TEST) + target_compile_options(${TGTNAME}_test_fw BEFORE PRIVATE ${CPUFLAGS_F4}) + target_sources(${TGTNAME}_test_fw PRIVATE "src/can/bxcan.c") + target_sources(${TGTNAME}_test_fw PRIVATE "src/device/device_f4.c") + target_link_options(${TGTNAME}_test_fw BEFORE PRIVATE ${CPUFLAGS_F4}) + target_link_libraries(${TGTNAME}_test_fw PRIVATE STM32_HAL_STM32F407xE STM32_USB_Device_Library_STM32F407xE) + set_target_properties(${TGTNAME}_test_fw PROPERTIES EXCLUDE_FROM_ALL TRUE) endfunction() function(add_g0b1_target TGTNAME) @@ -254,6 +281,14 @@ function(add_g0b1_target TGTNAME) target_sources(${TGTNAME}_fw PRIVATE "src/device/device_g0.c") target_link_options(${TGTNAME}_fw BEFORE PRIVATE ${CPUFLAGS_G0}) target_link_libraries(${TGTNAME}_fw PRIVATE STM32_HAL_STM32G0B1xx STM32_USB_Device_Library_STM32G0B1xx) + + add_target_common(${TGTNAME}_test STM32G0B1xx) + target_compile_definitions(${TGTNAME}_test_fw PRIVATE BOARD_${TGTNAME} STM32G0 USBD_TEST) + target_compile_options(${TGTNAME}_test_fw BEFORE PRIVATE ${CPUFLAGS_G0}) + target_sources(${TGTNAME}_test_fw PRIVATE "src/device/device_g0.c") + target_link_options(${TGTNAME}_test_fw BEFORE PRIVATE ${CPUFLAGS_G0}) + target_link_libraries(${TGTNAME}_test_fw PRIVATE STM32_HAL_STM32G0B1xx STM32_USB_Device_Library_STM32G0B1xx) + set_target_properties(${TGTNAME}_test_fw PROPERTIES EXCLUDE_FROM_ALL TRUE) endfunction() ########## generate list of targets. diff --git a/src/usbd_gs_can.c b/src/usbd_gs_can.c index 99f8d922..10fa6cd9 100644 --- a/src/usbd_gs_can.c +++ b/src/usbd_gs_can.c @@ -836,8 +836,16 @@ static uint8_t USBD_GS_CAN_SendFrame(USBD_HandleTypeDef *pdev, struct gs_host_fr return USBD_GS_CAN_Transmit(pdev, send_addr, len); } +#ifdef USBD_TEST +volatile int skip_send = 0; +#endif void USBD_GS_CAN_SendToHost(USBD_HandleTypeDef *pdev) { +#ifdef USBD_TEST + if (skip_send) + return; +#endif + USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData; bool was_irq_enabled = disable_irq(); diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000..6ee3c43f --- /dev/null +++ b/test/README.md @@ -0,0 +1,55 @@ +# candleLight_gsusb tests # + +The script on this directory performs a limited test for +device functionality. It is meant to be run with a device +under test (DUT) connected thought USB and JTAG, and +another CAN interface (AUX) used to send stimulus to the +DUT through the CAN bus. Therefore, DUT and AUX must both +* be visible in as system interfaces +* be connected ot the same CAN bus + +The test is sligtly intrusive, because it temporarily +suspends the FW capability of sending frames to the host +thorugh USB. This is done by building a special "test" +version of the FW which includes some extra code +instrumentation. + +## Running the test ## + +1. Prepare your HW setup. + * DUT and AUX connected to the same CAN bus + * DUT connected to the host by JTAG + * GDB server (openocd, most likely) running + +2. Start by building the special test version of the + code + + build$ make FYSETC_UCAN_test_fw + +3. Launch the test; with default parameters, it will + flash the device and take care of everything: + + build$ ../test/test.sh FYSETC_UCAN can1 can0 + Free buffers idle=62, after=62 + +4. For better coverage, launch the test in a loop using the shell: + + build$ i=0; while true; do i=$((i+1)); echo -n "Iteration $i: "; ../test/test.sh -r FYSETC_UCAN can1 can0 || break; done + Iteration 1: Free buffers idle=62, after=62 + Iteration 2: Free buffers idle=62, after=62 + Iteration 3: Free buffers idle=62, after=62 + Iteration 4: Free buffers idle=62, after=62 + Iteration 5: Free buffers idle=62, after=62 + Iteration 6: Free buffers idle=62, after=62 + Iteration 7: Free buffers idle=62, after=62 + [...] + +## Caveats ## + +* Interrupting the test might leave some background process running. + Make sure you kill them. When the test suceeds, it exits cleanly + and all the subprocesses are taken care of. + +* Checking the diff between the send and received frames fails + sometimes. This is why this check is not done by default + (it can be activated with `-d` parameter). diff --git a/test/test.sh b/test/test.sh new file mode 100755 index 00000000..61bd32c0 --- /dev/null +++ b/test/test.sh @@ -0,0 +1,151 @@ +#!/bin/sh + +GDB=arm-none-eabi-gdb + +exit_help() { + cat<$gdb_in <$gdb_in <$gdb_in <$tmpdir/get_free_buffers & + cat >$gdb_in <$gdb_in $binary >>$gdb_out 2>&1 & +gdb_pid=$! + +cat >$gdb_in <next + end + p \$count +end +target extended-remote $dut_gdb_remote +EOF + +test -z "$lflag" && echo "load" >$gdb_in +if [ -z "$rflag" ] +then + echo "run" >$gdb_in + sleep 10 +else + echo "continue" >$gdb_in +fi + +sudo ip link set $interface_aux down +sudo ip link set $interface_aux type can bitrate 500000 +sudo ip link set $interface_aux up + +sudo ip link set $interface_dut down +sudo ip link set $interface_dut type can bitrate 500000 +sudo ip link set $interface_dut up + +msgbuf_count_idle=`get_free_buffers` + +candump $interface_dut >>$candump_dut & +candump_dut_pid=$! +candump $interface_aux >>$candump_aux & +candump_aux_pid=$! + +set_skip_send 1 +for i in `seq 1 $msgbuf_count_idle`; do cansend $interface_aux 001#`printf %02x $i`; done 2> $tmpdir/cansend_aux.err +for i in `seq 1 $msgbuf_count_idle`; do cansend $interface_dut 000#`printf %02x $i`; done 2> $tmpdir/cansend_dut.err +set_skip_send 0 + +sleep 2 + +msgbuf_count_after=`get_free_buffers` +printf "Free buffers idle=%d, after=%d\n" $msgbuf_count_idle $msgbuf_count_after +test $msgbuf_count_idle -eq $msgbuf_count_after || exit 1 + +kill $candump_dut_pid $candump_aux_pid +kill -INT $gdb_pid +echo "quit" > $gdb_in +wait + +awk '{$1=""; print $0}' $candump_dut >> $tmpdir/frames_dut +awk '{$1=""; print $0}' $candump_aux >> $tmpdir/frames_aux +test -z "$dflag" || diff $tmpdir/frames_aux $tmpdir/frames_dut || exit 1 + +rm -rf $tmpdir From 90537520d0ba92eb5ab221feaee6653e51d1b142 Mon Sep 17 00:00:00 2001 From: Carlos Sanchez Date: Wed, 21 Jan 2026 13:09:14 +0100 Subject: [PATCH 2/4] test: avoid conditional compilation. --- src/usbd_gs_can.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/usbd_gs_can.c b/src/usbd_gs_can.c index 10fa6cd9..320e08db 100644 --- a/src/usbd_gs_can.c +++ b/src/usbd_gs_can.c @@ -836,15 +836,12 @@ static uint8_t USBD_GS_CAN_SendFrame(USBD_HandleTypeDef *pdev, struct gs_host_fr return USBD_GS_CAN_Transmit(pdev, send_addr, len); } -#ifdef USBD_TEST -volatile int skip_send = 0; -#endif +static volatile int skip_send; + void USBD_GS_CAN_SendToHost(USBD_HandleTypeDef *pdev) { -#ifdef USBD_TEST - if (skip_send) + if (IS_ENABLED(USBD_TEST) && skip_send) return; -#endif USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData; From f292516dcf0f759577bb7861490c98d21d7c6b76 Mon Sep 17 00:00:00 2001 From: Carlos Sanchez Date: Wed, 21 Jan 2026 13:11:01 +0100 Subject: [PATCH 3/4] test: exit cleanly when interrupted + doc fixes. --- test/README.md | 20 +++++++-------- test/test.sh | 69 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/test/README.md b/test/README.md index 6ee3c43f..3e55bec7 100644 --- a/test/README.md +++ b/test/README.md @@ -2,7 +2,7 @@ The script on this directory performs a limited test for device functionality. It is meant to be run with a device -under test (DUT) connected thought USB and JTAG, and +under test (DUT) connected thought USB and JTAG/SWD, and another CAN interface (AUX) used to send stimulus to the DUT through the CAN bus. Therefore, DUT and AUX must both * be visible in as system interfaces @@ -10,19 +10,19 @@ DUT through the CAN bus. Therefore, DUT and AUX must both The test is sligtly intrusive, because it temporarily suspends the FW capability of sending frames to the host -thorugh USB. This is done by building a special "test" +through USB. This is done by building a special "test" version of the FW which includes some extra code instrumentation. ## Running the test ## -1. Prepare your HW setup. +1. Prepare your HW setup: * DUT and AUX connected to the same CAN bus - * DUT connected to the host by JTAG + * DUT connected to the host by JTAG/SWD * GDB server (openocd, most likely) running 2. Start by building the special test version of the - code + code (append `_test_fw` suffix to your usual target name): build$ make FYSETC_UCAN_test_fw @@ -32,9 +32,11 @@ instrumentation. build$ ../test/test.sh FYSETC_UCAN can1 can0 Free buffers idle=62, after=62 -4. For better coverage, launch the test in a loop using the shell: +4. For better coverage, launch the test in a loop using the shell. + Consider using `timeout` as some error modes might make the script + wait forever: - build$ i=0; while true; do i=$((i+1)); echo -n "Iteration $i: "; ../test/test.sh -r FYSETC_UCAN can1 can0 || break; done + build$ i=0; while true; do i=$((i+1)); echo -n "Iteration $i: "; timeout 20 ../test/test.sh -r FYSETC_UCAN can1 can0 || break; done Iteration 1: Free buffers idle=62, after=62 Iteration 2: Free buffers idle=62, after=62 Iteration 3: Free buffers idle=62, after=62 @@ -46,10 +48,6 @@ instrumentation. ## Caveats ## -* Interrupting the test might leave some background process running. - Make sure you kill them. When the test suceeds, it exits cleanly - and all the subprocesses are taken care of. - * Checking the diff between the send and received frames fails sometimes. This is why this check is not done by default (it can be activated with `-d` parameter). diff --git a/test/test.sh b/test/test.sh index 61bd32c0..fc97ef72 100755 --- a/test/test.sh +++ b/test/test.sh @@ -1,7 +1,10 @@ #!/bin/sh +BITRATE=1000000 GDB=arm-none-eabi-gdb +trap cleanup EXIT INT TERM + exit_help() { cat<$gdb_in <$tmpdir/get_free_buffers & cat >$gdb_in </dev/null + kill "$candump_dut_pid" 2>/dev/null + kill "$candump_aux_pid" 2>/dev/null + if [ -n "$gdb_pid" ] + then + kill -INT $gdb_pid + echo "quit" > $gdb_in + fi + if $keep_tmpdir + then + printf "Keeping temporary directory in %s\n" $tmpdir + else + rm -rf $tmpdit + fi + trap - EXIT + exit } +keep_tmpdir=false + dut_gdb_remote=:3333 dflag= lflag= @@ -63,15 +88,11 @@ do l) lflag=1;; r) rflag=1; lflag=1;; h) exit_help;; - ?) exit_error;; + ?) exit_arguments;; esac done shift $(($OPTIND - 1)) -if [ $# -lt 3 ] -then - printf "Not enough arguements.\n" - exit_error -fi +test $# -lt 3 && exit_arguments "Not enough arguements.\n" binary=$1_test_fw interface_dut=$2 @@ -88,6 +109,14 @@ candump_aux=$tmpdir/candump_aux mkfifo $gdb_in +if ! command -v $GDB >/dev/null +then + printf "%s not found\n" $GDB + exit 2 +fi + +keep_tmpdir=true + $GDB <>$gdb_in $binary >>$gdb_out 2>&1 & gdb_pid=$! @@ -114,14 +143,15 @@ else fi sudo ip link set $interface_aux down -sudo ip link set $interface_aux type can bitrate 500000 +sudo ip link set $interface_aux type can bitrate $BITRATE sudo ip link set $interface_aux up sudo ip link set $interface_dut down -sudo ip link set $interface_dut type can bitrate 500000 +sudo ip link set $interface_dut type can bitrate $BITRATE sudo ip link set $interface_dut up -msgbuf_count_idle=`get_free_buffers` +get_free_buffers +msgbuf_count_idle=$free_buffers candump $interface_dut >>$candump_dut & candump_dut_pid=$! @@ -135,17 +165,14 @@ set_skip_send 0 sleep 2 -msgbuf_count_after=`get_free_buffers` +get_free_buffers +msgbuf_count_after=$free_buffers + printf "Free buffers idle=%d, after=%d\n" $msgbuf_count_idle $msgbuf_count_after test $msgbuf_count_idle -eq $msgbuf_count_after || exit 1 -kill $candump_dut_pid $candump_aux_pid -kill -INT $gdb_pid -echo "quit" > $gdb_in -wait - awk '{$1=""; print $0}' $candump_dut >> $tmpdir/frames_dut awk '{$1=""; print $0}' $candump_aux >> $tmpdir/frames_aux test -z "$dflag" || diff $tmpdir/frames_aux $tmpdir/frames_dut || exit 1 -rm -rf $tmpdir +keep_tmpdir=false From 3d607a58850e4502b9bebd49b1a1d99cd30f8db5 Mon Sep 17 00:00:00 2001 From: Carlos Sanchez Date: Wed, 21 Jan 2026 13:23:52 +0100 Subject: [PATCH 4/4] test: allow selecting CAN bitrate. --- test/test.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/test.sh b/test/test.sh index fc97ef72..6aa2bfce 100755 --- a/test/test.sh +++ b/test/test.sh @@ -1,6 +1,5 @@ #!/bin/sh -BITRATE=1000000 GDB=arm-none-eabi-gdb trap cleanup EXIT INT TERM @@ -8,6 +7,7 @@ trap cleanup EXIT INT TERM exit_help() { cat<