Most timing mysteries are constraint mistakes. A short, standard battery of checks before looking at slack finds them. Tools provide this under names like check_timing; the categories are generic.
Category | Finding | Action |
|---|---|---|
No clock | Sequential pins with no clock | Add or fix generated clocks and case analysis |
Unconstrained endpoints | Endpoints with no requirement | Add input/output delays and clocks |
Generated clock problems | Master missing or ambiguous | Fix the source pin |
Loops | Combinational loops in the timing graph | Break by design or a documented disable |
Multiple clocks per pin | Pins reached by more than one clock | Intentional mux, or a missing exclusive group |
Constant conflicts | Case analysis values that contradict logic | Correct the mode constants |
Ignored exceptions | Exceptions with no matching objects or overridden | Remove or fix |
report = {"no_clock": 0, "unconstrained": 12, "gen_clock": 0, "loops": 1, "multi_clock": 0,
"const_conflict": 0, "ignored_exceptions": 3}
blocking = {k: v for k, v in report.items() if v}
assert blocking == {"unconstrained": 12, "loops": 1, "ignored_exceptions": 3}
ready = not blocking
assert ready is False
print("PASS: not ready for slack review; blocking findings:", blocking)The report has three blocking categories, so reading slack is premature. The rule is simple: the sanity checks are clean (or explained) before slack is trusted. This is the gate between constraint development and timing closure.
The check battery runs in CI on every constraint change, and its output is archived.
Each remaining finding has a waiver with a reason, never a blanket suppression.
A check that went from 0 to non-zero is investigated before any other change.
Not necessarily. Zero findings means no detected structural errors; it does not prove the clock relationships and exceptions are right. They still need the review items from the earlier lessons.