Digital Design / Module 9: Static timing
52. Constraints, false and multicycle paths
Tell the tool what's really required.
See it move
The idea
Timing tools don't know your design's intent. Constraints tell the tool what clocks exist, how signals enter and leave the chip, and which paths are special. Wrong constraints are dangerous: an over-relaxed path produces a chip that fails, with no warning from the tools.
The basics
create_clock -name clk -period 2.0 [get_ports clk]
set_input_delay 0.5 -clock clk [get_ports din]
set_output_delay 0.4 -clock clk [get_ports dout]Input and output delays describe the part of the cycle consumed outside your block.
False paths
A false path tells the tool not to time a path, because it can't matter. Paths between unrelated asynchronous clocks, handled by , are a classic example.
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]Be careful, because this stops all checking of those paths. A clock groups command is often clearer for unrelated clocks. Modern practice also adds max-delay limits on gray-coded crossings.
Multicycle paths
Some paths don't need to finish in one cycle. If data updates only every second cycle, due to an enable, it may legally take two.
set_multicycle_path 2 -setup -from [get_pins a_reg/CP] -to [get_pins b_reg/D]
set_multicycle_path 1 -hold -from [get_pins a_reg/CP] -to [get_pins b_reg/D]Why the hold line is needed
By default, the hold check is done one edge before the setup edge. So a multicycle setup of 2 moves the hold check from edge 0 to edge 1, which would demand that data stay unchanged for a whole extra cycle. That's wrong for nearly every design, so you add a multicycle hold of N-1 to move it back. Move both sliders in the animation above to see the checks shift. These edges are counted on one shared clock. When the two clocks have different frequencies, whether cycles count on the launch or capture clock matters, so check your tool's documentation for -start and -end.
Common mistake
Adding a multicycle path because a path is failing. That silently hides a real bug if the logic doesn't actually have an enable guaranteeing the extra cycle. Every exception needs a design reason you can state, and teams often verify these with formal tools.
Key ideas
- create_clock defines the clock.
- False paths are excluded from timing.
- Multicycle paths allow more than one cycle, usually with the hold adjusted too.
Your exercise
Interview questions
coreMulticycle setup of 2: what about hold?
Usually specify multicycle hold of 1, to keep the hold check at the original edge.
basicA block has a 2.0 ns clock. Its input port has set_input_delay 0.6 ns, and the first flop has a 0.2 ns setup time. How much time is left for the logic between the port and that flop?
The input delay says the outside world has already used 0.6 ns of the cycle before the signal reaches the port. The capturing flop also needs 0.2 ns of setup before the next edge. So the logic gets 2.0 - 0.6 - 0.2 = 1.2 ns, ignoring clock uncertainty. Without the input delay line, the tool would wrongly allow the full 1.8 ns.
basicWhat is the difference between a false path and a multicycle path?
A false path removes the path from timing completely, so neither setup nor hold is checked on it. A multicycle path still checks the path, but gives it N clock periods for setup instead of one. Use a false path only when the timing of the path can never matter, such as an asynchronous crossing handled by a synchronizer. Use a multicycle path when the logic matters but the design guarantees extra cycles, usually through an enable.
coreLaunch and capture flops both have an enable that is high once every 4 cycles. The clock is 1.0 ns, clock-to-Q is 0.1 ns, setup is 0.1 ns, and the logic takes up to 3.5 ns. What is the slack as a single-cycle path, and as a multicycle path? Which two constraint lines do you write?
The data arrives at 0.1 + 3.5 = 3.6 ns after the launch edge. As a single-cycle path it is required by 1.0 - 0.1 = 0.9 ns, so the slack is -2.7 ns. With four cycles it is required by 4 x 1.0 - 0.1 = 3.9 ns, so the slack is +0.3 ns. You write set_multicycle_path 4 -setup and set_multicycle_path 3 -hold, so the hold check goes back to the launch edge.
coreAn engineer adds set_multicycle_path 2 -setup on a path, and the next timing run shows a large hold violation on that same path, which passed before. What happened, and should you let the tool fix it?
By default the hold check sits one edge before the setup edge. Moving setup to edge 2 drags hold to edge 1, so the tool now demands that the data stay unchanged for a whole extra period after the launch. That is not a real requirement, so letting the tool fix it would add delay buffers that waste area and eat into the setup margin. The right fix is set_multicycle_path 1 -hold, which moves the hold check back to the launch edge.
coreThis path fails single-cycle timing, so someone proposes a two-cycle multicycle path from a_reg to b_reg. Is that safe?
reg [7:0] a_reg; wire [15:0] slow_out = a_reg * a_reg; // slow logic, takes ~2 cycles always @(posedge clk) begin a_reg <= a_next; // loads on every edge if (en) b_reg <= slow_out; // en is high every second cycle end
No. a_reg loads on every edge, so slow_out starts changing again one cycle after each launch. When b_reg captures two edges after a launch, the logic is already working on the next value, and with a long path its bits can be a mix of two launches. The extra cycle is only real if the launch flop also holds its value, for example by giving a_reg the same enable as b_reg. A multicycle constraint never adds time; it only stops the tool from checking.
coreWhat happens if you set an input or output delay smaller than the real external delay?
The tool thinks the logic inside your block has more of the cycle than it really does. Paths through those ports are then checked against a budget that is too generous, and they can report positive slack while failing in silicon. Nothing in the reports warns you, because the tool trusts the constraint. That is why external delays should come from the real interface budget, not from a number that makes timing pass.
advancedAll paths between two asynchronous clocks are made false paths. One of those paths is a Gray-coded FIFO pointer. What check have you lost, and how is it usually put back?
With a false path, the tool no longer limits how long each pointer bit takes to cross, so one bit could in principle arrive much later than the others. Gray code is only safe when a single bit is in flight at a time, so the bits must arrive close together, roughly within one clock period. Teams put a bound back with a maximum delay constraint on those crossing paths, so the skew between pointer bits is checked. A clock groups command is often clearer than a false path for declaring the clocks unrelated in the first place.
advancedA path has a 1.5 ns clock, a minimum arrival of 0.4 ns after the launch edge, and a 0.05 ns hold time. With set_multicycle_path 2 -setup alone, what is the hold slack? What is it after adding the hold line?
With setup moved to edge 2, the default hold check moves to edge 1. The data must then not change until 1.5 + 0.05 = 1.55 ns after launch, but it changes at 0.4 ns, so the slack is 0.4 - 1.55 = -1.15 ns. Adding set_multicycle_path 1 -hold moves the check back to edge 0, where the slack is 0.4 - 0.05 = +0.35 ns. The path was fine all along; only the constraint was incomplete. Clock skew is ignored here.