Run Limits
Every labwired test run is bounded by a limits block. max_steps is
required; everything else is optional and defaults to unbounded. Unknown keys
are rejected, so the list below is exhaustive.
schema_version: "1.0"
inputs:
firmware: "firmware.elf"
limits:
max_steps: 1000000
wall_time_ms: 10000
no_progress_steps: 1000
Keys
| Key | Type | Stops the run when |
|---|---|---|
max_steps |
required | the step count is reached |
max_cycles |
optional | the CPU cycle count is reached (includes peripheral wait states) |
max_uart_bytes |
optional | captured UART output reaches this size |
wall_time_ms |
optional | this much real time has elapsed, regardless of progress |
no_progress_steps |
optional | the PC has not changed for this many steps |
max_vcd_bytes |
optional | the VCD trace reaches this size (interactive mode only) |
Cycles are not steps: a step that hits a wait-stated peripheral costs more than
one cycle, so max_cycles has to be set from a cycle budget, not scaled off
max_steps.
Early stop on assertions
A run can finish as soon as its assertions hold, rather than burning the full step budget:
| Key | Default | Effect |
|---|---|---|
stop_when_assertions_pass |
false |
stop once all runtime assertions pass |
stop_when_assertions_pass_settle_steps |
100000 |
keep executing this many steps past the first passing moment before accepting the result |
stop_when_assertions_pass_min_steps |
0 |
never early-stop before this many steps have run |
The settling window closes the print-then-crash hole: firmware that emits its acceptance token and then faults breaks with the fault reason instead of certifying as passed. Lower it only if you know the firmware cannot fault after the token.
Choosing values
- CI: always set
wall_time_ms. A hung build costs more than a false timeout, and it is the only limit that bounds pathological cases you did not anticipate. - Infinite loops:
no_progress_stepscatches a PC that stops moving. Raise it above the default if the firmware intentionally spins — a WFI idle loop or a busy-wait on a peripheral flag will otherwise trip it. - Verbose firmware:
max_uart_bytesbounds the capture buffer. Logging in a hot loop is the usual way a run exhausts memory. - VCD: traces grow with instruction count and can reach hundreds of
megabytes on a million-step run. Enable VCD per debugging session, and pair
it with
max_vcd_bytesor a reducedmax_steps.
Limits are independent — the run stops at whichever trips first, so setting both a step and a time bound is normal.