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}