Who signs the die size? Nobody can, until the area adds up. The integrator receives numbers for sixteen blocks (fifteen modules plus the PLL macro) from different owners, each measured differently; the area table splits them into eighteen rows because the CPU core and its cache, and the power switches with their isolation cells, get rows of their own. From those rows the integrator must produce one total the project can sign. The NanoSoC specification gives the full table, and the script below re-adds it independently: block subtotal 0.585 mm2, overhead 0.070 mm2, core 0.655 mm2, budget 0.940 mm2, utilisation 69.7%. If your addition disagrees, the sheet is right and your copy is stale.
A roll-up has four rungs. Blocks report standard-cell plus macro area per block; on NanoSoC the two SRAMs, the I-cache and the ROM together are 40.5% of the block total, which is normal for a memory-heavy small chip. An overhead line covers the clock tree, routing and fill at 12% of the block area. The core total is blocks plus overhead. Utilisation is core total divided by the die budget. Each rung has one owner and one formula, shown beside the table so a reviewer can recompute any line in seconds.
Rung | NanoSoC value | Formula |
|---|---|---|
Block subtotal | 0.585 mm2 (PD_SW 0.372, PD_AON 0.213) | Sum of the 18 block rows |
Clock tree, routing, fill | 0.070 mm2 | 0.585 x 0.12, rounded |
Core area | 0.655 mm2 | 0.585 + 0.070 |
Die budget and utilisation | 0.940 mm2, 69.7% | 0.655 / 0.940 |
Memory share of blocks | 40.5% | (0.100 + 0.100 + 0.025 + 0.012) / 0.585 |
The script below re-adds the per-block areas and checks every rung, including the per-domain split and the memory share. It is the standing regression check for the area table: run it whenever any block number changes.
areas = [0.150, 0.025, 0.015, 0.040, 0.012, 0.010, 0.006, 0.004,
0.100, 0.010, 0.100, 0.012, 0.008, 0.008, 0.006, 0.004, 0.015, 0.060]
assert len(areas) == 18
subtotal = round(sum(areas), 3)
assert subtotal == 0.585, subtotal
overhead = round(subtotal * 0.12, 3)
assert overhead == 0.070, overhead
core = round(subtotal + overhead, 3)
assert core == 0.655, core
util = core / 0.940
assert abs(util - 0.697) < 0.001, util
pd_sw = 0.150 + 0.025 + 0.015 + 0.040 + 0.012 + 0.010 + 0.006 + 0.004 + 0.100 + 0.010
pd_aon = round(subtotal - pd_sw, 3)
assert pd_sw == 0.372 and pd_aon == 0.213, (pd_sw, pd_aon)
mem_share = (0.100 + 0.100 + 0.025 + 0.012) / subtotal
assert abs(mem_share - 0.405) < 0.002, mem_share
print(f"PASS: subtotal 0.585, overhead 0.070, core 0.655, util {util*100:.1f}%")The four rungs with formulas, quoted against the same specification version as the RTL.
The per-domain split (0.372 versus 0.213) because power and layout consume it downstream.
The largest block named: the CPU core at 0.150 mm2, about a quarter of the block total.
Re-run the script after every block update; a mismatch means the table and the script disagree about which rows exist. The usual cause is a new row — power switches and isolation cells at 0.010 mm2 were once that row — added to the table but not to the checker, or rounded in the wrong place. Fix the checker first, then the table, then the consumers.
Two consumers read the roll-up next, and each needs a different cut. Power multiplies area by leakage density per domain, so the PD_SW and PD_AON subtotals feed the power roll-up directly; a misclassified block corrupts both budgets at once. Layout reads the macro share: at 40.5% memory the floorplan is macro-dominated, so SRAM placement leads and logic fills around it, not the reverse. When a block estimate changes, update all three consumers from the same edit or the section's budgets will disagree with each other by review time.
The subtotal becomes 0.591, overhead 0.071, core 0.662, utilisation 70.4%. The script catches it; the margin lesson decides whether that is acceptable.