The ballout is a proposal until the board team agrees it routes: an assignment that escapes cleanly on paper can still demand more via rows than the board stack allows, or place balls the board must keep apart. The pinout review is the meeting where both sides sign one table, and this lesson runs it for NanoSoC: the agenda, the escape arithmetic, and the database that stops the two sides drifting apart afterwards.
Escape routing is the board's problem the chip creates. Balls on the outer row of the array escape on the surface layer of the board; each ring deeper needs a via down to an inner layer, so the number of rings the board will use bounds how deep a signal may sit. The review checks three things. First, depth: no signal sits deeper than the agreed escapable ring. On NanoSoC's 6 x 6 array every signal ball is on the outer ring, so none needs a via at all; only supply balls sit deeper, where they go straight to planes. Second, adjacency: the neighbours the chip assumed, such as guards beside the crystal, still hold after the board spreads out. Third, identity: every ball on the chip drawing names the same net as the board netlist, checked by script, because a renamed ball is a board respin that looks like success.
The standing fix for identity drift is a single pin database owned by the integrator: one table of pad, ball, net and direction that generates the chip constraint file, the board netlist import and the review slides. Either side may propose a move; neither side moves alone; the database regenerates all three consumers after every accepted move. The review cadence is fixed too: a preliminary review when the ballout is sketched, a final review when placement freezes, and a re-review after any change that touches a ball.
The script below holds the chip's ball table and the board's netlist as two tables and checks them against each other: every ball must name the same pad on both sides, and every signal ball must lie within the agreed escapable depth. It then runs the check on a board netlist in which the board team swapped two balls to ease a route, and on a chip table in which the SPI clock has drifted to the centre; both must be rejected.
GRID = [
["XTAL_IN", "XTAL_OUT", "RESET_N", "GPIO8", "GPIO9", "GPIO10"],
["GPIO15", "VSS_CORE0", "VSS_IO1", "VDD_IO1", "VSS_CORE1", "GPIO11"],
["GPIO13", "VDD_CORE0", "NC", "NC", "VDD_CORE1", "GPIO12"],
["GPIO14", "VDD_CORE2", "NC", "NC", "VDD_CORE3", "GPIO7"],
["NC", "VSS_CORE2", "VSS_IO0", "VDD_IO0", "VSS_CORE3", "GPIO5"],
["GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO6"],
]
ESCAPE_DEPTH = 0 # board-agreed: signals only on the outer ring (teaching value)
def table(grid):
return {(r, c): n for r, row in enumerate(grid) for c, n in enumerate(row)}
def depth(rc):
r, c = rc
return min(r, c, 5 - r, 5 - c)
def check(chip, board):
assert set(chip) == set(board), "different ball sets"
for ball in chip:
assert chip[ball] == board[ball], ("ball", ball, chip[ball], "versus", board[ball])
for ball, pad in chip.items():
if pad.startswith(("GPIO", "RESET", "XTAL")):
assert depth(ball) <= ESCAPE_DEPTH, (pad, "too deep to escape")
deep = sum(1 for b, p in chip.items() if depth(b) > 0 and p != "NC")
return deep
chip = table(GRID)
board = dict(chip)
assert check(chip, board) == 12 # 12 supply balls sit inside the outer ring; none is a signal
swapped = dict(board)
swapped[(5, 2)], swapped[(5, 3)] = board[(5, 3)], board[(5, 2)] # board swaps SCK and MOSI
drift = dict(chip)
drift[(2, 2)], drift[(5, 2)] = "GPIO2", "NC" # SCK drifts into the centre
for c, b in ((chip, swapped), (drift, drift)):
try:
check(c, b)
raise SystemExit("a bad table was accepted")
except AssertionError:
pass
print("PASS: chip and board tables identical, no signal deeper than the outer ring; a swapped ball and a drifted clock rejected")The single pin database named as the source, with chip, board and slides all generated from it.
Escape depth quoted per fast signal against the board-agreed ring limit.
Review minutes recording every ball move with both sides' sign-off.
Re-run the identity diff the night before tapeout: export ball names from the chip database and net names from the board netlist and join them mechanically. On the bench, a swapped-ball suspect is confirmed by toggling one pin at a time from firmware and probing the board ball: the pin that moves the wrong net names the swapped row.
Because two edited files drift: the chip constraint, the board import and the review slides must all change together, and only regeneration from one source guarantees they do. A promise to update is a future divergence with a friendly voice.