Constraint Quality & Common Mistakes
Constraint Quality & Common Mistakes
A timing report that says "all paths met" means nothing if the constraints were wrong. A clock you forgot to define has no failing paths because it has no paths at all. The hardest bugs in timing are not violations; they are paths that were never checked. This chapter is about making sure your constraints are complete and correct, the mistakes that happen most often, and a workflow for debugging them.
What "Complete and Correct" Means
A constraint set is complete when every path that should be analyzed is analyzed, and correct when each path is judged against the right requirement. Completeness is mostly about coverage. Every clock is defined. Every input and output port has a delay. Every generated clock that physically exists has been declared. If any of these are missing, some paths fall out of the analysis and the report looks clean while real timing risk hides. Correctness is about the right numbers and references. Each I/O delay is measured against the clock that actually launches or captures it. Each exception points at the paths it was meant for and no others. A clock period matches the real frequency.

The tool helps but cannot decide intent. It can list ports with no delay set. It cannot know that you meant a path to be a false path. Quality checking is partly automated reporting and partly human review of intent.
Unconstrained Paths
An unconstrained path is one the tool does not check because nothing told it how. The most common cause is a missing clock or a missing I/O delay. Consider an input port feeding a flip-flop. If you never set an input delay on that port, the tool has no idea when the signal arrives from outside. Many tools then leave the path unconstrained: they do not check it. The path could be far too slow and the report would still say nothing.
# illustrative — names vary by tool
# Ask the tool to list ports with no input or output delay set
report_ports -unconstrained
# Ask for a count of paths that have no timing requirement
report_timing -unconstrained -max_paths 50
The fix is to find the unconstrained ports and give them delays. A blanket safety net is to set a default delay on all inputs and outputs first, then override specific ports with real numbers. That way nothing is silently skipped.

Two subtle mistakes cause more false-clean reports than almost anything else.
Missing generated clocks. A divided clock, a clock from an on-chip generator, or a clock gated and relaunched must be declared with a generated clock. If you forget, the flip-flops on that clock either get no clock (paths unchecked) or get the wrong clock (paths checked against the wrong period). A divideby-two clock checked against the original fast period looks like it fails badly; checked against nothing looks perfect. Both are wrong.
# standard SDC — portable across compliant tools
# The divided clock must be declared, or its paths are checked wrong or not at all
create_clock -name ref_clk -period 5.00 [get_ports ref_clk]
create_generated_clock -name ref_clk_div2 \
-source [get_ports ref_clk] -divide_by 2 [get_pins div_reg/Q]
Wrong I/O delay reference. An input delay must name the clock that actually times the path at the other end of the interface. If you reference the wrong clock, the tool measures arrival against the wrong edge and the slack is meaningless. This is easy to do when a design has several clocks with similar names.
| Mistake | Symptom | Consequence |
|---|---|---|
| Missing clock | No paths reported for that domain | Whole domain unchecked |

