The DMA's block constraints pin its test modes, waive its asynchronous reset release, and bound its APB inputs against a 96 MHz clock that its file defines locally. At the top, the test pins belong to the test controller, the reset release belongs to the synchroniser the wrapper supplies, and the clock belongs to the previous lesson. Keeping the file intact double-constrains, under-constrains and mis-constrains at once. The way through is a sort: per constraint class, what survives, what is rewritten against top clocks, and what is deleted.
Four classes decide every line. Clock definitions are deleted from block files and live only at the top, as the previous lesson demands. Boundary input and output delays depend on what sits across the boundary. Delays between blocks that are now timed together are deleted, because the real driver and the real load exist and are timed directly; keeping them would double-constrain the path. Delays on real chip pins are rewritten against the board plan. Delays on a block that stays a budgeted partition are re-budgeted, and the interface-budget discipline from STA governs that rewrite. Case analysis and mode constants are replaced by the top-level mode matrix: a block's test-mode setting becomes one case of the chip's modes, owned with the other modes in one place. Exceptions, false paths and multicycle paths survive only with evidence re-qualified at the top, which is the next lesson. The checker below enforces the sort mechanically: it reads each block line's class, applies the verdict, and refuses any file whose clock definitions, stale boundary delays or unqualified exceptions reach the top.
# The merge sort: every block-constraint line gets exactly one verdict.
def verdict(kind, detail):
if kind == "clock_def":
return "delete: defined at the top only"
if kind == "io_delay":
where = detail.get("across")
if where == "block": # both sides now timed together
return "delete: real driver and load are timed at the top"
if where == "chip_pin":
return "rewrite: against the board plan"
if where == "partition":
return "re-budget: block stays a budgeted partition"
return "unknown: human decision required"
if kind == "case_mode":
return "replace: fold into the top-level mode matrix"
if kind == "exception":
ok = detail.get("evidence") and not detail.get("expired", True)
return "keep" if ok else "quarantine: re-qualify or drop"
return "unknown: human decision required"
lines = [("clock_def", {}), ("io_delay", {"across": "block"}),
("io_delay", {"across": "chip_pin"}), ("io_delay", {"across": "partition"}),
("case_mode", {}),
("exception", {"evidence": "cdc-report-41", "expired": False}),
("exception", {"evidence": None, "expired": True})]
got = [verdict(k, d) for k, d in lines]
assert got[0].startswith("delete") and got[1].startswith("delete")
assert got[2].startswith("rewrite") and got[3].startswith("re-budget")
assert got[4].startswith("replace")
assert got[5] == "keep" and got[6].startswith("quarantine")
assert sum(1 for g in got if g == "keep") == 1
assert verdict("io_delay", {}).startswith("unknown") # unclassified lines are not guessed
print("PASS: 7 lines sorted; 1 exception kept, 1 quarantined, unclassified delay refused")The sorted list per block: every line classified, every clock definition gone, every rewrite traced to a top clock.
The mode matrix covering test, mission and sleep cases in one file instead of scattered case constants.
Zero unqualified exceptions surviving the trip, and no boundary delay left between two blocks that are timed together; quarantine is a decision with an owner, not a parking lot.
After the merge, compare constrained-clock coverage before and after: every top clock must constrain strictly more endpoints than the sum of the block assumptions it replaced, because top paths the blocks never saw now exist. If coverage drops on any clock, a block file was deleted rather than rewritten and its boundary delays went with it. For the synthesis-side handoff view see Physical-Aware Synthesis and Handoff.
Quarantine and re-qualify: the replicated path is a new path the block evidence never saw. The evidence must be rerun against the top-level instance, or the exception is dropped and the path timed.