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}