A gap in the ring is not a cosmetic flaw. An unclosed supply ring starves a whole side of the die, a missing corner cell breaks the path the protection topic depends on, and a noisy output placed beside the crystal pads undoes the choice of analog pads. This lesson is about the arrangement of NanoSoC's pads around the die and what must be true of it before the package can use it. Pad choice is the previous lesson; per-pin options are the next.
The I/O ring is the perimeter row of pad cells, filler cells and corner cells around the core; the physical design course covers how it is built. The integrator needs three facts about it. The supply rails run around the whole ring and pass through all four corner cells, so the ring is checked as one closed loop, not as four independent sides. Each side needs supply pads so no stretch of core logic sits far from a feed. And pad order is an integration decision: quiet pins are grouped, the crystal has only supply pads or its own partner as neighbours, and the two I/O supply pairs are placed far apart, one in each half of the ring, so the switching-noise lesson can split the outputs between them.
NanoSoC's ring has 31 pads in 8, 8, 8 and 7 on the four sides. The die side is 0.970 mm and the ring needs 640 µm for its longest side, so every side fits with room to spare (the arithmetic is in the pad-count topic). The crystal pads sit on the north side between two core ground pads and two core supply pads, which is why they have only quiet neighbours. I/O supply pair 0 sits on the east side, among GPIO1 to GPIO6, and pair 1 on the south side, among GPIO8 to GPIO10.
The script below holds the ring by side, then checks what the review asks for: the side counts, a supply pad on every side, each crystal pad having only supply or crystal neighbours, the longest side against the usable length, and one I/O pair in each half of the ring. It then moves the crystal beside a GPIO, and also moves both I/O pairs next to each other, and requires both layouts to be rejected.
SIDES = {
"N": ["VDD_CORE0", "VSS_CORE0", "XTAL_IN", "XTAL_OUT", "VSS_CORE1", "VDD_CORE1", "RESET_N", "GPIO0"],
"E": ["GPIO1", "GPIO2", "GPIO3", "VDD_IO0", "VSS_IO0", "GPIO4", "GPIO5", "GPIO6"],
"S": ["GPIO7", "VSS_CORE2", "VDD_CORE2", "GPIO8", "GPIO9", "GPIO10", "VDD_IO1", "VSS_IO1"],
"W": ["GPIO11", "GPIO12", "GPIO13", "GPIO14", "GPIO15", "VSS_CORE3", "VDD_CORE3"],
}
PITCH_UM, USABLE_UM = 80, 849.5 # illustrative pitch; usable length of one die side
def check(sides):
flat = [p for s in "NESW" for p in sides[s]]
assert [len(sides[s]) for s in "NESW"] == [8, 8, 8, 7], "side counts"
assert len(flat) == 31 and len(set(flat)) == 31
for s in "NESW":
assert any(p.startswith(("VDD", "VSS")) for p in sides[s]), s + " has no supply pad"
assert len(sides[s]) * PITCH_UM <= USABLE_UM, s + " does not fit"
quiet = lambda p: p.startswith(("VDD_CORE", "VSS_CORE", "XTAL"))
for i, p in enumerate(flat):
if p.startswith("XTAL"):
for nb in (flat[i - 1], flat[(i + 1) % len(flat)]):
assert quiet(nb), p + " has a noisy neighbour " + nb
io = [i for i, p in enumerate(flat) if p.startswith("VDD_IO")]
gap = min(io[1] - io[0], len(flat) - (io[1] - io[0]))
assert gap >= 8, "I/O supply pairs are not in opposite halves of the ring"
return max(len(v) for v in sides.values()) * PITCH_UM
assert check(SIDES) == 640
bad1 = {k: list(v) for k, v in SIDES.items()}
bad1["N"][3], bad1["N"][7] = bad1["N"][7], bad1["N"][3] # XTAL_OUT now sits beside RESET_N and GPIO1
bad2 = {k: list(v) for k, v in SIDES.items()}
bad2["E"][5:7], bad2["S"][6:8] = ["VDD_IO1", "VSS_IO1"], ["GPIO4", "GPIO5"] # both I/O pairs side by side
for bad in (bad1, bad2):
try:
check(bad)
raise SystemExit("a bad ring was accepted")
except AssertionError:
pass
print("PASS: ring 8/8/8/7, longest side 640 um, crystal quiet, I/O pairs apart; two bad rings rejected")A pad-order sketch with supplies spread across sides, quiet pins grouped, and the crystal beside supply pads only.
The two I/O supply pairs in opposite halves of the ring, with the outputs each one serves named.
Rail continuity confirmed per rail: four supply rails, four corners, no open.
A review target, not a computed result: no core cell farther than about half a side from a supply pad.
Verify the ring on paper before layout: list the pads per side, confirm the counts add to the 31 of the inventory, and trace each rail from side to side through the corners. A side that collects many outputs and no supply pad is the layout the switching-noise lesson will reject; move a supply pair there now, while moving it costs a sketch rather than a respin.
The rails no longer close around that corner: one side's supplies are fed only from the far end, and a discharge event on that side has no complete path. The protection review fails before silicon does.