Every pin spent on test is a pad on the ring and a ball in the array, and sharing test functions onto mission pins is the obvious saving. The saving has a price list: debug that vanishes in sleep, a test mode entered by accident, straps misread through a shared driver. This lesson prices both sides for the seven extension pins before choosing. NanoSoC is core-limited, not pad-limited, so the price of extra pins is not a larger die; it shows up in the package and the noise budget.
Sharing saves pins and spends risk. A JTAG port shared with GPIO saves four balls but loses the debugger exactly when the chip misbehaves: in low-power states, during GPIO contention, or after firmware has reprogrammed the pins. A test-mode pin shared with a mission input saves one ball but needs a multiplexing rule the whole project must obey, plus a safe state visible in every mode. Dedicating all seven is the safe choice, and it has three consequences for NanoSoC. The pad total rises from 31 to 38, which still fits the perimeter (3.040 mm of the 3.878 mm available, 38 of 40 slots). The 6 x 6 package of 36 balls no longer holds them, so the array grows to 7 x 7 = 49 balls. And TDO is an output, a seventeenth one: the worst-case GPIO write was 8 and 8 outputs against a limit of 8 per I/O pair, so the extra output either needs a third I/O supply pair (two more pads, 40 pads, every slot used) or a rule that shifting out debug data and a full-port GPIO write never coincide. The count of 38 above leaves that open.
The general rule transfers to bigger chips: dedicate the pins whose absence destroys debuggability, the debug clock and mode pins first, and share only what a written multiplexing contract covers, with the shared function proven harmless in every power state. A shared pin without a contract is a future failure that looks like a saving.
The script below prices the options with one function that returns balls and spare balls and refuses what the perimeter cannot hold. It checks the base design (31 pads, 36 balls, 5 spare), the dedicated extension (38 pads, 49 balls, 11 spare), the third-pair variant (40 pads, 49 balls, 9 spare), and the fact that the 17th output breaks the per-pair limit. Two further cases must be refused: a 41st pad, which the perimeter cannot hold, and a count that claims the 36-ball package still fits 38 pads.
import math
PERIM_MM, PITCH_MM, SLOTS = 3.878, 0.080, 40 # from the pad-count topic
SSO_LIMIT, PAIRS = 8, 2 # per VDD_IO/VSS_IO pair, illustrative
EXT = {"JTAG": 4, "TEST_MODE": 1, "BOOT_MODE": 2}
def plan(pads, balls_available=None):
assert pads <= SLOTS and pads * PITCH_MM <= PERIM_MM, "the perimeter cannot hold " + str(pads)
n = math.ceil(math.sqrt(pads))
balls = n * n
if balls_available is not None:
assert pads <= balls_available, "the package has too few balls"
return balls, balls - pads
assert sum(EXT.values()) == 7
base = 31
assert plan(base) == (36, 5)
dedicated = base + sum(EXT.values())
assert dedicated == 38 and plan(dedicated) == (49, 11)
assert round(dedicated * PITCH_MM, 3) == 3.040
outputs = 16 + 1 # the GPIO pads plus TDO
assert outputs > PAIRS * SSO_LIMIT # 17 outputs exceed the capacity of 16
assert plan(dedicated + 2) == (49, 9) # a third I/O supply pair: 40 pads, every slot used
for pads, avail in ((dedicated + 3, None), (dedicated, 36)):
try:
plan(pads, avail)
raise SystemExit("an impossible plan was accepted")
except AssertionError:
pass
shared = base # all seven shared onto GPIO pads: nothing added
states_lost = ["sleep-debug", "gpio-contention-debug", "early-boot-debug"]
assert plan(shared) == (36, 5) and len(states_lost) == 3
print("PASS: dedicating 7 pins gives 38 pads, 49 balls, 11 spare; TDO needs a third pair (40 pads); sharing keeps 31 pads and loses 3 debug states")Each test pin marked dedicated or shared with the reason, not a default.
Every shared pin covered by a multiplexing contract naming all modes and states.
The debug states lost by sharing listed explicitly before the choice, not discovered after.
The noise budget re-run for any new output pad, and the package array re-sized.
Verify the choice against the array fit and the power states: the dedicated pins must exist in the ballout and must work in SLEEP, not just RUN. A sharing proposal arriving late is re-priced with this script and re-reviewed, never accepted as a pure saving.
BOOT_MODE first: the straps are sampled once at reset and then idle, so the shared pin needs a defined pull (NanoSoC's GPIO pads already reset with a pull-down), sampling confined to the reset release, and the GPIO driver held off until after sampling. TEST_MODE next. JTAG TCK and TMS last, never first, since the debugger needs them most when the chip misbehaves.