code wiki / _hdl_build / nx_fpga_ram.nx
nx_fpga_ram.nx source
↩ module page · 138 lines · 5831 B
1// nx_fpga_ram.nx -- LIB: RUNG 17 -- a true RAM fabric on the SEQUENTIAL fabric. Same structure as the register file
2// (nx_fpga_regfile) -- R words x W bits of DFF state, an addressed clocked WRITE port + an addressed combinational
3// READ port, LUT4 address decode -- BUT with NO hardwired address: EVERY word (including address 0) is writable and
4// reads back what was written. That is the difference between a register file (x0 must read 0) and a memory (word 0
5// is just a word). This is what lets data live at address 0 AND lets instructions be FETCHED from a memory fabric.
6//
7// PIs (npi = 2*AB + W + 1): raddr[0..AB-1] | waddr[AB..2AB-1] | wdata[2AB..2AB+W-1] | we[2AB+W].
8// NEVER-BRICK (#26): bounded state (caller-owned q[]), bounded tick, deterministic, no hardware write.
9// license_tier: ORIGINAL
10import "nx_fpga_seq.nx"
11import "nx_fpga_lut.nx"
12import "nx_fpga_fabric.nx"
13import "nx_syscalls.nx"
14
15func ram_addr_inputs(src: *i64, cell: i64, addr0_pi: i64, ab: i64, const0_src: i64) -> i64 {
16 var j: i64 = 0
17 while j < 4 {
18 if j < ab { src[cell*4+j] = addr0_pi + j } else { src[cell*4+j] = const0_src }
19 j = j + 1
20 }
21 return 0
22}
23
24func seq_build_ram(R: i64, W: i64, AB: i64, kind: *i64, init: *i64, src: *i64, po_src: *i64) -> i64 {
25 let npi: i64 = 2*AB + W + 1
26 let RADDR0: i64 = 0
27 let WADDR0: i64 = AB
28 let WDATA0: i64 = 2*AB
29 let WE: i64 = 2*AB + W
30 let kAND: i64 = fl_gate_to_lut4(FL_AND)
31 let kOR: i64 = fl_gate_to_lut4(FL_OR)
32 let kMUX: i64 = fl_gate_to_lut4(FL_MUX)
33 // ---- cell-index plan (Dmux spans ALL R words, not R-1) ----
34 let C0: i64 = R*W // const0
35 let WSEL: i64 = R*W + 1 // wsel_r = WSEL + r
36 let WRITE: i64 = WSEL + R // write_r = WRITE + r
37 let DBASE: i64 = WRITE + R // Dmux[r][i] = DBASE + r*W + i (r=0..R-1)
38 let RSEL: i64 = DBASE + R*W // rsel_r = RSEL + r
39 let GBASE: i64 = RSEL + R // gated[r][i] = GBASE + r*W + i
40 let OBASE: i64 = GBASE + R*W // or[i][k] = OBASE + i*(R-1) + k (k=0..R-2)
41 let const0_src: i64 = npi + C0
42
43 // const0
44 kind[C0]=0; init[C0]=0; src[C0*4+0]=0; src[C0*4+1]=0; src[C0*4+2]=0; src[C0*4+3]=0
45
46 // DFF cells (state) + their D wiring -- EVERY word fed by its Dmux (no x0 hardwire)
47 var r: i64 = 0
48 while r < R {
49 var i: i64 = 0
50 while i < W {
51 let dff: i64 = r*W + i
52 kind[dff]=1; init[dff]=0
53 src[dff*4+0] = npi + (DBASE + r*W + i)
54 src[dff*4+1]=0; src[dff*4+2]=0; src[dff*4+3]=0
55 i = i + 1
56 }
57 r = r + 1
58 }
59 // write decode: wsel_r = (waddr==r), write_r = wsel_r AND we
60 r = 0
61 while r < R {
62 let ws: i64 = WSEL + r
63 kind[ws]=0; init[ws]=(1 << r); ram_addr_inputs(src, ws, WADDR0, AB, const0_src)
64 let wr: i64 = WRITE + r
65 kind[wr]=0; init[wr]=kAND; src[wr*4+0]=npi+ws; src[wr*4+1]=WE; src[wr*4+2]=npi+ws; src[wr*4+3]=npi+ws
66 r = r + 1
67 }
68 // D-feed MUX for ALL r=0..R-1: Dmux[r][i] = write_r ? wdata_i : word[r][i]
69 r = 0
70 while r < R {
71 var i: i64 = 0
72 while i < W {
73 let dm: i64 = DBASE + r*W + i
74 kind[dm]=0; init[dm]=kMUX
75 src[dm*4+0]=npi+(WRITE+r); src[dm*4+1]=npi+(r*W+i); src[dm*4+2]=WDATA0+i; src[dm*4+3]=npi+(r*W+i)
76 i = i + 1
77 }
78 r = r + 1
79 }
80 // read: rsel_r = (raddr==r); gated[r][i] = rsel_r AND word[r][i]; out_i = OR over r
81 r = 0
82 while r < R {
83 let rs: i64 = RSEL + r
84 kind[rs]=0; init[rs]=(1 << r); ram_addr_inputs(src, rs, RADDR0, AB, const0_src)
85 var i: i64 = 0
86 while i < W {
87 let g: i64 = GBASE + r*W + i
88 kind[g]=0; init[g]=kAND; src[g*4+0]=npi+rs; src[g*4+1]=npi+(r*W+i); src[g*4+2]=npi+rs; src[g*4+3]=npi+rs
89 i = i + 1
90 }
91 r = r + 1
92 }
93 // OR-reduce per bit
94 var i: i64 = 0
95 while i < W {
96 var k: i64 = 0
97 while k < R - 1 {
98 let oc: i64 = OBASE + i*(R-1) + k
99 kind[oc]=0; init[oc]=kOR
100 var in0: i64 = npi + (GBASE + 0*W + i)
101 if k > 0 { in0 = npi + (OBASE + i*(R-1) + k - 1) }
102 let in1: i64 = npi + (GBASE + (k+1)*W + i)
103 src[oc*4+0]=in0; src[oc*4+1]=in1; src[oc*4+2]=in0; src[oc*4+3]=in0
104 k = k + 1
105 }
106 po_src[i] = npi + (OBASE + i*(R-1) + (R-2))
107 i = i + 1
108 }
109 return npi
110}
111
112// total cell count for an R x W RAM (Dmux now spans R words, not R-1)
113func ram_ncells(R: i64, W: i64) -> i64 { return (R*W) + 1 + R + R + R*W + R + R*W + W*(R-1) }
114
115// WRITE word[addr] = val on a clock tick (we=1). raddr idle.
116func ram_write(R: i64, W: i64, AB: i64, kind: *i64, init: *i64, src: *i64, pi: *i64, cellout: *i64, q: *i64, addr: i64, val: i64) -> i64 {
117 var j: i64 = 0
118 while j < AB { pi[j] = 0; pi[AB+j] = (addr >> j) & 1; j = j + 1 }
119 var i: i64 = 0
120 while i < W { pi[2*AB + i] = (val >> i) & 1; i = i + 1 }
121 pi[2*AB + W] = 1
122 seq_tick(ram_ncells(R, W), 2*AB + W + 1, kind, init, src, pi, cellout, q)
123 return 0
124}
125
126// READ word[addr] combinationally (no tick; we=0).
127func ram_read(R: i64, W: i64, AB: i64, kind: *i64, init: *i64, src: *i64, pi: *i64, cellout: *i64, q: *i64, po_src: *i64, addr: i64) -> i64 {
128 var j: i64 = 0
129 while j < AB { pi[j] = (addr >> j) & 1; pi[AB+j] = 0; j = j + 1 }
130 var i: i64 = 0
131 while i < W { pi[2*AB + i] = 0; i = i + 1 }
132 pi[2*AB + W] = 0
133 seq_eval(ram_ncells(R, W), 2*AB + W + 1, kind, init, src, pi, cellout, q)
134 var s: i64 = 0
135 i = 0
136 while i < W { s = s | ((fab_po(po_src[i], 2*AB + W + 1, pi, cellout) & 1) << i); i = i + 1 }
137 return s
138}