The most expensive sentence in signoff is that a fix is small so it must be cheap. A one-gate logic change can force an all-layer change if no spare gate sits where the signal can reach, while a hundred-gate change in a spare-rich block fixes in metal. Cost follows what layers must change and what must be re-verified, not the size of the diff. This lesson prices the paths in engineering weeks, mask and fab time, and residual risk. Its one job is the comparison; the process around it is next.
A metal-only ECO changes a subset of the metal and via layers, often including low layers where the spare cells sit, and leaves every base layer untouched. Wafers can sometimes be held at an earlier stage and only the changed masks remade. It can fix miswiring, wrong tie-offs, inverted polarity and small logic errors, provided spare cells exist within reach and timing still closes. An all-layer ECO changes base layers: it needs the full mask set but reuses most of the implementation database, so it is the middle option when a fix needs new cells or new placement but not a new design. A full respin repeats synthesis, implementation and signoff: it is forced by any change the other two cannot absorb, such as a map, interface or power-intent change.
Cost has two parts. Engineering effort, in weeks, is the model below. External cost and time, the mask set, the fab cycle time and the wafer-lot lead time, usually dominates the calendar on real chips and is shown here as a share of the mask layers remade and a fab time. Every figure is a NanoSoC teaching value in illustrative weeks or layers, not a quote. A schedule-risk allowance is shown beside the effort, never inside it.
# Price the three paths. All figures are illustrative NanoSoC
# teaching values (engineering weeks, mask layers out of an assumed 40,
# fab weeks); they are not vendor or foundry data.
effort = {
"metal": {"edit": 2, "eco-timing": 3, "equivalence": 3,
"focused-regression": 7},
"all-layer": {"edit": 2, "eco-timing": 4, "equivalence": 3,
"focused-regression": 7, "re-signoff": 14},
"respin": {"resynthesis": 4, "place-route": 12, "full-signoff": 22,
"full-regression": 22},
}
mask_layers = {"metal": 6, "all-layer": 40, "respin": 40}
fab_weeks = {"metal": 6, "all-layer": 12, "respin": 12}
risk_allowance = {"metal": 3, "all-layer": 6, "respin": 12} # separate
bill = {p: sum(items.values()) for p, items in effort.items()}
assert bill == {"metal": 15, "all-layer": 30, "respin": 60}, bill
assert bill["respin"] / bill["metal"] == 4.0
calendar = {p: bill[p] + fab_weeks[p] for p in bill} # effort, then fab
assert calendar == {"metal": 21, "all-layer": 42, "respin": 72}, calendar
assert mask_layers["metal"] < mask_layers["respin"]
def path(c):
"""Route a change to the cheapest path that can absorb it."""
if c.get("map") or c.get("power_intent") or c.get("interface"):
return "respin"
if c.get("needs_new_cells") and not c.get("spare_reach"):
return "all-layer"
if not c.get("timing_closes", True):
return "all-layer"
return "metal"
assert path({}) == "metal" # re-route (the interrupt miswire)
assert path({"needs_new_cells": True, "spare_reach": True}) == "metal"
assert path({"needs_new_cells": True, "spare_reach": False}) == "all-layer"
assert path({"timing_closes": False}) == "all-layer"
assert path({"map": True}) == "respin"
assert path({"power_intent": True, "needs_new_cells": True,
"spare_reach": True}) == "respin"
print("PASS: effort 15 / 30 / 60 weeks, calendar 21 / 42 / 72, five routes checked")The ECO request states which path it takes and why the other path cannot: spare reach for metal, re-verification scope for respin.
The bill in effort weeks, mask layers and fab weeks is attached with the same line items as above, so two ECOs can be compared.
A map, interface or power-intent change is never priced as metal-only.
Check the path claim against the spare map: the miswired signal must physically reach a spare cell of the right kind with timing to spare, confirmed by the implementation owner, not assumed by the requester. If the reach fails, the ECO reprices to an all-layer change or a respin immediately, before any editing starts; discovering it after the edit wastes both the edit and the week.
# not-run: the path test, NanoSoC teaching values (illustrative)
metal-only if: spare of right kind within reach (or a pure re-route)
AND timing closes AND no map, interface or power-intent change
all-layer if: new cells or placement needed, no spare in reach
respin if: map, interface or power-intent change
effort 15 / 30 / 60 weeks; calendar 21 / 42 / 72 with fab timeNot metal-only unless a spare flop with the right clock and reset sits within reach and the cone re-verifies; without one it is an all-layer change, since a new cell needs base layers. The diff size is irrelevant; the new state is what prices it.