code wiki / _hdl_build / nx_fpga_fabric.nx
nx_fpga_fabric.nx source
↩ module page · 49 lines · 2777 B
1// nx_fpga_fabric.nx -- LIB: RUNG 5 of the sovereign FPGA-boot sim. A FABRIC is an array of LUT4 cells wired
2// together by ROUTING; a BITSTREAM is (per-cell LUT init + per-input wire source). fabric_eval executes the
3// fabric driven ONLY by the bitstream -- exactly what a real FPGA does after configuration: it no longer knows
4// the source netlist, only the loaded SRAM bits. This is the leap from "one LUT" (rung 4) to "a configured
5// fabric computes a real multi-gate circuit." Builds on nx_fpga_lut (lut4_eval).
6//
7// WIRE SOURCE ENCODING (the routing): an input source `s` is a primary-input index if s < npi, else it is the
8// output of cell (s - npi). Cells are evaluated in index order, so a topologically-sorted fabric (cell k only
9// wires from PIs or cells < k) is a single forward pass -- no comb loop (and the gate proves outputs are total).
10//
11// BITSTREAM layout (flat i64 arrays, caller-owned -- never-brick #26: pure memory, no real hardware write):
12// ncells, npi
13// init[k] 16-bit LUT SRAM for cell k
14// src[k*4 + 0..3] the 4 input wire sources for cell k
15// npo, po_src[p] each primary output's wire source
16// license_tier: ORIGINAL
17import "nx_fpga_lut.nx"
18import "nx_syscalls.nx"
19
20// resolve a wire source to its current bit value: PI (s<npi) or a prior cell's output.
21func fab_resolve(s: i64, npi: i64, pi: *i64, cellout: *i64) -> i64 {
22 if s < npi { return pi[s] & 1 }
23 return cellout[s - npi] & 1
24}
25
26// EXECUTE the fabric from the bitstream: fill cellout[0..ncells) by evaluating each LUT4 over its wired inputs.
27// Returns 0 (ok). Bounded: exactly ncells cell evaluations, each O(1). Deterministic. No comb loops if sorted.
28func fab_eval(ncells: i64, npi: i64, inits: *i64, src: *i64, pi: *i64, cellout: *i64) -> i64 {
29 var k: i64 = 0
30 while k < ncells {
31 let a: i64 = fab_resolve(src[k*4 + 0], npi, pi, cellout)
32 let b: i64 = fab_resolve(src[k*4 + 1], npi, pi, cellout)
33 let c: i64 = fab_resolve(src[k*4 + 2], npi, pi, cellout)
34 let d: i64 = fab_resolve(src[k*4 + 3], npi, pi, cellout)
35 cellout[k] = lut4_eval(inits[k], a, b, c, d)
36 k = k + 1
37 }
38 return 0
39}
40
41// read a primary output (after fab_eval has filled cellout)
42func fab_po(po_src: i64, npi: i64, pi: *i64, cellout: *i64) -> i64 {
43 return fab_resolve(po_src, npi, pi, cellout)
44}
45
46// compute the 16-bit LUT init for an arbitrary up-to-4-input truth function given as a 16-bit table `tt`
47// (bit i = desired output for input-index i). This IS the synthesizer step: function truth table -> LUT SRAM.
48// (Identity for a full 16-bit table; provided so fabric builders express a cell's function as a truth table.)
49func fab_tt_to_init(tt: i64) -> i64 { return tt & 0xffff }