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}