Open the MCP session

Every MCP session starts with an initialize handshake. The server replies with its protocolVersion and serverInfo.


Discover the tools

List every callable tool with its JSON input schema. An agent should call this once and cache it.


What MCUs/peripherals can I instantiate?

Lists the supported MCU cores and SVD-derived peripheral device types. The peripheral rows are derived from the engine's PeriphType registry, so newly-shipped blocks (e.g. the Ethernet/GMAC MACs) appear here automatically.

Supported MCU/peripheral types:
- cortex-m0/m3/m4/m7/m33/m55: ARM Cortex-M cores (arm-generic-fdt, M-profile)
- cortex-a15: ARM Cortex-A15 (A-profile)
- riscv32: RISC-V RV32 core (riscv32-softmmu)
- st-usart: STM32 G4 USART — SVD-derived UART (STM32G474)
- st-fdcan: STM32 FDCAN / Bosch M_CAN — SVD-derived CAN-FD (STM32G474)
- st-eth: STM32H743 Ethernet — Synopsys DWC EQOS MAC, SVD-derived (STM32H743)
- nxp-enet: NXP i.MX RT1062 Ethernet — Freescale FEC MAC, SVD-derived (MIMXRT1062)
- nxp-s32k3-gmac: NXP S32K344 GMAC — Synopsys DWC EQOS MAC, SVD-derived (SDV/SOAFEE MCU)
- s32k1xx-flexcan: NXP FlexCAN mailbox — SVD-derived CAN-FD (S32K144 CAN0)
- ieee802154 / nrf52840-radio / arm-cc310 / gpio / nrf52-uarte / flexray …


What inter-node links can I wire?

Lists the supported link protocols for multi-node worlds.

Supported link types:
- uart: inter-node UART serial link …
- can: CAN / CAN-FD …
- ieee802154: IEEE 802.15.4 radio …


Which vendor register maps are vendored?

