Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ jobs:
runs-on: ubuntu-latest
container:
image: chocotechnologies/dmboot:1.0.0
# The network test connects the emulated Ethernet to a TAP interface
options: --cap-add=NET_ADMIN --device=/dev/net/tun

permissions:
contents: read
Expand All @@ -166,6 +168,9 @@ jobs:
git submodule update --recursive

- name: Run Renode emulation tests
env:
# Fail instead of skipping the network test if the TAP can't be opened
DMBOOT_TEST_NETWORK: "1"
run: |
./scripts/run_renode_tests.sh

Expand Down
4 changes: 4 additions & 0 deletions configs/board/stm32f746g-disco/board.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ set(TARGET "STM32F746xG" CACHE STRING "Target microcontroller" FORCE)
# virtual COM port) runs on, as named in the Renode platform - emulation mode
# exposes it as a TCP terminal (see DMBOOT_RENODE_UART_PORT)
set(DMBOOT_RENODE_CONSOLE_UART "sysbus.usart1" CACHE STRING "Renode peripheral of the console UART")

# Ethernet controller (eth0) as named in the Renode platform - emulation mode
# can connect it to a TAP interface on the host (see DMBOOT_RENODE_TAP)
set(DMBOOT_RENODE_ETHERNET "sysbus.ethernet" CACHE STRING "Renode peripheral of the Ethernet controller")
5 changes: 5 additions & 0 deletions configs/board/stm32f746g-disco/flash.dmd
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ dhcpc service=dhcp@.ini
dhcpc rules=dhcp.rules
test_dmeth
#
# Telnet: telnetd (dmtcp + dmtelnet) listens on TCP port 23; every connection
# becomes a tty node with its own console@ / dmell, like a UART does.
#
telnetd driver=telnetd.ini
#
# SD card: on-board microSD slot (CN3) on SDMMC1. dmdevfs creates the host
# node /dev/dmsdio0, the dmdevmon service (common flash.dmd) monitors the
# card detect pin and announces /dev/dmsdio0/0 when a card is inserted.
Expand Down
2 changes: 2 additions & 0 deletions configs/renode/renode.resc.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ machine StartGdbServer 3333

@DMBOOT_RENODE_UART_TERMINAL@

@DMBOOT_RENODE_NETWORK@

@DMBOOT_RENODE_START@
1 change: 1 addition & 0 deletions docs/cmake-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ These options are defined in the top-level `CMakeLists.txt` and control the over
| `DMBOOT_EMULATION` | BOOL | `OFF` | Enable Renode emulation mode instead of hardware mode (OpenOCD). |
| `DMBOOT_RENODE_WAIT_FOR_GDB` | BOOL | `OFF` | In emulation mode, keep the Renode machine paused until a GDB client (e.g. `monitor-gdb`) continues it, so no log output is produced before the monitor attaches. |
| `DMBOOT_RENODE_UART_PORT` | STRING | `3456` | In emulation mode, TCP port of the raw Renode terminal attached to the board's console UART (`DMBOOT_RENODE_CONSOLE_UART`, set by the board config, e.g. `sysbus.usart1` for `stm32f746g-disco`). Connect to it (e.g. `nc localhost 3456`) to use the shell on that UART. Empty disables it. |
| `DMBOOT_RENODE_TAP` | STRING | *(empty)* | In emulation mode, host TAP interface (e.g. `tap0`) that the board's Ethernet controller (`DMBOOT_RENODE_ETHERNET`, set by the board config, e.g. `sysbus.ethernet` for `stm32f746g-disco`) is connected to through a Renode switch, so the host can reach the board over IP. Needs root and `/dev/net/tun`; empty leaves the Ethernet unconnected. |

## VFS Options

