code wiki / _hdl_build / nx_alu_divider.nx

nx_alu_divider.nx source

↩ module page · 109 lines · 4779 B

1// nx_alu_divider.nx -- SYNTHESIZE a correct divider as a gate-network (the 2// invention engine's FIRST real hardware invention; closes the SIL-1 stub where 3// the ALU emitter emitted an ADD cell for DIV). 4// 5// There is no DIV cell kind by design -- division must be BUILT from primitives. 6// This synthesizes a restoring divider (MSB-first, `width` stages) into an 7// NxGsim netlist using only SHL/SHR/SUB/LT/XOR/MUX/OR/AND/CONST cells: 8// rem=0, quo=0; for i = width-1 .. 0: 9// rem_in = (rem << 1) | ((a >> i) & 1) 10// ge = !(rem_in < b) 11// rem = ge ? rem_in - b : rem_in 12// quo = quo | (ge << i) 13// The result is a pure combinational network the gate-level sim (nx_nxgate_sim) 14// can verify 1:1 against behavioral a/b -- so the loop can PROVE a correct 15// divider replaces the stub, not just CATCH the stub. (Operands assumed 16// non-negative + small enough that signed LT == unsigned compare; the full 17// 64-bit unsigned compare is a later widening, mechanical.) 18// 19// This is a gate-network GENERATOR -- parameterised by width, it emits the 20// divider; that is the "invention" artifact, distinct from a hand-built netlist. 21// license_tier: ORIGINAL 22 23import "nx_nxgate_sim.nx" 24import "nx_cell_sink.nx" 25 26// append a CONST cell driving a fresh net = `val`; return the net id. 27func div_const(g: *NxGsim, val: i64) -> i64 { 28 let nn: i64 = g.n_nets 29 g.n_nets = nn + 1 30 let nc: i64 = g.n_cells 31 g.cells[nc].kind = NX_GATE_KIND_CONST 32 g.cells[nc].fanout = nn 33 g.cells[nc].f0 = 0 - 1 34 g.cells[nc].f1 = 0 - 1 35 g.cells[nc].f2 = 0 - 1 36 g.cells[nc].val = val 37 g.n_cells = nc + 1 38 return nn 39} 40 41// append a 2-input cell (kind a b) -> fresh net; return the net id. 42func div_op2(g: *NxGsim, kind: i64, a: i64, b: i64) -> i64 { 43 let nn: i64 = g.n_nets 44 g.n_nets = nn + 1 45 let nc: i64 = g.n_cells 46 g.cells[nc].kind = kind 47 g.cells[nc].fanout = nn 48 g.cells[nc].f0 = a 49 g.cells[nc].f1 = b 50 g.cells[nc].f2 = 0 - 1 51 g.cells[nc].val = 0 52 g.n_cells = nc + 1 53 return nn 54} 55 56// append a MUX (sel ? t : f) -> fresh net; return the net id. 57func div_mux(g: *NxGsim, sel: i64, t: i64, f: i64) -> i64 { 58 let nn: i64 = g.n_nets 59 g.n_nets = nn + 1 60 let nc: i64 = g.n_cells 61 g.cells[nc].kind = NX_GATE_KIND_MUX 62 g.cells[nc].fanout = nn 63 g.cells[nc].f0 = sel 64 g.cells[nc].f1 = t 65 g.cells[nc].f2 = f 66 g.cells[nc].val = 0 67 g.n_cells = nc + 1 68 return nn 69} 70 71// Synthesize the restoring divider against a CELL SINK (the canonical form): 72// emits the SAME divider gate-network to either an in-memory NxGsim (Verifier) 73// or a .nxgate text sink (shipping emitter). na (dividend) / nb (divisor) are 74// pre-existing net ids in the sink's space. Writes the remainder net to 75// rem_out[0]; returns the quotient net. Cell order is identical to the legacy 76// NxGsim form below, so MEM-mode netlists are byte-identical (gates unchanged). 77func nx_div_synth_sink(k: *NxCellSink, na: i64, nb: i64, width: i64, rem_out: *i64) -> i64 { 78 let c0: i64 = nx_sink_const(k, 64, 0) 79 let c1: i64 = nx_sink_const(k, 64, 1) 80 var rem: i64 = c0 81 var quo: i64 = c0 82 var i: i64 = width - 1 83 while i >= 0 { 84 let ci: i64 = nx_sink_const(k, 64, i) 85 let shifted: i64 = nx_sink_cell(k, NX_GATE_KIND_SHL, 64, rem, c1, 0 - 1, 2) // rem << 1 86 let abit_sh: i64 = nx_sink_cell(k, NX_GATE_KIND_SHR, 64, na, ci, 0 - 1, 2) // a >> i 87 let abit: i64 = nx_sink_cell(k, NX_GATE_KIND_AND, 64, abit_sh, c1, 0 - 1, 2) // (a>>i) & 1 88 let rem_in: i64 = nx_sink_cell(k, NX_GATE_KIND_OR, 64, shifted, abit, 0 - 1, 2) // (rem<<1) | bit 89 let lt: i64 = nx_sink_cell(k, NX_GATE_KIND_LTU, 64, rem_in, nb, 0 - 1, 2) // rem_in <u b -> 0/1 90 let ge: i64 = nx_sink_cell(k, NX_GATE_KIND_XOR, 64, lt, c1, 0 - 1, 2) // ge = !lt 91 let sub: i64 = nx_sink_cell(k, NX_GATE_KIND_SUB, 64, rem_in, nb, 0 - 1, 2) // rem_in - b 92 let rem_next: i64 = nx_sink_cell(k, NX_GATE_KIND_MUX, 64, ge, sub, rem_in, 3) // ge ? sub : rem_in 93 let qbit: i64 = nx_sink_cell(k, NX_GATE_KIND_SHL, 64, ge, ci, 0 - 1, 2) // ge << i 94 let quo_next: i64 = nx_sink_cell(k, NX_GATE_KIND_OR, 64, quo, qbit, 0 - 1, 2) // quo | qbit 95 rem = rem_next 96 quo = quo_next 97 i = i - 1 98 } 99 rem_out[0] = rem 100 return quo 101} 102 103// Legacy NxGsim entry point -- now a thin MEM-sink wrapper over the canonical 104// nx_div_synth_sink (ONE divider algorithm). Byte-identical netlist to before. 105func nx_div_synth(g: *NxGsim, na: i64, nb: i64, width: i64, rem_out: *i64) -> i64 { 106 let k: *NxCellSink = sys_mmap(64) as *NxCellSink 107 nx_sink_init_mem(k, g) 108 return nx_div_synth_sink(k, na, nb, width, rem_out) 109}