Golden Reference: Hardware vs. Simulation Parity
LabWired validates selected simulator behavior against real hardware. One evidence channel is a two-phase pipeline that captures execution from a real board and compares its program-counter sequence with a simulator run intended to use the same firmware ELF. This document describes that PC-trace pipeline, the published evidence, and how to reproduce it.
The published proof
For NUCLEO-H563ZI, the following artifacts are committed under examples/nucleo-h563zi/golden-reference/:
| File | What it is |
|---|---|
hw_trace.json |
Instruction-level trace from the real board (OpenOCD + GDB-MI). |
sim_trace.json |
Instruction-level trace from labwired running the same firmware. |
determinism_report_h563.json |
Aggregate audit verdict. Current published run: status: PASS, 50 compared steps. |
result.json |
Simulator's structured run result (status, steps, cycles, stop reason). |
snapshot.json, uart.log, junit.xml |
Supporting artifacts (final architectural state, UART output, CI report). |
Verifying the published report is a one-line read:
jq '{status, steps_compared, matches: (.results | length)}' \
examples/nucleo-h563zi/golden-reference/determinism_report_h563.json
Phase 1 — capture from hardware
scripts/capture_hardware_trace.py drives a connected NUCLEO-H563ZI via OpenOCD + GDB-MI. For N steps it:
- Starts OpenOCD with the
stlink-dapinterface andstm32h5xtarget config. - Loads the firmware ELF onto the board.
- Halts at reset.
- For each step: reads all registers, captures the PC, and single-steps the CPU.
- Emits
hw_trace.jsonas a JSON array of{step, pc, registers}records.
Required tooling on the host: openocd, gdb-multiarch, pygdbmi. The script's defaults assume the demo firmware ELF and system YAML — adjust the constants at the top to retarget.
Phase 2 — audit against the simulator
scripts/repro_golden_reference.sh takes a captured hw_trace.json and:
- Runs the simulator with the same firmware + system YAML, capturing
sim_trace.json. - Invokes
scripts/labwired-audit.py, which: - Aligns the two traces by first-common-PC (the real board executes a few cycles of bootloader stub before reaching the firmware entry, which the simulator does not model).
- Walks both traces step-by-step, comparing PCs.
- Uses an explicitly declared prefix scope because the fixed hardware capture can be shorter than the simulator run.
- Emits
determinism_report.jsonwith per-step match/mismatch, the comparison scope, and an aggregatestatus.
The audit does not itself hash the firmware ELF or compare registers or UART. Retain those as separate evidence when they are required; a PC-sequence PASS covers only the declared trace scope.
Reproducing the published H563 run:
./scripts/repro_golden_reference.sh \
examples/nucleo-h563zi/golden-reference/hw_trace.json \
target/thumbv7em-none-eabihf/release/firmware-h563-demo \
configs/systems/nucleo-h563zi-demo.yaml \
NUCLEO-H563ZI
The audit report lands in out/golden-reference/. Compare it against the committed examples/nucleo-h563zi/golden-reference/determinism_report_h563.json.
Why this matters
A static "we simulate Cortex-M33" claim is a wishlist. A captured hardware trace, a simulator trace, a diff, and a committed report is evidence.
The pipeline answers one early question an embedded engineer asks before trusting a simulator: does execution follow the same PC path over this captured scope? Architectural state divergence between sim and hardware is the bug class that ruins firmware development workflows — peripherals that "work in the sim" but lock up the chip, race conditions that only appear on metal, and so on. PC parity does not exclude every such divergence, but the retained traces turn this particular claim into an inspectable artifact.
Extending to a new board
The capture script is hardcoded to H563 paths today. For a new board:
- Build the smoke firmware for the new chip; record the ELF path.
- Confirm OpenOCD has a target config (
target/<chip>.cfg) for your debug probe. - Copy
capture_hardware_trace.py, edit theOPENOCD_ARGS,FIRMWARE_ELF, andSYSTEM_YAMLconstants. - Capture, audit, commit the artifacts under
examples/<board>/golden-reference/.
A future iteration would parameterize the capture script over CLI args. For now it is intentionally simple — the trace data is the artifact, not the script.