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}