code wiki / _hdl_build / nx_energy_model.nx
nx_energy_model.nx source
↩ module page · 48 lines · 2603 B
1// nx_energy_model.nx -- STATE-INDEPENDENT energy estimation, pure NishiLang.
2//
3// The S-class answer to "you can only measure energy when not charging": you do
4// NOT measure the battery at all. Energy is estimated from a CHARGE-INDEPENDENT
5// activity counter -- cycles (rdtsc/rdcycle, available on EVERY target including a
6// PMU-less VM) or retired instructions (perf, where a PMU exists) -- times a
7// per-unit energy coefficient:
8//
9// energy = activity_count * energy_per_unit
10//
11// activity_count does not depend on whether the node is plugged in, so this yields
12// a real energy number on AC, on a battery-less server, on a supercomputer node,
13// or on a discharging garden sensor -- ANY power state, ANY hardware.
14//
15// The coefficient is the only chip-specific part. A real joule sensor (RAPL, an
16// INA219 on the rail, or a discharging fuel-gauge) CALIBRATES it once:
17// energy_per_unit = real_energy_measured / activity_count_for_that_work
18// Where no sensor is present we use a documented PRIOR (clearly labeled, not a
19// measured claim) and refine it the moment a sensor appears. So a sensor IMPROVES
20// the number; it is never REQUIRED to get one.
21//
22// Research (Cardinal #4, real sources): per-operation / per-cycle energy figures --
23// Horowitz, "Computing's Energy Problem (and what we can do about it)," ISSCC 2014;
24// event-driven accounting -- Bellosa, ACM SIGOPS EW 2000; PMC power models -- Isci
25// & Martonosi, MICRO 2003; Bircher & John, ISPASS 2007.
26
27import "nx_syscalls.nx"
28const EM_MAGIC_1000000: i64 = 1000000
29
30// Prior coefficient in FEMTOJOULES per reference cycle. ~5e6 fJ = 5 nJ/cycle is the
31// order of magnitude for one active modern core (~15 W at ~3 GHz: 15/3e9 = 5 nJ).
32// This is a PRIOR to be calibrated per chip -- NOT a measured value for any
33// specific node. (Horowitz ISSCC 2014 gives the per-op pJ figures this scales from.)
34const EM_PRIOR_FJ_PER_CYCLE: i64 = 5000000
35
36// energy (femtojoules) from a state-independent activity count + coefficient.
37func em_energy_fj(activity: i64, fj_per_unit: i64) -> i64 { return activity * fj_per_unit }
38
39// CALIBRATE the coefficient from a real joule measurement over a known activity
40// count: fj_per_unit = real_energy_fj / activity. -1 on bad input.
41func em_calibrate_fj_per_unit(real_energy_fj: i64, activity: i64) -> i64 {
42 if activity <= 0 { return 0 - 1 }
43 return real_energy_fj / activity
44}
45
46// convenience: femtojoules -> picojoules and -> nanojoules (integer, truncating).
47func em_fj_to_pj(fj: i64) -> i64 { return fj / 1000 }
48func em_fj_to_nj(fj: i64) -> i64 { return fj / EM_MAGIC_1000000 }