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}