code wiki / _hdl_build / nx_div_pick.nx
nx_div_pick.nx source
↩ module page · 67 lines · 2780 B
1// nx_div_pick.nx -- the HARDWARE-ADAPTIVE divider picker: the Nishi Seed's
2// "grow to meet the hardware's capabilities optimally" mechanism, concretely.
3//
4// For a target described by (width W, multiplier model use_wide_mul), it BUILDS
5// each candidate divider gate-net (radix-2, radix-4, Newton/Goldschmidt),
6// MEASURES each one's HONEST critical-path latency with the real gate-delay cost
7// model (nx_lat_honest: AND=1, ADD=log2 W, MUL=2*log2 W), and RETURNS the kind
8// with the minimum -- the optimal divider FOR THAT HARDWARE. This is pure
9// search + cost model (no LLM): the Fitness leg ranking Generator variants, the
10// same loop a deployed Seed runs to auto-tune its stack to the silicon it lands
11// on (a chip with a fast wide multiplier picks Goldschmidt; a minimal one picks a
12// radix divider). Reuses the proven dividers + the proven honest latency metric.
13// license_tier: ORIGINAL
14
15import "nx_newton_struct.nx" // nx_newton_struct (shared with the latency race)
16import "nx_alu_divider_r4.nx" // nx_div_synth (radix-2) + nx_div_synth_r4
17import "nx_latency_metric.nx" // nx_lat_honest + nx_lat_log2_ceil
18const NXDP_MAGIC_4096: i64 = 4096
19
20const NXDP_R2: i64 = 0
21const NXDP_R4: i64 = 1
22const NXDP_NEWTON: i64 = 2
23
24// fresh empty NxGsim with `ninputs` primary-input nets (generous caps).
25func nxdp_gsim(ninputs: i64) -> *NxGsim {
26 let vals: *i64 = sys_mmap(NXDP_MAGIC_4096 * 8) as *i64
27 let cells: *NxGsimCell = sys_mmap(NXDP_MAGIC_4096 * 48) as *NxGsimCell
28 let g: *NxGsim = sys_mmap(64) as *NxGsim
29 g.vals = vals
30 g.n_nets = ninputs
31 g.cells = cells
32 g.n_cells = 0
33 return g
34}
35
36// Newton iteration count for width W (the proven scaling ceil(log2(W/4))+1):
37// W=16 -> 3, W=64 -> 5.
38func nxdp_newton_iters(W: i64) -> i64 {
39 return nx_lat_log2_ceil(W / 4) + 1
40}
41
42// THE PICKER. depth is a caller-allocated per-net scratch buffer (>= max n_nets).
43func nx_div_pick(W: i64, use_wide_mul: i64, depth: *i64) -> i64 {
44 let ro: *i64 = sys_mmap(8) as *i64
45 let iters: i64 = nxdp_newton_iters(W)
46
47 let g2: *NxGsim = nxdp_gsim(2)
48 ro[0] = 0
49 nx_div_synth(g2, 0, 1, W, ro) // radix-2 (W stages)
50 let l2: i64 = nx_lat_honest(g2, depth, W)
51
52 let g4: *NxGsim = nxdp_gsim(2)
53 ro[0] = 0
54 nx_div_synth_r4(g4, 0, 1, W, ro) // radix-4 (W/2 stages)
55 let l4: i64 = nx_lat_honest(g4, depth, W)
56
57 let gn: *NxGsim = nxdp_gsim(2)
58 nx_newton_struct(gn, 0, 1, W, iters, 2, use_wide_mul) // Newton (~log W iters)
59 let ln: i64 = nx_lat_honest(gn, depth, W)
60
61 // argmin over the measured honest latencies.
62 var best: i64 = NXDP_R2
63 var bl: i64 = l2
64 if l4 < bl { bl = l4; best = NXDP_R4 }
65 if ln < bl { bl = ln; best = NXDP_NEWTON }
66 return best
67}