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}