The supply pairs the budget bought only help if the switching current actually uses them: an output whose nearest return is far away drives its surge through a long inductive loop, and the noise lesson's arithmetic assumed a short one. Pairing, placing each output within reach of its supply pair, is what makes the budget real, and drive strength is the knob that trades the edge speed a link needs against the allowance its neighbours share. This lesson sets both for NanoSoC.
Every output current flows in a loop: out through the signal pad, back through the nearest supply pad. The loop size sets the inductance, so the rule is geometric. Each GPIO pad is served by one named I/O pair, the nearest along the ring, and no pad is more than 6 slots (480 µm) from a pad of that pair. The four SPI pads, the fastest signals on the chip, are held to a tighter rule: within 2 slots. On the NanoSoC ring they satisfy it by position: SPI sits on P2 to P5, which are the pads on either side of I/O pair 0, within one or two slots of it. MISO and the UART receive pad are inputs and draw no switching current; the crystal pads are analog and never share a pair.
Drive strength is then set pin by pin, with a single rule: every output uses the default drive (an illustrative 4 mA, medium slew) unless a timing number demands more. A stronger drive is not free, because the fast drive of the I/O library halves the number of outputs the pair tolerates switching together, so one fast output uses the allowance of two default outputs. The weighted count below is the whole method: a pair's load is the number of its outputs that can switch together, counting a fast one twice, and it must not exceed 8.
Group | Pair | Timing need | Drive (teaching) | Weight in the pair's count |
|---|---|---|---|---|
P0 to P7 (UART, SPI, GPIO) | Pair 0 (east) | UART 115 kbaud, SPI edges, slow GPIO | Default | 8 outputs, 8 of 8 |
P8 to P15 (GPIO) | Pair 1 (south) | Slow control | Default | 8 outputs, 8 of 8 |
Any pad raised to the fast drive | Its pair | A measured timing reason | Fast | Counts twice: 9 of 8, fails |
The script below takes the NanoSoC ring and checks both rules on it: every GPIO within 6 slots of its pair, every SPI pad within 2 slots, and the weighted count of each pair at most 8. It then raises one LED pin to the fast drive and requires the weighted check to fail, and moves the SPI pads away from pair 0 and requires the distance rule to fail.
RING = ["VDD_CORE0", "VSS_CORE0", "XTAL_IN", "XTAL_OUT", "VSS_CORE1", "VDD_CORE1", "RESET_N", "GPIO0",
"GPIO1", "GPIO2", "GPIO3", "VDD_IO0", "VSS_IO0", "GPIO4", "GPIO5", "GPIO6",
"GPIO7", "VSS_CORE2", "VDD_CORE2", "GPIO8", "GPIO9", "GPIO10", "VDD_IO1", "VSS_IO1",
"GPIO11", "GPIO12", "GPIO13", "GPIO14", "GPIO15", "VSS_CORE3", "VDD_CORE3"]
PAIR_OF = {("GPIO%d" % i): (0 if i < 8 else 1) for i in range(16)} # P0-P7 pair 0, P8-P15 pair 1
SPI = ["GPIO2", "GPIO3", "GPIO4", "GPIO5"] # SCK, MOSI, MISO, CS_N
LIMIT, MAX_SLOTS, SPI_SLOTS = 8, 6, 2 # illustrative
def dist(ring, pad, pair):
n = len(ring)
i = ring.index(pad)
return min(min(abs(i - ring.index(s + str(pair))), n - abs(i - ring.index(s + str(pair))))
for s in ("VDD_IO", "VSS_IO"))
def check(ring, fast=()):
for pad, pair in PAIR_OF.items():
assert dist(ring, pad, pair) <= MAX_SLOTS, (pad, "too far from its pair")
for pad in SPI:
assert dist(ring, pad, PAIR_OF[pad]) <= SPI_SLOTS, (pad, "SPI pad too far")
for pair in (0, 1):
load = sum(2 if p in fast else 1 for p, q in PAIR_OF.items() if q == pair)
assert load <= LIMIT, ("pair", pair, "weighted load", load)
check(RING)
for bad in ({"fast": ("GPIO9",)}, {"fast": ("GPIO3",)}):
try:
check(RING, **bad)
raise SystemExit("a fast drive should have failed")
except AssertionError:
pass
moved = list(RING)
moved[9], moved[24] = "GPIO11", "GPIO2" # SCK swapped with a pair 1 pad, far from pair 0
try:
check(moved)
raise SystemExit("a distant SPI pad should have failed")
except AssertionError:
pass
print("PASS: every GPIO within 6 slots, SPI within 2, 8 of 8 per pair; a fast drive and a distant SPI pad are rejected")A named feed list per supply pair, matching the noise lesson's groups exactly.
No output orphaned from its pair in the ring order, with the distances written down.
Each raised drive justified by a timing number and paid for in the pair's weighted count.
Verify pairing against the placed ring order, not the logical pin list: the check above runs on the order, and the order is what layout implements. If a late change moves a driver, re-run the checker on the new order before signing the move; a swap of two pads can orphan a fast output from its return or tip a pair over its limit.
Nothing visible at first, which is the trap: the LED still lights. But the pair's weighted load goes from 8 to 9 against a limit of 8, so the worst-case port write now exceeds the allowance, and the failure appears on the board as noise on a quiet neighbour. Drive is never free; it is spent from a shared allowance.