Protection fails silently: a missing clamp passes every functional test and fails only on the bench or in the field, so the checklist is the product, and this lesson writes it for NanoSoC. A pin added late is how protection escapes happen, so the checklist has to be something that can fail, in code, when the ring changes.
The checklist has six items. First, every signal pad has its own primary diode pair to its rail: 19 cells, of which 17 are digital pads protected to the I/O rails and 2 are crystal pads protected to the core rails with low-capacitance devices; no exception for any exposed pin. Second, a rail clamp is built into every supply pad cell, a large device that shorts the rail to ground during an event: 6 clamps, 4 on the core supply and 2 on the I/O supply. Third, the two grounds, VSS_CORE and VSS_IO, are one ground on the die, tied through a back-to-back diode pair so that discharge current can cross between them. Fourth, the rails close through all four corners, the closure the ring lesson checked for supplies. Fifth, distance: no signal pad is more than 6 slots (480 µm) from a rail clamp of its own rail. Sixth, the evidence is documentary: the schematic shows each clamp, the layout confirms each rail, and the review minutes record each item; protection is never verified by functional simulation.
All of this is on the die, in the pad cells and the ring. The package carries no clamp, and no clamp or protection cell sits on the boundary between the always-on and switchable domains, because the header switch is on the die and is not a pad. The guard balls drawn in the ballout keep the crystal quiet; they are not part of the discharge path, which is the clamps.
The script below builds the clamp table from the pad ring, then checks every item through one function: a clamp entry for each of the 19 signal pads, a rail clamp for each of the 6 supply pad cells, the ground link, and the distance rule computed from the ring order. It then runs the function on three broken data sets: one clamp entry deleted, a late pin added to the ring without an entry, and the I/O supply pads moved close together.
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"]
MAX_SLOTS = 6 # a signal pad is at most 6 slots from a clamp of its rail
is_signal = lambda p: p.startswith(("GPIO", "RESET", "XTAL", "TEST"))
def build(ring):
pad_clamps = {p: "diode_pair" for p in ring if is_signal(p)}
rail_clamps = {p: "rail_clamp" for p in ring if p.startswith("VDD")}
return pad_clamps, rail_clamps
def check(ring, pad_clamps, rail_clamps, ground_link=True):
n = len(ring)
signals = [p for p in ring if is_signal(p)]
missing = [p for p in signals if p not in pad_clamps]
assert not missing, ("no pad clamp for", missing)
unclamped = [p for p in ring if p.startswith("VDD") and p not in rail_clamps]
assert not unclamped, ("no rail clamp in", unclamped)
assert ground_link, "no diode pair between VSS_CORE and VSS_IO"
worst = {}
for p in signals:
rail = "VDD_CORE" if p.startswith("XTAL") else "VDD_IO"
i = ring.index(p)
d = min(min(abs(i - j), n - abs(i - j)) for j, q in enumerate(ring) if q.startswith(rail))
assert d <= MAX_SLOTS, (p, "is", d, "slots from a clamp of its rail")
worst[rail] = max(worst.get(rail, 0), d)
return len(signals), len(rail_clamps), worst
pads, rails = build(RING)
assert check(RING, pads, rails) == (19, 6, {"VDD_CORE": 2, "VDD_IO": 6})
late = RING + ["TEST_MODE"]
close = list(RING)
close[10], close[22] = close[22], close[10] # both I/O supply pairs now on the same side
cases = [(RING, {k: v for k, v in pads.items() if k != "GPIO7"}, rails),
(late, pads, rails),
(close, pads, rails)]
for ring, pc, rc in cases:
try:
check(ring, pc, rc)
raise SystemExit("a protection gap was accepted")
except AssertionError:
pass
print("PASS: 19 pad clamps, 6 rail clamps, ground link, worst 6 slots; a deleted clamp, a late pin and clustered I/O supplies are rejected")One clamp row per signal pad, including the crystal pads, generated from the pad list.
One rail clamp per supply pad cell, with the clamp location stated as on the die.
The ground link between the two grounds, and the corner closure signed per corner with schematic and layout references.
The distance rule computed from the placed ring, not asserted.
Verify by inspection of the review data, not by test: count clamp rows against the pad list and rail clamps against the supply pads, exactly as the script does. A protection escape is found by audit, a pin added late without a clamp row, never by a failing functional test, because functional tests cannot see protection at all.
A clamp row for the pin, a re-run of the distance rule for its position, and a corner re-sign, before the pin is accepted. Late pins are how protection escapes happen.