code wiki / _hdl_build / nx_fpga_adder_gate.nx

nx_fpga_adder_gate.nx source

↩ module page · 164 lines · 8055 B

1import "nx_gate_gn.nx" 2import "nx_gate_base.nx" 3// nx_fpga_adder_gate.nx -- GATE for RUNG 6: a WIDE gate (N-bit ADD) tech-mapped into a LUT4 NETWORK runs on the 4// fabric and equals native integer add. Proves the bridge from bit-level fabric (rung 5) to CPU-scale datapath. 5// T1 W=1 EXHAUSTIVE (8 combos) -- the 1-bit full adder as a 2-LUT network. 6// T2 W=4 EXHAUSTIVE (16x16x2 = 512) -- 4-bit ripple-carry, every operand pair + cin. 7// T3 W in {8,16,32}: carry-propagation edge KATs + deterministic-LFSR sweep vs native add (incl carry-out). 8// T4 NEVER-BRICK -- bounded (2W cells), total (outputs {0,1}), deterministic. 9// T5 LIAR-KILL -- corrupting ONE carry-LUT init makes the wide sum wrong on some operand (the network truly 10// computes via the bitstream; the check bites). 11// GREEN iff all pass. expect_exit: 0 license_tier: ORIGINAL 12import "nx_fpga_adder.nx" 13import "nx_fpga_fabric.nx" 14import "nx_syscalls.nx" 15 16func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 17" as *u8); return ok } 18 19// run the adder fabric for (a,b,cin) at the given width; returns 1 if (sum,carry) == native add, else 0. 20func adder_ok(width: i64, npi: i64, inits: *i64, src: *i64, po_src: *i64, pi: *i64, co: *i64, a: i64, b: i64, cin: i64) -> i64 { 21 fab_adder_load_pi(width, a, b, cin, pi) 22 fab_eval(2*width, npi, inits, src, pi, co) 23 let cout: *i64 = sys_mmap(16) as *i64 24 let sum: i64 = fab_adder_read(width, npi, po_src, pi, co, cout) 25 let mask: i64 = (1 << width) - 1 26 let total: i64 = (a & mask) + (b & mask) + (cin & 1) 27 let exp_sum: i64 = total & mask 28 let exp_cry: i64 = (total >> width) & 1 29 if sum == exp_sum { if cout[0] == exp_cry { return 1 } } 30 return 0 31} 32 33func main() -> i64 { 34 gw("=== nx_fpga_adder_gate: RUNG 6 -- WIDE ADD tech-mapped to a LUT4 network, run on the fabric, vs native add ===\n" as *u8) 35 var pass: i64 = 0; var total: i64 = 0 36 37 // shared max-size buffers (max width 32 -> 64 cells) 38 let inits: *i64 = sys_mmap(8 * 128) as *i64 39 let src: *i64 = sys_mmap(8 * 512) as *i64 40 let po: *i64 = sys_mmap(8 * 68) as *i64 41 let pi: *i64 = sys_mmap(8 * 70) as *i64 42 let co: *i64 = sys_mmap(8 * 130) as *i64 43 44 // ---- T1: W=1 exhaustive ---- 45 var npi: i64 = fab_build_ripple_adder(1, inits, src, po) 46 var c1: i64 = 0; var b1: i64 = 0 47 var x: i64 = 0 48 while x < 8 { 49 let a: i64=x&1; let b: i64=(x>>1)&1; let cin: i64=(x>>2)&1 50 c1 = c1 + 1 51 if adder_ok(1, npi, inits, src, po, pi, co, a, b, cin) == 0 { b1 = b1 + 1 } 52 x = x + 1 53 } 54 total=total+1; if b1==0 { if c1==8 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) } 55 gw("T1 W=1 full-adder network EXHAUSTIVE: " as *u8); gn(c1); gw(" combos, wrong=" as *u8); gn(b1); gw("\n" as *u8) 56 57 // ---- T2: W=4 exhaustive ---- 58 npi = fab_build_ripple_adder(4, inits, src, po) 59 var c2: i64 = 0; var b2: i64 = 0 60 var a: i64 = 0 61 while a < 16 { 62 var bb: i64 = 0 63 while bb < 16 { 64 var ci: i64 = 0 65 while ci < 2 { 66 c2 = c2 + 1 67 if adder_ok(4, npi, inits, src, po, pi, co, a, bb, ci) == 0 { b2 = b2 + 1 } 68 ci = ci + 1 69 } 70 bb = bb + 1 71 } 72 a = a + 1 73 } 74 total=total+1; if b2==0 { if c2==512 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) } 75 gw("T2 W=4 ripple-carry EXHAUSTIVE: " as *u8); gn(c2); gw(" combos, wrong=" as *u8); gn(b2); gw("\n" as *u8) 76 77 // ---- T3: W in {8,16,32} edge KATs + deterministic-LFSR sweep ---- 78 let widths: *i64 = sys_mmap(8 * 4) as *i64 79 widths[0]=8; widths[1]=16; widths[2]=32 80 var c3: i64 = 0; var b3: i64 = 0 81 var wi: i64 = 0 82 while wi < 3 { 83 let W: i64 = widths[wi] 84 npi = fab_build_ripple_adder(W, inits, src, po) 85 let mask: i64 = (1 << W) - 1 86 // edge KATs that stress carry propagation 87 let ka: *i64 = sys_mmap(8 * 16) as *i64 88 let kb: *i64 = sys_mmap(8 * 16) as *i64 89 let kc: *i64 = sys_mmap(8 * 16) as *i64 90 ka[0]=0; kb[0]=0; kc[0]=0 91 ka[1]=mask; kb[1]=0; kc[1]=0 92 ka[2]=mask; kb[2]=mask; kc[2]=0 93 ka[3]=mask; kb[3]=1; kc[3]=0 // full carry ripple 94 ka[4]=mask; kb[4]=mask; kc[4]=1 95 ka[5]=(mask/3); kb[5]=(mask/3)*2; kc[5]=0 // ~0x5555 + ~0xAAAA 96 ka[6]=1; kb[6]=mask; kc[6]=0 97 ka[7]=mask; kb[7]=0; kc[7]=1 98 var ki: i64 = 0 99 while ki < 8 { 100 c3 = c3 + 1 101 if adder_ok(W, npi, inits, src, po, pi, co, ka[ki], kb[ki], kc[ki]) == 0 { b3 = b3 + 1 } 102 ki = ki + 1 103 } 104 // deterministic-LFSR sweep (62-bit-masked LCG -> stays positive; constant seed -> reproducible) 105 var lcg: i64 = 88172645463325252 106 var t: i64 = 0 107 while t < 256 { 108 lcg = (lcg * 2862933555777941757 + 3037000493) & 4611686018427387903 109 let av: i64 = (lcg >> 5) & mask 110 lcg = (lcg * 2862933555777941757 + 3037000493) & 4611686018427387903 111 let bv: i64 = (lcg >> 5) & mask 112 let cv: i64 = lcg & 1 113 c3 = c3 + 1 114 if adder_ok(W, npi, inits, src, po, pi, co, av, bv, cv) == 0 { b3 = b3 + 1 } 115 t = t + 1 116 } 117 wi = wi + 1 118 } 119 total=total+1; if b3==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 120 gw("T3 W in {8,16,32} KAT+LFSR vs native add: " as *u8); gn(c3); gw(" checks, wrong=" as *u8); gn(b3); gw(" (incl carry-out)\n" as *u8) 121 122 // ---- T4: never-brick (W=16): bounded cells, outputs {0,1}, deterministic ---- 123 npi = fab_build_ripple_adder(16, inits, src, po) 124 var t4ok: i64 = 1 125 var m: i64 = 0 126 while m < 16 { 127 let cellv: i64 = co[m] 128 m = m + 1 129 } 130 fab_adder_load_pi(16, 12345, 54321, 1, pi) 131 fab_eval(32, npi, inits, src, pi, co) 132 let cob: *i64 = sys_mmap(16) as *i64 133 let s1: i64 = fab_adder_read(16, npi, po, pi, co, cob) 134 let co2: *i64 = sys_mmap(8 * 130) as *i64 135 fab_eval(32, npi, inits, src, pi, co2) 136 let s2: i64 = fab_adder_read(16, npi, po, pi, co2, cob) 137 if s1 != s2 { t4ok = 0 } 138 var cc: i64 = 0 139 while cc < 32 { if co[cc] < 0 { t4ok = 0 } if co[cc] > 1 { t4ok = 0 } cc = cc + 1 } 140 total=total+1; if t4ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 141 gw("T4 never-brick (#26): bounded 2W-cell pass, every cell output in {0,1}, deterministic, zero hw-state writes\n" as *u8) 142 143 // ---- T5: liar-kill -- corrupt ONE carry-LUT init -> wide sum wrong somewhere ---- 144 npi = fab_build_ripple_adder(8, inits, src, po) 145 inits[3] = inits[3] ^ 1 // corrupt carry cell of bit 1 (cell index 3) 146 let mask8: i64 = 255 147 var liar_wrong: i64 = 0 148 var lt: i64 = 0 149 var llcg: i64 = 1234567 150 while lt < 64 { 151 llcg = (llcg * 2862933555777941757 + 3037000493) & 4611686018427387903 152 let av: i64 = (llcg >> 5) & mask8 153 llcg = (llcg * 2862933555777941757 + 3037000493) & 4611686018427387903 154 let bv: i64 = (llcg >> 5) & mask8 155 if adder_ok(8, npi, inits, src, po, pi, co, av, bv, 0) == 0 { liar_wrong = liar_wrong + 1 } 156 lt = lt + 1 157 } 158 total=total+1; if liar_wrong > 0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 159 gw("T5 liar-kill: 1-bit carry-LUT corruption makes the 8-bit sum wrong in " as *u8); gn(liar_wrong); gw("/64 random operands\n" as *u8) 160 161 gw("\n=== nx_fpga_adder_gate " as *u8); gn(pass); gw("/" as *u8); gn(total) 162 if pass == total { gw(" GREEN (a WIDE ADD tech-maps to a LUT4 network that runs on the fabric == native add, to 32-bit; the bridge to the rv64 ALU on the simulated FPGA)\n" as *u8); sys_exit(0); return 0 } 163 gw(" RED\n" as *u8); sys_exit(1); return 1 164}