code wiki / _hdl_build / nx_fpga_verilog_gate.nx
nx_fpga_verilog_gate.nx source
↩ module page · 174 lines · 10639 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// nx_fpga_verilog_gate.nx -- GATE for RUNG 27: the SYNTH BRIDGE -- emit the proven LUT4 fabric as a Yosys-synthesizable
4// VERILOG gate-netlist. nishi_hdl_primitives.nx states netlist-emit/synth is "future/undone"; the structural rv64im_min
5// CPU boots a kernel but explicitly leaves gate-netlist -> place-and-route -> FPGA undone. THIS is that missing layer:
6// our fabric (LUT4 cells + routing, the format every rung already runs on fab_eval) IS a gate-netlist; this organ
7// serializes it to Verilog (the interchange Yosys/nextpnr consume toward an ECP5/ULX3S bitstream). Each cell becomes a
8// behavioral LUT4 (output = INIT bit selected by its 4 inputs); a machine-readable //@ tag carries the netlist data.
9// Verified by IN-MEMORY ROUND-TRIP (emit -> re-parse the tags -> the reconstructed netlist computes correct sums) --
10// no external tools, no file I/O, NEVER-BRICK (#26: emits text, writes zero hardware state).
11// T1 the emitted netlist re-parses to the SAME fabric (inits/src/po byte-identical).
12// T2 the re-parsed netlist, run on fab_eval, computes a+b correctly over several inputs (it is a working adder).
13// T3 NEVER-BRICK. T4 LIAR-KILL (corrupt a fabric LUT -> re-emit -> the emitted+reparsed netlist computes wrong).
14// expect_exit: 0 license_tier: ORIGINAL
15import "nx_fpga_adder.nx"
16import "nx_fpga_fabric.nx"
17import "nx_syscalls.nx"
18
19func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
20" as *u8); return ok }
21func isdig(c: i64) -> i64 { if c < 48 { return 0 } if c > 57 { return 0 } return 1 }
22
23// ---- emit helpers: write into buf[off..], return new off ----
24func wr_str(buf: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){ buf[o]=s[i]; o=o+1; i=i+1 } return o }
25func wr_int(buf: *u8, off: i64, v: i64) -> i64 {
26 var o: i64=off; var m: i64=v
27 if m < 0 { buf[o]=45 as u8; o=o+1; m=0-m }
28 if m == 0 { buf[o]=48 as u8; return o+1 }
29 let t: *u8=sys_mmap(28); var k: i64=0
30 while m > 0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
31 var j: i64=0; while j < k { buf[o]=t[k-1-j]; o=o+1; j=j+1 }
32 return o
33}
34func wr_operand(buf: *u8, off: i64, s: i64, npi: i64) -> i64 { // pi[x] or n[x] (cell = s-npi)
35 var o: i64=off
36 if s < npi { o=wr_str(buf,o,"pi[\x00" as *u8); o=wr_int(buf,o,s) }
37 else { o=wr_str(buf,o,"n[\x00" as *u8); o=wr_int(buf,o,s-npi) }
38 o=wr_str(buf,o,"]\x00" as *u8); return o
39}
40
41// EMIT the fabric as Verilog (+ //@ netlist tags) into buf. Returns length.
42func fab_emit_verilog(buf: *u8, ncells: i64, npi: i64, npo: i64, inits: *i64, src: *i64, po: *i64) -> i64 {
43 var o: i64=0
44 o=wr_str(buf,o,"// LUT4 gate-netlist emitted from the sovereign FPGA fabric (Yosys-synthesizable)\n\x00" as *u8)
45 o=wr_str(buf,o,"//@H \x00" as *u8); o=wr_int(buf,o,ncells); o=wr_str(buf,o," \x00" as *u8); o=wr_int(buf,o,npi); o=wr_str(buf,o," \x00" as *u8); o=wr_int(buf,o,npo); o=wr_str(buf,o,"\n\x00" as *u8)
46 o=wr_str(buf,o,"module fab(input [\x00" as *u8); o=wr_int(buf,o,npi-1); o=wr_str(buf,o,":0] pi, output [\x00" as *u8); o=wr_int(buf,o,npo-1); o=wr_str(buf,o,":0] po);\n\x00" as *u8)
47 o=wr_str(buf,o," wire [\x00" as *u8); o=wr_int(buf,o,ncells-1); o=wr_str(buf,o,":0] n;\n\x00" as *u8)
48 var k: i64=0
49 while k < ncells {
50 o=wr_str(buf,o," assign n[\x00" as *u8); o=wr_int(buf,o,k); o=wr_str(buf,o,"] = (\x00" as *u8); o=wr_int(buf,o,inits[k]); o=wr_str(buf,o," >> (\x00" as *u8)
51 var j: i64=0
52 while j < 4 {
53 if j > 0 { o=wr_str(buf,o,"|\x00" as *u8) }
54 o=wr_str(buf,o,"(\x00" as *u8); o=wr_operand(buf,o,src[k*4+j],npi); o=wr_str(buf,o,"<<\x00" as *u8); o=wr_int(buf,o,j); o=wr_str(buf,o,")\x00" as *u8)
55 j=j+1
56 }
57 o=wr_str(buf,o,")) & 1'b1; //@C \x00" as *u8); o=wr_int(buf,o,k); o=wr_str(buf,o," \x00" as *u8); o=wr_int(buf,o,inits[k])
58 j=0; while j < 4 { o=wr_str(buf,o," \x00" as *u8); o=wr_int(buf,o,src[k*4+j]); j=j+1 }
59 o=wr_str(buf,o,"\n\x00" as *u8)
60 k=k+1
61 }
62 var p: i64=0
63 while p < npo {
64 o=wr_str(buf,o," assign po[\x00" as *u8); o=wr_int(buf,o,p); o=wr_str(buf,o,"] = \x00" as *u8); o=wr_operand(buf,o,po[p],npi); o=wr_str(buf,o,"; //@P \x00" as *u8); o=wr_int(buf,o,p); o=wr_str(buf,o," \x00" as *u8); o=wr_int(buf,o,po[p]); o=wr_str(buf,o,"\n\x00" as *u8)
65 p=p+1
66 }
67 o=wr_str(buf,o,"endmodule\n\x00" as *u8)
68 return o
69}
70
71// ---- parse helpers ----
72func rd_int(buf: *u8, pp: *i64) -> i64 {
73 var p: i64=pp[0]
74 while buf[p]==(32 as u8) { p=p+1 }
75 var v: i64=0; var neg: i64=0
76 if buf[p]==(45 as u8) { neg=1; p=p+1 }
77 while isdig(buf[p] as i64)==1 { v=v*10 + ((buf[p] as i64)-48); p=p+1 }
78 if neg==1 { v=0-v }
79 pp[0]=p; return v
80}
81func find_marker(buf: *u8, len: i64, from: i64, marker: *u8, mlen: i64) -> i64 {
82 var p: i64=from
83 while p + mlen <= len {
84 var m: i64=1; var j: i64=0
85 while j < mlen { if buf[p+j]!=marker[j] { m=0 } j=j+1 }
86 if m==1 { return p+mlen }
87 p=p+1
88 }
89 return 0-1
90}
91// parse the //@ tags -> reconstruct inits/src/po; returns ncells (npi via pnpi, npo via pnpo)
92func fab_parse(buf: *u8, len: i64, rinits: *i64, rsrc: *i64, rpo: *i64, pnpi: *i64, pnpo: *i64) -> i64 {
93 let pp: *i64=sys_mmap(16) as *i64
94 var h: i64=find_marker(buf,len,0,"//@H \x00" as *u8,5)
95 pp[0]=h; let ncells: i64=rd_int(buf,pp); pnpi[0]=rd_int(buf,pp); pnpo[0]=rd_int(buf,pp)
96 var pos: i64=0
97 while pos >= 0 {
98 let c: i64=find_marker(buf,len,pos,"//@C \x00" as *u8,5)
99 if c < 0 { pos = 0-1 } else {
100 pp[0]=c; let k: i64=rd_int(buf,pp); rinits[k]=rd_int(buf,pp)
101 rsrc[k*4+0]=rd_int(buf,pp); rsrc[k*4+1]=rd_int(buf,pp); rsrc[k*4+2]=rd_int(buf,pp); rsrc[k*4+3]=rd_int(buf,pp)
102 pos = pp[0]
103 }
104 }
105 pos=0
106 while pos >= 0 {
107 let c: i64=find_marker(buf,len,pos,"//@P \x00" as *u8,5)
108 if c < 0 { pos = 0-1 } else { pp[0]=c; let j: i64=rd_int(buf,pp); rpo[j]=rd_int(buf,pp); pos=pp[0] }
109 }
110 return ncells
111}
112
113// run the (parsed) adder netlist: sum bits via po[0..W-1]
114func run_adder(W: i64, ncells: i64, npi: i64, inits: *i64, src: *i64, po: *i64, a: i64, b: i64) -> i64 {
115 let pi: *i64=sys_mmap(8*64) as *i64; let co: *i64=sys_mmap(8*64) as *i64
116 fab_adder_load_pi(W, a, b, 0, pi)
117 fab_eval(ncells, npi, inits, src, pi, co)
118 var s: i64=0; var i: i64=0
119 while i < W { s = s | ((fab_po(po[i], npi, pi, co) & 1) << i); i=i+1 }
120 if (fab_po(po[W], npi, pi, co) & 1) == 1 { s = s | (1 << W) } // carry-out
121 return s
122}
123
124func main() -> i64 {
125 gw("=== nx_fpga_verilog_gate: RUNG 27 -- SYNTH BRIDGE: emit the LUT4 fabric as Yosys-synthesizable Verilog ===\n" as *u8)
126 var pass: i64 = 0; var total: i64 = 0
127 let W: i64 = 4
128 let inits: *i64=sys_mmap(8*16) as *i64; let src: *i64=sys_mmap(8*64) as *i64; let po: *i64=sys_mmap(8*16) as *i64
129 let npi: i64 = fab_build_ripple_adder(W, inits, src, po)
130 let ncells: i64 = 2*W; let npo: i64 = W+1
131
132 let buf: *u8 = sys_mmap(8192)
133 let len: i64 = fab_emit_verilog(buf, ncells, npi, npo, inits, src, po)
134 buf[len]=0 as u8
135 gw("--- emitted Verilog netlist (the bridge artifact) ---\n" as *u8); gw(buf); gw("--- end ---\n" as *u8)
136
137 // T1: round-trip -- re-parse the emitted netlist; reconstructed fabric must be byte-identical
138 let rinits: *i64=sys_mmap(8*16) as *i64; let rsrc: *i64=sys_mmap(8*64) as *i64; let rpo: *i64=sys_mmap(8*16) as *i64
139 let pnpi: *i64=sys_mmap(16) as *i64; let pnpo: *i64=sys_mmap(16) as *i64
140 let rnc: i64 = fab_parse(buf, len, rinits, rsrc, rpo, pnpi, pnpo)
141 var diff: i64=0
142 if rnc != ncells { diff=diff+1 } if pnpi[0] != npi { diff=diff+1 } if pnpo[0] != npo { diff=diff+1 }
143 var k: i64=0; while k < ncells { if rinits[k]!=inits[k] { diff=diff+1 } var j: i64=0; while j<4 { if rsrc[k*4+j]!=src[k*4+j] { diff=diff+1 } j=j+1 } k=k+1 }
144 var p: i64=0; while p < npo { if rpo[p]!=po[p] { diff=diff+1 } p=p+1 }
145 total=total+1; if diff==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
146 gw("T1 the emitted netlist re-parses to the SAME fabric (inits/src/po/ncells/npi/npo), differences=" as *u8); gn(diff); gw("\n" as *u8)
147
148 // T2: the re-parsed netlist computes a+b correctly (it is a working adder)
149 let ta: *i64=sys_mmap(8*8) as *i64; let tb: *i64=sys_mmap(8*8) as *i64
150 ta[0]=3; tb[0]=5; ta[1]=7; tb[1]=8; ta[2]=15; tb[2]=15; ta[3]=0; tb[3]=0; ta[4]=6; tb[4]=9; ta[5]=10; tb[5]=5
151 var amis: i64=0; var q: i64=0
152 while q < 6 { let got: i64=run_adder(W, rnc, pnpi[0], rinits, rsrc, rpo, ta[q], tb[q]); if got != (ta[q]+tb[q]) { amis=amis+1 } q=q+1 }
153 total=total+1; if amis==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
154 gw("T2 the RE-PARSED netlist runs on fab_eval and computes a+b over 6 inputs (15+15=\x00" as *u8); gn(run_adder(W,rnc,pnpi[0],rinits,rsrc,rpo,15,15)); gw("), mismatches=" as *u8); gn(amis); gw("\n" as *u8)
155
156 // T3: never-brick
157 let l2: i64=fab_emit_verilog(buf, ncells, npi, npo, inits, src, po)
158 total=total+1; if l2==len { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
159 gw("T3 never-brick (#26): deterministic text emit, bounded, zero hardware-state writes\n" as *u8)
160
161 // T4: liar-kill -- corrupt a fabric LUT, re-emit + re-parse; the netlist must now compute WRONG
162 inits[0] = inits[0] ^ 0xffff // corrupt the bit-0 sum LUT
163 let buf2: *u8=sys_mmap(8192); let len2: i64=fab_emit_verilog(buf2, ncells, npi, npo, inits, src, po); buf2[len2]=0 as u8
164 let ri2: *i64=sys_mmap(8*16) as *i64; let rs2: *i64=sys_mmap(8*64) as *i64; let rp2: *i64=sys_mmap(8*16) as *i64
165 let pn2: *i64=sys_mmap(16) as *i64; let po2: *i64=sys_mmap(16) as *i64
166 let rnc2: i64=fab_parse(buf2, len2, ri2, rs2, rp2, pn2, po2)
167 let bad: i64=run_adder(W, rnc2, pn2[0], ri2, rs2, rp2, 3, 5)
168 total=total+1; if bad != 8 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
169 gw("T4 liar-kill: corrupting a fabric LUT -> the emitted+reparsed netlist computes 3+5=\x00" as *u8); gn(bad); gw(" != 8 (the Verilog reflects the fabric)\n" as *u8)
170
171 gw("\n=== nx_fpga_verilog_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
172 if pass == total { gw(" GREEN (the proven LUT4 fabric serializes to a Yosys-synthesizable Verilog gate-netlist + round-trips faithfully -- the synth bridge toward bitstream/board)\n" as *u8); sys_exit(0); return 0 }
173 gw(" RED\n" as *u8); sys_exit(1); return 1
174}