In CI and HIL, every test run should start from a known image, with nothing carried over from the run before.
Linux Boards
For Linux boards, netboot is usually the cleanest way to get there.
A management node serves DHCP, hands the target a kernel over TFTP, and exports a root filesystem over NFS. Every power cycle comes up on a fresh image. No SD cards to swap, no stale filesystem state, and no quiet local changes on one developer's bench.
Keep this on an isolated test network rather than your office LAN. A DHCP server meant for test devices shouldn't be in a position to answer unrelated machines on the corporate network.
MCU Targets
For MCU targets, OpenOCD over a debug probe is the equivalent. A Raspberry Pi Pico flashed as CMSIS-DAP is enough to get going, and the official Raspberry Pi Debug Probe is a tidy option if you don't want to flash your own.
The flow is straightforward:
- Halt the target
- Flash the firmware
- Verify the image
- Release reset
- Run the test
In OpenOCD, that comes out as something like:
openocd \
-f interface/cmsis-dap.cfg \
-c "transport select swd" \
-f target/stm32f4x.cfg \
-c "init" \
-c "reset halt" \
-c "program {firmware.elf} verify" \
-c "reset run" \
-c "shutdown"The order matters: halt before flashing, verify after writing, and only release reset once the new image is in place. Each test run starts from a clean slate.
The reset line is worth thinking about up front. The standard 3-pin SWD cable on a Pico-based probe carries SWDIO, SWDCLK, and GND, which is enough to flash a healthy board, but not necessarily enough to recover one that is wedged. CI ends up running broken firmware by design, so the rig needs a way to bring a stuck target back without someone walking over to it. Wiring a GPIO from the management node to the DUT reset pin and asserting it before OpenOCD runs is usually the simplest fix.