Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions src/usbd_gs_can.c
Original file line number Diff line number Diff line change
Expand Up @@ -836,8 +836,13 @@ static uint8_t USBD_GS_CAN_SendFrame(USBD_HandleTypeDef *pdev, struct gs_host_fr
return USBD_GS_CAN_Transmit(pdev, send_addr, len);
}

static volatile int skip_send;

void USBD_GS_CAN_SendToHost(USBD_HandleTypeDef *pdev)
{
if (IS_ENABLED(USBD_TEST) && skip_send)
return;

USBD_GS_CAN_HandleTypeDef *hcan = (USBD_GS_CAN_HandleTypeDef*)pdev->pClassData;

bool was_irq_enabled = disable_irq();
Expand Down
53 changes: 53 additions & 0 deletions test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 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/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
* 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
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:
* DUT and AUX connected to the same CAN bus
* 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 (append `_test_fw` suffix to your usual target name):

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.
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: "; 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
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 ##

* 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).
180 changes: 180 additions & 0 deletions test/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#!/bin/sh

GDB=arm-none-eabi-gdb

trap cleanup EXIT INT TERM

exit_help() {
cat<<EOF
Usage: $0 [OPTION] BOARD INTERFACE_DUT INTERFACE_AUX
-b CAN bitrate [500000]
-d check diff between sent/received frames
-g GDB remote address [:3333]
-l skip program loading
-r skip device reset (implies -l)
-h display this help and exit
EOF
exit 0
}

exit_arguments() {
printf "$@"
printf "Try '%s -h' for more information.\n" $0
exit 2
}

set_skip_send() {
kill -INT $gdb_pid
tail -f -n 0 $gdb_out | grep -q led_update &
cat >$gdb_in <<EOF
tbreak led_update
continue
EOF
wait $! || exit 1
cat >$gdb_in <<EOF
set skip_send=$1
continue
EOF
}

get_free_buffers() {
kill -INT $gdb_pid
tail -f -n 0 $gdb_out | grep -q led_update &
cat >$gdb_in <<EOF
tbreak led_update
continue
EOF
wait $! || exit 1
sleep 1
tail -f -n 0 $gdb_out | head -1 >$tmpdir/get_free_buffers &
cat >$gdb_in <<EOF
count_nexts hGS_CAN.list_frame_pool
continue
EOF
wait $! || exit 1
free_buffers=`sed 's/$[[:digit:]]* = //' $tmpdir/get_free_buffers`
}

cleanup() {
kill "$!" 2>/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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tmpdir

fi
trap - EXIT
exit
}

keep_tmpdir=false

bitrate=500000
dut_gdb_remote=:3333
dflag=
lflag=
rflag=
while getopts b:dg:lrh name
do
case $name in
b) bitrate=$OPTARG;;
d) dflag=1;;
g) dut_gdb_remote=$OPTARG;;
l) lflag=1;;
r) rflag=1; lflag=1;;
h) exit_help;;
?) exit_arguments;;
esac
done
shift $(($OPTIND - 1))
test $# -lt 3 && exit_arguments "Not enough arguements.\n"

binary=$1_test_fw
interface_dut=$2
interface_aux=$3

tmpdir=/tmp/`basename $0`.$PPID
rm -rf $tmpdir
mkdir -p $tmpdir

gdb_in=$tmpdir/gdb_in
gdb_out=$tmpdir/gdb_out
candump_dut=$tmpdir/candump_dut
candump_aux=$tmpdir/candump_aux

mkfifo $gdb_in
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a corresponding close/delete for this fifo ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is created inside $tmpdir which is removed at the end of the script, so no need for explicit removal (same as any other file).
One problem of the script as it is is when it timeouts (which is actually a common failure mode, as the DUT HardFaults) it hangs and when interrupting it it leaves a lot of slate subprocess and $tmpdir is silently kept. I am updating the script so it includes a timeout, removes subprocesses, and chimes about keeping the $tmpdir for examination on failures.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw: you can use trap to automatically execute a bash function on a signal, i.e. exit of the script.

trap_exit_handler() {
    echo 'insert cleanup stuff here'
}

trap 'trap_exit_handler' 0 1 15

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I know, that is what I am doing. This script started as a quick & dirty thing to catch the bug, that is why the "failing" exit was not so taken care of.


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=$!

cat >$gdb_in <<EOF
define count_nexts
set \$count = 0
set \$i = \$arg0.next
while \$i != &(\$arg0)
set \$count = \$count + 1
set \$i = \$i->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 $bitrate
sudo ip link set $interface_aux up

sudo ip link set $interface_dut down
sudo ip link set $interface_dut type can bitrate $bitrate
sudo ip link set $interface_dut up

get_free_buffers
msgbuf_count_idle=$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

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

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

keep_tmpdir=false