Expand Down
71 changes: 55 additions & 16 deletions scripts/run_renode_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
#
# This script reproduces the Renode CI tests locally.
# It builds the firmware with emulation mode, starts Renode, runs monitor-gdb
# to capture firmware logs, verifies the expected log messages, and then
# checks that the shell answers on the console UART.
# to capture firmware logs, verifies the expected log messages, checks that
# the shell answers on the console UART and - when it can open a TAP
# interface - checks the network (DHCP, ping both ways, telnet).
#
# The network step needs root and /dev/net/tun (in Docker:
# --cap-add=NET_ADMIN --device=/dev/net/tun). Without them it is skipped,
# unless DMBOOT_TEST_NETWORK=1 makes it mandatory (as CI does).
#
# Usage:
# ./scripts/run_renode_tests.sh [SOURCE_DIR [BUILD_DIR]]
Expand All @@ -25,13 +30,29 @@ EXPECTED_LOGS="$SOURCE_DIR/configs/renode/expected_logs.txt"
VERIFY_SCRIPT="$SOURCE_DIR/scripts/verify_renode_logs.sh"
UART_TEST_SCRIPT="$SOURCE_DIR/scripts/test_renode_uart.py"
UART_PORT=3456
NETWORK_TEST_SCRIPT="$SOURCE_DIR/scripts/test_renode_network.py"
TAP_INTERFACE=tap0

# Timeouts (seconds)
# Upper bound only: step 4 stops as soon as every expected line is captured
MONITOR_TIMEOUT=300
UART_TIMEOUT=90
# Renode has to outlive both the monitor and the UART test
CONNECT_TIMEOUT=$((MONITOR_TIMEOUT + UART_TIMEOUT + 60))
NETWORK_TIMEOUT=120
# Renode has to outlive the monitor and the UART and network tests
CONNECT_TIMEOUT=$((MONITOR_TIMEOUT + UART_TIMEOUT + NETWORK_TIMEOUT + 60))

# The network test needs a TAP interface: root and /dev/net/tun
NETWORK_TEST=0
if [ "$(id -u)" -eq 0 ] && [ -c /dev/net/tun ]; then
NETWORK_TEST=1
elif [ "${DMBOOT_TEST_NETWORK:-0}" = "1" ]; then
echo "✗ DMBOOT_TEST_NETWORK=1, but the network test needs root and /dev/net/tun"
exit 1
fi
RENODE_TAP=""
if [ "$NETWORK_TEST" -eq 1 ]; then
RENODE_TAP="$TAP_INTERFACE"
fi

echo "=============================================="
echo " dmod-boot Renode emulation tests"
Expand All @@ -44,12 +65,13 @@ echo ""
# -------------------------------------------------------
# Step 1 – Build firmware with emulation mode enabled
# -------------------------------------------------------
echo "[1/5] Building firmware with emulation mode enabled..."
echo "[1/6] Building firmware with emulation mode enabled..."
cmake -DCMAKE_BUILD_TYPE=Debug \
-DBOARD="$BOARD" \
-DDMBOOT_EMULATION=ON \
-DDMBOOT_RENODE_WAIT_FOR_GDB=ON \
-DDMBOOT_RENODE_UART_PORT="$UART_PORT" \
-DDMBOOT_RENODE_TAP="$RENODE_TAP" \
-S "$SOURCE_DIR" \
-B "$BUILD_DIR"
cmake --build "$BUILD_DIR" --config Debug
Expand All @@ -63,7 +85,7 @@ echo ""
# -------------------------------------------------------
# Step 2 – Verify install-firmware target
# -------------------------------------------------------
echo "[2/5] Testing install-firmware target..."
echo "[2/6] Testing install-firmware target..."
cmake --build "$BUILD_DIR" --target install-firmware
if [ ! -f "$BUILD_DIR/renode_firmware.elf" ]; then
echo "✗ renode_firmware.elf not found after install-firmware"
Expand All @@ -76,7 +98,7 @@ echo ""
# -------------------------------------------------------
# Step 3 – Start Renode in the background
# -------------------------------------------------------
echo "[3/5] Starting Renode emulation..."
echo "[3/6] Starting Renode emulation..."
CONNECT_LOG="$BUILD_DIR/connect.log"
timeout "$CONNECT_TIMEOUT" cmake --build "$BUILD_DIR" --target connect > "$CONNECT_LOG" 2>&1 &
CONNECT_PID=$!
Expand All @@ -96,11 +118,12 @@ echo ""
# -------------------------------------------------------
# Step 4 – Run monitor-gdb and verify firmware logs
# -------------------------------------------------------
echo "[4/5] Running monitor-gdb to capture firmware logs..."
echo "[4/6] Running monitor-gdb to capture firmware logs..."
MONITOR_LOG="$BUILD_DIR/monitor.log"
# The monitor stays attached through step 5: it keeps resuming the target
# between its reads, whereas stopping it mid-read could leave the target halted
timeout "$((MONITOR_TIMEOUT + UART_TIMEOUT))" cmake --build "$BUILD_DIR" --target monitor-gdb > "$MONITOR_LOG" 2>&1 &
# The monitor stays attached through steps 5 and 6: it keeps resuming the
# target between its reads, whereas stopping it mid-read could leave the
# target halted
timeout "$((MONITOR_TIMEOUT + UART_TIMEOUT + NETWORK_TIMEOUT))" cmake --build "$BUILD_DIR" --target monitor-gdb > "$MONITOR_LOG" 2>&1 &
MONITOR_PID=$!

