A budget built from assumed activity — the CPU at full switching, the fabric at burst efficiency — meets measured silicon that ran a different workload at a different voltage and temperature. The gap is normal; unexplained, it is a risk. This lesson teaches how to compare the two honestly and what a miss buys.
Compare like with like: same workload, same state, same voltage and temperature, with the activity factors written down. NanoSoC's RUN budget assumes the CPU executing, one DMA job streaming and all clocks on; IDLE assumes the CPU in wfi with the DMA and SBR clocks gated; SLEEP assumes the switch open with SRAM0 retained. A RUN measurement taken while the DMA ran back-to-back, against a budget that assumes one DMA job, is not an overrun — it is a different workload. Normalise first: scale the budget's dynamic terms by the measured activity ratio, keep the leakage term as it is (leakage does not follow activity), and only then judge the residual.
Compare | Budget assumes | Silicon must match |
|---|---|---|
RUN | CPU plus one streaming DMA job | Same job, same length, same clock enables |
IDLE | CPU gated, DMA and SBR gated, PLL on | Same gate enables, same peripheral enables |
SLEEP | Switch open, SRAM0 retained, peripherals mostly idle | Same retention, same wake enables |
The script scales the budget's dynamic power to the measured activity, adds the unchanged leakage, then judges the residual; it shows that the raw delta would have failed the band while the normalised one passes. It also re-checks the clamp count, because a missing clamp is a different kind of finding: it usually shows up as a functional failure, not as a power number.
dyn, leak = 77.10, 5.64 # mW, specification RUN dynamic and leakage
measured = 88.10 # mW, illustrative silicon reading
activity_ratio = 1.05 # silicon ran 5% more beats per second (illustrative)
band = 0.05 # acceptance band, teaching value
raw = (measured - (dyn + leak)) / (dyn + leak)
assert raw > band, raw # judged raw, the chip would be flagged
budget_scaled = dyn * activity_ratio + leak # only the dynamic term scales
residual = (measured - budget_scaled) / budget_scaled
assert abs(budget_scaled - 86.60) < 0.01, budget_scaled
assert abs(residual - 0.017) < 0.002, residual
assert residual < band # within the band once normalised
# A reading that stays out of band after normalisation must still be caught.
assert (92.00 - budget_scaled) / budget_scaled > band
# Clamp cross-check: expected 114 = 47 + 52 + 13 + 2; count what is present.
expected = {"apb_cdc": 47, "sram0": 52, "rom": 13, "irq": 2}
assert sum(expected.values()) == 114
def missing_clamps(present):
return sum(expected.values()) - sum(present.values())
assert missing_clamps(expected) == 0
assert missing_clamps({**expected, "irq": 0}) == 2 # the planted bug is caught
print(f"PASS: raw +{raw*100:.1f}% flagged; normalised {budget_scaled:.2f} mW budget, residual {residual*100:.1f}% < 5%; 114 clamps")// Clamp-0 isolation cell: NanoSoC teaching model. Floating inputs from
// a switched-off domain are clamped to 0 while the isolate enable holds.
module nano_iso_clamp (
input logic in_sw,
input logic iso_en,
output logic out_aon
);
assign out_aon = iso_en ? 1'b0 : in_sw;
endmoduleEvery measurement quotes workload, state, voltage and temperature beside the number.
Activity normalisation shown before the verdict; raw deltas are never judged.
An acceptance band written down in advance (5% here as a teaching value), with misses routed to governance.
When the residual exceeds the band after normalisation, suspect leakage first: measure at two temperatures and split the excess into temperature-flat (dynamic) and temperature-steep (leakage) parts. Then suspect the ungated: clocks found by comparing the CLK_EN bits and the CPU wfi state with the budget's assumptions. Check the clamp count only if the excess is in SLEEP: a floating input on an unclamped line can add a little current there, but in RUN two missing clamps cannot explain a visible share of 82.74 mW. The power-virus question — everything switching at once — belongs to signoff simulation, not to this comparison.
Split the residual into dynamic and leakage with two temperatures: measure at the cold and hot corners with the same workload, and the flat part across temperature is dynamic while the steep part is leakage. If the hot corner carries the excess, suspect retention leakage or a missing power switch; if both corners agree, suspect activity — an ungated clock or a busier-than-budget workload. One comparison across temperature replaces a week of arguing about which half of the power model is wrong.
The clock enables and the activity ratio: an ungated DMA clock (compare CLK_EN with the budget) or a busier workload explains a flat 10% before any other theory. Then split dynamic from leakage across temperatures. Check the clamp count only if the excess is in SLEEP.