A split breaks two things: test and budget anchoring. Stacking an untested die onto a good one multiplies the cost of the fault by the assembly; a budget anchored to one die no longer describes either. Known-good-die means each die is tested before assembly to a standard that makes stacking economic, and every budget is re-anchored per die with the link as a row in each.
Test each die with its own pads and patterns before it disappears into the package: scan, memory test and functional vectors through the die's own test access, plus a link loopback that proves the bumps it will bond with. Then compare the two outcomes with the same assumptions, because known-good-die does not change the die yields. Without it the stack yield is y_a x y_b x y_asm; with it, bad dies are discarded before assembly, so the shipped stack yield is about y_asm and the price is the die-test cost and the silicon of the discarded dies. Re-anchor each budget per die: area against that die's own limits, power against its own thermal path, timing with the link latency added to crossing paths (the slow peripheral read grows by the link's cycles), and bandwidth with the link as the narrowest row.
Budget | Die A (compute) | Die B (always-on) | Link row |
|---|---|---|---|
Area, blocks (mm2) | 0.484 | 0.101 | Bumps: about 1.0 mm2 of array (bump-limited) |
RUN power (mW) | 74.32 | 8.42 | About 0.31 (illustrative I/O power) |
Slow peripheral read (ns) | 281.25 on-die path | n/a | +166.67 illustrative: 447.92 |
CPU single-word fetch (ns) | 93.75, unchanged | n/a | Not crossing: SRAM0 and ROM stay on die A |
Bandwidth (MB/s) | 361.4 per fabric link | 19.2 slow APB | 19.2 of 48.0 peak |
The script compares the two cases with identical die yields (the Poisson values of the split lesson at an illustrative 0.1 defects per mm2 and a 99.5% assembly yield), checks that the budgets still add up per die, and shows the comparison can fail if known-good-die stops paying. Test cost is not priced here.
import math
a, b = 0.484, 0.101 # blocks per die, mm2
y_a, y_b, y_asm = math.exp(-0.1 * a), math.exp(-0.1 * b), 0.995 # illustrative yields
stack_without = y_a * y_b * y_asm # untested dies stacked: all three must be good
stack_with = y_asm # only dies that passed test are stacked
assert abs(y_a - 0.953) < 0.001 and abs(y_b - 0.990) < 0.001
assert abs(stack_without - 0.9385) < 0.001
assert stack_with > stack_without # same die yields in both lines
# The price of known-good-die: silicon of the discarded dies.
silicon_without = (a + b) / stack_without
silicon_with = (a / y_a + b / y_b) / stack_with
assert silicon_with < silicon_without # still cheaper per shipped stack
assert abs((silicon_without - silicon_with) / silicon_without - 0.016) < 0.003
# The comparison can fail: with perfect dies, known-good-die screens nothing.
def kgd_pays(ya, yb, yasm):
return yasm > ya * yb * yasm
assert kgd_pays(y_a, y_b, y_asm)
assert not kgd_pays(1.0, 1.0, y_asm)
# Re-anchored budgets: per-die RUN power adds back to the NanoSoC 82.74 mW.
p_b = 0.36 + 0.46 + 0.25 + 0.23 + 0.62 + 6.50
p_a = 65.98 + (6.00 + 1.20) + (1.00 + 0.14) # PD_SW + SRAM0 + ROM
assert abs(p_a + p_b - 82.74) < 0.01, (p_a, p_b)
read_slower = 281.25 + 166.67 # slow peripheral read + illustrative link, ns
assert abs(read_slower - 447.92) < 0.01
print(f"PASS: stack yield {stack_without*100:.1f}% -> {stack_with*100:.1f}% with KGD; dies {p_a:.2f}/{p_b:.2f} mW; read {read_slower:.0f} ns")Each die has a test plan that runs before assembly, with link loopback included.
Every budget table has per-die columns plus the link row; no single-die total survives the split.
The yield comparison uses the same die yields with and without known-good-die, and states what the test and the discarded silicon cost.
Verify per die first, then the link, then the stack: a failure after assembly bisects by re-running the die vectors through the package test access. If a die passes alone but the stack fails, suspect the bumps (opens, shorts, link timing) before suspecting logic. Plan the boot across the split too: die B holds the PLL and the reset controller, so die A cannot leave reset until die B is powered, linked and running; the release order of the single-die reset (fabric first, CPU last) now spans the link, and the bring-up plan names which die answers each step.
Because the link spends all four budgets (bandwidth, latency, power and bumps) and a table without it describes neither die. The link is a component with an owner, not overhead.