Three numbers decide the supply, the package and the battery story: 82.74, 16.89 and 1.40 mW. The power table has eighteen rows (sixteen blocks, with the CPU core and its cache and the power switches split out) with dynamic and leakage columns across three states, and the totals — 82.74, 16.89, 1.40 mW — decide nothing if they do not add up. The script below re-adds them independently, per domain and per state, so any stale copy is caught by arithmetic rather than by silicon.
Each block contributes dynamic power when it switches plus leakage whenever it is powered. RUN switches everything a DMA job needs; IDLE gates the CPU, DMA and SBR clocks so their dynamic terms fall to near zero while leakage stays; SLEEP switches the whole PD_SW domain off so only 2% switch leakage plus retained SRAM0 remains, and the PLL goes dark. Two facts dominate the table. The CPU core is 39.20 mW of the 82.74 RUN total, 47.4%: the processor buys the performance and pays the power. And leakage in SLEEP is 1.24 of the 1.40 mW: when nothing switches, what leaks is everything.
State | PD_SW (mW) | PD_AON (mW) | Total (mW) |
|---|---|---|---|
RUN | 65.98 | 16.76 | 82.74 |
IDLE | 6.83 | 10.06 | 16.89 |
SLEEP | 0.06 | 1.34 | 1.40 |
The script re-adds the dynamic and leakage columns per state and checks the domain splits and the two headline shares.
dyn_run = [38.00, 4.00, 3.00, 5.00, 3.00, 2.00, 1.00, 0.50, 6.00,
0.00, 6.00, 1.00, 0.30, 0.40, 0.20, 0.20, 0.50, 6.00]
leak = [1.20, 0.30, 0.12, 0.32, 0.10, 0.08, 0.05, 0.03, 1.20,
0.08, 1.20, 0.14, 0.06, 0.06, 0.05, 0.03, 0.12, 0.50]
assert abs(sum(dyn_run) - 77.10) < 0.005
assert abs(sum(leak) - 5.64) < 0.005
assert abs(sum(dyn_run) + sum(leak) - 82.74) < 0.01
dyn_idle = [1.90, 0.20, 0.30, 0.50, 0.00, 0.00, 0.10, 0.05, 0.30,
0.00, 0.30, 0.00, 0.30, 0.40, 0.20, 0.20, 0.50, 6.00]
assert abs(sum(dyn_idle) + sum(leak) - 16.89) < 0.02
dyn_sleep = [0.00]*10 + [0.00, 0.00, 0.03, 0.04, 0.02, 0.02, 0.05, 0.00]
leak_sleep = [0.02, 0.01, 0.00, 0.01, 0.00, 0.00, 0.00, 0.00, 0.02,
0.00, 0.72, 0.14, 0.06, 0.06, 0.05, 0.03, 0.12, 0.00]
assert abs(sum(dyn_sleep) + sum(leak_sleep) - 1.40) < 0.02
cpu_share = (38.00 + 1.20) / 82.74
assert abs(cpu_share - 0.474) < 0.002
# Idle factors (specification 10): gated rows must fall by exactly their factor,
# the always-on rows and the PLL must not move between RUN and IDLE.
gated = {"cpu core": (38.00, 1.90, 0.05), "i-cache": (4.00, 0.20, 0.05),
"async bridge": (3.00, 0.30, 0.10), "xbar": (5.00, 0.50, 0.10),
"axi2apb": (1.00, 0.10, 0.10), "apb_cdc": (0.50, 0.05, 0.10),
"sram1": (6.00, 0.30, 0.05), "sram0": (6.00, 0.30, 0.05)}
for name, (run, idle, factor) in gated.items():
assert abs(idle / run - factor) < 1e-9, name
always_on = {"uart": (0.30, 0.30), "spi": (0.40, 0.40), "gpio": (0.20, 0.20),
"intc": (0.20, 0.20), "rstpwr": (0.50, 0.50), "pll": (6.00, 6.00)}
assert all(run == idle for run, idle in always_on.values())
print(f"PASS: RUN 82.74, IDLE 16.89, SLEEP 1.40 mW; CPU share {cpu_share*100:.1f}%; idle factors hold")Dynamic and leakage quoted separately per row: a single blended column cannot size the supply or the battery.
Per-domain subtotals, because the switch and the always-on supply are sized independently.
The two headline facts (CPU share, SLEEP leakage dominance) stated, not left for the reader to compute.
Re-run the script whenever any row changes; the usual breakage is an IDLE factor edited in prose but not in the table, or a new block added to RUN but forgotten in SLEEP. Check the SLEEP column hardest: every PD_SW row must read zero dynamic and near-zero leakage, and any nonzero is either a missing clock gate or a missing isolation entry.
Use the IDLE column as a clock-gating check on the rows that are meant to be gated: the CPU falls from 38.00 to 1.90 mW (factor 0.05), the fabric from 5.00 to 0.50 and the bridges to a tenth, while the PLL stays at 6.00 because wake latency forbids switching it off. The always-on peripherals (UART, SPI, GPIO, INTC, the reset controller) and the PLL sit at factor 1.0 in IDLE by assumption, so a flat row there is correct, not a finding. Among the gated rows (CPU, cache, fabric, bridges, SRAMs), one that barely moves between RUN and IDLE is a clock that is not really gated — usually a missing enable or an enable qualified in the wrong domain. Compare each gated row's RUN-to-IDLE ratio with its stated factor; a mismatch is the finding.
Leakage: retained SRAM0 at 0.72 mW plus the always-on peripherals, controller and their leakage. Switching is nearly gone; leaking is not.