code wiki / _hdl_build / nx_nbit.nx

nx_nbit.nx source

↩ module page · 67 lines · 3999 B

1// nx_nbit.nx -- LIB: NHDL H5 -- sovereign PLACE-and-ROUTE + BITSTREAM for the VIRTUAL FPGA (the nextpnr/prjtrellis 2// competitor). H4 synthesizes a logical netlist (cells + nets); this is the BACK-END that maps it onto a PHYSICAL 3// 2-D grid of LUT tiles (PLACEMENT) with inputs addressed by grid coordinate (ROUTING), serializes a sovereign 4// BITSTREAM (our own, like prjtrellis is for ECP5), and the VIRTUAL FPGA loads + evaluates it. The placement is a 5// real permutation (column-major), so correctness proves routing follows arbitrary placement -- not a relabel. 6// "Simulate the chip+board BEFORE buying": the whole RTL->synth->P&R->bitstream->virtual-board chain runs in-sim. 7// NEVER-BRICK (#26): pure memory, bounded, deterministic, zero hardware-state writes. license_tier: ORIGINAL 8// 9// HONEST scope: 1 LUT/tile, full routability assumed (every tile reachable). The CONSTRAINED-routing optimization 10// (limited channels + simulated annealing -- nextpnr's hard part) is the depth rung; this proves the P&R DATA FLOW 11// (place -> route -> bitstream -> virtual eval == netlist) sovereignly + correctness-preservingly. 12import "nx_nhdl.nx" 13import "nx_fpga_lut.nx" 14import "nx_syscalls.nx" 15 16// PLACE: assign each cell to a DISTINCT grid tile via a column-major permutation (R rows x C cols, R*C >= ncells). 17func nbit_place(ncells: i64, R: i64, C: i64, tile_of: *i64) -> i64 { 18 var k: i64=0 19 while k<ncells { tile_of[k]=(k % R)*C + (k / R); k=k+1 } 20 return 0 21} 22 23// resolve a net to its value: a PI (s<npi) or a placed cell's tile value (grid-addressed routing). 24func nbit_resolve(s: i64, npi: i64, pi: *i64, tile_of: *i64, tileval: *i64) -> i64 { 25 if s<npi { return pi[s] & 1 } 26 return tileval[tile_of[s-npi]] & 1 27} 28 29// VIRTUAL FPGA: load the placed bitstream and EVALUATE (cell/topo order; each cell's value stored at its TILE). 30func nbit_eval(ncells: i64, npi: i64, npo: i64, tile_of: *i64, init: *i64, src: *i64, po: *i64, pi: *i64, tileval: *i64, out: *i64) -> i64 { 31 var k: i64=0 32 while k<ncells { 33 let a: i64=nbit_resolve(src[k*4+0], npi, pi, tile_of, tileval) 34 let b: i64=nbit_resolve(src[k*4+1], npi, pi, tile_of, tileval) 35 let c: i64=nbit_resolve(src[k*4+2], npi, pi, tile_of, tileval) 36 let d: i64=nbit_resolve(src[k*4+3], npi, pi, tile_of, tileval) 37 tileval[tile_of[k]]=lut4_eval(init[k], a, b, c, d) 38 k=k+1 39 } 40 var p: i64=0 41 while p<npo { out[p]=nbit_resolve(po[p], npi, pi, tile_of, tileval); p=p+1 } 42 return 0 43} 44 45// emit a grid-addressed input reference: " P<pi>" (a primary input) or " T<tile>" (a placed tile's output) 46func nbit_wsrc(buf: *u8, o: i64, s: i64, npi: i64, tile_of: *i64) -> i64 { 47 var oo: i64=o 48 if s<npi { oo=nh_ws(buf,oo," P\x00" as *u8); oo=nh_wi(buf,oo,s) } 49 else { oo=nh_ws(buf,oo," T\x00" as *u8); oo=nh_wi(buf,oo,tile_of[s-npi]) } 50 return oo 51} 52 53// EMIT the sovereign bitstream text (grid-addressed): per tile, LUT init + 4 grid input sources. Returns length. 54func nbit_emit(buf: *u8, R: i64, C: i64, npi: i64, npo: i64, ncells: i64, tile_of: *i64, init: *i64, src: *i64, po: *i64) -> i64 { 55 var o: i64=0 56 o=nh_ws(buf,o,".nbit 1\n.grid \x00" as *u8); o=nh_wi(buf,o,R); o=nh_ws(buf,o," \x00" as *u8); o=nh_wi(buf,o,C) 57 o=nh_ws(buf,o," \x00" as *u8); o=nh_wi(buf,o,npi); o=nh_ws(buf,o," \x00" as *u8); o=nh_wi(buf,o,npo); o=nh_ws(buf,o," \x00" as *u8); o=nh_wi(buf,o,ncells); o=nh_ws(buf,o,"\n\x00" as *u8) 58 var k: i64=0 59 while k<ncells { 60 o=nh_ws(buf,o,"T \x00" as *u8); o=nh_wi(buf,o,tile_of[k]); o=nh_ws(buf,o," \x00" as *u8); o=nh_wi(buf,o,init[k]) 61 o=nbit_wsrc(buf,o,src[k*4+0],npi,tile_of); o=nbit_wsrc(buf,o,src[k*4+1],npi,tile_of); o=nbit_wsrc(buf,o,src[k*4+2],npi,tile_of); o=nbit_wsrc(buf,o,src[k*4+3],npi,tile_of) 62 o=nh_ws(buf,o,"\n\x00" as *u8); k=k+1 63 } 64 o=nh_ws(buf,o,".po\x00" as *u8); var p: i64=0; while p<npo { o=nbit_wsrc(buf,o,po[p],npi,tile_of); p=p+1 } 65 o=nh_ws(buf,o,"\n.end\n\x00" as *u8) 66 return o 67}