code wiki / _hdl_build / nx_fpga_pnr.nx
nx_fpga_pnr.nx source
↩ module page · 240 lines · 9864 B
1// nx_fpga_pnr.nx -- LIB: sovereign PLACE-AND-ROUTE (omniforge SI M4, the nextpnr replacement) for the LUT4
2// fabric. Input = the nx_fpga_fabric bitstream netlist (ncells, npi, inits[], src[4*ncells]; a source s is
3// PI s if s<npi else the output of cell s-npi). P&R does what fab_eval's implicit "index order" does NOT:
4// (1) PLACE each cell on a DISTINCT 2D grid site, honoring topological columns -- every driver sits in a
5// strictly-earlier column than its sink, so signals flow left->right with no combinational loop.
6// (2) ROUTE every wire (PI->cell and cell->cell) as a shortest Manhattan path via Lee/BFS over a grid whose
7// EDGES carry a finite channel capacity -- so routing CONGESTS and can FAIL. Routing success is
8// capacity-gated, never fabricated.
9// P&R preserves logic, so fab_eval over the (routed) driver map reproduces the golden function; the gate proves
10// faithfulness + that routing is load-bearing (corrupt a net -> diverge) + that capacity exhaustion -> UNROUTABLE.
11// never-brick #26: pure memory, BFS bounded by W*H nodes, deterministic, ZERO hardware writes. license_tier: ORIGINAL
12import "nx_fpga_fabric.nx" // fab_eval (logic eval; P&R preserves logic)
13import "nx_syscalls.nx"
14
15// levelize: cell level = 1 + max(driver levels); a PI driver contributes level 0. Requires topological src
16// (cell k only wires from PIs or cells < k -- the fabric convention). Returns the max cell level (= last column).
17func pnr_levelize(ncells: i64, npi: i64, src: *i64, level: *i64) -> i64 {
18 var maxl: i64 = 0
19 var k: i64 = 0
20 while k < ncells {
21 var lv: i64 = 0
22 var i: i64 = 0
23 while i < 4 {
24 let s: i64 = src[k*4 + i]
25 if s >= npi {
26 let dl: i64 = level[s - npi]
27 if dl > lv { lv = dl }
28 }
29 i = i + 1
30 }
31 level[k] = lv + 1
32 if level[k] > maxl { maxl = level[k] }
33 k = k + 1
34 }
35 return maxl
36}
37
38// place: PIs in column 0 (rows 0..npi-1); cell k in column level[k], next free row in that column. site = row*W + col.
39// Returns 0 on success, or 0-1 if infeasible (npi>H, a needed column >= W, or a column overflows H). colnext is
40// W-long scratch. PIs (col 0) and cells (col>=1) never collide; cells in a column take distinct rows -> all sites distinct.
41func pnr_place(ncells: i64, npi: i64, level: *i64, W: i64, H: i64,
42 pi_site: *i64, cell_site: *i64, colnext: *i64) -> i64 {
43 if npi > H { return 0 - 1 }
44 var j: i64 = 0
45 while j < npi { pi_site[j] = j*W + 0; j = j + 1 }
46 var c: i64 = 0
47 while c < W { colnext[c] = 0; c = c + 1 }
48 var k: i64 = 0
49 while k < ncells {
50 let col: i64 = level[k]
51 if col >= W { return 0 - 1 }
52 let row: i64 = colnext[col]
53 if row >= H { return 0 - 1 }
54 cell_site[k] = row*W + col
55 colnext[col] = row + 1
56 k = k + 1
57 }
58 return 0
59}
60
61// route one net (sx,sy)->(tx,ty) over the WxH grid; an edge is traversable iff its remaining capacity > 0.
62// On success: decrement capacity along the chosen shortest path and return its hop-length. On failure: return 0-1.
63// Scratch arrays vis/came/q are each >= W*H long. cap_h is H*(W-1) long, cap_v is (H-1)*W long.
64func pnr_route_net(W: i64, H: i64, cap_h: *i64, cap_v: *i64,
65 sx: i64, sy: i64, tx: i64, ty: i64,
66 vis: *i64, came: *i64, q: *i64) -> i64 {
67 let n: i64 = W * H
68 var i: i64 = 0
69 while i < n { vis[i] = 0; came[i] = 0 - 1; i = i + 1 }
70 let start: i64 = sy*W + sx
71 let target: i64 = ty*W + tx
72 if start == target { return 0 }
73 var qh: i64 = 0
74 var qt: i64 = 0
75 vis[start] = 1
76 q[qt] = start; qt = qt + 1
77 var found: i64 = 0
78 while qh < qt {
79 let cur: i64 = q[qh]; qh = qh + 1
80 if cur == target { found = 1; qh = qt }
81 if found == 0 {
82 let cx: i64 = cur % W
83 let cy: i64 = cur / W
84 if cx + 1 < W {
85 let eh: i64 = cy*(W-1) + cx
86 if cap_h[eh] > 0 {
87 let nb: i64 = cy*W + (cx+1)
88 if vis[nb] == 0 { vis[nb]=1; came[nb]=cur; q[qt]=nb; qt=qt+1 }
89 }
90 }
91 if cx - 1 >= 0 {
92 let eh: i64 = cy*(W-1) + (cx-1)
93 if cap_h[eh] > 0 {
94 let nb: i64 = cy*W + (cx-1)
95 if vis[nb] == 0 { vis[nb]=1; came[nb]=cur; q[qt]=nb; qt=qt+1 }
96 }
97 }
98 if cy + 1 < H {
99 let ev: i64 = cy*W + cx
100 if cap_v[ev] > 0 {
101 let nb: i64 = (cy+1)*W + cx
102 if vis[nb] == 0 { vis[nb]=1; came[nb]=cur; q[qt]=nb; qt=qt+1 }
103 }
104 }
105 if cy - 1 >= 0 {
106 let ev: i64 = (cy-1)*W + cx
107 if cap_v[ev] > 0 {
108 let nb: i64 = (cy-1)*W + cx
109 if vis[nb] == 0 { vis[nb]=1; came[nb]=cur; q[qt]=nb; qt=qt+1 }
110 }
111 }
112 }
113 }
114 if found == 0 { return 0 - 1 }
115 var len: i64 = 0
116 var node: i64 = target
117 while node != start {
118 let p: i64 = came[node]
119 let nx2: i64 = node % W
120 let ny2: i64 = node / W
121 let px: i64 = p % W
122 let py: i64 = p / W
123 if py == ny2 {
124 var lx: i64 = px
125 if nx2 < lx { lx = nx2 }
126 let eh: i64 = py*(W-1) + lx
127 cap_h[eh] = cap_h[eh] - 1
128 } else {
129 var ly: i64 = py
130 if ny2 < ly { ly = ny2 }
131 let ev: i64 = ly*W + px
132 cap_v[ev] = cap_v[ev] - 1
133 }
134 len = len + 1
135 node = p
136 }
137 return len
138}
139
140// route ALL wires: each cell's 4 input nets (driver_site -> cell_site). Returns total wirelength; sets ok[0]=1
141// iff every net routed, else 0. A net whose driver and sink share a site contributes length 0 (no channel use).
142func pnr_route_all(ncells: i64, npi: i64, src: *i64,
143 pi_site: *i64, cell_site: *i64,
144 W: i64, H: i64, cap_h: *i64, cap_v: *i64,
145 vis: *i64, came: *i64, q: *i64, ok: *i64) -> i64 {
146 var wl: i64 = 0
147 var allok: i64 = 1
148 var k: i64 = 0
149 while k < ncells {
150 let csite: i64 = cell_site[k]
151 let tx: i64 = csite % W
152 let ty: i64 = csite / W
153 var i: i64 = 0
154 while i < 4 {
155 let s: i64 = src[k*4 + i]
156 var dsite: i64 = 0
157 if s < npi { dsite = pi_site[s] } else { dsite = cell_site[s - npi] }
158 let sx: i64 = dsite % W
159 let sy: i64 = dsite / W
160 let r: i64 = pnr_route_net(W, H, cap_h, cap_v, sx, sy, tx, ty, vis, came, q)
161 if r < 0 { allok = 0 } else { wl = wl + r }
162 i = i + 1
163 }
164 k = k + 1
165 }
166 ok[0] = allok
167 return wl
168}
169
170// EXTENDED router (SHARED with the PCB autorouter): like pnr_route_net but (a) a NODE can be KEEPOUT-blocked
171// (block[node]==1 -> not traversable) so traces from different nets stay NODE-disjoint on a single layer (no
172// shorts / crossings = the core DRC), and (b) the chosen path is written to pathbuf (target..start, len+1 nodes)
173// so the caller can mark those nodes blocked for later nets. Returns hop-length, or 0-1 if unroutable. Same grid +
174// edge-capacity model -> ONE router serves FPGA place-and-route AND PCB copper autorouting.
175func pnr_route_net_ex(W: i64, H: i64, cap_h: *i64, cap_v: *i64, block: *i64,
176 sx: i64, sy: i64, tx: i64, ty: i64,
177 vis: *i64, came: *i64, q: *i64, pathbuf: *i64) -> i64 {
178 let n: i64 = W * H
179 var i: i64 = 0
180 while i < n { vis[i] = 0; came[i] = 0 - 1; i = i + 1 }
181 let start: i64 = sy*W + sx
182 let target: i64 = ty*W + tx
183 if start == target { pathbuf[0] = start; return 0 }
184 var qh: i64 = 0
185 var qt: i64 = 0
186 vis[start] = 1
187 q[qt] = start; qt = qt + 1
188 var found: i64 = 0
189 while qh < qt {
190 let cur: i64 = q[qh]; qh = qh + 1
191 if cur == target { found = 1; qh = qt }
192 if found == 0 {
193 let cx: i64 = cur % W
194 let cy: i64 = cur / W
195 if cx + 1 < W {
196 let eh: i64 = cy*(W-1) + cx
197 if cap_h[eh] > 0 { let nb: i64 = cy*W + (cx+1); if vis[nb] == 0 { if block[nb] == 0 { vis[nb]=1; came[nb]=cur; q[qt]=nb; qt=qt+1 } } }
198 }
199 if cx - 1 >= 0 {
200 let eh: i64 = cy*(W-1) + (cx-1)
201 if cap_h[eh] > 0 { let nb: i64 = cy*W + (cx-1); if vis[nb] == 0 { if block[nb] == 0 { vis[nb]=1; came[nb]=cur; q[qt]=nb; qt=qt+1 } } }
202 }
203 if cy + 1 < H {
204 let ev: i64 = cy*W + cx
205 if cap_v[ev] > 0 { let nb: i64 = (cy+1)*W + cx; if vis[nb] == 0 { if block[nb] == 0 { vis[nb]=1; came[nb]=cur; q[qt]=nb; qt=qt+1 } } }
206 }
207 if cy - 1 >= 0 {
208 let ev: i64 = (cy-1)*W + cx
209 if cap_v[ev] > 0 { let nb: i64 = (cy-1)*W + cx; if vis[nb] == 0 { if block[nb] == 0 { vis[nb]=1; came[nb]=cur; q[qt]=nb; qt=qt+1 } } }
210 }
211 }
212 }
213 if found == 0 { return 0 - 1 }
214 var idx: i64 = 0
215 pathbuf[idx] = target; idx = idx + 1
216 var len: i64 = 0
217 var nd: i64 = target
218 while nd != start {
219 let pr: i64 = came[nd]
220 let nx2: i64 = nd % W
221 let ny2: i64 = nd / W
222 let px: i64 = pr % W
223 let py: i64 = pr / W
224 if py == ny2 {
225 var lx: i64 = px
226 if nx2 < lx { lx = nx2 }
227 let eh: i64 = py*(W-1) + lx
228 cap_h[eh] = cap_h[eh] - 1
229 } else {
230 var ly: i64 = py
231 if ny2 < ly { ly = ny2 }
232 let ev: i64 = ly*W + px
233 cap_v[ev] = cap_v[ev] - 1
234 }
235 pathbuf[idx] = pr; idx = idx + 1
236 len = len + 1
237 nd = pr
238 }
239 return len
240}