code wiki / _hdl_build / synth_emit_alu.nx
synth_emit_alu.nx source
↩ module page · 167 lines · 9060 B
1// synth_emit_alu.nx -- per-module Verilog logic emitter for the ALU.
2//
3// First per-RTL-module logic emitter -- demonstrates the pattern
4// the remaining 5 (decoder, regfile, csr, clint, uart) will follow.
5//
6// Reads the declared NxRv64imAluPorts handles (op_in, a_in, b_in,
7// result) + emits a combinational `always_comb` block with a case
8// statement over the 29 NX_RV64IM_ALU_* op codes. Plugged into the
9// nishi_synth skeleton between the BEGIN/END logic-injection markers.
10//
11// Status: SEED. 2026-05-26. Pattern reference for the remaining
12// 5 module emitters per hdl/HACKING.md "Adding a per-module logic
13// emitter" recipe.
14
15import "nx_syscalls.nx"
16import "nishi_hdl_primitives.nx"
17import "nishi_synth.nx"
18import "rv64im_min_alu.nx"
19
20// Emit a 64-bit hex literal in Verilog format: 64'h<hex>
21func nx_synth_emit_hex64(s: *NxSynthSink, value: i64) -> i64 {
22 nx_synth_emit_str(s, "64'h" as *u8)
23 let digits: *u8 = sys_mmap(17) // 16 hex chars + nul
24 var i: i64 = 15
25 while i >= 0 {
26 let nyb: i64 = (value >> (i * 4)) & 0xf
27 if nyb < 10 { digits[15 - i] = (0x30 + nyb) as u8 }
28 if nyb >= 10 { digits[15 - i] = (0x57 + nyb) as u8 } // 'a' = 0x61, 'a' - 10 = 0x57
29 i = i - 1
30 }
31 return nx_synth_emit_bytes(s, digits, 16)
32}
33
34// Emit the operand-port name lookup. V1: hard-codes the port names
35// the synth skeleton would assign to NxRv64imAluPorts handles.
36// Future: take *NxRv64imAluPorts and look up the actual generated
37// identifiers via the module's signal-name table.
38func nx_synth_emit_op_case_line(s: *NxSynthSink, op_kind: i64, expr: *u8) -> i64 {
39 nx_synth_emit_str(s, " " as *u8)
40 nx_synth_emit_dec(s, op_kind)
41 nx_synth_emit_str(s, ": result = " as *u8)
42 nx_synth_emit_str(s, expr)
43 nx_synth_emit_str(s, ";\n" as *u8)
44 return NX_HDL_OK
45}
46
47// Emit one always_comb block that implements every ALU op. The
48// dispatch is a case statement over op_in; each branch is a single
49// Verilog expression that maps directly to gate-level synthesis.
50//
51// Verilog operators used:
52// + - & | ^ ~ (arithmetic + bitwise; map to ripple-carry
53// adder + AND/OR/XOR gates)
54// < > <= >= (signed compare; Verilog uses $signed wrap
55// for forced signed semantics)
56// << >> >>> (logical-left, logical-right, arithmetic-right)
57// {32{x}} (replication for sign-extend)
58// $signed(...) (force signed interpretation for SLT)
59
60func nx_synth_emit_logic_alu(s: *NxSynthSink) -> i64 {
61 if s.valid != 1 { return 0 - NX_HDL_BAD_KIND }
62
63 nx_synth_emit_str(s, " // ---- BEGIN logic injection (ALU) ----\n" as *u8)
64 nx_synth_emit_str(s, " always_comb begin\n" as *u8)
65 nx_synth_emit_str(s, " case (op_in)\n" as *u8)
66
67 // RV64I base
68 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_ADD, "a_in + b_in" as *u8)
69 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_SUB, "a_in - b_in" as *u8)
70 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_SLL, "a_in << b_in[5:0]" as *u8)
71 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_SLT, "{63'b0, ($signed(a_in) < $signed(b_in))}" as *u8)
72 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_SLTU, "{63'b0, (a_in < b_in)}" as *u8)
73 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_XOR, "a_in ^ b_in" as *u8)
74 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_SRL, "a_in >> b_in[5:0]" as *u8)
75 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_SRA, "$signed(a_in) >>> b_in[5:0]" as *u8)
76 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_OR, "a_in | b_in" as *u8)
77 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_AND, "a_in & b_in" as *u8)
78
79 // RV64I-64 W-suffix (32-bit ops, sign-extended to 64)
80 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_ADDW,
81 "{{32{(a_in[31:0]+b_in[31:0])[31]}}, a_in[31:0]+b_in[31:0]}" as *u8)
82 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_SUBW,
83 "{{32{(a_in[31:0]-b_in[31:0])[31]}}, a_in[31:0]-b_in[31:0]}" as *u8)
84 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_SLLW,
85 "{{32{(a_in[31:0]<<b_in[4:0])[31]}}, a_in[31:0]<<b_in[4:0]}" as *u8)
86 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_SRLW,
87 "{{32{(a_in[31:0]>>b_in[4:0])[31]}}, a_in[31:0]>>b_in[4:0]}" as *u8)
88 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_SRAW,
89 "{{32{$signed(a_in[31:0])>>>b_in[4:0]}}, ($signed(a_in[31:0])>>>b_in[4:0])[31:0]}" as *u8)
90
91 // M extension 64-bit
92 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_MUL, "a_in * b_in" as *u8)
93 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_MULH,
94 "($signed({{64{a_in[63]}}, a_in}) * $signed({{64{b_in[63]}}, b_in})) >> 64" as *u8)
95 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_MULHSU,
96 "($signed({{64{a_in[63]}}, a_in}) * {64'b0, b_in}) >> 64" as *u8)
97 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_MULHU,
98 "({64'b0, a_in} * {64'b0, b_in}) >> 64" as *u8)
99 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_DIV,
100 "(b_in == 64'b0) ? 64'hFFFFFFFFFFFFFFFF : $signed(a_in) / $signed(b_in)" as *u8)
101 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_DIVU,
102 "(b_in == 64'b0) ? 64'hFFFFFFFFFFFFFFFF : a_in / b_in" as *u8)
103 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_REM,
104 "(b_in == 64'b0) ? a_in : $signed(a_in) % $signed(b_in)" as *u8)
105 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_REMU,
106 "(b_in == 64'b0) ? a_in : a_in % b_in" as *u8)
107
108 // M extension 32-bit (W-suffix)
109 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_MULW,
110 "{{32{(a_in[31:0]*b_in[31:0])[31]}}, (a_in[31:0]*b_in[31:0])[31:0]}" as *u8)
111 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_DIVW,
112 "(b_in[31:0] == 32'b0) ? 64'hFFFFFFFFFFFFFFFF : {{32{($signed(a_in[31:0])/$signed(b_in[31:0]))[31]}}, ($signed(a_in[31:0])/$signed(b_in[31:0]))[31:0]}" as *u8)
113 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_DIVUW,
114 "(b_in[31:0] == 32'b0) ? 64'hFFFFFFFFFFFFFFFF : {{32{(a_in[31:0]/b_in[31:0])[31]}}, a_in[31:0]/b_in[31:0]}" as *u8)
115 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_REMW,
116 "(b_in[31:0] == 32'b0) ? {{32{a_in[31]}}, a_in[31:0]} : {{32{($signed(a_in[31:0])%$signed(b_in[31:0]))[31]}}, ($signed(a_in[31:0])%$signed(b_in[31:0]))[31:0]}" as *u8)
117 nx_synth_emit_op_case_line(s, NX_RV64IM_ALU_REMUW,
118 "(b_in[31:0] == 32'b0) ? {{32{a_in[31]}}, a_in[31:0]} : {{32{(a_in[31:0]%b_in[31:0])[31]}}, a_in[31:0]%b_in[31:0]}" as *u8)
119
120 // Default: INVALID + any out-of-range op_kind.
121 nx_synth_emit_str(s, " default: result = 64'b0;\n" as *u8)
122 nx_synth_emit_str(s, " endcase\n" as *u8)
123 nx_synth_emit_str(s, " end\n" as *u8)
124 nx_synth_emit_str(s, " // ---- END logic injection (ALU) ----\n" as *u8)
125
126 return NX_HDL_OK
127}
128
129// ===== Wrapper: emit the full ALU module Verilog =================================================
130//
131// Combines the synth skeleton (port + signal declarations) with the
132// ALU-specific logic emitter. V1 takes a pre-built NxHdlModule that
133// the caller populated via nx_rv64im_alu_build().
134
135func nx_synth_module_to_verilog_alu(m: *NxHdlModule, s: *NxSynthSink) -> i64 {
136 if s.valid != 1 { return 0 - NX_HDL_BAD_KIND }
137
138 // Module header + port list + structural connectivity.
139 nx_synth_emit_module(s, m)
140
141 // The skeleton ends with `endmodule\n`. To inject logic between
142 // the placeholders, future Synth API will accept a callback; V1
143 // ships the logic as a separate emit users invoke after the
144 // skeleton + before `endmodule` (caller responsibility).
145 //
146 // For this V1 demo we emit a SECOND module suffixed _logic so
147 // the gate-level synthesis path can pick it up alongside the
148 // structural module. Production wiring lives in commit
149 // 7 of the per-module-emitter series (synth API extension to
150 // accept logic-injection callbacks per module instance).
151 nx_synth_emit_str(s, "\n// ===== Logic-only synthesis variant =====\n" as *u8)
152 nx_synth_emit_str(s, "// Future commit: nishi-synth gets a callback API so this\n" as *u8)
153 nx_synth_emit_str(s, "// emits BETWEEN the structural ports + the endmodule;\n" as *u8)
154 nx_synth_emit_str(s, "// today the two halves ship as separate modules that\n" as *u8)
155 nx_synth_emit_str(s, "// the wrapper file unifies via instantiation.\n\n" as *u8)
156 nx_synth_emit_str(s, "module nx_rv64im_alu_logic (\n" as *u8)
157 nx_synth_emit_str(s, " input wire [7:0] op_in,\n" as *u8)
158 nx_synth_emit_str(s, " input wire [63:0] a_in,\n" as *u8)
159 nx_synth_emit_str(s, " input wire [63:0] b_in,\n" as *u8)
160 nx_synth_emit_str(s, " output reg [63:0] result\n" as *u8)
161 nx_synth_emit_str(s, ");\n\n" as *u8)
162
163 nx_synth_emit_logic_alu(s)
164
165 nx_synth_emit_str(s, "endmodule\n" as *u8)
166 return NX_HDL_OK
167}