| description | The labwired command-line interface is the primary entry point for running simulations, testing, debugging, and managing assets. |
|---|
The labwired command-line interface is the primary entry point for running simulations, testing, debugging, and managing assets. This page documents every command listed by labwired --help. The built-in help is authoritative when the two disagree; run labwired <command> --help for the version you have.
These flags are declared as global options, so every subcommand accepts them. The per-command tables below list only that command's own flags.
| Option | Description |
|---|---|
-t, --trace |
Enable instruction-level execution tracing. test writes trace.json into --output-dir. |
--json |
Output errors and diagnostics as structured JSON for agent consumption. |
--vcd <PATH> |
Output a Value Change Dump (VCD) trace file to the specified path. |
--rtt |
Emit SEGGER RTT output: interactive runs echo drained up-channel bytes and write stdin into down-channel 0; test writes rtt.log and enables rtt_contains. |
--semihosting |
Emit ARM semihosting output: interactive runs echo drained bytes to stdout; test writes semihosting.log and enables semihosting_contains. |
--itm |
Emit ITM stimulus port 0: interactive runs echo the byte stream to stdout; test writes itm.log and enables itm_contains. |
-V, --version |
Print version information. |
-h, --help |
Print help. labwired help [COMMAND] prints the same help for a subcommand. |
Running labwired with no subcommand starts the interactive runner:
labwired --firmware <ELF> --system <YAML> [OPTIONS]| Option | Description |
|---|---|
-f, --firmware <PATH> |
Path to the firmware ELF file. |
-s, --system <PATH> |
Path to the system manifest (YAML). |
--snapshot <PATH> |
Write a JSON state snapshot on exit; resume it with labwired machine load --snapshot <PATH>. |
--breakpoint <ADDR> |
Breakpoint PC address (decimal or 0x hex). Repeatable. |
--max-steps <N> |
Maximum number of steps to execute (default: 20000). |
--gdb <PORT> |
Start a GDB RSP server on the specified port (e.g. 3333). |
Plus the global flags. The top-level --max-steps default (20000) is separate from run --max-steps, which defaults to unlimited.
Start the server with labwired --firmware app.elf --system board.yaml --gdb 3333. The RSP surface is documented in GDB Integration; two debugger behaviours worth knowing:
- Watchpoints —
Z2/Z3/Z4arm a write/read/access watch on a byte range of1..=4096bytes, up to 16 concurrent ranges. They are software-tracked on CPU accesses only: DMA and debugger transfers (m/M/x) do not trip them, so inspecting a watched object cannot fire its own watchpoint. A hit stops the target with a bareT05/S05(nowatch:annotation; readPCback withg). Details: Watchpoints. P— writes a single register for real (reg=valueform), soset $pc = ...andreset $pctake effect; see the CPSR raw-id quirk in the same section.
Which clients have actually run against the stub is tracked in the conformance matrix; every cell is either CI-backed or marked untested.
Run a firmware ELF in the simulator using a chip descriptor. The chip's peripheral wiring is loaded, the firmware is fast-booted, and console output appears on stdout in real time.
labwired run --chip <CHIP> --firmware <ELF> [OPTIONS]| Option | Description |
|---|---|
--chip <PATH> |
Chip descriptor YAML. Required unless --system is given (the manifest then names the chip). |
--firmware <PATH> |
Path to the firmware ELF. Required. |
--system <PATH> |
Board manifest. With --chip, attaches its external_devices: before the run (ESP32-S3 only). Without --chip, selects the system-aware driver, which resolves the chip from the manifest, attaches external devices, and applies --stimulus (ARM only); an explicit --max-steps is then required. |
--stimulus <JSON> |
Declarative input stimulus, e.g. {"channel":"x","value":2.0,"after_cycles":3000000,"component":"fxos8700"}. Repeatable. Requires --system without --chip. |
--display-out <PATH> |
End-of-run dump of every attached parallel panel: binary PPM here plus a luma ASCII map on stderr. |
--stop-on <TEXT> |
End the run when the text appears on the console. ESP32-S3 only. See --stop-on below. |
--max-steps <N> |
Step budget (default: unlimited; required with --system without --chip). Coalesced idle cycles count against it on accelerated ARM runs. |
--allow-sim-error |
Exit 0 even when the run ends on a simulation fault (normally exit 3). |
--gpio-trace <PATH> |
JSON-line GPIO transition trace: {"sim_cycle":N,"pin":P,"from":B,"to":B}. |
--gpio-trace-format <MODE> |
bool (default) or 4state; the latter adds from_state/to_state (0, 1, z, x) and logs drive changes too. |
--bus-trace-out <PATH> |
Export the I²C/SPI/UART bus trace. .json writes raw events; any other extension writes VCD. See --bus-trace-out below. |
--analog-trace <PATH> |
In-core analog waveform trace from an adapter: analog co-simulation model. .csv writes time_ns,<channel>...; other extensions write VCD real variables. Empty unless a co-sim runner is attached. |
--rom-boot |
Boot from the real ROM reset vector (0x40000400) instead of fast-booting. ESP32-S3; requires LABWIRED_ESP32S3_FLASH (ROM auto-provisioned from the ESP toolchain or pinned with LABWIRED_ESP32S3_ROM/_DROM/_ROM_ELF). |
--break-at <HEX> |
Debug: PC address(es) to dump registers and --watch-mem words at first hit, then continue. Repeatable. |
--watch-mem <HEX> |
Debug: memory address(es) printed as u32 whenever a --break-at fires. Repeatable. |
--flash-image <PATH@HEX> |
ARM-only additional flash piece at an absolute address. Repeatable; ELF, Intel HEX, or raw binary. Overlaps are a hard error. |
--batched |
Drive the run through the batched orchestration instead of per-instruction step(). Fails rather than falls back when per-instruction instrumentation (--break-at, --watch-mem, --stop-on; on RISC-V also the WiFi bridge and DHCP trace) is active. ARM, RISC-V and Xtensa (ESP32 / ESP32-S3) all have batched paths. Prints a [batched] ... summary. |
--time-mode <MODE> |
max-speed (default, never sleeps) or realtime (sleeps when virtual time is ≥ 1 ms ahead of wall). |
--log-timestamps <MODE> |
none (default), virtual, real, or both. See --log-timestamps below. |
Plus the global flags.
--log-timestamps {none,virtual,real,both} prefixes the human-readable stderr event/log lines of a run — the labwired-cli ... progress and verdict lines and the tracing subscriber's event lines. The prefix is a fixed-format bracket:
| Value | Prefix | Meaning |
|---|---|---|
none |
nothing | Default. Every output stays byte-identical to a run without the flag. |
virtual |
[t=virt=1.500000000s] |
Simulation time from cycles / cpu_hz. Deterministic: two runs of the same firmware compare equal. |
real |
[t=real=+0.123456789s] |
Wall-clock since process start. Explicitly not comparable between runs. |
both |
virtual and real in one bracket |
Both numbers. |
With both, the bracket joins the two numbers:
[t=virt=1.500000000s|real=+0.123456789s] ...
With a prefixed mode active, the tracing formatter drops its own RFC 3339 wall-clock stamp so the virtual stream is byte-stable. result.json gains virtual_time_ns for virtual/both and host_time_ns for real/both; default none adds neither key and leaves the file unchanged.
Receipt — test over the committed UART fixture, with a two-input script whose paths are absolute (inputs resolve relative to the script file):
schema_version: "1.0"
inputs:
firmware: "/path/to/tests/fixtures/uart-ok-thumbv7m.elf"
system: "/path/to/configs/systems/ci-fixture-uart1.yaml"
limits:
max_steps: 1000
assertions: []labwired test --script /tmp/cli-ref/logts/script.yaml \
--no-uart-stdout --output-dir /tmp/cli-ref/logts/out \
--log-timestamps virtualReal output (trimmed — the full run also prints the ELF entry-point and idle_ff lines; wall-seconds vary between runs):
[t=virt=0.000000000s] INFO labwired_core::system::builder: Loading chip descriptor: ../chips/ci-fixture-cortex-m3-uart1.yaml
[t=virt=0.000000000s] labwired-cli test: hosted fast path (exact windows up to 1024 cycles)
[t=virt=0.000012500s] PASS 0/0 checks · script · 1000 steps · 0.00s
The same run's result.json carries "virtual_time_ns": 12500 and no host_time_ns key.
--bus-trace-out <PATH> exports the universal I²C/SPI bus trace captured during the run. A .json extension writes the raw event list (each event has seq, cycle, bus, and the native payload); any other extension writes a VCD that opens directly in GTKWave / PulseView / sigrok. UART octets are recorded too, even though the flag's help names I²C/SPI.
Receipt — the committed C3 blinky, 57 UART octets:
labwired run --chip configs/chips/esp32c3.yaml \
--firmware examples/esp32c3-blinky/firmware/esp32c3_blinky.elf \
--max-steps 800000 --bus-trace-out /tmp/cli-ref/blinky.vcdLED Olabwired-cli run: bus trace (57 events) -> "/tmp/cli-ref/blinky.vcd"
The VCD declares one 8-bit wire (UART octets) and stamps each event at its sequence number with a 1 µs timescale:
$timescale 1 us $end
$scope module bus_trace $end
$var wire 8 ! uart0 $end
$upscope $end
$enddefinitions $end
#1
b01000011 !
b01000011 is 0x43 (C), the first byte of the firmware banner. Point the same flag at blinky.json for the lossless event list (seq 1, cycle 13312, bus uart0, payload {"protocol":"uart","direction":"tx","byte":67}). More on the ring and formats: Hook surfaces.
--stop-on <TEXT> (ESP32-S3 only; it needs the USB-Serial-JTAG console) ends the run as soon as the text appears on the firmware's console. The console is mirrored into a buffer and scanned every 100,000 steps; the first match logs the step and breaks the loop. It cannot be combined with --batched.
Receipt — the committed S3 Arduino source-debug fixture; 81 is the fifth loop() print (one interleaved GPIO INFO line is elided between the console byte and the match line):
LABWIRED_ESP32S3_FLASH=tests/fixtures/source-debug/esp32s3-arduino-flash.bin \
labwired run --chip configs/chips/esp32s3.yaml \
--firmware tests/fixtures/source-debug/esp32s3-arduino.elf \
--rom-boot --max-steps 200000000 --stop-on 8181labwired-cli run: --stop-on "81" matched at step 101700000
labwired-cli run: reached --max-steps 200000000; pc=0x42020cd6 appcpu_pc=0x42020cd6
Two caveats:
- The
reached --max-stepsline is the stepping loop's unconditional epilogue; it prints after any stop, including a match. The--stop-on ... matchedline is the real stop reason. - A marker that occurs in boot output matches during boot.
--stop-on 27matches the ROM bannerESP-ROM:esp32s3-20210327at step 100000, not application output:
ESP-ROM:esp32s3-20210327
ESP-ROM:1
labwired-cli run: --stop-on "27" matched at step 100000
labwired-cli run: reached --max-steps 200000000; pc=0x40048839 appcpu_pc=0x40000400
Pick a marker unique to the point you care about. The hook surface and the shared observation story are in Hook surfaces.
Runs a CI-friendly test script with assertions. Script schema, exit codes, stop reasons, and the result.json contract live in CI Test Runner.
labwired test --script <YAML> [OVERRIDES]| Option | Description |
|---|---|
-f, --firmware <PATH> |
Override the firmware in a single-machine script only; environment topology comes from inputs.env. |
-s, --system <PATH> |
Override the system manifest in a single-machine script only; environment topology comes from inputs.env. |
-c, --script <PATH> |
Path to the test script (YAML). Required. |
--max-steps <N> |
Override the script's step limit. |
--breakpoint <ADDR> |
Breakpoint PC address. Repeatable. |
--no-uart-stdout |
Disable UART stdout echo (still captured for assertions/artifacts). |
--output-dir <PATH> |
Artifact directory: result.json, snapshot.json, uart.log, rtt.log, semihosting.log, itm.log, junit.xml; requested trace.json, coverage, run-manifest, and fault artifacts land here too. |
--junit <PATH> |
Also write the JUnit XML report to this path. |
--max-cycles <N> |
Override the cycle limit. |
--max-uart-bytes <N> |
Override the UART output limit. |
--detect-stuck <N> |
Fail if the PC does not change for N steps (hang detection). Hidden alias: --no-progress. |
--max-vcd-bytes <N> |
Override the VCD size limit (bytes). |
--analog-trace <PATH> |
Analog waveform trace (.csv or VCD real variables). |
--trace-max <N> |
Maximum number of instructions to trace. |
--coverage |
Collect line/branch/function coverage from the unmodified ELF into coverage.info (LCOV), coverage.cobertura.xml, and coverage.json. See Firmware coverage. |
--rom-boot |
Faithful ESP32-C3 boot (mask ROM → 2nd-stage bootloader → app); requires LABWIRED_ESP32C3_FLASH with bootloader@0x0, partition table@0x8000, app@0x10000. |
--run-manifest |
Write run-manifest.json (input hashes, engine version, result subset, coverage summary, wall-clock-free digest). |
--capture-app-entry <PATH> |
Faithful rom-boot: snapshot the machine at application entry into a .lwrs blob while the run continues to --max-steps. |
--resume-snapshot <PATH> |
Resume from a .lwrs snapshot instead of cold-booting, after validating its chip + firmware self-key against the current inputs. |
--no-key |
Explicitly do not send LABWIRED_API_KEY, even when it is set. |
--watch-gpio <PERIPHERAL:PIN> |
Logic-analyzer edge capture for a pad, e.g. gpio8:8. Repeatable; channels land in result.json's logic_edges block. |
--heartbeat-file <PATH> |
Supervisor liveness file, rewritten atomically at most once a second with {"steps":N,"cycles":C}. |
--log-timestamps <MODE> |
none, virtual, real, or both. See --log-timestamps above. |
Plus the global flags, including -t/--trace (saved to trace.json) and --vcd.
Report ESP32-S3 register-level peripheral coverage against the SVD: every register is probed behaviourally (read/write sentinel) and classified Modelled / Indeterminate / Unmodelled. Prints a human-readable table.
| Option | Description |
|---|---|
--svd <PATH> |
ESP32-S3 SVD; otherwise auto-discovered from PlatformIO or LABWIRED_ESP32S3_SVD. |
--json-out <PATH> |
Write the full coverage matrix as JSON. |
Plus the global flags.
Merge firmware-coverage LCOV files (from test --coverage, gcov/gcovr, llvm-cov, …) into one report: counts add up, and a branch stays "never evaluated" only if no run evaluated it. Prints a summary, writes LCOV / Cobertura / JSON, and can fail the build under a threshold. Worked example and CI setup: Firmware coverage.
| Option | Description |
|---|---|
<INPUT>... |
LCOV files, or directories searched recursively for *.info. Earlier merge results and the -o target are skipped. |
-o, --output <PATH> |
Merged LCOV tracefile. |
--cobertura <PATH> |
Cobertura XML for GitLab / Jenkins / Azure DevOps. |
--source-root <DIR> |
Cobertura <source> base; repeatable, default .. |
--json-out <PATH> |
Summary, per-file numbers, and every uncovered branch as JSON. |
--fail-under-lines <PCT> |
Exit 1 below this line-coverage percentage. |
--fail-under-branches <PCT> |
Exit 1 below this branch-coverage percentage. |
Plus the global flags.
List the chips bundled with the CLI, usable as inputs.chip in a test script or as a manifest's chip: field without copying any YAML. No command-specific flags; accepts the global flags.
Utilities for Asset Foundry.
Import an SVD file and convert it to Strict IR (JSON).
| Option | Description |
|---|---|
-i, --input <SVD> |
Input SVD file. |
-o, --output <JSON> |
Output JSON file. |
--flash-base <ADDR> |
Optional flash base address. |
--flash-size <SIZE> |
Optional flash size, e.g. 512KB. |
--ram-base <ADDR> |
Optional RAM base address. |
--ram-size <SIZE> |
Optional RAM size, e.g. 128KB. |
Generate Rust code from Strict IR.
| Option | Description |
|---|---|
-i, --input <JSON> |
Input Strict IR file. |
-o, --output <RS> |
Output Rust source file. |
Initialize a new project skeleton.
| Option | Description |
|---|---|
-o, --output <DIR> |
Output directory. Required. |
-c, --chip <CHIP> |
Chip name or path to a chip descriptor. |
Add a peripheral to an existing chip descriptor.
| Option | Description |
|---|---|
-c, --chip <YAML> |
Target chip descriptor. |
-i, --id <NAME> |
New peripheral ID. |
--type <TYPE> |
Peripheral type (default strict_ir). |
-b, --base <ADDR> |
Base memory address. |
--ir-path <PATH> |
Path to the IR descriptor (JSON). |
Validate a System Manifest and its referenced Chip.
| Option | Description |
|---|---|
--system <YAML> |
System manifest to validate. |
--chip <YAML> |
Chip descriptor to validate. |
List available chip descriptors.
| Option | Description |
|---|---|
-f, --filter <TEXT> |
Filter string for chip names. |
--format <FORMAT> |
Output format: text (default) or json. |
Validate an off-chip component IR spec (YAML).
| Option | Description |
|---|---|
<SPEC> |
Path to the device descriptor YAML. |
--json |
Emit machine-readable JSON on stdout. |
Ingest an SVD into runnable declarative PeripheralDescriptor YAML — type: declarative peripherals, no codegen or recompile (unlike import-svd).
| Option | Description |
|---|---|
-i, --input <SVD> |
Input SVD file. |
-o, --output-dir <DIR> |
Directory to write <peripheral>.yaml descriptors into. |
--filter <NAMES> |
Only ingest these peripherals (comma-separated). Default: all. |
--json |
Emit a machine-readable JSON summary on stdout. |
Machine state control operations.
Load a machine state from a JSON snapshot and resume simulation.
| Option | Description |
|---|---|
-s, --snapshot <JSON> |
Path to the snapshot file. Required. |
--max-steps <N> |
Override the maximum number of steps to execute. |
Plus the global flags.
Capture a binary runtime snapshot (.lwrs) mid-flight for fast replay in the playground, restorable by WasmSimulator::apply_runtime_snapshot.
Boot a firmware, step N times, write a runtime snapshot blob.
| Option | Description |
|---|---|
--firmware <ELF> |
Path to the firmware ELF. Required. |
--steps <N> |
Number of cycles to run before taking the snapshot. Required. |
--output <PATH> |
Output .lwrs path. Required. |
--system <YAML> |
Board manifest declaring external peripherals to attach. |
--profile <NAME> |
Firmware profile (default and only supported value: arduino-esp32). |
--progress-every <N> |
Progress line every N steps; 0 = silent (default 5000000). |
--trace-out <PATH> |
JSON instruction trace of the last --trace-last retired instructions; forces the interpreter. |
--trace-last <N> |
Trace ring size (default 4096). |
Plus the global flags.
One-shot agent debug probe: loads machine + firmware, resolves optional breakpoints (address / symbol / line), runs until stop or --max-steps, and returns stop reason, PC, location, registers, and serial as JSON. Observational only — proven is always false.
| Option | Description |
|---|---|
-f, --firmware <ELF> |
Firmware ELF (required in v1; there is no flash-only rom-boot path). |
-s, --system <YAML> |
System manifest. Required. |
--breakpoints-json <JSON> |
JSON array of breakpoint specs. |
--breakpoints-file <PATH> |
Breakpoints JSON file (alternative to --breakpoints-json). |
--max-steps <N> |
Step budget (default 2000000). |
--read <LIST> |
Comma list: serial,regs,pc,location,fault (default all). fault is present only when the firmware faulted. |
--step-after-stop |
After the primary stop, take one single-instruction step. |
--output-dir <DIR> |
Write result.json here (also prints JSON to stdout). |
Plus the global flags.
Inject scheduled faults (register/memory bit flips, instruction skips) and report the lockstep verdict against a golden run as JSON. Verdicts: masked, latent, diverged, output_changed, crashed, not_injected.
| Option | Description |
|---|---|
-f, --firmware <ELF> |
Firmware ELF. Required. |
-s, --system <YAML> |
System manifest; its chip: is a built-in name or a path. Required. |
--plan <JSON> |
Plan file {"until_cycle": N, "faults": [...]}. Conflicts with --faults-json/--until-cycle. |
--faults-json <JSON> |
Faults as a JSON array, e.g. [{"at_cycle":500,"kind":"register_bit_flip","register":"R0","bit":3}]. Requires --until-cycle. Kinds: register_bit_flip, memory_bit_flip, instruction_skip. |
--until-cycle <N> |
Stop when the golden machine reaches this cycle. |
--output-dir <DIR> |
Also write the report to <dir>/fault-report.json. |
Plus the global flags.
Coverage-guided fuzzing in the simulator: mutates an input byte stream injected into the firmware's RAM buffer, drives execution with AFL-style edge coverage, and reports crashes. Exits non-zero when a crash is found.
| Option | Description |
|---|---|
--chip <YAML> |
Chip descriptor. Required. |
--system <YAML> |
System manifest. Required. |
--firmware <ELF> |
Firmware ELF following the fuzz contract. Required. |
--max-iters <N> |
Max fuzzing iterations (default 200000). |
--max-steps <N> |
Max steps per run; past it is a hang (default 1000000). |
--seed <N> |
RNG seed (default 3735928559); deterministic for a fixed seed. |
--seed-input <HEX> |
Seed input as hex bytes, e.g. 5000. Repeatable. |
--collect <N> |
Collect up to N distinct crashes instead of stopping at the first. |
--crashes-out <PATH> |
Write crashing inputs as a JSON array of byte arrays. |
--input-len-addr <ADDR> |
Contract: u32 input-length word (default 0x20002800). |
--input-data-addr <ADDR> |
Contract: input data buffer (default 0x20002804). |
--verdict-addr <ADDR> |
Contract: u32 verdict word (default 0x20003000). |
--done-magic <HEX> |
Clean-completion verdict (default 0xC0DEF022). |
--fault-magic <HEX> |
Fault/panic verdict (default 0xDEADFA17). |
Plus the global flags.
Run the Tier-1 chip × peripheral validation matrix and export it.
| Option | Description |
|---|---|
--json-out <PATH> |
Write the matrix as JSON (the committed snapshot is docs/coverage/tier1-matrix.json). |
--run-url <URL> |
Evidence link stamped into every cell that carries evidence (skips na and unrecorded). |
Plus the global flags.
Step a manifest-declared co-simulation model through the real runner/adapter chain and print the routed outputs.
| Option | Description |
|---|---|
<SYSTEM> |
System manifest declaring cosim_models. |
--set <PATH=VALUE> |
Seed a signal-store path before stepping, e.g. --set plant.channels.11.enabled=false. Repeatable. |
--steps <N> |
Model steps to advance (default 1). |
--json |
Print routed outputs as JSON instead of a table. |
--analog-trace <PATH> |
Analog waveform trace (.csv or VCD real variables); only adapter: analog models produce samples. |
Plus the global flags.
Reduced RV1103 Linux direct boot with strict artifacts and a guest acceptance receipt.
| Option | Description |
|---|---|
--artifacts <DIR> |
Directory produced by scripts/prepare-rv1103-linux.py. Required. |
--receipt <PATH> |
Full bounded console, artifact/backend hashes, and acceptance evidence. Required. |
--cpu-hz <N> |
Functional instruction tick calibration (default 1200000000; not physical timing). |
--max-cycles <N> |
Default 24000000000. |
--max-retirements <N> |
Default 2000000000. |
--max-fuel <N> |
Machine fuel, also counting idle fast-forward skips (default 24000000000). |
--wall-seconds <N> |
Default 1800. |
--console-bytes <N> |
UART2 byte cap, at most 64 KiB (default 65536). |
Plus the global flags.
Strict canonical smart-basket Linux and persistent power-plant local acceptance.
| Option | Description |
|---|---|
--artifacts <DIR> |
Required. |
--receipt <PATH> |
Required. |
--max-cycles <N> |
Default 4000000000. |
--max-retirements <N> |
Default 2000000000. |
--max-fuel <N> |
Default 4000000000. |
--wall-seconds <N> |
Default 3600. |
--console-bytes <N> |
Default 65536. |
Plus the global flags.
RUST_LOG: Controls logging level (e.g.,info,debug).- Native logging uses
tracing. - Example:
RUST_LOG=labwired_core=debug
- Native logging uses
LABWIRED_API_KEY: Sent bytestunless--no-keyis given.LABWIRED_ESP32S3_FLASH,LABWIRED_ESP32S3_ROM,LABWIRED_ESP32S3_ROM_DROM,LABWIRED_ESP32S3_ROM_ELF: ROM-boot inputs forrun --rom-bootandtest.LABWIRED_ESP32C3_FLASH: Merged flash image fortest --rom-boot/--capture-app-entry/--resume-snapshot.