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}