code wiki / _hdl_build / nx_fpga_bitstream.nx

nx_fpga_bitstream.nx source

↩ module page · 73 lines · 3115 B

1// nx_fpga_bitstream.nx -- LIB: sovereign BITSTREAM ENCODER (omniforge SI M4 = the prjtrellis replacement, no Lattice 2// tools). After place-and-route, the fabric configuration (per-cell LUT init + the routing wire-sources + primary 3// outputs) is serialized into a packed BINARY bitstream -- the bits that load onto the fabric to make it compute the 4// circuit. The twin property: encode -> decode -> run on fab_eval reproduces the design EXACTLY (a loaded bitstream 5// behaves like the design). Decodes back too (round-trip), and writes a real .nbit file (the bridge to JTAG + a 6// physical boot). Format: "NBIT" magic, u16 {ncells,npi,npo}, per cell {init,4x src} u16, then npo x po_src u16. 7// never-brick #26: pure memory + a file write (no hardware), bounded, deterministic. license_tier: ORIGINAL 8import "nx_fpga_fabric.nx" 9import "nx_syscalls.nx" 10const K_MAGIC_65535: i64 = 65535 11 12func bs_put16(buf: *u8, pos: i64, v: i64) -> i64 { 13 buf[pos] = (v & 255) as u8 14 buf[pos+1] = ((v >> 8) & 255) as u8 15 return pos + 2 16} 17func bs_get16(buf: *u8, pos: i64) -> i64 { 18 return (buf[pos] as i64) | ((buf[pos+1] as i64) << 8) 19} 20 21// encode the placed+routed fabric config into a packed bitstream; returns byte size. 22func bs_encode(inits: *i64, src: *i64, po_src: *i64, ncells: i64, npi: i64, npo: i64, buf: *u8) -> i64 { 23 var p: i64 = 0 24 buf[p] = 78 as u8; p = p + 1 25 buf[p] = 66 as u8; p = p + 1 26 buf[p] = 73 as u8; p = p + 1 27 buf[p] = 84 as u8; p = p + 1 28 p = bs_put16(buf, p, ncells) 29 p = bs_put16(buf, p, npi) 30 p = bs_put16(buf, p, npo) 31 var k: i64 = 0 32 while k < ncells { 33 p = bs_put16(buf, p, inits[k] & K_MAGIC_65535) 34 var i: i64 = 0 35 while i < 4 { p = bs_put16(buf, p, src[k*4 + i]); i = i + 1 } 36 k = k + 1 37 } 38 var j: i64 = 0 39 while j < npo { p = bs_put16(buf, p, po_src[j]); j = j + 1 } 40 return p 41} 42 43// decode a bitstream back into a fabric config. Returns bytes consumed, or 0-1 on bad magic. hdr_out=[ncells,npi,npo]. 44func bs_decode(buf: *u8, inits_out: *i64, src_out: *i64, po_out: *i64, hdr_out: *i64) -> i64 { 45 if (buf[0] as i64) != 78 { return 0 - 1 } 46 if (buf[1] as i64) != 66 { return 0 - 1 } 47 if (buf[2] as i64) != 73 { return 0 - 1 } 48 if (buf[3] as i64) != 84 { return 0 - 1 } 49 var p: i64 = 4 50 let ncells: i64 = bs_get16(buf, p); p = p + 2 51 let npi: i64 = bs_get16(buf, p); p = p + 2 52 let npo: i64 = bs_get16(buf, p); p = p + 2 53 hdr_out[0] = ncells; hdr_out[1] = npi; hdr_out[2] = npo 54 var k: i64 = 0 55 while k < ncells { 56 inits_out[k] = bs_get16(buf, p); p = p + 2 57 var i: i64 = 0 58 while i < 4 { src_out[k*4 + i] = bs_get16(buf, p); p = p + 2; i = i + 1 } 59 k = k + 1 60 } 61 var j: i64 = 0 62 while j < npo { po_out[j] = bs_get16(buf, p); p = p + 2; j = j + 1 } 63 return p 64} 65 66// write the bitstream to a sovereign .nbit file (the .bit equivalent, no prjtrellis). 67func bs_write(path: *u8, buf: *u8, size: i64) -> i64 { 68 let fd: i64 = sys_openat_wr(path, 420) 69 if fd < 0 { return 0 - 1 } 70 sys_write(fd, buf, size) 71 sys_close(fd) 72 return 0 73}