Lists the vendored CMSIS-SVD register maps. The list is generated at build time from assets/svd/vendor/**, so a newly-vendored SVD appears automatically.

Vendored SVD register maps:
- MIMXRT1062_ENET: MIMXRT1062 ENET CMSIS-SVD (NXP, ENET peripheral extracted)
- S32K144: S32K144 CMSIS-SVD (NXP / Freescale Semiconductor)
- STM32G474: STM32G474 CMSIS-SVD (STMicroelectronics)
- STM32H743: STM32H743 CMSIS-SVD (STMicroelectronics, via modm-io redistribution)
- nrf52840: nRF52840 CMSIS-SVD (Nordic Semiconductor)


Which SoCs are simulated, and what is actually validated?

The per-MCU capability registry: every simulated SoC and, per silicon block, a CI-grounded maturity (✅ shipped & CI-gated · 🟡 in progress · 🔬 emerging), its validating tests, and what the SVD lists as not-yet-modeled. No `mcu` arg gives a one-line-per-MCU index; pass `mcu` (e.g. 'nrf52840') for the full per-block view. Derived from the assets/mcu/ manifests — a newly-manifested SoC appears automatically.

VirtMCU simulated MCUs (maturity: ✅ shipped & CI-gated · 🟡 in progress · 🔬 emerging). Pass `mcu` for the full per-block view:
- nrf52840 (nordic, Cortex-M4F): 12 block(s) [12✅]
- rt1062 (nxp, Cortex-M7): 14 block(s) [14✅]
- stm32g474 (st, Cortex-M4F): 4 block(s) [3✅ · 1🔬]


See the running nodes

List the active simulated nodes and their links. Use this first to learn node_ids and link_ids.

node 0: cortex-m4 (links: none)


Get the wiring graph

The full nodes+links graph (who is wired to whom), as structured data.

{"nodes":[…],"links":[…]}


Flash a node

Upload an ELF for a node (hex-encoded bytes, or a local path on the CLI). The engine validates the ELF magic and persists it as the launch artifact. Pairs with start_node.

uploaded firmware for node 0 (66972 bytes); call start_node to run it


Run the node

Begin/resume execution (QMP cont) and release the clock-barrier hold — the unfreeze primitive for a node launched frozen.

node 0 started


Wait for a console marker

Advance virtual time until a condition holds. Conditions: events_contains, vtime_at_least, console {node_id, contains}, or link {link_id, contains}. This is the deterministic 'wait for output' primitive.

condition met at vtime 1200000 ns


Drain console output

Read and drain a node's buffered guest->host console output since the last read.

HELLO


Read the simulation clock

The current virtual time in nanoseconds (latest observed vtime). Virtual time is deterministic and independent of wall-clock.

vtime_ns: 1200000


Dump CPU registers

Read a node's CPU registers via QMP (a deterministic snapshot at the current vtime).

R00=00000000 R01=4006b000 … PC=00000410 …


Inspect guest memory

Read a guest physical memory range (hex address + length, capped at 1 MiB/call). Useful to read fault-status registers (CFSR @ 0xE000ED28) or an SRAM capture buffer.

0xe000ed28: 00 00 00 00


Decode a Cortex-M fault

After a crash, decode WHY the firmware faulted (the SCB fault-status registers) AND WHERE (the faulting PC, recovered from the exception stack frame) — the agent-usable HardFault inspection (no breakpoint/gdb). Pair with a fault-handler marker + run_until, then read_fault + read_registers.

Fault detected (CFSR=0x00000082 HFSR=0x40000000): DACCVIOL: data access violation (MPU); FORCED: escalated to HardFault (a configurable fault fired). Faulting address = 0x20004000
Faulting PC = 0x08000420 (recovered from the MSP exception frame at SP=0x2000ffe0); stacked LR=0x08000311, xPSR=0x61000000.


Enumerate the guest memory map

The flattened guest-physical memory map (RAM + every peripheral MMIO window), read from the LIVE QOM memory tree — reflects reality even if a device aliases/relocates at runtime. Use to find exactly where a peripheral lives before read_memory/read_registers, or to sanity-check the world's declared layout.

Memory regions (node 0):
- 0x08000000-0x08001000 (4096 bytes, i/o): gic_dist
- 0x09000000-0x09001000 (4096 bytes, i/o): pl011
- 0x40000000-0x41000000 (16777216 bytes, ram): memory@40000000


Patch guest memory

Write bytes into a guest physical address (hex address + hex-encoded bytes, capped at 1 MiB/call) — the write peer of read_memory. Debug-only and non-deterministic (RFC-0008): use only while the node is parked at a quantum boundary, never mid-execution. Reaches the M-profile CPU's own address-space view (NVIC/bit-band/PPB), not just the board's plain RAM/flash tree.

wrote 4 bytes at 0x20000000


Disassemble guest code

Decode ARM/Thumb instructions at a guest physical address (hex address + instruction count, capped at 256/call; thumb:false for ARM32 A-profile code). Useful to inspect a symbol's machine code, or the instructions around a faulting PC (pair with read_fault).

0x8: mov.w r0, #0x9000000
0xc: movs r1, #0x4d
e: str r1, [r0]


Tail the engine log

Read a node's QEMU process log (combined stdout+stderr) — where sim_info!/sim_debug!/sim_err! telemetry lines land. No live session needed, so it stays readable after a crash/hang; pair with read_fault for a full post-mortem. Without since_line, returns the most recent lines (a tail); pass since_line to page forward through a long run.

[QEMU 0 STDERR] [VTime:    0.00 ms] [Node: 0] [INFO ] [nrf52_clock] clock: node=0 fed='sim-abc123'


Resolve an address to function@file:line

Resolve a bare guest code address to function@file:line (DWARF) or the symbol name (symtab-only), against the node's uploaded firmware ELF — the same B1 symbolizer read_fault/causal_query annotate their PCs with, standalone. Use after read_fault/causal_query/read_events name a raw PC you want a human-readable label for.

0x08000420  compute at sample.c:11


Reconstruct the call stack at a vtime

The call stack open AT virtual time vtime on a node — a Cortex-M backtrace reconstructed by replaying the call/return event trace (RFC-0019 B7) up to vtime, outermost frame first, each call target symbolized against the node's uploaded ELF. Requires trace capture active before vtime (run the sim via run_until first — same precondition as causal_query).

Call stack at vtime 1500000 (outermost first):
#0 main at sample.c:20
#1 compute at sample.c:9


Read a bounded write trace

Every memory write to addr whose virtual time falls in [lo, hi] (inclusive both ends) — a bounded window over the RFC-0019 B6 data-write trace, each with its writer PC symbolized. Requires trace capture active before the window (run the sim via run_until first — same precondition as causal_query).

Writes to 0x2000f000 in vtime [0, 2000000]:
  vtime 500000 wrote 0x0 from compute at sample.c:11
  vtime 1000000 wrote 0x1 from compute at sample.c:11
  vtime 1500000 wrote 0x2 from compute at sample.c:11


Diff guest memory before/after

Before/after guest-memory delta for one address: the value in effect (RFC-0019 B6 state@T) at before_vtime versus after_vtime, each annotated with its writer's symbolized PC, and whether it changed. Requires trace capture active before before_vtime (run the sim via run_until first — same precondition as causal_query).

Memory diff for 0x2000f000: before (vtime 0) = never written; after (vtime 2000000) = 0x2 (written from compute at sample.c:11); changed = true


Arm a software breakpoint

Arm a deterministic breakpoint at a PC (use the symbol's address from your ELF; addr=0 disarms). The node halts AT the next quantum boundary — virtual time stays stable and the PDES barrier never deadlocks (unlike gdb/QMP stop). Arm it before the firmware reaches the address; pair with run_until {breakpoint}, then read_fault / read_registers, then resume.

node 0 breakpoint armed at 0x8000420


Wait for a breakpoint hit

Advance virtual time until a node halts at a software breakpoint (see set_breakpoint). Optional {node_id} filter. The node pauses at the quantum boundary with vtime stable; inspect with read_registers / read_fault, then resume.

condition met


Resume after a breakpoint

Release a breakpoint pause so the node resumes executing under virtual time — the counterpart to set_breakpoint + run_until {breakpoint}.

node 0 resumed


Arm a data watchpoint

Arm a deterministic break-on-access watchpoint at a guest address (the memory-access peer of set_breakpoint) — length in bytes (1..=8), kind write (the only kind implemented today). The node halts AT the next quantum boundary — virtual time stays stable and the PDES barrier never deadlocks (unlike a real hardware watchpoint trap). Arm it before the firmware performs the access; pair with run_until {breakpoint}, then read_memory / read_registers, then resume.

node 0 watchpoint armed at 0x4000ffbc len=4


Disarm a data watchpoint

Disarm a node's software data watchpoint without releasing an already-triggered Time Authority pause (release that separately with resume). The counterpart to set_watchpoint.

node 0 watchpoint disarmed


Auto-stop at the node's HardFault handler

Arm a breakpoint at the node's OWN HardFault_Handler — no ELF/symbol address needed (unlike set_breakpoint): reads the node's live vector table (SCB VTOR + the HardFault vector entry) and arms it via the same determinism-safe mechanism. Arm right after upload_firmware/start_node, then wait with run_until {breakpoint} and decode with read_fault.

node 0 fault-autostop armed at HardFault_Handler 0x0800044c (VTOR=0x08000000); wait with run_until {breakpoint}, then read_fault


Send console input

Write host->guest bytes to a node's console (the passthrough boundary; no vtime, routed immediately).

wrote 1 byte to node 0 console


Tap inter-node frames

Frames seen crossing inter-node links (the wiretap tap): src node, vtime, seq, raw bytes (hex). Optional filters: link_id, since_vtime.


Inject a frame on a link

Inject a frame onto an inter-node PDES link, scheduled at a virtual time (pass vtime_ns verbatim for reproducible scenarios; omit/0 for as-soon-as-possible). A past vtime is rejected, never crashing the sim. Use write_console for console input.

injected 4 bytes on link 1 at vtime 2000000 ns


Arm a deterministic link fault

Arm a fault on a PDES link: the Nth REAL frame observed on link_id (1-indexed) is dropped, delayed, or has one payload byte XOR-corrupted — only genuine node-to-node traffic, never a test-injected frame. spec_id is a caller-assigned unique id (fresh per call). fire_vtime_ns gates WHEN the fault arms (0/omit = immediately). Get link_id from get_topology.

armed spec_id 0 on link 1


Read the causal event log

The global vtime-ordered event log (link routes + lifecycle). Optional filters: kind, since_vtime, node (src), link_id.

Summary: node 0 -> link 0 (7 bytes)


Reset a node to its reset vector

Relaunch a node's firmware from the reset vector (virtual time rewinds to 0); the determinism-safe restart — the node re-joins as a fresh boot, reusing the last-uploaded firmware.

node 0 reset; rejoined at vtime 0


Time-travel to an earlier virtual time

Rewind (or fast-forward) the simulation to EXACTLY vtime_ns and hold there, then inspect with read_memory / read_registers. A backward goto deterministically resets the node to boot and replays to the target (RFC-0008: re-running to the same vtime reproduces the same state); a forward goto is an exact-landing advance. Single-node sessions only; exact for guests that read no clock/cycle counter (the no-clock-read class).

goto reached 2000000 ns


Step backward in time by one quantum

Step the simulation BACKWARD by step_ns — a thin goto(current_vtime - step_ns) over the time-travel goto verb (reset-to-boot + deterministic replay, RFC-0008). step_ns is ε, typically one quantum (the prior quantum boundary). Inspect after with read_memory / read_registers. Single-node sessions; the no-clock-read class.

reverse_step 1000000 ns: vtime 3000000 -> 2000000; goto reached 2000000 ns


Find what caused a fault, then rewind to it

Time-travel debugging (RFC-0019 B8): given a suspect address and the fault's virtual time, compose the last writer of that address before the fault (with the value it wrote) and the call path open at the fault, symbolized against the node's firmware. Returns a goto target (the writer's vtime) to rewind to the cause. Run the sim via run_until first so trace capture precedes the fault. Single-node sessions.

Causal query for addr 0x2000f000 at vtime 2000000:
  last writer: vtime 1500000 wrote 0x2 from compute @ sample.c:11
  goto target: vtime 1500000
  call path at fault (outermost first):
    #0 main @ sample.c:20
    #1 compute @ sample.c:9


Request an MCU we don't have

Agents are our customers — requesting a part is a prioritization signal. Recorded to the operator's feedback sink.

Recorded request_mcu — thank you; agent requests prioritize our roadmap. (…)


Request a link protocol

Request an inter-node protocol you need.

Recorded request_link — thank you …


Request a vendored SVD

Request a CMSIS-SVD register map to be vendored.

Recorded request_svd — thank you …


Report a fidelity gap

Report a bug or fidelity gap, optionally with a deterministic repro (world + firmware + seed).

Summary: LPUART RDRF clears one byte early

Recorded report_issue — thank you …


Suggest a capability

Suggest a capability you wish existed.

Summary: set_breakpoint(symbol) + HardFault auto-trap

Recorded suggest_feature — thank you …


Ask an in-band support question

Ask a support question; returns guidance + doc pointers (keyword-routed).

Question: How do I read console output from a node?

Console: use write_console to send input and read_console to drain output …


Check your usage budget

Returns your usage so you can self-pace. `session` is THIS session vCPU-second budget toward its TTL cap (vcpu_seconds_used / session_pct_used / expires_in_s); `account` is your rolling-window account usage (window / pct_used / resets_in_s) when the control plane signed a usage grant onto session-create, else null (the engine never sees raw quota, only the %). Both null when run standalone. Call it to decide whether to keep working or wrap up.


Compile source to a firmware ELF (then upload + run)

The cloud write→compile→run loop, all MCP: compile your C/asm to an ELF (no local toolchain), then upload_firmware{node_id, firmware_hex: elf_hex} → reset_node{node_id} → run_until/read_console. 'mcu' is the target core; 'sources' maps filename → base64 of the file bytes. Returns {elf_hex, elf_sha256, warnings}, or compiler diagnostics on failure. Hosted-session only.


Admit and enqueue a fuzz campaign

fuzz_start admits and enqueues a campaign, returning a campaign_id IMMEDIATELY — it never blocks (a campaign runs minutes-to-hours on a separate worker, never inside this tool call). vcpu_ceiling_secs is checked up-front against your session's remaining fuzz budget; too large and the call is refused with both numbers named. global_seed/firmware_hash/world_usda_hash/engine_revision are the determinism inputs a later crash replay needs. Poll with fuzz_status{campaign_id}. Hosted-session only (MAX tier).


Poll a fuzz campaign's state

Poll a campaign's current state: REQUESTED → ADMITTED → RUNNING → {COMPLETED|FAILED|CANCELLED|BUDGET_EXHAUSTED}. Terminal states are reached by the worker, never by you waiting on this call. BUDGET_EXHAUSTED is a normal terminal state with its artifacts intact, not an error. Hosted-session only (MAX tier).


Fetch a fuzz campaign's evolving input corpus

fuzz_corpus returns a campaign's evolving input corpus: the input samples (hex-encoded) that triggered new code coverage during the search — the interesting seeds the fuzzer keeps and mutates to explore further. Empty while the campaign is still ADMITTED/RUNNING or has no corpus yet; an unknown campaign_id is a loud tool error, never a fabricated corpus. Hosted-session only (MAX tier).


Fetch a fuzz campaign's REPORTABLE crashes

crash_list returns a campaign's REPORTABLE crashes (x2-deterministic-replay-PROMOTED crashes only), never unreproducible crashes. Returns the crash records promoted to REPORTED status during the fuzz campaign — empty list while the campaign is still ADMITTED/RUNNING or has no reportable crashes yet; an unknown campaign_id is a loud tool error, never a fabricated list. Hosted-session only (MAX tier).


Deterministically replay a specific promoted crash

crash_repro deterministically replays a specific promoted crash artifact from a fuzz campaign and returns the replay result. The replay uses the recorded determinism inputs (global seed, firmware hash, world .usda hash, engine revision, crashing input) to reproduce the crash bit-identically on every invocation. Returns the replay result (the x2-bit-identical outcome / the reproduction evidence) — a non-deterministic result here is a product defect, not a flaky test. An unknown campaign_id or crash_id is a loud tool error, never a fabricated replay. Hosted-session only (MAX tier).

crash 'crash-abc-001' not found or not promoted


Rewind to the last write of an address

Time-travel debugging (RFC-0019 B9): rewind to the LAST write of an address before the current virtual time AND land there — a find-then-goto wrapper composing the last-writer query (like causal_query) with the backward goto verb (reset-to-boot + deterministic replay, RFC-0008). The node ends up parked at that write, ready for read_memory / read_registers. No write before now → fails loud. Run the sim via run_until first so trace capture precedes now. Single-node sessions; the no-clock-read class.

no write to 0x2000f000 before vtime <current> — nothing to reverse_continue to


Find a working end-to-end demo close to what you want to build

The scenario gallery: curated, working end-to-end VirtMCU demos (cross-vendor Ethernet, two Wi-Fi nodes, a 3-ECU CAN segment, sensor->actuator control), each with a use-case story, the backing integration test(s) that prove it runs, and a CI-grounded maturity. No id gives a one-line-per-scenario index; pass id (e.g. eth_cross_vendor) for the full story + backing tests. Derived from the gallery/ manifests.

Cross-vendor Ethernet frame exchange [✅] — eth_cross_vendor

Two MCUs from different silicon vendors talk Ethernet to each other with no shared driver code.
Node 0 runs an ST STM32H743 whose EQOS (GMAC) MAC drives the link; node 1 runs an NXP part whose
ENET (FEC) MAC drives the same medium. Each firmware brings its own vendor MAC up from the
register level, then transmits an Ethernet frame; the coordinator carries every frame across the
PDES quantum barrier so the exchange is bit-identical on every run. This is the proof that
VirtMCU's `eth-core` shared per-protocol layer (RFC-0018) lets a vendor frontend (EQOS vs FEC)
differ entirely while the on-wire Ethernet behaviour stays interoperable — a real two-vendor
network, deterministically replayable.

Backing tests:
- test_eth_cross_vendor_exchange (CI-gated)
- test_eth_cross_vendor_deterministic (CI-gated)

MCUs: stm32h743, rt1062

Vendor notes: ST EQOS and NXP FEC are completely different MAC designs (descriptor formats, register maps,
DMA models). They interoperate only because the Ethernet frame on the medium is vendor-neutral;
the shared `eth-core` carries it while each frontend keeps its own SVD-derived register decode.


Save a checkpoint of the current virtual time

Save a named checkpoint of the CURRENT virtual time (RFC-0019) so you can later snapshot_restore back to it without tracking the vtime yourself. Returns a snapshot id. A thin id/vtime handle over the goto time-travel mechanism (re-running to the same vtime reproduces the same state, RFC-0008) — it remembers WHEN, it does not freeze/copy guest memory. Single-node sessions; the no-clock-read class (same contract as goto).

snapshot snap-1 saved: node 0 @ vtime 3000000 ns


List saved checkpoints

List checkpoints saved this session via snapshot_save (id, node, vtime_ns), or describe one by id. Pure introspection — no guest I/O.

snap-1: node 0 @ vtime 3000000 ns


Time-travel back to a saved checkpoint

Time-travel back to a snapshot_save'd checkpoint by id — equivalent to goto(vtime_ns) at the vtime that was current when you called snapshot_save. A backward restore deterministically resets the node to boot and replays to that vtime (RFC-0008); a forward restore is an exact-landing advance. Single-node sessions only; the no-clock-read class. Fails loud on an unknown id — call snapshot_info to list saved ones.

snapshot snap-1 restored: node 0 -> vtime 3000000 ns; goto reached 3000000 ns


Decode the ARM vector table

After uploading firmware, read and decode the Cortex-M vector table (stored at VTOR, the Vector Table Offset Register): returns the initial stack pointer (word[0]), reset handler address (word[1] with Thumb bit masked), and exception/IRQ vector addresses. Use to verify the firmware loaded correctly and compare against your ELF symbol table to locate exception handlers.

Vector table at VTOR=0x08000000:
  Initial SP: 0x20008000
  Reset Vector: 0x08000420 (raw 0x08000421)
  Exception Vectors:
    NMI: 0x08000500 (raw 0x08000501)
    HardFault: 0x08000520 (raw 0x08000521)
    MemManage: 0x00000000 (raw 0x00000000)
    BusFault: 0x00000000 (raw 0x00000000)
    UsageFault: 0x00000000 (raw 0x00000000)
    Reserved[0]: 0x00000000 (raw 0x00000000)
    Reserved[1]: 0x00000000 (raw 0x00000000)
    Reserved[2]: 0x00000000 (raw 0x00000000)
    SVCall: 0x00000000 (raw 0x00000000)
    PendSV: 0x00000000 (raw 0x00000000)
    SysTick: 0x00000000 (raw 0x00000000)


Know your tier + every tool's min_tier, and how to upgrade

Notion-style access map over every MCP tool: {tool, min_tier, category, status, callable_by_this_session} plus the tier/price table and upgrade_url. callable_by_this_session is honestly null -- this MCP child process cannot see which paid tier the current session authenticated as (only the broker resolves and enforces that, by design, so a client can never forge a higher tier); compare your own known account tier against each tool's min_tier yourself (tiers are cumulative by rank: student < pro < max). Sourced from the same capabilities.toml SSoT the broker's enforcement gate and the public pricing page read, so this can never drift from either.

This MCP child process cannot see which paid tier the current session is authenticated as: only the broker frontdoor resolves the tier, and the child deliberately receives no tier over env (design doc 23-mcp-capability-tiering.md §6 — a child-side tier gate is impossible by design, which is also the anti-forgery guarantee: no client-supplied tier means no forgeable tier). So `callable_by_this_session` is always null here, never a fabricated true/false — compare your own known account tier against each tool's `min_tier` (tiers are cumulative: student < pro < max by `rank`), or call a tool and read the `isError` upgrade payload if you are unsure. See `upgrade_url` to upgrade.


Shrink a crashing input to minimal reproducible form

crash_minimize deterministically shrinks a crashing input while preserving its crash signature, using delta-debugging (Zeller ddmin). Takes campaign_id and crash_id to identify which crash to minimize. Returns the minimized input (hex-encoded), minimized artifact address, original and minimized sizes, and the preserved signature. The minimization oracle: a candidate input preserves the crash IFF its replay produces the exact same signature (byte-identical) as the original, and the replay is x2 stable. An unknown campaign_id or crash_id is a loud tool error, never a fabricated result. Hosted-session only (MAX tier).


Fetch a fuzz campaign's code coverage

Report code coverage for a campaign (per-region breakdown).


Rewind to a real write (the boot-time TX buffer store)

Time-travel debugging (RFC-0019 B9), the genuinely-successful counterpart to lifecycle-reverse-continue: nrf52_uarte_tx_probe writes its "HELLO\n" TX buffer to 0x20000010 as its very first instructions (Reset_Handler, before UARTE_ENABLE), so this address has exactly one write, made near vtime 0 -- stable regardless of how far the shared session has otherwise advanced by the time this record runs. Run the sim via run_until first so trace capture precedes the query. Single-node sessions; the no-clock-read class.

reverse_continue to last write to 0x20000010: vtime <current> -> <write vtime>; goto reached <write vtime> ns


Drive a GPIO input pin

On the gpio_demo world the firmware's input port (IDR) is wired to link 1. Drive it by injecting a u32 little-endian pin word: to set IDR=0x00001234, send the 4 little-endian bytes 34120000 (NOT 00001234). The demo firmware spin-reads IDR and prints 'N0:idr=00001234' to the console once a nonzero word arrives (read it with read_console). Pass vtime_ns for a reproducible scenario; omit it (or 0) for as-soon-as-possible.

injected at vtime 5000000 (link 1, seq 0, 4 bytes)


Observe a GPIO pin frame

Tap link 1 with read_link to see the pin-state frames crossing the GPIO link on the gpio_demo world: the bytes you drove with inject_frame (your input edge) and the firmware's own ODR writes (its output edge) — each a u32 little-endian pin word. The observe stream buffers from the moment you first tap, so call read_link before advancing the clock to catch the firmware's boot-time ODR=0x0000ABCD edge (payload cdab0000).

[vtime 5000000 link 1 src node 4294967294 seq 0] 4 bytes: 34120000