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}