code wiki / _hdl_build / synth_emit_regfile_gates.nx

synth_emit_regfile_gates.nx source

↩ module page · 192 lines · 8182 B

1// synth_emit_regfile_gates.nx -- per-module register-file emitter for .nxgate. 2// 3// SECOND per-RTL-module emitter in the SOVEREIGN canonical chain 4// (after synth_emit_alu_gates.nx). Emits the .nxgate gate netlist 5// for rv64im_min_regfile.nx: a 31-deep D-flip-flop bank (x1..x31; 6// x0 is hardwired zero) with one write port and two combinational 7// read ports. 8// 9// This is the FIRST emitter to exercise the SEQUENTIAL (.FLOP) half 10// of the .nxgate format -- synth_emit_alu_gates.nx was purely 11// combinational (no clocked storage). It also exercises AND-gating 12// (write-enable) which the ALU cascade never used. 13// 14// Gate structure (matches every in-order RV64 core: SiFive E2 .. Rocket): 15// x0: constant 0 (no flop; reads return 0, writes drop) 16// x1..x31 each: q_i = DFF(d_i, clk, rst) ; rst -> 0 17// d_i = MUX(we_i, q_i, rd_value) ; hold unless written 18// we_i = AND(write_en, (rd_idx == i)) 19// rs1_data = 32:1 read cascade: (rs1_idx == i) ? q_i : prev (seed = 0) 20// rs2_data = 32:1 read cascade: (rs2_idx == i) ? q_i : prev (seed = 0) 21// 22// The per-index 5-bit constant is SHARED across the write match and 23// both read matches (one .CONST per register, not three). 24// 25// Net-linkage conventions (TIGHTER than synth_emit_alu_gates.nx; see 26// the RECONCILE block at the end of this file): 27// 1. Every internal net gets an explicit `.NET <id> width=<W>` line 28// so the netlist is width-complete. The ALU seed referenced 29// cell-output nets that were never declared with a width. 30// 2. `.CONST <netid> width value` uses the NET handle as its id, so 31// the constant net is self-consistently linked to its consumers 32// (the ALU seed used a running cell id that no fanin referenced). 33// 3. MUX fanin order follows the .nxgate spec verbatim: 34// (sel, in0, in1) where in0 is the sel==0 value, in1 the sel==1 35// value. (out = sel ? in1 : in0.) 36// 37// Status: SEED. 2026-05-28. Covers all 31 writable regs + x0-zero + 38// dual read. Verified structurally by synth_emit_regfile_gates_smoke.nx 39// (port/flop/cell/const/net counts + format markers + all-OK verdicts). 40// Functional gate-sim equivalence vs the behavioural regfile awaits 41// nishi-pnr's gate simulator (M5). 42// 43// License-honest: storage + access semantics per the RISC-V 44// Unprivileged ISA (x0 hardwired-zero); no external code. 45 46import "nx_syscalls.nx" 47import "nishi_hdl_primitives.nx" 48import "nishi_synth.nx" 49import "nishi_synth_gates.nx" 50import "rv64im_min_regfile.nx" 51 52// ===== Net helpers (width-complete) ================================================= 53 54// Allocate an internal net + emit its `.NET <id> width=<W>` line. 55func nx_rf_net(s: *NxSynthSink, m: *NxHdlModule, width: i64) -> i64 { 56 let n: i64 = nx_hdl_wire(m, width) 57 if n < 0 { return n } 58 nx_synth_emit_str(s, ".NET " as *u8) 59 nx_gate_emit_net_ref(s, n) 60 nx_synth_emit_str(s, " width=" as *u8) 61 nx_synth_emit_dec(s, width) 62 nx_synth_emit_str(s, "\n" as *u8) 63 return n 64} 65 66// Allocate a constant net + emit `.CONST <netid> width=<W> value=<V>`. 67// The net handle IS the .CONST id (convention 2 above). 68func nx_rf_const(s: *NxSynthSink, m: *NxHdlModule, width: i64, value: i64) -> i64 { 69 let n: i64 = nx_hdl_wire(m, width) 70 if n < 0 { return n } 71 nx_gate_emit_const(s, n, width, value) 72 return n 73} 74 75// 2-input combinational cell: out = KIND(a, b). Returns the out net. 76func nx_rf_bin(s: *NxSynthSink, m: *NxHdlModule, kind: i64, 77 a: i64, b: i64, width: i64, cell_id: i64) -> i64 { 78 let out: i64 = nx_rf_net(s, m, width) 79 if out < 0 { return out } 80 let fin: *i64 = (sys_mmap(16)) as *i64 81 fin[0] = a 82 fin[1] = b 83 nx_gate_emit_cell(s, cell_id, kind, out, fin, 2) 84 return out 85} 86 87// 3-input MUX: out = sel ? in1 : in0. Fanin order per .nxgate spec 88// (sel, in0, in1). Returns the 64-bit out net. 89func nx_rf_mux(s: *NxSynthSink, m: *NxHdlModule, 90 sel: i64, in0: i64, in1: i64, cell_id: i64) -> i64 { 91 let out: i64 = nx_rf_net(s, m, NX_RV64IM_RF_WIDTH) 92 if out < 0 { return out } 93 let fin: *i64 = (sys_mmap(32)) as *i64 94 fin[0] = sel 95 fin[1] = in0 96 fin[2] = in1 97 nx_gate_emit_cell(s, cell_id, NX_GATE_KIND_MUX, out, fin, 3) 98 return out 99} 100 101// ===== Top-level emit ================================================= 102// 103// Caller passes an NxHdlModule already populated by nx_rv64im_rf_build 104// (ports declared) + a sink. Emits the full .nxgate netlist. 105 106func nx_emit_regfile_gates(s: *NxSynthSink, m: *NxHdlModule, 107 ports: *NxRv64imRegfilePorts) -> i64 { 108 if s.valid != 1 { return 0 - NX_HDL_BAD_KIND } 109 110 nx_gate_emit_header(s, m) 111 nx_gate_emit_nets(s, m) // .NET for any build-time WIRE/REG (regfile declares none) 112 113 // Per-register workspace (slot 0 unused; x0 is constant zero). 114 let q_net: *i64 = (sys_mmap(8 * NX_RV64IM_RF_N_REGS)) as *i64 115 let idx_const: *i64 = (sys_mmap(8 * NX_RV64IM_RF_N_REGS)) as *i64 116 var cell_seq: i64 = 0 117 118 // ----- Phase 1: per-reg shared index const + write DFF ----- 119 var i: i64 = 1 120 while i < NX_RV64IM_RF_N_REGS { 121 // 5-bit constant carrying the register index i (shared by all 122 // three comparators for this register). 123 idx_const[i] = nx_rf_const(s, m, NX_RV64IM_RF_IDX_BITS, i) 124 125 // q_i: stored value net (declared now; the flop below drives it). 126 q_net[i] = nx_rf_net(s, m, NX_RV64IM_RF_WIDTH) 127 128 // we_i = write_en AND (rd_idx == i) 129 let rd_match: i64 = nx_rf_bin(s, m, NX_GATE_KIND_EQ, ports.rd_idx, idx_const[i], 1, cell_seq) 130 cell_seq = cell_seq + 1 131 let we: i64 = nx_rf_bin(s, m, NX_GATE_KIND_AND, ports.write_en, rd_match, 1, cell_seq) 132 cell_seq = cell_seq + 1 133 134 // d_i = we ? rd_value : q_i (hold unless written this cycle) 135 let d: i64 = nx_rf_mux(s, m, we, q_net[i], ports.rd_value, cell_seq) 136 cell_seq = cell_seq + 1 137 138 // q_i = DFF(d_i) on clk, reset -> 0 139 nx_gate_emit_flop(s, cell_seq, q_net[i], d, ports.clk, ports.reset) 140 cell_seq = cell_seq + 1 141 142 i = i + 1 143 } 144 145 // ----- Phase 2a: rs1 read cascade (x0 reads 0) ----- 146 var prev1: i64 = nx_rf_const(s, m, NX_RV64IM_RF_WIDTH, 0) 147 i = 1 148 while i < NX_RV64IM_RF_N_REGS { 149 let m1: i64 = nx_rf_bin(s, m, NX_GATE_KIND_EQ, ports.rs1_idx, idx_const[i], 1, cell_seq) 150 cell_seq = cell_seq + 1 151 prev1 = nx_rf_mux(s, m, m1, prev1, q_net[i], cell_seq) 152 cell_seq = cell_seq + 1 153 i = i + 1 154 } 155 nx_hdl_connect(m, ports.rs1_data, prev1) 156 nx_synth_emit_str(s, "// rs1_data <- " as *u8) 157 nx_gate_emit_net_ref(s, prev1) 158 nx_synth_emit_str(s, "\n" as *u8) 159 160 // ----- Phase 2b: rs2 read cascade (x0 reads 0) ----- 161 var prev2: i64 = nx_rf_const(s, m, NX_RV64IM_RF_WIDTH, 0) 162 i = 1 163 while i < NX_RV64IM_RF_N_REGS { 164 let m2: i64 = nx_rf_bin(s, m, NX_GATE_KIND_EQ, ports.rs2_idx, idx_const[i], 1, cell_seq) 165 cell_seq = cell_seq + 1 166 prev2 = nx_rf_mux(s, m, m2, prev2, q_net[i], cell_seq) 167 cell_seq = cell_seq + 1 168 i = i + 1 169 } 170 nx_hdl_connect(m, ports.rs2_data, prev2) 171 nx_synth_emit_str(s, "// rs2_data <- " as *u8) 172 nx_gate_emit_net_ref(s, prev2) 173 nx_synth_emit_str(s, "\n" as *u8) 174 175 nx_gate_emit_end(s) 176 return NX_HDL_OK 177} 178 179// ===== RECONCILE (sovereignty follow-up) ================================================= 180// 181// synth_emit_alu_gates.nx (the first emitter) diverges from the 182// .nxgate spec in two ways this file corrects: 183// A. it omits `.NET` width declarations for cell-output nets, and 184// B. its `.CONST` id is a running cell counter that no fanin 185// references (the match EQ fans in a separately-allocated wire 186// whose value is never set). 187// C. its MUX fanin is (sel, in1, in0), reversed vs the spec's 188// (sel, in0, in1). 189// When nishi-pnr (the .nxgate consumer, M5) lands and pins the 190// contract, retrofit synth_emit_alu_gates.nx to whichever single 191// convention nishi-pnr accepts so both emitters agree. Tracked as a 192// one-commit cleanup; additive, no behaviour change to this file.