code wiki / _hdl_build / nx_fpga_addsub_gate.nx

nx_fpga_addsub_gate.nx source

↩ module page · 150 lines · 7609 B

1import "nx_gate_gn.nx" 2import "nx_gate_base.nx" 3// nx_fpga_addsub_gate.nx -- GATE: the op-selected ADD/SUB ALU arithmetic unit runs on the LUT4 fabric and equals 4// native add/subtract. Proves subtraction (two's-complement borrow) AND op-selection (a control bit picks the op) 5// -- the heart of the CPU ALU, all from the bitstream. 6// T1 W=4 EXHAUSTIVE (16x16x2 sub = 512) -- every operand pair, ADD and SUB. 7// T2 W in {8,16,32}: borrow/underflow KATs + LFSR sweep (sub from the stream) vs native (sub? a-b : a+b)&mask. 8// T3 OP-SELECT REAL -- ADD(a,b) != SUB(a,b) for operands where they must differ -> the control bit truly selects. 9// T4 NEVER-BRICK -- bounded (3W cells), total ({0,1}), deterministic. 10// T5 LIAR-KILL -- corrupting the control-XOR init breaks SUB -> the fabric runs the bitstream; the check bites. 11// GREEN iff all pass. expect_exit: 0 license_tier: ORIGINAL 12import "nx_fpga_addsub.nx" 13import "nx_syscalls.nx" 14 15func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 16" as *u8); return ok } 17 18func native_addsub(a: i64, b: i64, sub: i64, mask: i64) -> i64 { 19 if sub == 1 { return (a - b) & mask } 20 return (a + b) & mask 21} 22 23func main() -> i64 { 24 gw("=== nx_fpga_addsub_gate: op-selected ADD/SUB ALU unit on the LUT4 fabric, vs native add/sub ===\n" as *u8) 25 var pass: i64 = 0; var total: i64 = 0 26 27 let inits: *i64 = sys_mmap(8 * 128) as *i64 28 let src: *i64 = sys_mmap(8 * 512) as *i64 29 let po: *i64 = sys_mmap(8 * 40) as *i64 30 let pi: *i64 = sys_mmap(8 * 70) as *i64 31 let co: *i64 = sys_mmap(8 * 130) as *i64 32 33 // ---- T1: W=4 exhaustive ---- 34 var npi: i64 = fab_build_addsub(4, inits, src, po) 35 let mask4: i64 = 15 36 var c1: i64 = 0; var b1: i64 = 0 37 var a: i64 = 0 38 while a < 16 { 39 var bb: i64 = 0 40 while bb < 16 { 41 var su: i64 = 0 42 while su < 2 { 43 c1 = c1 + 1 44 let got: i64 = fab_addsub_run(4, npi, inits, src, po, pi, co, a, bb, su) 45 if got != native_addsub(a, bb, su, mask4) { b1 = b1 + 1 } 46 su = su + 1 47 } 48 bb = bb + 1 49 } 50 a = a + 1 51 } 52 total=total+1; if b1==0 { if c1==512 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) } 53 gw("T1 W=4 ADD/SUB EXHAUSTIVE: " as *u8); gn(c1); gw(" combos, wrong=" as *u8); gn(b1); gw("\n" as *u8) 54 55 // ---- T2: W in {8,16,32} borrow KATs + LFSR ---- 56 let widths: *i64 = sys_mmap(8 * 4) as *i64 57 widths[0]=8; widths[1]=16; widths[2]=32 58 var c2: i64 = 0; var b2: i64 = 0 59 var wi: i64 = 0 60 while wi < 3 { 61 let W: i64 = widths[wi] 62 npi = fab_build_addsub(W, inits, src, po) 63 let mask: i64 = (1 << W) - 1 64 // borrow/underflow KATs (sub=1) + add KATs (sub=0) 65 let ka: *i64 = sys_mmap(8*16) as *i64; let kb: *i64 = sys_mmap(8*16) as *i64; let ks: *i64 = sys_mmap(8*16) as *i64 66 ka[0]=0; kb[0]=1; ks[0]=1 // 0-1 = max (full borrow) 67 ka[1]=5; kb[1]=5; ks[1]=1 // a-a = 0 68 ka[2]=mask; kb[2]=mask; ks[2]=1 // max-max = 0 69 ka[3]=1; kb[3]=2; ks[3]=1 // 1-2 underflow 70 ka[4]=mask; kb[4]=1; ks[4]=0 // max+1 = 0 carry-wrap 71 ka[5]=mask; kb[5]=mask; ks[5]=0 // max+max 72 ka[6]=(mask/3); kb[6]=(mask/3)*2; ks[6]=1 73 ka[7]=0; kb[7]=0; ks[7]=1 // 0-0 = 0 74 var ki: i64 = 0 75 while ki < 8 { 76 c2 = c2 + 1 77 let got: i64 = fab_addsub_run(W, npi, inits, src, po, pi, co, ka[ki], kb[ki], ks[ki]) 78 if got != native_addsub(ka[ki], kb[ki], ks[ki], mask) { b2 = b2 + 1 } 79 ki = ki + 1 80 } 81 var lcg: i64 = 7777777777 82 var t: i64 = 0 83 while t < 256 { 84 lcg = (lcg * 2862933555777941757 + 3037000493) & 4611686018427387903 85 let av: i64 = (lcg >> 5) & mask 86 lcg = (lcg * 2862933555777941757 + 3037000493) & 4611686018427387903 87 let bv: i64 = (lcg >> 5) & mask 88 let sv: i64 = (lcg >> 3) & 1 89 c2 = c2 + 1 90 let got: i64 = fab_addsub_run(W, npi, inits, src, po, pi, co, av, bv, sv) 91 if got != native_addsub(av, bv, sv, mask) { b2 = b2 + 1 } 92 t = t + 1 93 } 94 wi = wi + 1 95 } 96 total=total+1; if b2==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 97 gw("T2 W in {8,16,32} borrow-KAT+LFSR vs native: " as *u8); gn(c2); gw(" checks, wrong=" as *u8); gn(b2); gw("\n" as *u8) 98 99 // ---- T3: op-select is REAL: ADD != SUB where they must differ ---- 100 npi = fab_build_addsub(8, inits, src, po) 101 var diffs: i64 = 0; var samples: i64 = 0 102 var aa: i64 = 1 103 while aa < 6 { 104 var bcur: i64 = 1 105 while bcur < 6 { 106 let radd: i64 = fab_addsub_run(8, npi, inits, src, po, pi, co, aa, bcur, 0) 107 let rsub: i64 = fab_addsub_run(8, npi, inits, src, po, pi, co, aa, bcur, 1) 108 samples = samples + 1 109 if radd != rsub { diffs = diffs + 1 } // a+b != a-b whenever 2b != 0 mod 256 (b in 1..5 -> always) 110 bcur = bcur + 1 111 } 112 aa = aa + 1 113 } 114 total=total+1; if diffs==samples { if samples>0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) } 115 gw("T3 op-select real: the control bit changes the result in " as *u8); gn(diffs); gw("/" as *u8); gn(samples); gw(" (a+b != a-b)\n" as *u8) 116 117 // ---- T4: never-brick (W=16) ---- 118 npi = fab_build_addsub(16, inits, src, po) 119 let r1: i64 = fab_addsub_run(16, npi, inits, src, po, pi, co, 40000, 25000, 1) 120 var t4ok: i64 = 1 121 var cc: i64 = 0 122 while cc < 48 { if co[cc] < 0 { t4ok = 0 } if co[cc] > 1 { t4ok = 0 } cc = cc + 1 } 123 let r2: i64 = fab_addsub_run(16, npi, inits, src, po, pi, co, 40000, 25000, 1) 124 if r1 != r2 { t4ok = 0 } 125 total=total+1; if t4ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 126 gw("T4 never-brick (#26): 3W-cell bounded pass, outputs in {0,1}, deterministic, zero hw-state writes\n" as *u8) 127 128 // ---- T5: liar-kill -- corrupt bit-0's SUM LUT in its carry_in=1 half (which SUB always uses) -> SUB breaks ---- 129 npi = fab_build_addsub(8, inits, src, po) 130 inits[8] = inits[8] ^ 240 // adder sum cell of bit 0 (index = width = 8); ^0xF0 flips its c=1 (carry-in=1) entries 131 let mask8: i64 = 255 132 var liar_wrong: i64 = 0 133 var lt: i64 = 0 134 var llcg: i64 = 424242 135 while lt < 64 { 136 llcg = (llcg * 2862933555777941757 + 3037000493) & 4611686018427387903 137 let av: i64 = (llcg >> 5) & mask8 138 llcg = (llcg * 2862933555777941757 + 3037000493) & 4611686018427387903 139 let bv: i64 = (llcg >> 5) & mask8 140 let got: i64 = fab_addsub_run(8, npi, inits, src, po, pi, co, av, bv, 1) // SUB 141 if got != native_addsub(av, bv, 1, mask8) { liar_wrong = liar_wrong + 1 } 142 lt = lt + 1 143 } 144 total=total+1; if liar_wrong > 0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 145 gw("T5 liar-kill: a corrupted bit-0 sum-LUT (carry-in=1 half) breaks SUB in " as *u8); gn(liar_wrong); gw("/64 operands\n" as *u8) 146 147 gw("\n=== nx_fpga_addsub_gate " as *u8); gn(pass); gw("/" as *u8); gn(total) 148 if pass == total { gw(" GREEN (op-selected ADD/SUB ALU unit runs on the simulated fabric == native; subtraction + op-selection proven -- the CPU datapath core)\n" as *u8); sys_exit(0); return 0 } 149 gw(" RED\n" as *u8); sys_exit(1); return 1 150}