code wiki / _hdl_build / nx_alu_netlist_build_div.nx
nx_alu_netlist_build_div.nx source
↩ module page · 175 lines · 8547 B
1// nx_alu_netlist_build_div.nx -- the ALU op-select netlist with the PROVEN
2// restoring divider (nx_div_synth) wired into the DIV/DIVU/REM/REMU subnets,
3// in place of the ADD stub. This is the loop's FIRST hardware invention
4// COMPOSED into context: the Generator (nx_div_synth) emits a divider gate-
5// network, the Verifier (nx_gsim_run) proves it matches behavioral div/rem
6// INSIDE the full ALU op-select cascade -- not just in isolation.
7//
8// Same topology as nx_alu_netlist_build (CONST opcode + subnet + EQ match;
9// cascade MUX), built in nx_nxgate_sim's append convention (div_const/div_op2/
10// div_mux from nx_alu_divider.nx) so the divider sub-network slots in directly.
11// Non-div ops still use the SINGLE-SOURCE nx_alu_op_to_gate_kind (one cell).
12// DIV/DIVU take the divider's quotient; REM/REMU take its remainder.
13//
14// Width 64 (full RV64). Operands assumed non-negative + small enough that the
15// signed LT in the restoring loop equals the unsigned compare -- the documented
16// V1 envelope of nx_div_synth; the full 64-bit unsigned widening is mechanical.
17
18import "nx_alu_netlist_build.nx" // NxAluNetlist, NX_ALUNL_*, nx_alu_op_to_gate_kind
19import "nx_alu_divider.nx" // div_const / div_op2 / div_mux / nx_div_synth
20import "rv64im_min_alu.nx"
21import "nx_mul_wide.nx" // nx_mul_wide_synth (high-product for mulh)
22const NX_MAGIC_4294967295: i64 = 4294967295
23const NX_MAGIC_4294967296: i64 = 4294967296
24
25const NX_ALU_DIV_WIDTH: i64 = 64
26
27// mulh subnet: high 64 bits of a*b, with two's-complement sign corrections.
28// mulhu = hi(a*b) (do_a=0, do_b=0)
29// mulhsu = hi - (a<0 ? b : 0) (do_a=1, do_b=0)
30// mulh = hi - (a<0 ? b : 0) - (b<0 ? a : 0) (do_a=1, do_b=1)
31// a<0 via signed LT against 0 -- matches behavioral nx_rv64im_mulh_*.
32func nxnl_mulh(g: *NxGsim, na: i64, nb: i64, do_a: i64, do_b: i64) -> i64 {
33 let hio: *i64 = sys_mmap(8) as *i64
34 hio[0] = 0
35 let lo: i64 = nx_mul_wide_synth(g, na, nb, hio)
36 var hi: i64 = hio[0]
37 let c0: i64 = div_const(g, 0)
38 if do_a == 1 {
39 let sa: i64 = div_op2(g, NX_GATE_KIND_LT, na, c0)
40 let cora: i64 = div_mux(g, sa, nb, c0)
41 hi = div_op2(g, NX_GATE_KIND_SUB, hi, cora)
42 }
43 if do_b == 1 {
44 let sb: i64 = div_op2(g, NX_GATE_KIND_LT, nb, c0)
45 let corb: i64 = div_mux(g, sb, na, c0)
46 hi = div_op2(g, NX_GATE_KIND_SUB, hi, corb)
47 }
48 return hi
49}
50
51// sign-extend the low 32 bits of v across bits 63..32 (gate form of sext32).
52func nxnl_sext32(g: *NxGsim, v: i64) -> i64 {
53 let cmask: i64 = div_const(g, NX_MAGIC_4294967295) // 0xffffffff
54 let c31: i64 = div_const(g, 31)
55 let c1: i64 = div_const(g, 1)
56 let chi: i64 = div_const(g, 0 - NX_MAGIC_4294967296) // 0xFFFFFFFF00000000
57 let c0: i64 = div_const(g, 0)
58 let low: i64 = div_op2(g, NX_GATE_KIND_AND, v, cmask)
59 let sbsh: i64 = div_op2(g, NX_GATE_KIND_SHR, low, c31)
60 let sb: i64 = div_op2(g, NX_GATE_KIND_AND, sbsh, c1) // bit 31
61 let fill: i64 = div_mux(g, sb, chi, c0)
62 return div_op2(g, NX_GATE_KIND_OR, low, fill)
63}
64
65// W-suffix UNSIGNED divide/remainder: operate on the low 32 bits, sign-extend the
66// 32-bit result. want_rem=0 -> DIVUW (b32==0 -> all-ones); want_rem=1 -> REMUW
67// (b32==0 -> sext32(a)). The 32-bit operands are non-negative so the restoring
68// divider's signed-LT == unsigned-compare envelope holds.
69func nxnl_wdiv_u(g: *NxGsim, na: i64, nb: i64, want_rem: i64) -> i64 {
70 let cmask: i64 = div_const(g, NX_MAGIC_4294967295)
71 let c0: i64 = div_const(g, 0)
72 let a32: i64 = div_op2(g, NX_GATE_KIND_AND, na, cmask)
73 let b32: i64 = div_op2(g, NX_GATE_KIND_AND, nb, cmask)
74 let ro: *i64 = sys_mmap(8) as *i64
75 ro[0] = 0
76 let q: i64 = nx_div_synth(g, a32, b32, 32, ro)
77 let r: i64 = ro[0]
78 let bz: i64 = div_op2(g, NX_GATE_KIND_EQ, b32, c0) // b32 == 0
79 if want_rem == 0 {
80 let qse: i64 = nxnl_sext32(g, q)
81 let allone: i64 = div_const(g, 0 - 1)
82 return div_mux(g, bz, allone, qse)
83 }
84 let rse: i64 = nxnl_sext32(g, r)
85 let ase: i64 = nxnl_sext32(g, na)
86 return div_mux(g, bz, ase, rse)
87}
88
89// W-suffix SIGNED divide/remainder: sext32 the operands, divide magnitudes
90// unsigned, sign-correct (quotient sign = sign(a)^sign(b); remainder sign =
91// sign(a)), sign-extend. want_rem=0 -> DIVW (b32==0 -> all-ones); want_rem=1 ->
92// REMW (b32==0 -> sext32(a)). The abs-then-unsigned-divide naturally yields the
93// RV overflow result (INT_MIN/-1 -> sext32(2^31) = INT_MIN), so no special case.
94func nxnl_wdiv_s(g: *NxGsim, na: i64, nb: i64, want_rem: i64) -> i64 {
95 let cmask: i64 = div_const(g, NX_MAGIC_4294967295)
96 let c0: i64 = div_const(g, 0)
97 let a32: i64 = nxnl_sext32(g, na)
98 let b32: i64 = nxnl_sext32(g, nb)
99 let sa: i64 = div_op2(g, NX_GATE_KIND_LT, a32, c0) // a < 0
100 let sb: i64 = div_op2(g, NX_GATE_KIND_LT, b32, c0) // b < 0
101 let aa: i64 = div_mux(g, sa, div_op2(g, NX_GATE_KIND_SUB, c0, a32), a32) // |a|
102 let bb: i64 = div_mux(g, sb, div_op2(g, NX_GATE_KIND_SUB, c0, b32), b32) // |b|
103 let ro: *i64 = sys_mmap(8) as *i64
104 ro[0] = 0
105 let qu: i64 = nx_div_synth(g, aa, bb, 32, ro)
106 let ru: i64 = ro[0]
107 let qsign: i64 = div_op2(g, NX_GATE_KIND_XOR, sa, sb)
108 let q: i64 = div_mux(g, qsign, div_op2(g, NX_GATE_KIND_SUB, c0, qu), qu)
109 let r: i64 = div_mux(g, sa, div_op2(g, NX_GATE_KIND_SUB, c0, ru), ru)
110 let b32low: i64 = div_op2(g, NX_GATE_KIND_AND, nb, cmask)
111 let bz: i64 = div_op2(g, NX_GATE_KIND_EQ, b32low, c0)
112 if want_rem == 0 {
113 let qse: i64 = nxnl_sext32(g, q)
114 let allone: i64 = div_const(g, 0 - 1)
115 return div_mux(g, bz, allone, qse)
116 }
117 let rse: i64 = nxnl_sext32(g, r)
118 let ase: i64 = nxnl_sext32(g, na)
119 return div_mux(g, bz, ase, rse)
120}
121
122func nx_alu_netlist_build_div(g: *NxGsim, info: *NxAluNetlist) -> i64 {
123 let sub_nets: *i64 = sys_mmap(8 * NX_RV64IM_ALU_N) as *i64
124 let match_nets: *i64 = sys_mmap(8 * NX_RV64IM_ALU_N) as *i64
125 let rem_out: *i64 = sys_mmap(8) as *i64
126
127 g.n_nets = 3 // 0=a 1=b 2=op_in primary inputs
128 g.n_cells = 0
129
130 // ----- Phase 1: per-op CONST + subnet + match -----
131 var op: i64 = 1
132 while op < NX_RV64IM_ALU_N {
133 let c_net: i64 = div_const(g, op)
134
135 var sub: i64 = 0 - 1
136 // div/rem family: emit the PROVEN divider, take quotient or remainder.
137 if op == NX_RV64IM_ALU_DIV { sub = nx_div_synth(g, NX_ALUNL_A, NX_ALUNL_B, NX_ALU_DIV_WIDTH, rem_out) }
138 if op == NX_RV64IM_ALU_DIVU { sub = nx_div_synth(g, NX_ALUNL_A, NX_ALUNL_B, NX_ALU_DIV_WIDTH, rem_out) }
139 if op == NX_RV64IM_ALU_REM { let q: i64 = nx_div_synth(g, NX_ALUNL_A, NX_ALUNL_B, NX_ALU_DIV_WIDTH, rem_out); sub = rem_out[0] }
140 if op == NX_RV64IM_ALU_REMU { let q: i64 = nx_div_synth(g, NX_ALUNL_A, NX_ALUNL_B, NX_ALU_DIV_WIDTH, rem_out); sub = rem_out[0] }
141 // mulh family: high 64 bits via the PROVEN wide multiplier + sign corrections.
142 if op == NX_RV64IM_ALU_MULHU { sub = nxnl_mulh(g, NX_ALUNL_A, NX_ALUNL_B, 0, 0) }
143 if op == NX_RV64IM_ALU_MULHSU { sub = nxnl_mulh(g, NX_ALUNL_A, NX_ALUNL_B, 1, 0) }
144 if op == NX_RV64IM_ALU_MULH { sub = nxnl_mulh(g, NX_ALUNL_A, NX_ALUNL_B, 1, 1) }
145 // W-suffix unsigned div/rem: 32-bit divide + sign-extend.
146 if op == NX_RV64IM_ALU_DIVUW { sub = nxnl_wdiv_u(g, NX_ALUNL_A, NX_ALUNL_B, 0) }
147 if op == NX_RV64IM_ALU_REMUW { sub = nxnl_wdiv_u(g, NX_ALUNL_A, NX_ALUNL_B, 1) }
148 // W-suffix signed div/rem.
149 if op == NX_RV64IM_ALU_DIVW { sub = nxnl_wdiv_s(g, NX_ALUNL_A, NX_ALUNL_B, 0) }
150 if op == NX_RV64IM_ALU_REMW { sub = nxnl_wdiv_s(g, NX_ALUNL_A, NX_ALUNL_B, 1) }
151 // everything else: single cell from the single-source kind map.
152 if sub < 0 {
153 let kind: i64 = nx_alu_op_to_gate_kind(op)
154 if kind >= 0 { sub = div_op2(g, kind, NX_ALUNL_A, NX_ALUNL_B) }
155 }
156 sub_nets[op] = sub
157
158 let m_net: i64 = div_op2(g, NX_GATE_KIND_EQ, NX_ALUNL_OPIN, c_net)
159 match_nets[op] = m_net
160 op = op + 1
161 }
162
163 // ----- Phase 2: cascade MUX -----
164 var prev: i64 = div_const(g, 0)
165 op = 1
166 while op < NX_RV64IM_ALU_N {
167 prev = div_mux(g, match_nets[op], sub_nets[op], prev)
168 op = op + 1
169 }
170
171 info.n_cells = g.n_cells
172 info.n_nets = g.n_nets
173 info.result = prev
174 return 0
175}