Over-constraining is asking for tighter timing than the design actually requires. People do it hoping for extra margin. It backfires. The tool spends effort and resources meeting a requirement that does not exist, inflating area, power, and runtime, and sometimes failing to close a target that was never real. A common form is shrinking a clock period below the true frequency "to be safe," or adding large input and output delays that no real interface imposes. The result is a design that is bigger and slower-tobuild than it needed to be, optimized against a phantom. The multicycle hold trap is the most famous correctness mistake. A multicycle path setup exception relaxes the setup check by moving the capture edge later, giving the path several clock periods instead of one. But the hold check, by default, moves with it. If you only relax setup and leave hold at its default, the tool may apply a hold check one full cycle after the relaxed setup edge, which is far too strict and creates impossible hold requirements. The rule: when you set a multicycle setup of N, you almost always also set a multicycle hold of N minus 1. This pulls the hold check back to the edge just before the relaxed capture edge, where it belongs.
# standard SDC — portable across compliant tools
# Give the path 3 cycles for setup; pull hold back by 2 so it checks the right edge
set_multicycle_path 3 -setup -from [get_pins src_reg/Q] -to [get_pins dst_reg/D]
set_multicycle_path 2 -hold -from [get_pins src_reg/Q] -to [get_pins dst_reg/D]
A worked example. A path can take 3 clock periods. The period is 4.00 ns, so the path has 12.00 ns for setup. You set multicycle setup of 3. If you stop there, the default hold check lands 3 cycles, 12.00 ns, after launch, demanding the data hold stable for an absurd 12 ns. You add multicycle hold of 2. Now hold checks at 1 cycle after launch, 4.00 ns, the correct relationship. Forgetting the hold line is the classic trap that creates hold violations that look unfixable.
A Constraint Quality Checklist and Debug Workflow
Run this checklist before trusting any timing report.
| Check | Question | How |
|---|---|---|
| Clocks | Is every clock defined? | List all clocks, compare to the design |
| Generated clocks | Is every derived clock declared? | Trace dividers and generators |
| Unconstrained ports | Does every port have a delay? | Report unconstrained ports |
| Unconstrained | Are any paths unchecked? | Report unconstrained paths |
paths
| I/O references | Does each delay name the right clock? | Review each interface |
|---|---|---|
| Exceptions | Does each false/multicycle path hit its target? | Report exceptions and what they cover |
| Multicycle hold | Is hold set N-1 for each setup N? | Review every multicycle setup |
| Over-constraint | Do periods and delays match reality? | Compare to spec |
The debugging workflow when a report looks suspicious:
- 1. Check coverage first. Report all clocks and all unconstrained ports and paths. A clean report on an
incomplete constraint set is the real danger.
- 2. Sanity-check clock count and periods against the design spec. A missing or mis-period clock
explains many odd results.
- 3. For any path with strange slack, report it in full and look at which clocks launch and capture it. A
wrong reference shows up here.
- 4. For exceptions, ask the tool to report which paths each exception actually covers. An exception that
matches nothing was probably mistyped.
- 5. For unfixable hold violations on a relaxed path, check the multicycle hold line. The N-1 hold is
almost always the missing piece.
- 6. If timing closes far too easily, suspect over-constraining was relaxed or, worse, real constraints
were dropped.

The mindset that prevents most bugs: never trust a passing report until you have proven the constraints are complete. A failing path is honest. A path that was never checked is a silent risk that walks straight into silicon.
Interview Q&A
every path that should be analyzed actually is: all clocks defined, all generated clocks declared, all ports given delays. Correct means each path is judged against the right requirement: the right clock referenced, the right period, exceptions hitting only their intended paths. You need both; a complete set with wrong numbers is still wrong.
so you know to fix it. An unconstrained path is not checked at all, so the report stays clean while the path may be far too slow. The most dangerous timing report is a passing one built on incomplete constraints, because the risk walks silently into silicon.
clock either get no clock, so their paths are not checked, or get the wrong clock, so they are checked against the wrong period. A divide-by-two path checked against the original fast period looks like a bad failure; checked against nothing looks perfect. Both hide the truth.
really needs, often by shrinking the period or inflating I/O delays to "buy margin." The tool then spends area, power, and runtime meeting a phantom requirement, producing a bigger, slower-to-build design and sometimes failing a target that was never real.
multicycle setup leaves the default hold check one full extra cycle after the relaxed capture edge, demanding an impossible hold time. You almost always set multicycle hold to N minus 1. For a setup of 3 you set a hold of 2, which pulls the hold check back to the cycle just before the relaxed capture edge where it belongs.
clocks, all unconstrained ports, and all unconstrained paths before believing any "all met" result. A clean report on an incomplete constraint set is the real hazard, so confirming completeness comes before reading slack numbers.
Key Takeaways
- Complete means every path is checked; correct means each is judged rightly — you need both.
- Unconstrained paths are the worst bug because they pass silently while never being analyzed.
- Missing generated clocks and wrong I/O references quietly mis-time whole domains.
- The multicycle hold trap: set hold to N-1 whenever you set a multicycle setup of N.
- Always verify coverage before trusting a passing report, and avoid over-constraining against requirements that do not exist.
ChipBuddy
← Home
Comments
Leave a Reply