Skip to main content

Module state

Module state 

Source
Expand description

STATE (vj-sim::state) — cross-track contract only: the Rust-owned 4-state arena’s SoA layout query shape, its value-change fan-out primitive, and the one canonical signal read/write/subscribe interface (ARCHITECTURE §2: “Signal read/write/subscribe: STATE owns the one canonical primitive; VPI/FST/debugger are pure consumers”). The arena implementation itself belongs to the state track, not this contracts wave.

Reconciliation: state.md calls the value-change listener trait ChangeListener; scheduler.md independently sketches the same contract under the name ValueChangeSink. Per the task’s naming and because scheduler.md’s signature is the more concrete of the two, this crate uses ValueChangeSink (ledger SC-071, RT-005) and does not separately define a ChangeListener.

Task 20 (ST-130) — dependency direction is an invariant, not an accident. RUNTIME consumes STATE’s reference read/write API (SignalAccess/ValueChangeSink) and its Bit/OwnedSVInt-adjacent FourStateValue type, never the reverse: FST/VPI/$display are consumers, not producers, of these interfaces, and no code anywhere in vj-sim::state calls into vj-runtime’s FST/VPI/$display formatting. This crate’s own Cargo.toml (crates/vj-sim/Cargo.toml) lists no vj-runtime dependency, which is the mechanically-checkable half of this invariant.

This is not a hypothetical edge: vj-runtime’s Cargo.toml already depends on vj-sim (since the very first crate-skeleton commit), and vj-runtime::signal_consumer::RuntimeSignalConsumer already consumes SignalAccess directly. Both halves of the invariant — the real, present edge in one direction and the absent edge in the other — are guarded by an automated test: crates/vj-sim/tests/dependency_direction.rs. Confirm live with cargo tree -p vj-sim -i vj-runtime (expected to report no path) or cargo test -p vj-sim --test dependency_direction; see docs/CONTRACTS.md’s “Wave A — wA-state notes” section for the fuller writeup, including for RUNTIME’s own wave to keep honoring as it grows more dependencies.

Re-exports§

pub use crate::driver_counts::*;

Modules§

event_plan
The design-wide event-pool plan (LRM 1800-2023 15.5, 23.6).
event_pool
The event pool: event handles (LRM 1800-2023 15.5.2 - 15.5.5).
force
force/release (LRM 1800-2023 §10.6.2): each engine’s table of forced cells (ForceTable, owned by its VcState), applied inside the one write primitive (write::commit_write_at) so that every writer – a blocking or nonblocking assignment, a continuous assignment, a port connection – is overridden the same way.
storage
The stored bit shape of every value type a signal or local can have.

Structs§

