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}