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 }