Signoff week without a freeze is a queue that never empties: timing closes on Monday's netlist while an ECO lands on Tuesday and the power rerun on Wednesday covers neither. The freeze stops the drift by declaring one set of revisions the tapeout candidate, so every check result means the same chip. Its one job is the discipline of the freeze; the script that enforces it is next.
A baseline is the frozen tuple of every versioned input and every derived database a result was run on: RTL, map, constraints, power intent, firmware and collateral, plus the synthesised netlist, the final layout database (with its checksum), the extracted parasitics, the library and technology-file revisions, and the tool versions. Results from the front half of the flow name the RTL revision; results from signoff name the netlist and layout revision as well. Freezing means three things at once: the revisions are recorded in the manifest, the sources are tagged so they can be retrieved exactly, and any change after the freeze follows the ECO discipline of the fifth topic instead of landing silently.
The word freeze covers two different dates, so this section uses two named milestones. The design freeze is when RTL, map, constraints, power intent and firmware are stable; after it only reviewed fixes enter, each logged as a post-freeze change with a reason, a risk note and re-verification. The signoff-complete date is the tapeout date: every open violation closed or waived, the implementation databases (netlist, layout, parasitics) frozen as the candidate. Between the two, closure fixes still move the netlist and layout, so results on those databases are rerun and the manifest moves; the design inputs do not move without the ECO gates. Entry rule for the design freeze: map and constraints stable and all disciplines reporting. Freezing too early multiplies ECOs; freezing too late means nothing was ever verified together.
On NanoSoC (illustrative dates) the design freeze falls on day 130. The pre-freeze weeks close the map at revision 7 and the constraints at revision 12; the freeze tags all fifteen RTL block revisions and the PLL macro, the decoder generated from the map, and the firmware image used in co-simulation, which is the 8 KB image loaded over SPI in the 12.581 ms boot. After the freeze, the only allowed edits are ECO-class changes with equivalence re-proven. Firmware is a signoff input, because the boot ROM self-test, the 64-word DMA self-test (a 750 ns job inside an 8.385 us ROM step) and the power-state walk all ran against that image.
A digest of the tuple makes the freeze checkable in one line, but only if it covers contents. Hashing revision names alone cannot catch a rebuilt firmware image or layout that keeps its name and changes its bytes, which is the classic silent drift. The digest must cover content hashes, not only names, and it must not depend on the order the keys are listed in.
# A frozen baseline is a tuple; a result is valid only if its input
# tuple equals the frozen one. The digest covers CONTENT hashes, and is
# tested with a reversed key order and with a same-name rebuild.
import hashlib
def sha(data):
return hashlib.sha256(data).hexdigest()[:12]
def entry(name, content):
return name + ":" + sha(content)
frozen = {
"rtl": "214", "map": "7", "sdc": "12", "upf": "5",
"fw": entry("boot-img-33", b"image bytes v33"),
"netlist": entry("n88", b"netlist bytes 88"),
"layout": entry("gds-31", b"layout bytes 31"),
}
def digest(d):
body = "|".join(k + "=" + d[k] for k in sorted(d))
return hashlib.sha256(body.encode()).hexdigest()[:16]
d1 = digest(frozen)
shuffled = {k: frozen[k] for k in reversed(list(frozen))}
assert list(shuffled) != list(frozen) # the order really differs
assert digest(shuffled) == d1 # and cannot change the digest
drifted = dict(frozen, sdc="13")
assert digest(drifted) != d1 # a new constraint revision moves it
rebuilt = dict(frozen, fw=entry("boot-img-33", b"image bytes v33 REBUILT"))
assert rebuilt["fw"].split(":")[0] == "boot-img-33" # same name
assert digest(rebuilt) != d1 # new contents still move the digest
print("PASS: digest stable under reorder, moves on drift and on a same-name rebuild")The design-freeze date, the signoff-complete date and the frozen tuple are written in the manifest, with tags that retrieve every input.
No check result predates the design freeze on any design input it names, and every signoff result names the current netlist and layout; anything older was rerun or is waived explicitly.
The firmware image, the netlist and the layout are identified by content hash as well as by name.
Retrieve the tag on a clean checkout and rerun the fastest check, the map alignment and overlap script: if it disagrees with the recorded result, the freeze is broken and nothing else can be trusted. Then confirm the firmware image hash against the boot log; a rebuilt image with the same name but new contents is the classic silent drift.
No, unless it follows ECO discipline: equivalence re-proven and affected checks rerun. After the freeze there are no cleanups, only logged changes, because functional intent is not something a diff can prove by inspection.