code wiki / _hdl_build / nx_fpga_adder.nx
nx_fpga_adder.nx source
↩ module page · 80 lines · 3626 B
1// nx_fpga_adder.nx -- LIB: RUNG 6 of the sovereign FPGA-boot sim -- TECH-MAP a WIDE gate into a LUT4 NETWORK.
2// The R4 tech-map covered only single-LUT primitives; a real CPU's ALU is WIDE (64-bit ADD/SUB/compare). This
3// rung decomposes an N-bit ADD into a fabric of LUT4 cells: a ripple-carry chain of full-adders, one
4// sum-LUT (parity3) + one carry-LUT (maj3) per bit, with the carry wired bit-to-bit (the routing). The emitted
5// fabric runs on fab_eval (rung 5) driven ONLY by its bitstream. This is the bridge to running the actual
6// rv64im ALU netlist on the simulated fabric (rung 9). Builds on nx_fpga_fabric / nx_fpga_lut.
7//
8// FABRIC LAYOUT for width W (npi = 2W+1): PI 0..W-1 = a bits, PI W..2W-1 = b bits, PI 2W = carry-in.
9// cell 2i = SUM bit i (LUT init = parity3 of a_i,b_i,carry_i)
10// cell 2i+1 = CARRY bit i (LUT init = maj3 of a_i,b_i,carry_i)
11// carry_i source: i==0 -> PI(2W) (cin); else -> cell (2i-1) output (prev bit's carry) [topologically sorted]
12// POs: po_src[i]=npi+2i (sum bit i, i=0..W-1); po_src[W]=npi+(2W-1) (final carry-out)
13// NEVER-BRICK (#26): pure integer, bounded (2W cells), total, deterministic, zero hardware-state writes.
14// license_tier: ORIGINAL
15import "nx_fpga_fabric.nx"
16import "nx_fpga_lut.nx"
17import "nx_syscalls.nx"
18
19// 3-input truth-table LUT inits (the synthesis step: enumerate the function over a,b,c; d ignored)
20func fab_parity3_init() -> i64 {
21 var tt: i64 = 0; var i: i64 = 0
22 while i < 16 { let a: i64=i&1; let b: i64=(i>>1)&1; let c: i64=(i>>2)&1; if (a^b^c)==1 { tt = tt | (1<<i) } i=i+1 }
23 return tt
24}
25func fab_maj3_init() -> i64 {
26 var tt: i64 = 0; var i: i64 = 0
27 while i < 16 { let a: i64=i&1; let b: i64=(i>>1)&1; let c: i64=(i>>2)&1; if (a+b+c)>=2 { tt = tt | (1<<i) } i=i+1 }
28 return tt
29}
30
31// EMIT the ripple-carry adder fabric for `width` into inits/src/po_src. Returns npi (= 2*width+1).
32// ncells = 2*width, npo = width+1. Caller allocates: inits[2W], src[2W*4], po_src[W+1].
33func fab_build_ripple_adder(width: i64, inits: *i64, src: *i64, po_src: *i64) -> i64 {
34 let npi: i64 = 2*width + 1
35 let cin_pi: i64 = 2*width
36 let par: i64 = fab_parity3_init()
37 let maj: i64 = fab_maj3_init()
38 var i: i64 = 0
39 while i < width {
40 let sumc: i64 = 2*i
41 let cryc: i64 = 2*i + 1
42 let a_pi: i64 = i
43 let b_pi: i64 = width + i
44 var carry_src: i64 = cin_pi
45 if i > 0 { carry_src = npi + (2*(i-1) + 1) }
46 inits[sumc] = par
47 src[sumc*4 + 0] = a_pi; src[sumc*4 + 1] = b_pi; src[sumc*4 + 2] = carry_src; src[sumc*4 + 3] = a_pi
48 inits[cryc] = maj
49 src[cryc*4 + 0] = a_pi; src[cryc*4 + 1] = b_pi; src[cryc*4 + 2] = carry_src; src[cryc*4 + 3] = a_pi
50 po_src[i] = npi + sumc
51 i = i + 1
52 }
53 po_src[width] = npi + (2*width - 1)
54 return npi
55}
56
57// load operand bits a,b (width bits each) + cin into the PI array pi[0..2W]
58func fab_adder_load_pi(width: i64, a: i64, b: i64, cin: i64, pi: *i64) -> i64 {
59 var i: i64 = 0
60 while i < width {
61 pi[i] = (a >> i) & 1
62 pi[width+i] = (b >> i) & 1
63 i = i + 1
64 }
65 pi[2*width] = cin & 1
66 return 0
67}
68
69// after fab_eval, assemble the width-bit sum integer from the sum POs; carry-out via carryout pointer
70func fab_adder_read(width: i64, npi: i64, po_src: *i64, pi: *i64, cellout: *i64, carryout: *i64) -> i64 {
71 var s: i64 = 0
72 var i: i64 = 0
73 while i < width {
74 let bit: i64 = fab_po(po_src[i], npi, pi, cellout)
75 s = s | (bit << i)
76 i = i + 1
77 }
78 carryout[0] = fab_po(po_src[width], npi, pi, cellout)
79 return s
80}