# Wait until every expected line has been captured, or MONITOR_TIMEOUT
Expand All @@ -123,24 +146,40 @@ echo ""
# -------------------------------------------------------
# Step 5 – Check that the shell answers on the console UART
# -------------------------------------------------------
echo "[5/5] Checking the shell on the console UART..."
echo "[5/6] Checking the shell on the console UART..."
UART_STATUS=0
python3 "$UART_TEST_SCRIPT" --port "$UART_PORT" --timeout "$UART_TIMEOUT" || UART_STATUS=$?
echo ""

# -------------------------------------------------------
# Step 6 – Check the network: DHCP, ping both ways, telnet
# -------------------------------------------------------
echo "[6/6] Checking the network..."
NETWORK_STATUS=0
if [ "$UART_STATUS" -ne 0 ]; then
echo "Skipped: the shell on the console UART does not work"
elif [ "$NETWORK_TEST" -eq 1 ]; then
python3 "$NETWORK_TEST_SCRIPT" --tap "$TAP_INTERFACE" --uart-port "$UART_PORT" \
--timeout "$NETWORK_TIMEOUT" || NETWORK_STATUS=$?
else
echo "Skipped: needs root and /dev/net/tun (set DMBOOT_TEST_NETWORK=1 to require it)"
fi
pkill -f dmlog_monitor 2>/dev/null || true

TEST_STATUS=$((UART_STATUS | NETWORK_STATUS))
echo ""
echo "=============================================="
if [ "$UART_STATUS" -eq 0 ]; then
if [ "$TEST_STATUS" -eq 0 ]; then
echo " Renode emulation tests PASSED"
else
echo " Renode emulation tests FAILED"
# The monitor kept logging during step 5 - whatever stopped the shell
# (e.g. a stack overflow) is at the end of its log
# The monitor kept logging during steps 5 and 6 - whatever stopped the
# shell or the network (e.g. a stack overflow) is at the end of its log
echo "--- monitor.log (tail) ---"
tail -n 40 "$MONITOR_LOG"
echo "--- connect.log (tail) ---"
tail -n 40 "$CONNECT_LOG"
fi
echo "=============================================="

exit "$UART_STATUS"
exit "$TEST_STATUS"
10 changes: 10 additions & 0 deletions scripts/targets.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ if(DMBOOT_RENODE_CONSOLE_UART AND DMBOOT_RENODE_UART_PORT)
else()
set(DMBOOT_RENODE_UART_TERMINAL "")
endif()
# Connect the board's Ethernet (if it names one) to a TAP interface on the
# host through a Renode switch, so the host can talk to the board over IP.
# Opening a TAP needs root (CAP_NET_ADMIN) and /dev/net/tun, so it is opt-in.
set(DMBOOT_RENODE_TAP "" CACHE STRING "Host TAP interface the board's Ethernet is connected to in emulation mode (needs root and /dev/net/tun); empty: not connected")
if(DMBOOT_RENODE_ETHERNET AND DMBOOT_RENODE_TAP)
set(DMBOOT_RENODE_NETWORK
"emulation CreateSwitch \"switch\"\nconnector Connect ${DMBOOT_RENODE_ETHERNET} switch\nemulation CreateTap \"${DMBOOT_RENODE_TAP}\" \"tap\"\nconnector Connect host.tap switch")
else()
set(DMBOOT_RENODE_NETWORK "")
endif()
set(RENODE_SCRIPT_PATH "${CMAKE_BINARY_DIR}/renode.resc")
configure_file(
${CMAKE_SOURCE_DIR}/configs/renode/renode.resc.in
Expand Down
Loading
Loading