LabWired The easy way to build hardware
Two STM32H5 boards on a virtual CAN bus run a full UDS diagnostic check.

Run a full ECU diagnostic check with no hardware

Category: Whitepaper

Phase 1: session & security 0/27 services
STM32H5
UDS TESTER
udslib client
virtual FDCAN
STM32H5
UDS ECU
udslib server
0x10 DiagnosticSessionControl
g_service_results @ 0x20010000 0x00000000
SERVICES 27/27 PASS · exit=0
The figure shows every exchange in the real gate. The tester firmware runs the exchanges in this order. main.c supplies the request bytes, the response SID, and the results bit. The bits do not fill in execution order. The SID is the index of each bit. The tester runs EcuReset last because the ECU reboots after EcuReset.

Automotive diagnostic tests are difficult. You want to test the diagnostics of your ECU. A workshop tool communicates with the UDS services. A rig that flashes ECU firmware communicates with the UDS services. The test needs an ECU on a bench. The test needs a CAN interface. The CAN interface can be PCAN, a Vector box, or a similar tool. The test needs wires and power. The tester controls the other end.

These items do not fit in a CI pipeline. You test the diagnostics by hand on hardware. The regression coverage is limited.

The goal was a full test in software. The test uses a real ECU, a real tester, and a real CAN bus. The test runs headless on a free CI runner.

The test setup

LabWired contains two virtual STM32H5 microcontrollers. A virtual FDCAN bus connects the two microcontrollers. One microcontroller runs a UDS tester. The tester is the client. The other microcontroller runs a UDS ECU. The ECU is the server. The ECU registers all 27 ISO-14229 services. Both microcontrollers run real udslib firmware. udslib is the open-source ISO-14229 stack. You can compile the same stack for a part. The test uses no mocks.

The tester sends a genuine request for every service. The request crosses the virtual bus. The real handler of the ECU runs. The response returns to the tester. The tester checks the bytes.

labwired · one deterministic process · no hardware in the loop STM32H563 UDS TESTER udslib client firmware.elf STM32H563 UDS ECU udslib server · unmodified firmware.elf 0x7E0 · request virtual FDCAN ISO-TP · CAN-FD 0x7E8 · response g_service_results @ 0x20010000 one bit per service, set only when the response crossed the bus and matched == 0x07FFFFFF
Both nodes are real firmware.elf builds. The builds run on modelled H563 silicon. The ECU build is stock udslib. The gate makes only one assertion. The assertion is the results word of the tester. The results word is memory_value @ 0x20010000 == 0x07FFFFFF.

At the end, the tester sets one bit for each service in a results word. A CI job reads the results word. The CI job asserts that the results word equals 0x07FFFFFF. All 27 services answered correctly across the bus. The result is 27/27. The exit code is 0. The test has no hardware in the loop.

An abridged trace from a real run follows. Each line is one UDS exchange. The exchange crosses the virtual CAN bus. The test checks the exchange.

Session & security
4 of 27
10DiagnosticSessionControl 29Authentication 27SecurityAccess 3ETesterPresent
Data services
8 of 27
22ReadDataByIdentifier 2EWriteDataByIdentifier 3DWriteMemoryByAddress 23ReadMemoryByAddress 24ReadScalingDataByIdentifier 2CDynamicallyDefineDataIdentifier 2AReadDataByPeriodicIdentifier 2FInputOutputControlByIdentifier
DTC services
4 of 27
19ReadDTCInformation 85ControlDTCSetting 14ClearDiagnosticInformation 86ResponseOnEvent
Programming / transfer
6 of 27
31RoutineControl 34RequestDownload 36TransferData 37RequestTransferExit 35RequestUpload 38RequestFileTransfer
Remaining + reset
5 of 27
28CommunicationControl 83AccessTimingParameter 87LinkControl 84SecuredDataTransmission 11EcuReset, last
The figure shows all 27 services in the order that the tester runs the services. The groups are not only for appearance. Writes run before the reads that check the writes. The tester keeps EcuReset until the very end so the reboot cannot erase the other 26 results.

The same gate runs on a clean GitHub Actions runner. The runner installs the toolchain. The runner builds both firmwares. The runner builds the emulator. The runner runs the gate. The run is successful from start to end. See the CI run. Also see LabWired for CI.

The next figure shows the udslib ECU on a virtual STM32H5.

Press Run. Watch the H5 answer a diagnostic ReadDataByIdentifier request over CAN-FD. The firmware is real udslib firmware. This run uses no hardware. Open the full-screen view

The test works. The test runs nightly on free GitHub runners. The test finds real bugs. Note which bugs the test found. Every bug was in the emulator. The bugs were not in udslib. All of the bugs are fixed and released.

The result

You get a full 27-service UDS diagnostic check. The check includes session control and security access. The check includes read by identifier and write by identifier. The check includes DTCs and routine control. The check includes the full sequence that programs the ECU and transfers the data. The check ends with ECU reset. The check runs across two virtual MCUs. The MCUs use a simulated CAN network. The check runs headless in CI. The cost is the cost of a GitHub runner minute. The ECU firmware uses udslib completely unmodified.

The CPU model runs your actual firmware. The model can show real bugs before a bench test.

Run the test and see the proof

Andrii Shylenko
Andrii Shylenko

Founder, LabWired.