code wiki / _hdl_build / synth_emit_alu_gates_smoke.nx
synth_emit_alu_gates_smoke.nx source
↩ module page · 109 lines · 5197 B
1// synth_emit_alu_gates_smoke.nx -- structural smoke for the ALU .nxgate emitter.
2//
3// ROOT-CAUSE FIX (2026-06-20): synth_emit_alu_gates.nx is a LIBRARY (no main); building it
4// standalone fails "UNDEFINED label: main" -- that was a build-as-executable mistake, NOT an nxasm
5// regression. A library organ is verified by its SMOKE GATE (with main), exactly like
6// synth_emit_regfile_gates_smoke.nx. This is that missing tool: it builds the rv64im_min ALU HDL
7// module, runs nx_emit_alu_gates into a sink, prints the netlist, then asserts the structural
8// invariants the emitter MUST satisfy -- well-formed (.NXGATE/.MODULE/.END), complete (4 ports,
9// ~4125 cells, divider-backed), and ON-ENUM (UNKNOWN x 0 = no cell emitted an out-of-enum kind).
10// .PORT x 4 (op_in, a_in, b_in, result)
11// .CELL ~4125 (divider-backed: full restoring divider for DIV/DIVU/REM/REMU + 29-op cascade)
12// .CONST ~498 (opcode + divider/widening constants)
13// .NET ~4623 (width-complete: every cell drives a declared net; cells <= nets)
14// UNKNOWN x 0 (the correctness invariant: no out-of-enum cell kind)
15// NOTE: output is ~270KB -> the sink buffer must be MB-scale (an earlier 262KB buf silently dropped
16// all cells because nx_synth_emit_bytes is all-or-nothing). Exact cell count tracks the emitter; the
17// gate floors at >100 + checks ports/end/unknown/width-completeness. Exit 0 = green. CORRECTNESS gate.
18// Mirrors the proven regfile smoke (same import set, dedup-safe). 100% sovereign.
19// license_tier: ORIGINAL expect_exit: 0
20import "nx_syscalls.nx"
21import "nishi_hdl_primitives.nx"
22import "nishi_synth.nx"
23import "nishi_synth_gates.nx"
24import "rv64im_min_alu.nx"
25import "synth_emit_alu_gates.nx"
26
27func nx_alusmoke_slen(p: *u8) -> i64 {
28 var n: i64 = 0
29 while p[n] != (0 as u8) { n = n + 1 }
30 return n
31}
32func nx_alusmoke_puts(p: *u8) -> i64 {
33 return sys_write(1, p, nx_alusmoke_slen(p))
34}
35func nx_alusmoke_putn(v: i64) -> i64 {
36 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
37 var m: i64 = v
38 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
39 let d: *u8 = sys_mmap(24); var k: i64 = 0
40 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
41 let o: *u8 = sys_mmap(24); var i: i64 = 0
42 while i < k { o[i] = d[k - 1 - i]; i = i + 1 }
43 sys_write(1, o, k); return 0
44}
45
46// Count non-overlapping occurrences of NUL-terminated needle in buf[0..used).
47func nx_alusmoke_count(buf: *u8, used: i64, needle: *u8) -> i64 {
48 let nlen: i64 = nx_alusmoke_slen(needle)
49 if nlen == 0 { return 0 }
50 var c: i64 = 0
51 var i: i64 = 0
52 while i + nlen <= used {
53 var j: i64 = 0
54 var ok: i64 = 1
55 while j < nlen {
56 if buf[i + j] != needle[j] { ok = 0 }
57 j = j + 1
58 }
59 if ok == 1 { c = c + 1 }
60 i = i + 1
61 }
62 return c
63}
64
65func main() -> i64 {
66 let sigbuf: *i64 = (sys_mmap(NX_HDL_MODULE_MAX_SIGNALS * 32)) as *i64
67 let namepool: *u8 = sys_mmap(256)
68 let m: *NxHdlModule = (sys_mmap(64)) as *NxHdlModule
69 let ports: *NxRv64imAluPorts = (sys_mmap(128)) as *NxRv64imAluPorts
70 let buf: *u8 = sys_mmap(8388608)
71 let s: *NxSynthSink = (sys_mmap(64)) as *NxSynthSink
72
73 nx_hdl_module_init(m, "rv64im_alu" as *u8, 10, sigbuf, namepool, 256)
74 nx_rv64im_alu_build(m, ports)
75 nx_synth_sink_init(s, buf, 8388608)
76
77 let rc: i64 = nx_emit_alu_gates(s, m, ports)
78
79 // print the netlist (follow-along)
80 sys_write(1, s.buf, s.used)
81
82 // observed counts (printed so the real emitter output is visible)
83 let n_port: i64 = nx_alusmoke_count(s.buf, s.used, ".PORT " as *u8)
84 let n_cell: i64 = nx_alusmoke_count(s.buf, s.used, ".CELL " as *u8)
85 let n_const: i64 = nx_alusmoke_count(s.buf, s.used, ".CONST " as *u8)
86 let n_net: i64 = nx_alusmoke_count(s.buf, s.used, ".NET " as *u8)
87 let n_unk: i64 = nx_alusmoke_count(s.buf, s.used, "UNKNOWN" as *u8)
88 nx_alusmoke_puts("\nALU emit counts: ports=" as *u8); nx_alusmoke_putn(n_port)
89 nx_alusmoke_puts(" cells=" as *u8); nx_alusmoke_putn(n_cell)
90 nx_alusmoke_puts(" consts=" as *u8); nx_alusmoke_putn(n_const)
91 nx_alusmoke_puts(" nets=" as *u8); nx_alusmoke_putn(n_net)
92 nx_alusmoke_puts(" unknown=" as *u8); nx_alusmoke_putn(n_unk); nx_alusmoke_puts("\n" as *u8)
93
94 // structural assertions
95 if rc != NX_HDL_OK { return 1 }
96 if s.used == 0 { return 2 }
97 if nx_alusmoke_count(s.buf, s.used, ".NXGATE 1" as *u8) != 1 { return 3 }
98 if nx_alusmoke_count(s.buf, s.used, ".MODULE " as *u8) != 1 { return 4 }
99 if n_port != 4 { return 5 }
100 if n_cell < 100 { return 6 } // divider-backed ALU = thousands of cells; floor, not exact
101 if n_const < 1 { return 7 }
102 if nx_alusmoke_count(s.buf, s.used, ".END" as *u8) != 1 { return 8 }
103 if n_unk != 0 { return 9 } // the correctness invariant: no out-of-enum cell kind
104 if n_net <= 0 { return 10 }
105 if n_cell > n_net { return 11 } // every cell drives a declared net (width-complete)
106
107 nx_alusmoke_puts("ALU .nxgate SMOKE PASS (divider-backed; 4 ports, on-enum, cells+nets width-complete; counts above)\n" as *u8)
108 return 0
109}