A metal-only fix without spare cells is a contradiction: there is nothing to stitch the correction from. Spares are the gates sprinkled across the layout, unconnected, waiting for an ECO to need an inverter, an AND gate or a flop exactly where the bug is. Provisioning them is an integrator decision, because only the integrator knows which blocks change late and which are left alone. This lesson sizes the NanoSoC sprinkle, shows the hookup cell in RTL, and prices what it buys. Its one job is the insurance policy; the bugs it pays out for belong to the errata topic.
Sprinkle spare flops, inverters and AND gates near late-changing logic: the interrupt controller inputs, the decoder selects, the reset and clock-enable controls. The NanoSoC teaching provision is one spare flop, two inverters and two AND gates per 500 standard flops in the interconnect, controller and bridge logic, with tie-off constants available at every spare input so an unused spare is quiet. The flop counts below are illustrative, not derived from the block areas of the course.
What spares buy is a small logic correction: a missing clamp-enable term, an inverted enable polarity, or one missing gating condition, fixed by adding an AND gate or an inverter from the spare pool in the right place. The module below shows the result in RTL: a combinational spare that ANDs in the missing condition, selected by a tie-off that is 0 until the ECO ties it to 1. A spare flop is for adding a missing registered term, and it changes the latency of that path by one cycle, so it needs its own clock-domain argument. The interrupt miswire of NanoSoC needs no spare at all: it is a re-route, reconnecting input 1 to the irq_dma net and tying input 6 low, which still counts as a metal-only fix.
// NanoSoC teaching values: spare hookup for a metal-only fix.
// Combinational: a spare inverter and a spare AND add a missing gating
// condition. TIE_SEL is tied low until an ECO needs the spare path; in
// silicon the select is the metal rewiring itself.
module nano_eco_spare_gate (
input logic orig, // original signal, driver left in place
input logic busy, // the missing gating condition
input logic tie_sel, // tied 0; the ECO ties it to 1
output logic out // signal continuing downstream
);
logic busy_n;
logic fix;
assign busy_n = ~busy; // spare inverter
assign fix = orig & busy_n; // spare AND
assign out = tie_sel ? fix : orig;
endmodule# Spare provisioning: per 500 logic flops in late-changing blocks,
# 1 spare flop, 2 inverters and 2 AND gates. Flop counts are
# illustrative, not derived from the block areas.
blocks = {"xbar": 3200, "axi2apb": 800, "rstpwr-ctrl": 400,
"intc": 120, "apb-dec": 150}
def provision(blocks):
spares = {}
for name, flops in blocks.items():
units = (flops + 499) // 500
spares[name] = {"flop": units, "inv": 2 * units, "and": 2 * units}
return spares
def covers(spares, block, need):
return all(spares[block].get(kind, 0) >= n for kind, n in need.items())
spares = provision(blocks)
assert spares["xbar"] == {"flop": 7, "inv": 14, "and": 14}, spares["xbar"]
assert spares["axi2apb"]["flop"] == 2
total_flops = sum(s["flop"] for s in spares.values())
total_inv = sum(s["inv"] for s in spares.values())
assert total_flops == 12, total_flops
assert total_inv == 24, total_inv
# the example fix (a missing gating condition in the reset and clock
# controller) needs one spare AND and one spare inverter there
need = {"and": 1, "inv": 1}
assert covers(spares, "rstpwr-ctrl", need)
# and the check can fail: a block with no provisioned logic is not covered
assert not covers(provision({"rstpwr-ctrl": 0}), "rstpwr-ctrl", need)
# a spare flop is the wrong kind for a combinational fix
assert not covers({"rstpwr-ctrl": {"flop": 3}}, "rstpwr-ctrl", need)
print("PASS: 12 spare flops, 24 inverters, 24 AND gates; coverage check rejects empty and wrong-kind spares")The spare map per block with the provisioning rule quoted, so a metal-only claim can be checked against it.
Unused spares tied off and quiet in every verification run: no floating inputs, no coverage hole blamed on them.
The hookup pattern above used for every fix, so each ECO is reviewable in minutes.
Prove the spares present and quiet: a structural check counts them against the map, and functional tests confirm the select tied off leaves every downstream signal unchanged. The re-route fix of the interrupt miswire needs no spare, but the same tie-off audit still applies to input 6, which must end up tied low. When a fix needs a spare that is not there, say respin at once; borrowing a functional gate as a spare is how a fix becomes two bugs.
No: the kind matters as much as the count, and a spare flop cannot stand in for combinational logic. The coverage check in the script rejects exactly this; the path test from the first lesson includes the kind, the reach and, for a flop, the clock.