code wiki / _hdl_build / nx_fpga_seq.nx

nx_fpga_seq.nx source

↩ module page · 74 lines · 4169 B

1// nx_fpga_seq.nx -- LIB: RUNG 10a -- the SEQUENTIAL (clocked, stateful) fabric. Every prior rung is purely 2// COMBINATIONAL (fab_eval = one forward pass, no memory). A real CPU is SEQUENTIAL: registers and a program 3// counter that HOLD state across clock cycles. This adds the DFF (the FPGA's sequential atom) to the fabric: 4// a cell can be a LUT4 (combinational) or a DFF (holds Q, latches D on a clock TICK). seq_eval settles the 5// combinational logic (DFFs output their CURRENT Q); seq_tick settles then latches every DFF's D->Q SIMULTANEOUSLY 6// (no race). Feedback through a DFF is legal (q -> +1 -> D) -- that is exactly a counter / program counter. 7// 8// REPRESENTATION (adds `kind` to the bitstream): kind[k] 0=LUT4, 1=DFF. LUT cell: init[k] + src[k*4+0..3] inputs. 9// DFF cell: src[k*4+0] = D source (init unused); q[k] = held state (caller-owned, initialized by the caller). 10// NEVER-BRICK (#26): state is bounded (caller-owned q[]), tick is bounded (ncells), deterministic (same q+inputs 11// -> same next q), zero hardware-state writes. Builds on nx_fpga_lut (lut4_eval) + nx_fpga_fabric (fab_resolve). 12// license_tier: ORIGINAL 13import "nx_fpga_lut.nx" 14import "nx_fpga_fabric.nx" 15import "nx_syscalls.nx" 16const K_MAGIC_65535: i64 = 65535 17 18// one combinational settle: LUT cells compute from inputs; DFF cells output their CURRENT held Q. 19func seq_eval(ncells: i64, npi: i64, kind: *i64, init: *i64, src: *i64, pi: *i64, cellout: *i64, q: *i64) -> i64 { 20 var k: i64 = 0 21 while k < ncells { 22 if kind[k] == 1 { cellout[k] = q[k] & 1 } else { 23 let a: i64 = fab_resolve(src[k*4+0], npi, pi, cellout) 24 let b: i64 = fab_resolve(src[k*4+1], npi, pi, cellout) 25 let c: i64 = fab_resolve(src[k*4+2], npi, pi, cellout) 26 let d: i64 = fab_resolve(src[k*4+3], npi, pi, cellout) 27 cellout[k] = lut4_eval(init[k], a, b, c, d) 28 } 29 k = k + 1 30 } 31 return 0 32} 33 34// one clock TICK: settle, compute every DFF's next-Q from the settled D, then latch ALL simultaneously. 35func seq_tick(ncells: i64, npi: i64, kind: *i64, init: *i64, src: *i64, pi: *i64, cellout: *i64, q: *i64) -> i64 { 36 seq_eval(ncells, npi, kind, init, src, pi, cellout, q) 37 let newq: *i64 = sys_mmap(8 * (ncells + 4)) as *i64 38 var k: i64 = 0 39 while k < ncells { if kind[k] == 1 { newq[k] = fab_resolve(src[k*4+0], npi, pi, cellout) & 1 } k = k + 1 } 40 k = 0 41 while k < ncells { if kind[k] == 1 { q[k] = newq[k] } k = k + 1 } 42 return 0 43} 44 45// EMIT a width-bit free-running COUNTER (= a program counter): W DFFs holding the count, fed by an increment 46// network (count+1 via a half-adder carry chain), wired back to the DFFs' D inputs. npi=1 (PI0 unused). ncells=3W+1. 47// DFF cells 0..W-1 (count bits, D = incrementer sum of that bit), const1 cell W, incrementer cells W+1..3W. 48func seq_build_counter(width: i64, kind: *i64, init: *i64, src: *i64, po_src: *i64) -> i64 { 49 let npi: i64 = 1 50 let kXOR: i64 = fl_gate_to_lut4(FL_XOR) 51 let kAND: i64 = fl_gate_to_lut4(FL_AND) 52 var i: i64 = 0 53 while i < width { 54 kind[i] = 1 // DFF 55 init[i] = 0 56 src[i*4+0] = npi + (width + 1 + 2*i) // D = incrementer SUM cell of bit i 57 src[i*4+1]=0; src[i*4+2]=0; src[i*4+3]=0 58 po_src[i] = npi + i // count bit i = DFF output 59 i = i + 1 60 } 61 kind[width]=0; init[width]=K_MAGIC_65535; src[width*4+0]=0; src[width*4+1]=0; src[width*4+2]=0; src[width*4+3]=0 // const1 62 i = 0 63 while i < width { 64 let sumc: i64 = width + 1 + 2*i 65 let cryc: i64 = width + 1 + 2*i + 1 66 let cnt_i: i64 = npi + i // DFF i output (current count bit) 67 var carry: i64 = npi + width // carry_0 = const1 (the +1) 68 if i > 0 { carry = npi + (width + 1 + 2*(i-1) + 1) } 69 kind[sumc]=0; init[sumc]=kXOR; src[sumc*4+0]=cnt_i; src[sumc*4+1]=carry; src[sumc*4+2]=cnt_i; src[sumc*4+3]=cnt_i 70 kind[cryc]=0; init[cryc]=kAND; src[cryc*4+0]=cnt_i; src[cryc*4+1]=carry; src[cryc*4+2]=cnt_i; src[cryc*4+3]=cnt_i 71 i = i + 1 72 } 73 return npi 74}