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}