Expand description
wprog_engine_rtl — the WHOLE-PROGRAM spatial engine: run an entire control-flow program on the
reconfigurable fabric, streamed over the wire, not just one straight-line block. Where the block engine
(block_engine_rtl) runs a single straight-line residual, this runs a whole ProgramCfgFabric — multiple
basic blocks with branch conditions (Select cells), a register file that survives block reconfiguration,
and a block-walk terminator (Jump / Branch / Return) — the RTL of run_program_cfg_on_grid driven by a
flat config, so ANY acyclic branchy program runs on ONE physical array by loading its config.
This module is the reference + config layer (pure Rust, provable without a simulator): serialize_program
flattens a ProgramCfgFabric into a WProgConfig, and eval_wprog_config interprets that flat config
exactly as the RTL will — proven bit-identical to run_program_cfg_on_grid (≡ the VM) by
wprog_engine_lock. The RTL emitter + the over-the-wire SoC lower this same flat config to silicon.
Correct-first scope: Arith + Select cells (arithmetic + branch decisions) — the whole acyclic branchy
control-flow family. The state plane (Mem cells → heap BRAM), loops (the loop fabric), and function
calls (a call stack) are the follow-ons that extend it toward the whole language.
Structs§
- WBlock
- One basic block: live-in registers, cells, register writes, terminator.
- WCell
- One cell.
kind: 0 = Arithop(ops[0], ops[1]), 1 = Selectcmp(ops[0],ops[1]) ? ops[2] : ops[3], 3 = HeapReadheap[ops[0]], 4 = HeapWriteheap[ops[0]] = ops[1](drivesops[1]), 5 = HeapAlloc (grow the heap byops[0]words, drive the new base pointer) — the state plane, mirroringeval_grid_with_heap. - WImage
- The flat, loadable image of a whole program: parallel per-field arrays the generic engine reads directly.
Block
b’s fields areblk_*[b]; its cells arecell_*[blk_cell_ptr[b] .. + blk_n_cells[b]]; and so on. - WProg
Config - A whole program flattened for the fabric.
- WRun
- Everything one engine run produces: the entry’s return value, the block-step count, the final heap, the peak
call-frame depth, and the output tape (every
Show, in program order). - WShow
- One
Showthe engine executed: the value tag the lowering assigned in the SHOW cell’scode(from its exact per-register type tracking, so a reused register is shown as what it holds now) and the raw 64-bit value. A Text needs the heap to render — seerender_show. This is exactly the record the RTL streams over the byte channel (REC_SHOW, tag, payload) the moment the cell runs, so the tape the model builds is the byte stream the host reads. - WSrc
- An operand source in the flat config: a live-in register (
In), a constant, or an earlier cell’s value. - WWork
- What a run did, in the units the engine’s cycle count is made of: every executed cell’s cycles with its data-dependent part priced from the data (a divide’s 64 steps, a power’s multiplies, a text’s or a copy’s length), and the blocks, register writes, calls and returns around them.
Enums§
- MemBackend
- The MEMORY BACKEND for the config-driven engine’s on-chip memories — a first-class, selectable choice of
substrate. The whole-program engine holds two kinds of memory: the STATE PLANE (the shared
heap+ the recursionsfileframe stack) and the CONFIG memories (the UART-streamed program image:blk_*,cell_*, live-ins, writes, constants). Each can live in LUT flip-flops or in block RAM. - WFault
- A hardware fault model injected into the golden interpreter. Running a program through the model under each
fault and comparing the result against a value observed on silicon localizes a synthesis/place-and-route
divergence to one state memory + one address bit in pure Rust:
evalis the ground truth, so a fault whose result equals the observed value characterizes the hardware defect. - WTerm
- A block’s terminator in the flat config.
Constants§
- FPU_V
- The proven bit-exact sequential IEEE-754 f64 FPU (
fpu.vbeside this module;benchmarks/silicon/fpu.vis its golden copy for the FPGA flows) —fp_addsub/fp_mul/fp_div/fp_sqrt(start/done handshake) + combinationalfp_cmp. Appended verbatim after the engine module so the over-the-wire SoC can instantiate the float units; the engine’s 64-bit register file / heap already carry f64 bit patterns, so the FPU is the only float hardware the engine needs. Bit-exact vs Rustf64over 41,521 vectors (its ownverify-fpu.sh), and the exponent path is timing-safe (see thefp_divpipeline lock). - MUL_
FUNC_ F - The combinational
func_ffor the over-the-wire SoC: no integer func code computes here. DivS/ModS/FloorDivS/Pow (codes 0–3) run on the engine’s sequential datapath (wpe_soc_template.vst==6, a magnitude-restoring divider and a repeat-multiply), MulHiU (4) and MulOvfS (12) on its multiplier unit (ST_MU..ST_MUW), and the float lane on the FPU, so no multiply or divide shares a cycle with the cell walk. Shared by all three SoC engines (behavioral/synth/hw) so they agree byte-for-byte; the sequencing and results are proven ≡FuncUnit::eval(== the VM) under iverilog. - WPE_
DEPTH - Frame-stack depth (max recursion).
- WPE_
HEAP - Heap state-plane depth (words) — a projection builds a weight Seq + activation Seqs.
- WPE_
HW_ DEPTH - WPE_
HW_ HEAP - WPE_
HW_ MAXARG - WPE_
HW_ MAXBLK - WPE_
HW_ MAXCELL - WPE_
HW_ MAXK - WPE_
HW_ MAXLIV - WPE_
HW_ MAXWR - WPE_
HW_ NREG - WPE_
MAXARG - Max TOTAL call arguments across the whole program — the flat
arg_opconfig memory. - WPE_
MAXBLK - Max basic blocks per program (
blk_*[0:MAXBLK-1], indexed by the block PC). - WPE_
MAXCELL - Max TOTAL cells across the whole program — the flat
cell_*config memory (indexed byblk_cell_ptr[b]+ci, an accumulating pointer) AND the per-block value arrayv[0:MAXCELL-1]. - WPE_
MAXK - Max distinct constants in the program’s konst table (the flat
konstmemory). - WPE_
MAXLIV - Max TOTAL live-in registers across the whole program — the flat
liv_regconfig memory. - WPE_
MAXWR - Max TOTAL register writes across the whole program — the flat
wr_*config memory (accumulating pointer). - WPE_
NREG - Register-file depth of the generic config-driven whole-program engine (per-register,
file[0:NREG-1]). - WPE_
STREAM_ CAPS - The
WPE_STREAM_*profile as the[usize; 10]caps arrayemit_wprog_engine_soc_verilog_backendexpects (order: NREG, MAXBLK, MAXCELL, MAXWR, MAXARG, MAXLIV, MAXK, HEAP, DEPTH, MAXV). - WPE_
STREAM_ DEPTH - Call-frame depth. Sized from the MEASURED peak across the benchmark suite (26, driven by
fib), rounded to a power of two — not from the transformer, which is call-free and needs none. This was 4 while the smaller XC7A100T profile was 8, and because the wire image carries no record of the profile it was built for, a depth-8 program streamed to the depth-4 engine ran and returned a plausible wrong number: on siliconbench_fibgave 8 instead of 21,wpe_rec820 instead of 36,wpe_recursion11 instead of 15. Bothwpe_cap_profile_lock(no dimension may shrink between profiles) andthe_stream_engine_profile_covers_every_benchmark_in_the_suite(the suite’s measured ceiling) now pin it. - WPE_
STREAM_ HEAP - Heap depth (words) for the streamed engine. Sized to
2^15 = 32768— a POWER OF TWO that maps to exactly ONE RAMB36E1 depth-bank at ×1 width (a single RAMB36 is 32768×1), so the whole 15-bitheap_raaddresses one clean tile with NO depth-cascade. This dodges a yosys 0.33synth_xilinxmis-map: for a heap whose depth is NOT a clean single-tile fit (e.g. the old 20480),memory_libmappicks a wide tile (×9 → 4096 deep) and then FAILS to depth-cascade — it wires every tile to the SAME 12-bit address (heap_ra[11:0]) and drops the top address bits (heap_ra[14:12]), so on silicon (and in post-synth gate-level sim) any heap read/write to an address - WPE_
STREAM_ MAXARG - WPE_
STREAM_ MAXBLK - WPE_
STREAM_ MAXCELL - WPE_
STREAM_ MAXK - Const-table depth (
konst[0:MAXK-1]). The transformer uses 41; a wider straight-line program (the postsynth high-register lock’s 90-value sum) needs 91 distinct constants, so MAXK=128 covers the family. Theuncheckedimage builder skips thekonst.len() <= MAXKguard, so an over-MAXK program would silently read a nonexistent const slot — 91 > the old 64 was one of the four caps the high-register lock overflowed. - WPE_
STREAM_ MAXLIV - WPE_
STREAM_ MAXV - Depth of the per-block cell-value scratch array
v(v[0:MAXV-1]).vis indexed ONLY by a cell’s position WITHIN ITS BLOCK (mode-2 operands + thev[ci]write), never by the flatblk_cell_ptr + ci, so it needs to hold only the MAX cells in any SINGLE block — not the whole-programMAXCELLtotal. The streamed transformer’s largest block is 116 cells; a wider straight-line block (the postsynth high-register lock’s 90-value sum lowers to a single 270-cell block) needs more, so MAXV=512 covers the family. UnderConfigBram(the streamed engine - WPE_
STREAM_ MAXWR - WPE_
STREAM_ NREG - WPE_
SYNTH_ DEPTH - WPE_
SYNTH_ HEAP - WPE_
SYNTH_ MAXARG - WPE_
SYNTH_ MAXBLK - WPE_
SYNTH_ MAXCELL - WPE_
SYNTH_ MAXK - WPE_
SYNTH_ MAXLIV - WPE_
SYNTH_ MAXWR - WPE_
SYNTH_ NREG
Functions§
- emit_
wprog_ engine_ soc_ testbench - A self-checking testbench that acts as the OVER-THE-WIRE HOST: it
$readmemhs the concatenated wire bytes (wire_<name>.hex), per-vector byte lengths (lens_<name>.hex), and theeval_wprog_configoracle (oracle_<name>.hex), then for each vector bit-bangs its bytes ontouart_rx, decodes the returned frame (0xA5sync +result[63:0]+cycles[31:0]+0x5A), and checks the result == the oracle. Proves the WHOLE wire→synthesize→run→wire path ≡ the VM under iverilog. - emit_
wprog_ engine_ soc_ verilog - Emit the UNIVERSAL over-the-wire whole-program engine SoC: one fixed netlist that RECEIVES any fitting
program’s flat image + params over a UART, fills its on-chip config memories (the loader FSM), seeds the
register file from the params (INIT), runs the interpret st-machine (==
eval_wprog_config== the VM), and streams the 64-bit result + run-cycle count back over the UART — then loops for the next program. The engine core (block-record wires,resolve/resolve_arg, thecase(st)walk) is the SAME logic proven inemit_wprog_engine_verilog; only the config source (wire vs$readmemh) and the result path (UART vs port) differ. Integer functional units (synthesizable — the sequential div/mod/floordiv/pow datapath lives in the template;func_fis the DSP multiplies only).CLKS_PER_BITsizes the UART bit period. - emit_
wprog_ engine_ soc_ verilog_ backend - Emit the over-the-wire engine with an EXPLICIT memory backend — the first-class “choose the substrate” entry
point.
Registers/Bramare the proven paths;ConfigBramadditionally maps the config image to block RAM (its registered-read FSM is the in-progress large-capacity build).caps[9](MAXV) sizes the per-block value arrayvindependently of the whole-programMAXCELLtotal. - emit_
wprog_ engine_ soc_ verilog_ backend_ debug - Emit the over-the-wire engine with an EXPLICIT memory backend AND an on-chip READBACK debug mode. When
debug_readbackis true the engine, at the load→run boundary (P_CLR done), FIRST streams a DEBUG FRAME back over the SAME UART tx — a fixed diagnostic slice of each major memory — then hands off to the interpret FSM, so real silicon can be compared against the known config-image/heap values byte-for-byte. The DEBUG FRAME is: sync0xD8, then for each memory in order {cell_kind, cell_op0, cell_op1, blk_term_kind, heap} entries[0..8), each as a 64-bit BIG-ENDIAN word (8 bytes MSB-first, matching the result frame’s byte order), then end byte0x8D— 1 + 588 + 1 = 322 bytes. The debug FSM walks each address through a REGISTERED BRAM read (issue → wait the latency → capture) and reuses the existing tx byte-sender.debug_readback=falseemits RTL BYTE-IDENTICAL toemit_wprog_engine_soc_verilog_backend(the whole debug region is excised + the parameter is not injected). Readback is only meaningful on theConfigBramstreamed engine — asserted here. - emit_
wprog_ engine_ soc_ verilog_ backend_ rdw - Emit the over-the-wire engine with an EXPLICIT backend AND the SIM-ONLY read-during-write collision detectors
active (
RDW_DETECT=1). The detectors compare each memory’s registered-read address (heap_ra/sf_ra/file_ra/v_ra) against every same-cycle write address during the interpret phase and$display+ count aRDW-COLLISIONwhenever they coincide — a cross-port read-during-write that a real RAMB36 returns UNDEFINED for but this behavioral sim idealizes as READ_FIRST (old data). This is a DIAGNOSTIC build only;rdw_detect=false(every other entry point) EXCISES the whole detector region so the shipped netlist is BYTE-IDENTICAL to before this feature (theRDW_DETECTparameter is not even injected into the header). - emit_
wprog_ engine_ soc_ verilog_ hw - Emit the DEPLOYED hold-safe over-the-wire engine SoC (the real bitstream): same logic as the synth engine,
but with the
WPE_HW_*cap profile that both synthesizes the loop’s compare correctly under yosys AND stays under the board’s hold-safe FF threshold. Runs loops/branches/writes, recursion (depth ≤3), and small collections over the wire on real silicon; proven ≡eval_wprog_config(RTL) + loop gate-sim ≡VM. - emit_
wprog_ engine_ soc_ verilog_ hw_ probed - The deployed hold-safe engine (
emit_wprog_engine_soc_verilog_hw) with its observer ports: the same logic and caps, plusprobe_{running, st, pc, cell, sp, hp, cycles}outputs wired straight from the engine’s own registers, for the Fabric Terminal’s activity panel. - emit_
wprog_ engine_ soc_ verilog_ synth - Emit the SYNTHESIZABLE over-the-wire engine SoC (real Nexys A7 bitstream): the same universal loader/INIT/
interpret/TX logic as
emit_wprog_engine_soc_verilog, but withSYNTH_FUNC_F, theWPE_SYNTH_*cap profile, and(* ram_style="registers" *)forced on every config/state memory so the open flow keeps the UART-streamed config in flip-flops. Proven ≡eval_wprog_configfor fitting programs under iverilog, then synthesized through yosys→nextpnr-xilinx to a bitstream. - emit_
wprog_ engine_ testbench - A self-checking testbench for the generic config-driven engine: it loads the params, pulses reset, runs to
done, and comparesresultto theeval_wprog_configoracle inwpe_{name}_out.hex(params inwpe_{name}_in.hex; the program image in thewpe_{name}_*.hexconfig memories the engine loads). - emit_
wprog_ engine_ verilog - Emit the GENERIC config-driven whole-program processor: ONE fixed microcoded engine that runs ANY program
by loading its
WImageinto on-chip config memories (via$readmemh) and interpreting it — fetch a block record, evaluate its cells one per clock (operands resolved from the register file / cell-value array / constant pool through the live-in table), commit its register writes, then follow the terminator, with a heap state plane and a frame stack forCall/Return. Proven ≡eval_wprog_config(≡ the VM) under iverilog for any loaded image.n_paramssizes only the input ports; every other bound is a fixed generic capacity, so the SAME netlist runs every program that fits. - emit_
wprog_ testbench - A self-checking testbench for the whole-program engine: for each param vector, pulse reset, run the FSM to
done, and compareresultto theeval_wprog_configoracle inwprog_{name}_out.hex(params inwprog_{name}_in.hex). Proves the RTL — register file + block walk + FRAME STACK — matches the model. - emit_
wprog_ verilog - Emit the WHOLE-PROGRAM ENGINE as synthesizable RTL: a register file + block-PC + a SEQUENTIAL per-cell
datapath (one cell per clock into a cell-value array
v, so the ordered HEAP STATE PLANE — HeapRead / HeapWrite / HeapAlloc against a heap BRAM — threads exactly as the model does) + a terminator FSM (Jump/Branch/Return/CALL) + a FRAME STACK that saves the caller’s register file on everyCalland restores it onReturn. So the WHOLE language family — branchy control flow, loops, recursion, division, and collections/strings/structs on the heap — runs on silicon, proven bit-identical toeval_wprog_config(≡ the VM) under iverilog. Cells: Arith / Select / Func (divider) / HeapRead / HeapWrite / HeapAlloc. - eval_
wprog_ config - Interpret the flat config exactly as the RTL will: a register file seeded with
params, a block-walk over the terminator graph, each block computing its cells (Arith/Select) over its live-in registers + prior cell values, committing its register writes, and branching on its terminator. Proven bit-identical torun_program_cfg_on_grid(≡ the VM) — the reference the whole-program RTL is checked against. - eval_
wprog_ config_ counted - Like
eval_wprog_configbut also returns the number of BLOCK-STEPS executed. Each block is one spatial tile group evaluated in a single fabric cycle, so this count IS the whole-program fabric’s cycle count. ONE authoritative evaluator shared by the result path and any cycle measurement — no drifting hand-copy. - eval_
wprog_ config_ counted_ heap - As
eval_wprog_config_countedbut also returns the final HEAP, so a caller can render a Text result (a char-list[len, byte0..]at the returned pointer) back to a string for verification. - eval_
wprog_ config_ counted_ heap_ preloaded - As
eval_wprog_config_counted_heapbut the heap starts PRE-LOADED withheap_init— the DATA-segment model of a chip that receives its weights streamed into memory separately from the kernel.loaded()lowers to heap pointer 0, soitem k of loaded()readsheap_init[k](heap_init[0]= the blob length,heap_init[1..]= the weights). The bump allocator is simplyheap.len(), so runtime collections allocate PAST the pre-loaded region automatically — no separatehpseeding needed. This is what lets the streamed transformer run ≡VM on the engine without Push-building its ~3,136 weights into the config. - eval_
wprog_ config_ faulted - The golden interpreter under an injected hardware fault (see
WFault). An empty fault set is byte-identical toeval_wprog_config_counted_heap_preloaded. - eval_
wprog_ config_ probed - As
eval_wprog_config_counted_heap_preloadedbut also returns the PEAK call-frame depth the run reached. - eval_
wprog_ config_ run - THE golden interpreter of the flat config — the one body every result / step / heap / depth / output-tape
query is a view of. Returns everything a run produces (
WRun), including theShowtape. - func_
code_ needs_ fpu - Does a
Funccell with this code run on the FPU lane (WITH_FPU)? The float units (add/sub/mul/div/sqrt/ floor, the IEEE compares, int→float, approx-eq) exist only when the engine is built with the FPU, and so does popcount (it rides the FPU’s combinational lane; the FPU-lessMUL_FUNC_Fanswers 0 for it). An image builtWITH_FPU(0)has no datapath for any of these and must not be handed such a program. The integer units — div/mod/floor-div/pow and the DSP multiplies — run on every image. - program_
calls_ fabric - Lift a WHOLE multi-function program (with function CALLS and recursion) to a
WProgConfigcarryingCallterminators — the source→config half of recursion (the frame-stackeval_wprog_configis the eval half).fn_nameis lifted FIRST so its entry is block 0 (the config’s entry); every function shares one global block index space, and aCalltargets the callee function’s entry block. Correct-first scope: integer bodies + calls (LoadConst-int / Move / Add·Sub·Mul / AddAssign / Div·Mod·FloorDiv / comparisons / Call / Return / Jump family) — enough for fib/sumdown/gcd-style recursion; collections-in-recursion is the follow-on (the heap arms would be added the same wayprogram_cfg_fabrichas them). - program_
main_ fabric - Lift a WHOLE LOGOS PROGRAM from its
## Main: the code region the compiler emits before the first function body (terminated byHalt), plus every function it transitively calls. Main takes no parameters and itsHaltis a top-levelReturn 0; itsShows become SHOW cells (kind 10) so the program’s OUTPUT — not just a return value — runs on the engine, ≡vm_outcome(src).outputline for line (program_output). - program_
output - A whole program’s OUTPUT on the engine model — every
Showrendered, one line each — the form that compares directly againstvm_outcome(src).output. - render_
show - Render one shown value the way the VM prints it: signed Int,
fmt_f64Float, the Text’s bytes, unsigned Word32,true/false. - serialize_
program - Flatten a whole-function CFG fabric into the fabric’s flat config.
- wprog_
config_ fits_ engine - Does
cfgfit the fixed generic engine? ReturnsErrnaming the first exceeded dimension. A program that overflows any bound CANNOT run on the one-size netlist, so its image must be rejected LOUDLY here rather than producing silently-wrong results (a flat-array overflow corrupts later blocks’ cells/writes; an over-NREGregister indexes a nonexistent file slot → 0 — the two failure modes the Mandelbrot kernel first exposed). Thecell_*/wr_*/liv_reg/arg_op/konstmemories are FLAT with accumulating per-block pointers, so their bounds are program TOTALS, not per-block. Called bywprog_config_image— every image is checked. - wprog_
config_ fits_ hw_ engine - Returns true iff
cfgfits the DEPLOYED hold-safe hardware engine’s cap profile (WPE_HW_*) — the tighter bounds the real bitstream can hold. Programs must satisfy these to run over the wire on silicon. - wprog_
config_ fits_ stream_ engine - Returns
Okiffcfgfits theWPE_STREAM_*profile (the whole streamed transformer’s engine). Every dimension is a program TOTAL (the flat config memories accumulate across blocks). Named-loudly on overflow so a growing model is caught before it silently corrupts the flat config image. - wprog_
config_ fits_ synth_ engine - Returns true iff
cfgfits the SYNTHESIZABLE engine’s smaller cap profile (a superset check of the generic engine’s fit): every over-the-wire program synthesized to hardware must satisfy these or it would overflow the register-backed config memories. The integer func units — Div/Mod/FloorDiv/Pow (sequential datapath) and MulHiU/MulOvfS (DSP) — all synthesize; only the float/popcount codes are rejected (the FPU follow-on). - wprog_
config_ image - Flatten a
WProgConfiginto its loadable image — the exact tables the generic engine walks: block records with base pointers into flat cell / write / arg / live-in pools, and a constant pool forConstoperands. - wprog_
config_ image_ unchecked - Build the flat wire image WITHOUT the generic-engine fit check — for streaming into a BIG-cap engine (the
backend/scale work: the pipelined BRAM engine sized to run every benchmark) whose caps exceed the generic
netlist. The caller guarantees the program fits the target cap profile; only the
MAXKkonst-table bound (shared by every profile) is still asserted here. - wprog_
from_ decision_ fabric - Wrap a single-block if-converted DECISION FABRIC as a one-block flat config, so its cells run on the
CROSSBAR whole-program engine (
eval_wprog_config). The crossbar datapath has full interconnect — a cell may read any earlier cell’s value directly — so a COMPUTED comparison predicate needs no spatial routing. This runs constructs the nearest-neighbour placer (route_loop_body) cannot place, e.g. aSet’s dedup scan, on the fabric ≡ the VM (the decision fabric’s lowering is proven; only the placement was the limit). - wprog_
nregs - The register-file width for a whole-program config: one 64-bit word per distinct register touched.