code wiki / (root) / nishi_synth.nx

nishi_synth.nx source

↩ module page · 266 lines · 10483 B

1// nishi_synth.nx -- WHEELER-ONLY Verilog emitter. 2// 3// !!! STATUS 2026-05-26: DEPRECATED FROM THE CANONICAL EMIT PATH. !!! 4// 5// This file emits Verilog 2005. Per the operator's cardinal of 6// 2026-05-26 ("our own verilog without any of this being something 7// we can be legally hit on") + [[feedback-open-source-licensed- 8// is-still-third-party]] + the audit in 9// hdl/SILICON_SOVEREIGNTY_AUDIT.md, Verilog is an EXTERNAL spec 10// (IEEE 1364) and emitting it bakes in third-party tools as 11// essential to the path. 12// 13// The sovereign canonical emit path is hdl/nishi_synth_gates.nx 14// (.nxgate v1 format, ours). 15// 16// This file STAYS for Wheeler-only test purposes per 17// [[feedback-c-is-wheeler-test-only]]: 18// - During development, run BOTH emitters in parallel 19// - Diff the resulting netlists for correctness 20// - NEVER ship Verilog as the canonical artifact 21// 22// All consumers of THIS file must be Wheeler test harnesses, not 23// production paths. If you find a non-Wheeler caller, file it as 24// a sovereignty regression. 25// 26// Reads an NxHdlModule (declared via nishi_hdl_primitives.nx 27// constructors) and emits a Verilog 2005 module definition. 28// 29// Status: SEED. 2026-05-26. Module-shape emit only -- declares 30// ports, wires, regs. The combinational LOGIC that drives the 31// outputs (decoder lookup tables, ALU compute, CSR mux, etc.) is 32// emitted by per-module "logic emitter" functions that follow-on 33// commits will write -- one per RTL module under hdl/. 34// 35// This is the structural skeleton; the behavioural layer plugs in 36// behind a small extension point (nx_synth_emit_logic_<module>). 37 38import "nx_syscalls.nx" 39import "nishi_hdl_primitives.nx" 40 41// ===== Output sink ================================================= 42// 43// V1: simple growable byte buffer. Caller allocates max_cap; emitter 44// writes bytes via nx_synth_emit_bytes(). Real synth flows pipe 45// straight to stdout / a file; the buffer abstraction keeps the 46// emit logic testable. 47 48struct NxSynthSink { 49 buf: *u8 50 used: i64 51 cap: i64 52 valid: i64 53} 54 55func nx_synth_sink_init(s: *NxSynthSink, buf: *u8, cap: i64) -> i64 { 56 if (s as i64) == 0 { return 0 - NX_HDL_BAD_KIND } 57 if (buf as i64) == 0 { return 0 - NX_HDL_BAD_KIND } 58 s.buf = buf 59 s.used = 0 60 s.cap = cap 61 s.valid = 1 62 return NX_HDL_OK 63} 64 65func nx_synth_emit_bytes(s: *NxSynthSink, src: *u8, n: i64) -> i64 { 66 if s.valid != 1 { return 0 - NX_HDL_BAD_KIND } 67 if s.used + n > s.cap { return 0 - NX_HDL_GRAPH_FULL } 68 var i: i64 = 0 69 while i < n { 70 s.buf[s.used + i] = src[i] 71 i = i + 1 72 } 73 s.used = s.used + n 74 return NX_HDL_OK 75} 76 77func nx_synth_emit_str(s: *NxSynthSink, src: *u8) -> i64 { 78 // strlen 79 var n: i64 = 0 80 while src[n] != (0 as u8) { n = n + 1 } 81 return nx_synth_emit_bytes(s, src, n) 82} 83 84// ===== Integer-to-decimal emit ================================================= 85// 86// Verilog widths + bus indices need decimal output. Substrate-style 87// recursive divide; renders 0..2^63 as up to 19 chars. 88 89func nx_synth_emit_dec(s: *NxSynthSink, value: i64) -> i64 { 90 if value == 0 { 91 return nx_synth_emit_str(s, "0" as *u8) 92 } 93 var n: i64 = value 94 if n < 0 { 95 nx_synth_emit_str(s, "-" as *u8) 96 n = 0 - n 97 } 98 // Build the digits backwards into a small stack buffer. 99 let digits: *u8 = sys_mmap(24) 100 var k: i64 = 0 101 while n > 0 { 102 let d: i64 = n % 10 103 digits[k] = (0x30 + d) as u8 104 n = n / 10 105 k = k + 1 106 } 107 // Emit reversed. 108 var i: i64 = k - 1 109 while i >= 0 { 110 let one: *u8 = sys_mmap(1) 111 one[0] = digits[i] 112 nx_synth_emit_bytes(s, one, 1) 113 i = i - 1 114 } 115 return NX_HDL_OK 116} 117 118// ===== Signal-name emit ================================================= 119// 120// V1: anonymous signals (no name_pool population yet) get 121// auto-generated `sig_<handle>` identifiers. When the constructor 122// layer grows named-signal support, this swaps to look up name_pool. 123 124func nx_synth_emit_signal_name(s: *NxSynthSink, handle: i64) -> i64 { 125 nx_synth_emit_str(s, "sig_" as *u8) 126 return nx_synth_emit_dec(s, handle) 127} 128 129// ===== Bus-range emit (`[W-1:0]` syntax) ================================================= 130// 131// Verilog uses `[high:low]` packed-array syntax for widths > 1. 132// Width 1 signals are scalar and need no brackets. 133 134func nx_synth_emit_range(s: *NxSynthSink, width: i64) -> i64 { 135 if width <= 1 { return NX_HDL_OK } 136 nx_synth_emit_str(s, " [" as *u8) 137 nx_synth_emit_dec(s, width - 1) 138 nx_synth_emit_str(s, ":0]" as *u8) 139 return NX_HDL_OK 140} 141 142// ===== Per-kind keyword ================================================= 143// 144// Verilog declares signals with kind-specific keywords. V1 maps 145// NX_HDL_KIND_* to the Verilog 2005 keyword. 146 147func nx_synth_kind_keyword(kind: i64) -> *u8 { 148 if kind == NX_HDL_KIND_WIRE { return "wire" as *u8 } 149 if kind == NX_HDL_KIND_REG { return "reg" as *u8 } 150 if kind == NX_HDL_KIND_INPUT { return "input wire" as *u8 } 151 if kind == NX_HDL_KIND_OUTPUT { return "output wire" as *u8 } 152 if kind == NX_HDL_KIND_INOUT { return "inout wire" as *u8 } 153 if kind == NX_HDL_KIND_CLOCK { return "input wire" as *u8 } // clk is just an input wire to Verilog 154 if kind == NX_HDL_KIND_RESET { return "input wire" as *u8 } 155 if kind == NX_HDL_KIND_CONST { return "localparam" as *u8 } 156 return "wire" as *u8 157} 158 159// ===== Module emit ================================================= 160// 161// Emits the Verilog module header, port list, internal signal 162// declarations, then leaves a marker comment where the per-module 163// logic emitter plugs in. The follow-on commits per-RTL-module 164// inject the combinational logic between the marker comments. 165 166func nx_synth_emit_module(s: *NxSynthSink, m: *NxHdlModule) -> i64 { 167 if s.valid != 1 { return 0 - NX_HDL_BAD_KIND } 168 if (m as i64) == 0 { return 0 - NX_HDL_BAD_KIND } 169 170 // module HEADER 171 nx_synth_emit_str(s, "// Auto-generated by nishi-synth. Source: nishi-silicon/hdl/\n" as *u8) 172 nx_synth_emit_str(s, "// DO NOT EDIT -- regenerated from NishiHDL module graph.\n\n" as *u8) 173 nx_synth_emit_str(s, "module " as *u8) 174 nx_synth_emit_bytes(s, m.name_buf, m.name_len) 175 nx_synth_emit_str(s, " (\n" as *u8) 176 177 // PORT LIST -- ports are signals with kind INPUT / OUTPUT / INOUT / 178 // CLOCK / RESET. Walk the signal table. 179 var port_count: i64 = 0 180 var i: i64 = 0 181 while i < m.n_signals { 182 let k: i64 = m.signals_buf[i * 4 + NX_HDL_SIGNAL_FIELD_KIND] 183 var is_port: i64 = 0 184 if k == NX_HDL_KIND_INPUT { is_port = 1 } 185 if k == NX_HDL_KIND_OUTPUT { is_port = 1 } 186 if k == NX_HDL_KIND_INOUT { is_port = 1 } 187 if k == NX_HDL_KIND_CLOCK { is_port = 1 } 188 if k == NX_HDL_KIND_RESET { is_port = 1 } 189 if is_port == 1 { 190 if port_count > 0 { nx_synth_emit_str(s, ",\n" as *u8) } 191 nx_synth_emit_str(s, " " as *u8) 192 nx_synth_emit_str(s, nx_synth_kind_keyword(k)) 193 nx_synth_emit_range(s, m.signals_buf[i * 4 + NX_HDL_SIGNAL_FIELD_WIDTH]) 194 nx_synth_emit_str(s, " " as *u8) 195 nx_synth_emit_signal_name(s, i) 196 port_count = port_count + 1 197 } 198 i = i + 1 199 } 200 nx_synth_emit_str(s, "\n);\n\n" as *u8) 201 202 // INTERNAL SIGNAL DECLARATIONS -- wires + regs. 203 i = 0 204 while i < m.n_signals { 205 let k: i64 = m.signals_buf[i * 4 + NX_HDL_SIGNAL_FIELD_KIND] 206 var is_internal: i64 = 0 207 if k == NX_HDL_KIND_WIRE { is_internal = 1 } 208 if k == NX_HDL_KIND_REG { is_internal = 1 } 209 if is_internal == 1 { 210 nx_synth_emit_str(s, " " as *u8) 211 nx_synth_emit_str(s, nx_synth_kind_keyword(k)) 212 nx_synth_emit_range(s, m.signals_buf[i * 4 + NX_HDL_SIGNAL_FIELD_WIDTH]) 213 nx_synth_emit_str(s, " " as *u8) 214 nx_synth_emit_signal_name(s, i) 215 nx_synth_emit_str(s, ";\n" as *u8) 216 } 217 i = i + 1 218 } 219 nx_synth_emit_str(s, "\n" as *u8) 220 221 // ASSIGN STATEMENTS -- for each signal with a driver, emit 222 // `assign sink = source;`. This is the structural-connectivity 223 // layer; the COMBINATIONAL LOGIC that derives intermediate 224 // values plugs in via per-module emitters (follow-on commits). 225 nx_synth_emit_str(s, " // ---- structural connectivity ----\n" as *u8) 226 i = 0 227 while i < m.n_signals { 228 let driver: i64 = m.signals_buf[i * 4 + NX_HDL_SIGNAL_FIELD_DRIVER] 229 if driver != NX_HDL_HANDLE_NONE { 230 nx_synth_emit_str(s, " assign " as *u8) 231 nx_synth_emit_signal_name(s, i) 232 nx_synth_emit_str(s, " = " as *u8) 233 nx_synth_emit_signal_name(s, driver) 234 nx_synth_emit_str(s, ";\n" as *u8) 235 } 236 i = i + 1 237 } 238 nx_synth_emit_str(s, "\n" as *u8) 239 240 // LOGIC PLUG-IN POINT -- per-RTL-module emitters (one per file 241 // under hdl/rv64im_min_*.nx) inject their combinational logic 242 // between these markers. V1 leaves a placeholder. 243 nx_synth_emit_str(s, " // ---- BEGIN logic injection ----\n" as *u8) 244 nx_synth_emit_str(s, " // TODO_SILICON: per-module logic emitter inserts here.\n" as *u8) 245 nx_synth_emit_str(s, " // nx_rv64im_decoder -- decode table + immediate extractors\n" as *u8) 246 nx_synth_emit_str(s, " // nx_rv64im_alu -- ALU compute table per op code\n" as *u8) 247 nx_synth_emit_str(s, " // nx_rv64im_regfile -- always_ff write port + dual read mux\n" as *u8) 248 nx_synth_emit_str(s, " // nx_rv64im_csr -- 9-slot address-decode + flop write enable\n" as *u8) 249 nx_synth_emit_str(s, " // nx_clint -- MTIME counter + MTIMECMP register + comparator\n" as *u8) 250 nx_synth_emit_str(s, " // nx_uart -- TX shift register + baud divider + LSR constant\n" as *u8) 251 nx_synth_emit_str(s, " // ---- END logic injection ----\n\n" as *u8) 252 253 nx_synth_emit_str(s, "endmodule\n" as *u8) 254 return NX_HDL_OK 255} 256 257// ===== Top-level synth driver ================================================= 258// 259// Highest-level entry point: caller hands in an NxHdlModule and a 260// sink; nx_synth_module_to_verilog writes a complete Verilog 2005 261// module definition. Future versions accept multiple modules and 262// emit one file per module + a top-level instantiation file. 263 264func nx_synth_module_to_verilog(m: *NxHdlModule, s: *NxSynthSink) -> i64 { 265 return nx_synth_emit_module(s, m) 266}