AliasChange
A change of a cell the scheduler has to announce because the write that caused it was made through a different name for the same storage.
AliasWindow
A sub-range of a root cell: width bits from bit lo, of memory row row when the root is a memory (an unpacked array’s element).
Arena
The one design-wide flat SoA backing store (ST-007: grouped by storage tier, not a per-signal heap object model). inline holds every bit_width <= 64 signal as a packed u128 (val low 64 / xzmask high 64); wide_aval/wide_bval hold every wider signal and every memory element, word-aligned, at identical relative offsets in both planes (ST-009); inline_2s/wide_2s are the 2-state-build counterparts (Task 10 exercises them; this task only reserves the fields).
ArenaSnapshot
ST-098: the one generic slice-granular checkpoint/restore primitive’s data — see this module’s top doc comment, deviation 4, for why range is a fifth field beyond the plan’s literal list, and deviation 5 for why build_mode/inline_2s/wide_2s exist beyond that (only one of {inline, wide_aval, wide_bval}/{inline_2s, wide_2s} is ever populated for a given snapshot — whichever plane-pair build_mode says is actually live — the other stays empty Vecs).
ChangeDelta
The bits one committed update changed, as plane words (aval/bval, the Wide encoding): lo_word is the plane-word index of old_a[0], so a windowed update of a huge vector carries only the words it touched. A bit/part-select event control (LRM 9.4.2) compares the selected bits of old and new to decide whether its operand changed, and takes an edge on the select’s least-significant bit.
ChangeResult
ST-109’s exact shape: commit_write’s return value.
Dispatch
The fixed 3-role VC listener dispatch table (ST-073, ST-074). See this module’s doc comment for why this is 3 named fields, not a collection.
DriveStrengthPair
A driver’s own strength for each value it might drive (state.md §4): s0 applies when this driver is driving 0, s1 when driving 1; driving X reduces to min(s0, s1) (see [effective_strength]).
DriverEntry
One driver’s own private contribution slot plus its strength (ST-034): source is a StateCellId this driver alone writes to (never shared read-modify-write storage with any other driver or with resolved).
DriverSet
The K+1 slots design (ST-034): resolved is one more StateCellId (StateEngine::alloc_cell) than the K entries in drivers — never a single shared slot every driver writes through. Hand-built by every test in this task (see this module’s own top doc comment, Scope note).
FrameArena
The STATE-owned backing storage for every process::ActivationRecord this crate hands out (ledger ST-103, ST-104, ST-110). Exactly three flat Vecs, one per ArenaRange-typed field an ActivationRecord can embed — see this module’s doc comment for why there are exactly three and not, say, one shared buffer.
InstanceBase
One concrete instance’s base index into the two global arrays, relative to which every one of its template’s template-relative super::SlotOffset values must be added (ST-129) to get a real address — that addition is ENGINE’s job, never performed here.
InstanceHandle
The concrete type this plan’s header design decision makes SimHandle point at (Task 8 adds the SimHandle conversion). layout/arena are private raw pointers; they stay valid for the life of this handle (never merely for the life of the StateEngine that minted them — see below) for two independent reasons (review fix, Task 7 r1 — the original comment here conflated them and was wrong about arena):
NbaWindow
A nonblocking write to part of a huge signal, in plane words: aval/ bval/mask cover the plane words from window on.
NetLayout
A net member’s electrical classification, when known (ledger ST-141).
RegionAssignment
This task’s stand-in for the netlist/analysis track’s not-yet-built static clustering pass’s real RegionId/write-set output (ST-122) — a real caller eventually supplies these from that pass; this wave’s own tests hand-build them directly.
RegionOverlapError
The loud, typed diagnosis check_region_disjoint returns instead of a generic “overlap detected” (ARCHITECTURE §7 item 9’s “loud, typed, diagnosable” discipline applied here): names the offending cell and both of the (in assignments-slice-order) conflicting regions it was claimed by.
RollbackMark
An opaque log-length marker (ST-036) — see UndoLog::checkpoint_marker. The wrapped index is absolute over the log’s entire lifetime (it includes entries UndoLog::fossil_collect has already trimmed), never merely an index into the log’s currently retained Vec — see UndoLog::rollback_to’s own doc comment for why that distinction is what makes the fossil-horizon check correct.
SlotDescriptor
One signal’s slot within a StateLayout (state.md Key Data Structures; ledger ST-141).
SlotLocation
The already-absolute (base already added) address StateEngine::commit_write reads/writes at; both InstanceHandle-relative accessor calls and StateCellId-keyed calls resolve down to this shape before calling in.
SlotOffset
Compile-time layout query result STATE hands to CODEGEN (state.md §3: “baked into compiled kernels as LLVM constants”; ledger ST-027, ST-037, ST-129, ST-134, ST-135). Pure Rust (not #[repr(C)]) — this crosses the CODEGEN/STATE boundary as ordinary Rust values at compile time, not as bytes across the compiled-kernel ABI, so it has no FFI-layout obligation. Task 7 adds PartialEq (previously Clone, Copy, Debug only): slot_of (ST-129) is required to return a byte-identical SlotOffset across repeated calls regardless of intervening allocate_instance calls, and the direct field-by-field way to prove that in a test is a structural equality check, not a Debug-string comparison. Every field is itself PartialEq (SlotClass already derives it; the rest are plain integers), so this is a safe, non-lossy derive addition.
SnapshotRing
A bounded ring of ArenaSnapshots: “current” (the most recently pushed entry, ring index 0) plus up to depth older entries. Never retains more than depth + 1 entries, no matter how many times Self::push is called – the “a design with no $past retention need pays no retention cost beyond entry 0” guard (see snapshot_ring_depth_0_design_allocates_no_ring below). Whether a ring is allocated at all for a given instance is a decision this type does not make – sched::control::Engine only ever constructs one when the design has at least one assertion/clocking-block/$past site to begin with (§1.4’s “not every instance, to avoid taxing designs with no SVA at all”); a design with none never calls Self::new and pays nothing.
StateArena
#[repr(C)] FFI view of the arena’s storage planes (ST-145) — the shape a compiled kernel’s accessor-ABI functions index through. wide_aval/ wide_bval are null in a 2-state-compiled binary.
StateCellId
Opaque per-signal handle, STATE-assigned (scheduler.md: “STATE-owned opaque handle”; ledger ST-109, ST-111, SC-037). Distinct from vj_abi::SlotId: SlotId is the ABI-facing, compile-time-constant index CODEGEN bakes into IR; StateCellId is the in-process (non-FFI) handle the scheduler/determinism/ runtime Rust code passes around at simulation time. STATE’s own implementation is free to make them numerically related, but the two names stay distinct contract types because their callers (CODEGEN vs. the scheduler) are on opposite sides of the ABI boundary.
StateEngine
STATE’s top-level owned state (arena storage + the compiled template set) — review fix: arena/templates are Rc-wrapped, not Box-wrapped or embedded by value. A SimHandle/InstanceHandle minted by Self::allocate_instance must stay valid for the life of the simulation run and cross the compiled-kernel FFI boundary, so the raw pointers it stores must survive StateEngine itself being moved — returned by value from a constructor, pushed into a Vec, or embedded in a future root object are all ordinary things a caller does (this task’s own regression test exercises exactly this). With arena/templates embedded directly, any such move would silently dangle every previously-minted handle — no compiler diagnostic, UB on the next accessor call.
StateKernelCtxFields
ST-131: this struct names only the fields STATE owns. The full KernelCtx a compiled kernel actually receives (current sim time, the rest of the suspend/resume contract) is jointly defined by ENGINE/CODEGEN and is out of this crate’s scope — it is not redefined here. This is a documented sub-shape other tracks’ real KernelCtx embeds (by value or by reference), never a claim that STATE owns or defines the whole thing.
StateLayout
Compile-time layout for one unique (module,params) template (state.md Key Data Structures; ledger ST-140). One StateLayout per distinct crate::netlist::TemplateKey (Task 5’s TemplateTable maps keys to these); inline_words/wide_words are the template-relative word counts Arena bulk-allocators (Task 7) reserve per instance.
TemplateRegistry
Task 7’s owned, 'static substitute for dedup::TemplateTable<'d> (see this module’s top doc comment) — the minimal (TemplateKey, StateLayout) registry StateEngine actually needs after elaboration, once the sv_lang::Design-borrowing dedup walk itself is done.
TemplateTable
One entry per unique (module,params) template; templates’ insertion order is first-seen order during the deterministic DFS walk below — ST-064’s “identical offsets across runs” precondition falls out of that determinism, not a separate sort.
TriregCharge
trireg’s own charge-decay state (state.md §4/§6): stored in StateEngine’s own side table (instance.rs’s trireg_charge field), never as a normal DriverEntry — trireg decay timing itself is a TIMING-subsystem hook this task only stores state for, not implemented here.
UndoLog
ST-036: the per-thread undo/reverse log — see this module’s top doc comment for why it is a plain, caller-owned value (like ()) rather than a StateEngine-owned field. Implements PreWriteHook: every commit_write(cell, new, &mut log) call appends (cell, old, at) here before the store happens (Task 8/ST-091’s own ordering guarantee), giving that hook its first real body.
VcRecord
The VC-buffer’s one record shape (ST-079, ST-109) — later drain_committed_changes consumes this type as-is, never redefined.
WorkerSlot
Opaque handle to one thread’s registered speculative VC buffer, returned by StateEngine::register_worker_thread. pub(crate) inner index: see this module’s top doc comment, deviation 1.

Enums§

BuildMode
Whether an Arena backs a 4-state (aval/bval, X/Z-capable) or 2-state (inline_2s/wide_2s, val-only) compiled binary. Feeds Task 10’s 2-state build path; this task only allocates/exposes the storage, it does not yet implement 2-state read/write semantics.
ChargeStrength
trireg’s charge-strength lattice — kept separate from DriveStrength (state.md §4: trireg charge decay is not part of the ordinary K+1 drive-strength fold combine_bit performs).
DefaultScopeError
Task 8 (sva T8, docs/superpowers/plans/2026-10-03-sva.md): LRM 1800-2023 14.12’s default clocking and 16.15’s default disable iff – the scoping half only (which single declaration, if any, covers a given scope): resolving the lexical-nesting/override rules both clauses state almost verbatim (“valid only within the scope containing the declaration … as well as any nested module[s] … It does not include instantiated modules” – 14.12; “provides a default … to all concurrent assertions in the scope and subscopes … the default extends to any nested module … However, if a nested module … has a [its own] declaration, then that … overrides any default … from outside” – 16.15). The resolution-rule half of 16.15 (composing this against an assertion’s own explicit disable iff clause, rules (a)/(b)/(c)) is T27’s job, consuming default_disable_of’s result – not reimplemented here.
DriveStrength
The 4-level drive-strength lattice (state.md §4 simplifies the LRM’s 8-level charge/drive split to these 4 for resolution purposes). Ord/PartialOrd are hand-written, not derived: the plan pins Supply > Strong > Pull > Weak, the reverse of this enum’s own declaration order (declaration order is what #[derive(Ord)] would use).
DriverError
The loud, typed rejection [combine_bit]/resolve_net_step return instead of panicking or silently guessing a winner for a net kind this fold cannot resolve (ARCHITECTURE §6.5’s “hard-error, not silently wrong” for nettype...with, applied to this crate’s own NetKind enum per ARCHITECTURE §7 item 9).
EdgeKind
Mirrors sv_lang::EdgeKind’s four variants (ledger SC-045, SC-067, SC-071, NL-047, NL-083). Kept as this crate’s own copy (rather than a re-export) because vj-sim’s cross-track contract layer does not depend on sv-lang in this wave (see docs/CONTRACTS.md open questions); the netlist track owns the real conversion once it wires the sv-lang dependency for its own elaboration work.
FourStateValue
STATE’s cross-subsystem four-state value representation (state.md §3/§5, ledger ST-008, ST-009, ST-099, ST-120): Narrow mirrors the accessor ABI’s FIXED inline u128 word (ARCHITECTURE §6.2 / ST-008: val in the low 64 bits, unk/xzmask in the high 64 bits, regardless of bit_width — vj_abi::bit4-compatible; not vj-codegen::value’s internal, width-tight i(2N) LLVM packing, which uses a different bit offset for any width < 64 — see vj_abi::accessor’s doc comment); Wide mirrors the aval/bval word planes (vj_abi::WideView-compatible). Reference/introspection shape only (VPI/$display/debugger/harness) — never the codegen-emitted hot path, which calls the accessor ABI directly.
HugeCommit
What huge_commit decided; the caller (sched::kernel_adapter, which owns the scheduler hooks this crate layer cannot name) carries it out.
LayoutError
The loud, typed error this and every later layout task returns instead of silently skipping a member kind this plan claims to handle (ARCHITECTURE §7 item 9 applied to SymbolKind).
ListenerRole
The fixed, small listener-role table state.md §5 dispatches value changes to.
NetKind
Mirrors sv_lang::NetKind’s variant set minus Interconnect (state.md §4: an interconnect net never gets a slot at all, so it has no NetLayout value to represent) and minus the Unknown error placeholder (never a real declared net’s kind). Kept as this crate’s own copy rather than a re-export of sv_lang::NetKind so this module’s exhaustive-match discipline (ARCHITECTURE §7 item 9) applies to a type this crate owns, independent of sv_lang’s own variant set ever changing underneath it.
NetShape
The multi-driver resolution shape a net’s storage takes (ledger ST-141): how many distinct strength-bearing drivers NetLayout’s owning net has and whether the full K+1 strength lattice must be evaluated to resolve it.
SlotClass
The storage tiers state.md §1 splits every slot into (ledger ST-020..ST-024, CG-107, CG-163).

Constants§

EVENT_CELL_WIDTH
Width of a design-declared event variable’s cell (state::layout, vj::run::signal_slots_of) – a 2-state SlotClass::Wide cell that compiled code (vj-codegen::expr::lower_event_trigger) treats as two 64-bit halves: the low half COUNTS triggers, the high half holds tick + 1 of the most recent trigger (0 = never), which is what e.triggered compares against the current time. Counting rather than toggling matters because blocking writes are flushed once at segment end: two toggles in one segment would cancel and report no change.

Traits§

ForceObserved
The corresponding runtime guard for external writes (ST-122’s own acceptance text: “an external write (force/release/vpi_put_value) never performs a direct disjoint-range write, only a serialized ForceObserved/on_force_observed path”). driver::force_driver/ release_driver (Task 14, retrofitted by this task) each call every listener registered via StateEngine::register_force_observed, so a future partitioning/independence assumption can invalidate itself the instant an external force/vpi_put_value touches a region-crossing net, rather than trusting a now-stale static classification (ARCHITECTURE §4/§9).
PreWriteHook
The ST-091/ST-031 “hook present in the calling convention from the first freeze” requirement made concrete now: commit_write takes hook: &mut dyn PreWriteHook as a required parameter, not an optional add-on, so a later real undo-log PreWriteHook implementer slots in without touching this signature or any of its call sites.
PureKernel
ST-132: a marker trait, no methods. A ProcessKernelFn/ProcessFn implementation that also implements PureKernel is asserting the contract ST-132 requires: it is derivable purely from (base, ctx), with no other mutable state (no hidden thread-local/global) reachable from inside it. This crate cannot compel a compiled kernel to implement this trait (CODEGEN’s own IR emitter, out of this wave’s scope, would need a #[forbid(...)]-style procedural check for a compiler-level guarantee) — STATE’s own enforcement mechanism this wave is the structural lint test below: proving that nothing STATE itself exposes to a kernel author encourages violating the contract (no STATE-owned API hands out a thread-local or global mutable handle a kernel could close over instead of going through (base, ctx)).
SignalAccess
STATE owns the one canonical signal read/write/subscribe primitive (ARCHITECTURE §2); RUNTIME’s VPI/FST/$display/debugger hosts are pure consumers of this trait, never a second implementation of signal access (vj-runtime::signal_consumer demonstrates this by depending on it).
ValueChangeSink
Called by STATE/NETS after any committed write or net resolution (scheduler.md §4: “the single, uniform fan-out point for variable writes AND net resolution”; ledger SC-071, RT-005).

Functions§

bits_to_aval_bval
bits (LSB first) as IEEE 1800 aval/bval word planes, at least one word.
bits_to_four_state
Packs a slice of sv_lang::Bit into STATE’s cross-subsystem FourStateValue (ST-010): <= 64 bits pack a Narrow(u128) with val in the low 64 bits and unk/xzmask in the high 64 bits (ARCHITECTURE §6.2 / ST-008’s FIXED accessor layout, independent of width), built bit-by-bit through Bit4::to_val_unk; > 64 bits pack Wide{aval,bval} word planes through Bit4::to_aval_bval (ST-148’s wide-plane polarity table) — bits[0] is bit 0 of both representations.
build_template_layout
cell_for_slot
check_region_disjoint
A build-time check (ST-122) confirming no StateCellId appears in more than one assignments entry’s cells set. Does not itself allocate region-disjoint arena sub-ranges (see this module’s top doc comment) — this function’s only job is the checkable invariant. engine is accepted (matching the plan’s pinned signature and this crate’s convention of every region-facing query taking the owning StateEngine) but this wave’s check is purely a property of assignments itself — a future wave that also validates each StateCellId against engine’s own allocated cell table (StateEngine::alloc_cell) can extend this function without changing its signature.
class_static_instance
Classes plan Task 4 (docs/superpowers/plans/2026-10-03-classes.md, LRM 8.9): the (InstanceId, TemplateKey) pair [StateEngine:: allocate_class_static_singletons] uses for one ClassTemplateId’s static-property singleton segment — a deterministic, pure function of class_template alone (never re-derived differently by two call sites), so every caller (that one allocate_instance call, and every later foreign-slot reference a Class::static_prop/obj.static_prop access mints) agrees on exactly which synthetic instance a given class template’s statics live in.
clocking_blocks
body’s clocking blocks, each with its input (and inout) clock variables, in declaration order – the extra slots of a template, in the order both the layout and the codegen signal map give them.
compile_source
This track’s own copy of vj-codegen/src/frontend.rs::compile_source (kept local rather than depending on vj-codegen for a 4-line helper); reused by every later task’s sv_lang fixture tests in this crate.
constant_expr_bits
The bits (LSB first) of the constant expression e, at its own type’s width. None when e is not an integral constant.
copy_slot_quiet
Copies src‘s current value into dst of the same instance without reporting a value change: the sampled value functions’ hidden P/S cells (crate::sva::sampled) are history, not signals – no listener waits on them, no waveform shows them, and their updates must not wake a process. src may be a foreign slot (a hierarchically connected signal); dst is always one of the instance’s own cells, of the same class and width as src by construction (hidden_slots clones the source’s shape).
default_clocking_of
LRM 14.12’s resolved default clocking block for scope: the nearest enclosing scope’s default clocking clocking_identifier; reference or inline [default] clocking ... endclocking declaration, as an actual ClockingBlock Symbol – None if no scope in the chain declares one.
default_disable_of
LRM 16.15’s resolved default disable condition for scope: the nearest enclosing scope’s default disable iff expr; declaration, together with the Symbol of the scope it was declared in – “Any signals referenced in the disable iff declaration that are resolved using scopes will be resolved from the scope of the declaration” (16.15), so a later compiling pass (T27) must bind expr’s names against that scope, never against the querying assertion’s own (potentially more deeply nested) scope. None if no scope in the chain declares one (16.15 rule (c), “no inference … equivalent to … a 1’b0 disable condition”).
force_driver
force/release/deassign (ST-055), modeled as a synthetic, singleton-partition dynamic driver: allocates one more DriverEntry (Supply-strength, so it always wins outright per the lattice above, matching force’s LRM override semantics), seeds its private contribution cell with value, pushes it onto set.drivers, and returns its StateCellId so the caller can later release_driver it.
four_state_of_constant
Extracts a FourStateValue from a sv_lang::ConstantValue, reusing sv_lang’s own constant-folding machinery rather than a parallel evaluator (ST-012): None for any non-integer constant (real/string/null/etc — this function only speaks the 4-state integer representation FourStateValue exists for).
four_state_to_owned_svint
Builds a sv_lang::OwnedSVInt of the given width/signedness from a FourStateValue, reusing sv_lang’s own owned-integer type rather than a locally-defined equivalent (ST-011): starts from OwnedSVInt::create_fill_x (every bit X, matching this function’s honest default for a bit this loop hasn’t visited yet) and threads each bit’s known (0/1) or Z state in via set_range/create_fill_z — an X bit is left untouched (already correct from the fill), a 0/1/Z bit overwrites its single-bit range.
huge_commit
Commits the dirty window of (handle, slot)’s mirror to the planes.
huge_commit_full_done
Called after the HugeCommit::Full write: the write itself went through write_slot, which invalidated the mirror; a mirror that is still current (revalidate) is made valid again.
huge_mark
Widens the dirty window of (handle, slot)’s mirror by the value bits [start, start + len) (clipped to the signal; len <= 0 is a no-op, which is what an out-of-range select that wrote nothing passes).
huge_nba_window
The windowed form of sig[start +: len] <= image: img is the PK image of the len-bit right-hand side (val plane at bit 0, unk plane at bit len). The target bits [start, start + len) are clipped to the signal, as a part-select write clips; None when nothing is left (the write has no effect). Costs O(len), never O(width).
huge_prime
The process shadow of the huge signal (handle, slot) of width bits: the signal’s mirror, valid and current. force_rebuild re-reads the planes even from a valid mirror – for the caller that knows external code wrote them behind the mirror’s back.
own_slot_count
The number of slots of handle’s instance’s own layout – the first foreign slot’s number.
packed_words
bits (LSB first) in codegen’s packed form – (unk << W) | val for W = bits.len() – as ceil(2W / 64) little-endian words (at least one): a collection element’s stored form (crate::collections).
read_by_hier_path
ST-118: resolves path through netlist’s own hierarchical-path table, then STATE’s own InstantiatedSignalId -> StateCellId map, then reads the live value through SignalAccess::read — the exact same primitive every other STATE consumer (the accessor ABI, driver::resolve_net_step) reads through (ST-152’s “no second, independently-populated store” discipline applied to introspection too). Returns None, never a fabricated/zero value or a panic, for a path netlist cannot resolve, or (structurally impossible in practice once allocate_instance is the only producer of InstantiatedSignalIds this map ever sees, but checked anyway rather than .unwrap()ed) for one netlist resolves to a signal this StateEngine never allocated storage for.
read_inline_2s
Task 13 — 2-state build-mode arena and read/write family (ledger ST-100, ST-101, ST-102, ST-139).
read_slot
Reads the current word(s) stored at loc, without any diffing — commit_write_at’s own old read (Task 9 retrofit: a free function over &Arena, not a StateEngine method, so it is callable from the accessor ABI’s InstanceHandle-only call sites too — see this module’s top doc comment).
read_wide_2s
Reads elem_word_count words starting at word_offset from the wide 2-state plane, no diffing.
real_sv_get_mem_word
The production binding of sv_get_mem_word (see real_sv_get_scalar).
real_sv_get_scalar
The production binding of the sv_get_scalar ABI symbol: sv_get_scalar after resolve_foreign_slot.
real_sv_get_wide
The production binding of sv_get_wide (see real_sv_get_scalar).
real_sv_rt_inline_ptrs
const u128 *const *sv_rt_inline_ptrs(SimHandle): for each slot number of handle’s instance, the address sv_get_scalar would read (null where the slot needs the accessor: not Inline, or a windowed alias). JIT code calls it once per segment and loads an Inline signal directly, so a process activation costs one call rather than one per signal. Valid for the rest of the kernel call (InstanceHandle::inline_ptr_table). Lives here, not in accessor_impl.rs, whose source-scan test forbids raw pointer returns (this is a read-only table, never base+offset arithmetic).
real_sv_rt_slot_owner
SimHandle sv_rt_slot_owner(SimHandle, SlotId): the instance whose storage slot of handle is – handle itself for one of its own slots, the instance a foreign slot names otherwise (see set_foreign_slots). A hierarchical call into another instance (u1.f(), LRM 23.6) passes this as the callee’s instance handle, so the callee’s memories and block events are u1’s own.
release_driver
The other half of force_driver: removes forced’s one DriverEntry from set.drivers and re-runs resolve_net_step, unwrapping its Ok — force/release are only ever exercised on ordinary, non-UserDefined nets in v1 (this module’s top doc comment, Scope note), so the Err arm is unreachable from this function and it never wraps it further.
resolve_foreign_slot
(handle, slot) itself for one of the instance’s own slots; for a foreign slot (see set_foreign_slots), the instance and slot it names. Every production entry point that reads or writes a slot’s storage for a compiled kernel resolves through this first (real_sv_get_*, real_sv_set_mem_word, force); a cell lookup needs no resolving (a foreign slot’s cell is in the instance’s own cell table).
resolve_mem_row
Task 11 (ST-138): state.md §3’s pinned formula, verbatim: word_offset' = word_offset + row_stride_words * row_index, row_index a runtime value. Panics (never a silent OOB read/write) if loc.class != SlotClass::Memory or row_index >= loc.num_rows as u64 — ARCHITECTURE §7 item 9’s “loud, never silent” discipline. The returned SlotLocation still has class: SlotClass::Memory and the SAME elem_word_count (the layout builder already sets elem_word_count == row_stride_words for every real Memory slot — one row’s own word count — so the resolved location slots directly into the existing read_slot/write_slot/ commit_write_at primitives with zero further change).
resolve_net_step
Reads all K contribution cells via SignalAccess::read, folds them bit-by-bit through [combine_bit] (propagating the first Err out via ?, never partially committing some bits and rejecting others), and commit_writes the result into set.resolved only on a fully-resolved Ok — schedulable exactly like any other write (ARCHITECTURE §6.2: this function is the pure function ENGINE later wraps as a synthetic sensitivity-driven process, not the scheduling itself).
resolve_strengths
Per-bit resolution of values like resolve_values, keeping each bit’s strength (LRM 28.12; None for z) for %v (LRM 21.2.1.5). Index 0 is bit 0. supply0/supply1 nets are Supply.
resolve_values
Resolves one width-bit net of net_kind from its drivers’ current values and strengths (each full-width; a bit a driver does not drive is z), per bit through [combine_bit]. A supply0/supply1 net is its constant unless a supply-strength driver disagrees (x). The result is shaped Narrow (up to 64 bits) or Wide.
set_foreign_slots
Task F3 (docs/superpowers/plans/2026-09-27-wave-A2-slice-finish.md, CG-125/NL-035): resolves slot (the same SlotId sv_get_scalar/ sv_set_scalar already address by) to the absolute StateCellId a synthetic inter-instance port-connection process needs for crate::sched::SchedulerOps::commit_blocking_write/ crate::sched::StaticSensitivity (registering a wake on the source side, writing through the canonical commit path on the destination side). Every other StateCellId in this crate is minted directly by StateEngine::alloc_cell/allocate_instance’s own per-slot loop (InstanceHandle::cell_for_slot, pub(crate)); this is the one place a caller outside this crate (vj’s hierarchy pipeline, which holds a SimHandle+SlotId pair from its own signal-slot bookkeeping but never runs allocate_instance’s loop itself) needs to look one back up. Gives handle’s instance its foreign slots: slot own + k (where own is the instance’s own slot count, returned) names targets[k], another instance’s own slot. A target that is itself a foreign slot is followed to its owner.
set_foreign_slots_windowed
set_foreign_slots where a target may be a window of its slot – a ref port bound to a select (IEEE 1800-2023 23.3.3.2, 13.5.2): such a foreign slot is a view of the target (state::alias) with its own cell, so a wait on it wakes only when the window changes.
set_mem_word_reporting_change
sv_set_mem_word’s body, returning the memory’s whole-array cell and whether the row’s stored value actually changed – what a scheduler-aware caller (sched::kernel_adapter::real_sv_set_mem_word) needs to wake processes sensitive to the memory.
splice_window
root with window replaced by new (in root’s own representation).
sv_get_mem_word
Task 12 (ST-024, ST-025, ST-114): memory-word accessors. slot addresses the whole SlotClass::Memory array the same way sv_get_wide/ sv_set_wide address a whole SlotClass::Wide signal ([slot_location_for] is shared, unchanged); the one extra step is resolve_mem_row, applied to that base location with the caller’s runtime row_index, per state.md §3’s pinned word_offset' = word_offset + row_stride_words * row_index formula. Both fns panic (never silently read/write past the array) if row_index is out of bounds or slot does not resolve to a SlotClass::Memory location — see resolve_mem_row’s own doc comment for the exact panic conditions.
sv_get_mem_word_inline
The sv_get_mem_word twin of sv_get_scalar_inline (ST-026, Task 12).
sv_get_scalar
Narrow (SlotClass::Inline) scalar read (ST-020): vj_abi::accessor’s FIXED val low 64 bits / unk high 64 bits layout, read straight out of the arena’s inline plane at handle’s own base-applied word offset.
sv_get_scalar_inline
Task 10 (ST-026): the concrete artifact ARCHITECTURE §7 item 1’s “accessor-ABI as the contract with alwaysinline wrappers” resolves to on STATE’s side — a thin, #[inline(always)] Rust-level wrapper around the extern "C" fn above, a pure pass-through never a second, divergent implementation. CODEGEN is expected to call these (or emit its own alwaysinline-attributed LLVM declaration of the extern "C" symbol directly — either way, STATE’s obligation is only that an inlinable entry point exists and is documented as the one sanctioned call shape).
sv_get_wide
Wide (SlotClass::Wide) non-memory read (ST-022): a non-owning WideView over handle’s own aval/bval plane words at slot’s base-applied offset — word_count words starting at that offset in each plane, per vj_abi::accessor’s documented shape.
sv_get_wide_inline
The sv_get_wide twin of sv_get_scalar_inline (ST-026).
sv_set_mem_word
ST-025: the sv_set_wide-shaped write counterpart, through the same resolve_mem_row step and the same commit_write_at primitive every other write in this crate goes through — never a second, ad hoc write path. cell is this slot’s one whole-memory StateCellId (the same handle.cell_for_slot(slot.0) sv_set_wide uses; a SlotClass::Memory slot mints exactly one cell for the whole array, not one per row — see StateEngine::allocate_instance’s own per-slot loop), so a VC record from this fn reports “this memory changed”, identifying the row via at/the caller’s own row_index, exactly as sv_set_wide already does for a whole non-memory wide signal.
sv_set_mem_word_inline
The sv_set_mem_word twin of sv_get_scalar_inline (ST-026, Task 12).
sv_set_scalar
Narrow scalar write (ST-021): writes value and reports whether it changed by routing through [commit_write_at] — the same VC-detection primitive StateEngine::commit_write uses — never a second, parallel write path. The extern "C" signature itself returns () (the frozen ABI type alias, vj_abi::accessor::SvSetScalarFn); the change report lands in the same speculative VC buffer every other commit_write_at caller’s changes do, observable via write::last_vc_record_for in tests.
sv_set_scalar_inline
The sv_set_scalar twin of sv_get_scalar_inline (ST-026).
sv_set_wide
Wide non-memory write (ST-023): copies value’s elem_word_count words out of the caller-supplied WideView into an owned FourStateValue::Wide and routes through [commit_write_at] — the wide-tier sibling of sv_set_scalar, through the identical shared primitive.
sv_set_wide_inline
The sv_set_wide twin of sv_get_scalar_inline (ST-026).
svint_bits
v’s bits, LSB first, at its full width (sv-lang 0.1.17 expands every width; before it, values past 1024 bits came back empty). None if the slice ever disagrees with bit_width() – never a fabricated value.
synthetic_z_layout
A layout of one slot per entry of widths holding no declared signal – the contribution cells of resolved nets’ drivers (vj’s nets): each Inline or Wide by width, starting all-z (not driving).
synthetic_z_layout_rows
synthetic_z_layout where cell k with rows[k] > 0 is a Memory slot of that many rows of widths[k] bits, every row all-z: the contribution cell of a driver of an unpacked array of nets, whose elements are resolved one by one (vj’s nets). A row starts at fills[k] (LSB first) when that is non-empty, else all-z: a user nettype’s driver has no z (its data type’s default value instead); a non-memory cell with a non-empty fills[k] starts at it too (a specify path’s staging cell starts x, LRM 30.5.1).
window_value
The value of window within root: Narrow up to 64 bits, Wide beyond, as the storage class of a slot of that width is.
write_inline_2s
Writes value at word_offset iff it differs from the currently-stored value (plain u64 equality – ST-102’s “without any mask comparison”: there is no mask half in this family to compare). Returns whether a real change occurred.
write_slot
Stores new at loc. Only commit_write_at (below) calls this in the ordinary write path, and only after four_state_changed has already confirmed a real change — see this function’s own SAFETY comments for the single-logical-writer argument that makes the two unsafe Arena accessor calls sound.
write_wide_2s
Writes values starting at word_offset in the wide 2-state plane iff they differ from what’s currently stored (element-wise u64 equality, no mask half). Returns whether a real change occurred.