Resistance sets the steady drop; capacitance covers the fast part. When the CPU and DMA start a transfer together, the current step arrives in nanoseconds, far faster than the board regulator can respond, and only stored charge nearby holds the rail until the regulator catches up. A capacitor in the wrong place is an open circuit at the frequency that matters. This lesson places the three levels of NanoSoC decoupling where each one works.
The target is an impedance: the allowed ripple divided by the current step. With the 45 mV allowance and the 183.87 mA peak that is about 0.245 ohm for NanoSoC's illustrative numbers. Each level of capacitance must hold the delivered impedance below the target across its band, and the levels together must cover every frequency from the regulator's bandwidth up to the fastest clock without a gap.
A capacitor is a capacitor only up to its self-resonance; beyond it, its mounting inductance (ESL) dominates. The impedance of one level is the magnitude of 2π f ESL minus 1 / (2π f C). Distance sets ESL, which is why proximity comes before value. A board capacitor of 10 µF with 1 nH of loop inductance resonates near 1.6 MHz and covers 100 kHz to 1 MHz. A package capacitor of 470 nF with 0.15 nH of loop inductance resonates near 19 MHz and covers 1 to 15 MHz. On-die capacitance of an effective 20 nF with about 1 pH of effective inductance covers 15 to 400 MHz, the range where the 24 MHz crystal clock, the 96 MHz fabric and the 192 MHz CPU switch. Below 100 kHz the regulator's own loop holds the rail. All of these are illustrative teaching values.
Two placement rules follow. On-die capacitance spreads with the load, under the CPU and the fabric where the current steps happen. Package capacitors hug the supply connections they serve: beside the core pairs for the core rail, beside the I/O pairs for the noisier driver rail. A third effect is the interaction between levels: two levels can resonate against each other between their bands, a parallel (anti-)resonance that makes the impedance worse at one frequency. The coverage table cannot see it; an impedance sweep can, and the cure is a damped or intermediate level.
The script below computes each level's impedance at the top of its band from its capacitance and its inductance, checks it against the target, checks that the bands join with no gap and reach the 192 MHz CPU clock, and prints the target. It then runs the same function on a package capacitor mounted too far away (3 nH of loop inductance instead of 0.15 nH), and on a set with a gap between two bands, and requires both to fail.
import math
Z_TARGET = 0.045 / 0.18387 # allowed ripple over the current step, about 0.245 ohm
LEVELS = {"board": (10e-6, 1.0e-9, 1e5, 1e6), # capacitance F, loop inductance H, band low, band high Hz
"package": (470e-9, 0.15e-9, 1e6, 15e6),
"die": (20e-9, 1.0e-12, 15e6, 400e6)} # all illustrative
def z_at(c, esl, f):
return abs(2 * math.pi * f * esl - 1 / (2 * math.pi * f * c))
def check(levels):
bands = sorted((lo, hi, name) for name, (c, esl, lo, hi) in levels.items())
for lo, hi, name in bands:
c, esl = levels[name][0], levels[name][1]
assert z_at(c, esl, hi) <= Z_TARGET, (name, "misses the target at", hi)
for (lo1, hi1, n1), (lo2, hi2, n2) in zip(bands, bands[1:]):
assert lo2 <= hi1, ("gap between", n1, "and", n2)
assert bands[-1][1] >= 192e6, "the CPU clock is not covered"
return Z_TARGET
assert round(check(LEVELS), 3) == 0.245
far = dict(LEVELS)
far["package"] = (470e-9, 3.0e-9, 1e6, 15e6) # the same capacitor mounted far away
gap = dict(LEVELS)
gap["die"] = (20e-9, 1.0e-12, 40e6, 400e6) # die band starts at 40 MHz, leaving 15 to 40 MHz open
for bad in (far, gap):
try:
check(bad)
raise SystemExit("a bad decoupling set was accepted")
except AssertionError:
pass
f_board = 1 / (2 * math.pi * math.sqrt(1.0e-9 * 10e-6))
assert 1.5e6 < f_board < 1.7e6
print(f"PASS: three levels hold {Z_TARGET:.3f} ohm from 100 kHz to 400 MHz; a far-mounted capacitor and a band gap are rejected")The target impedance written down with the ripple and the current step it comes from.
Each level placed by proximity to its load, with the loop inductance named as the reason.
Coverage continuous from regulator bandwidth to the fastest clock, with no gap band, and an impedance sweep for the resonances between levels.
The always-on domain's local coverage shown separately from the switchable domain's.
Verify coverage against a frequency sweep of the delivered impedance in analysis, and against a current-step measurement on the bench: fire the worst coincident step in firmware and watch the rail at the balls. A droop that recovers slowly points at missing bulk capacitance; a fast spike points at missing nearby capacitance. The timescale names the level.
Because at that frequency the inductance between the board and the die dominates: the far capacitor cannot deliver charge fast enough no matter its value. Only nearby package or on-die capacitance keeps the impedance low in that band.