Skip to content

Verilog · Chapter 18.2 · Timing Checks

$setup, $hold & $setuphold in Verilog — The Synchronous Timing Checks

The previous section established what setup and hold are, the keepout window around a clock edge. This page is how Verilog checks them, through three synchronous data-versus-clock timing tasks. The setup check verifies the data was stable for long enough before the clock edge, the hold check verifies it stayed stable for long enough after, and a combined check does both in one call. They share a simple core, since each measures the time between a data change and a clock edge and fires if that gap is smaller than a limit, but they hide a famous trap. The setup and hold checks take their data and clock arguments in opposite orders, and swapping them silently makes a hold check measure the wrong interval. This page covers the three tasks, the reference edge, the argument-order rule with a mnemonic, and the notifier that forces an unknown on the output.

Intermediate13 min readVerilogTiming Checks$setup$hold$setuphold

Chapter 18 · Section 18.2 · Timing Checks

1. The Engineering Problem

You understand the capture window (18.1): data must be stable for a setup time before the clock edge and a hold time after it. Now you are reading — or writing — a flip-flop's timing model, and you need Verilog to enforce that window. Three tasks do it:

the-three-synchronous-checks.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   specify
       // data stable BEFORE the edge:
       $setup(d, posedge clk, 1.0);
 
       // data stable AFTER the edge — note the arguments are SWAPPED:
       $hold(posedge clk, d, 0.5);
 
       // both at once (the form real cell models use):
       $setuphold(posedge clk, d, 1.0, 0.5, notifier);
   endspecify

Look closely at the first two lines. $setup lists the data first, the clock second. $hold lists the clock first, the data second. That reversal is not a typo — it is the language's rule, and getting it wrong produces a check that runs without error but measures the wrong thing.

$setup, $hold, and $setuphold each measure the time between a data change and a clock edge and fire when it is smaller than a limit. $setup takes (data, clock); $hold takes (clock, data) — the order is reversed — and $setuphold combines both with the clock first.

2. Mental Model — Each Check Lists Its Two Events in Time Order

3. $setup — Data Before the Clock

$setup checks that the data settled at least tsu before the reference clock edge.

setup-task.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // $setup(data_event, reference_event, limit [, notifier]);
   $setup(d, posedge clk, 1.0, notifier);
   //     ^data          ^reference   ^tsu  ^optional notifier
  • data_event (first) — the data signal whose stability is being judged (d).
  • reference_event (second) — the clock edge it is measured against (posedge clk).
  • limit — the required setup time (tsu).
  • Fires when (time_of_clock_edge − time_of_data_change) < tsu — i.e., the data changed too close before the edge (or after it). The data arrived too late.

The order reads in time: data, then clock — the data is supposed to be there first.

4. $hold — Data After the Clock (Arguments Reversed)

$hold checks that the data stayed stable at least th after the reference clock edge. Its arguments are in the opposite order to $setup.

hold-task.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // $hold(reference_event, data_event, limit [, notifier]);
   $hold(posedge clk, d, 0.5, notifier);
   //    ^reference   ^data ^th  ^optional notifier
  • reference_event (first) — the clock edge (posedge clk).
  • data_event (second) — the data signal (d).
  • limit — the required hold time (th).
  • Fires when (time_of_data_change − time_of_clock_edge) < th — i.e., the data changed too soon after the edge.

Again the order reads in time: clock, then data — the edge happens first, then the data must hold. The reversal versus $setup is the most error-prone detail in all of timing-check coding, and the temporal-order mnemonic (§2) is the cure.

5. $setuphold — Both in One Call

$setuphold performs the setup and hold checks together. It follows $hold's order — clock first, data second — then both limits.

setuphold-task.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // $setuphold(reference_event, data_event, setup_limit, hold_limit [, notifier]);
   $setuphold(posedge clk, d, 1.0, 0.5, notifier);
   //         ^reference   ^data ^tsu ^th  ^notifier
  • One task declares the whole window: tsu before the edge and th after it.
  • It is the form real cell models prefer — more compact, and it lets the tool treat the setup and hold limits as one coherent window (including support for negative limits, an advanced case where the window is offset from the edge).
  • Semantically it is exactly $setup(d, posedge clk, tsu) plus $hold(posedge clk, d, th) — one call, both checks, sharing a notifier.

If you remember only one of the three, remember $setuphold — it is what you will most often read in a library cell.

6. The Reference Event and Conditioned Checks

The reference (and data) events can be edge-qualified and conditioned:

reference-and-conditions.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   $setup(d, posedge clk, 1.0);          // rising-edge reference
   $hold (negedge clk, d, 0.5);          // falling-edge reference
 
   // Conditioned check — only active when an enable is true (&&&):
   $setup(d, posedge clk &&& en, 1.0);   // check only while en = 1
  • posedge / negedge / edge select which clock transition is the reference. A flop that captures on the rising edge uses posedge clk.
  • &&& adds a condition — the check is active only while the condition holds. This models cells whose timing requirement applies only in certain modes (e.g. only when an enable is asserted), and avoids false violations on cycles where the path is not active.

7. The Notifier — Turning a Violation into an X

Each check takes an optional final argument: a notifier register.

notifier.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   reg notifier;                         // declared in the module
   specify
       $setuphold(posedge clk, d, 1.0, 0.5, notifier);
   endspecify
   // On any violation, the check TOGGLES 'notifier'. The cell's functional
   // model watches notifier and forces q to X when it changes.
  • When a check fires, it toggles the notifier register.
  • The cell's functional model (a UDP or behavioural block) includes the notifier so that a toggle drives the output to X — modelling the metastable, unknowable capture.
  • Without a notifier, a violation still prints a message but does not corrupt the output — so production cell models always wire one up.

This is the link from "a check fired" to "an X appeared downstream." The full mechanism — how the notifier reaches the output, and the X-propagation it triggers — is the subject of 18.5.

Visual A — what each task measures

The three synchronous checks and their intervals

data flow
The three synchronous checks and their intervals$setup(d, clk,tsu)gap data→edge must be ≥ tsu$hold(clk, d, th)gap edge→data must be ≥ th$setuphold(clk,d, tsu, th)both, one call
$setup measures the data-to-clock gap and fires if it is below tsu (data too late). $hold measures the clock-to-data gap and fires if it is below th (data too soon). $setuphold checks both around one edge. Note the argument order: setup is (data, clock); hold and setuphold are (clock, data).

8. Where They Live, and Where the Limits Come From

  • They live in the specify block (17.4), alongside path delays — one region of the cell model, two roles.
  • They run in gate-level simulation, on a delay-annotated netlist; in zero-delay RTL there is nothing to check (18.1, Chapter 18 overview).
  • The limits come from the cell library via SDF. The specify block declares which checks exist and against which edge; an SDF file back-annotates the real tsu/th numbers. So you rarely hand-author these in your own RTL — you encounter them in cell models and must read them correctly.

9. Common Mistakes

  1. Swapping the $setup/$hold argument order. $setup is (data, clock); $hold is (clock, data). Reverse them and the check measures the wrong interval — silently (§4, DebugLab). Use the temporal-order mnemonic.
  2. Wrong reference edge. A rising-edge flop must reference posedge clk; using negedge checks the wrong transition.
  3. Forgetting the notifier. Without it, a violation prints a message but the output is not forced to X, so downstream logic never sees the failure (§7).
  4. Omitting a needed condition. A path active only in one mode needs &&&; without it, the check can fire spuriously on cycles where the path is irrelevant (§6).
  5. Expecting them to synthesise. Timing checks are simulation-only — synthesis ignores the whole specify block (Chapter 17 overview).

10. Debugging Lab

The hold check that silently checked nothing

11. Interview Q&A

12. Exercises

Exercise 1 — Write the checks

For a rising-edge flop with tsu = 0.8 ns and th = 0.3 ns on data d, write (a) separate $setup and $hold calls and (b) the equivalent $setuphold. Pay attention to argument order.

Exercise 2 — Spot the bug

What is wrong with $hold(d, posedge clk, 0.4);, and what does the simulator actually check as written?

Exercise 3 — Which fires?

Data changes 0.6 ns before a posedge and again 0.2 ns after it. With $setuphold(posedge clk, d, 0.8, 0.3, notifier), which check(s) fire and why?

Exercise 4 — Conditioned check

Explain what $setup(d, posedge clk &&& mode, 1.0) checks, and why the &&& mode is there.

13. Summary

$setup, $hold, and $setuphold are the synchronous data-vs-clock timing checks:

  • $setup(data, clock, tsu) — data first; fires when the clock arrives less than tsu after the data (data too late).
  • $hold(clock, data, th)clock first (reversed); fires when the data arrives less than th after the clock (data too soon).
  • $setuphold(clock, data, tsu, th) — both at once, clock first; the form real cell models use.
  • The mnemonic — list the events in temporal order (earlier first): setup = (data, clock), hold = (clock, data).
  • The notifier — toggles on a fire and lets the cell force the output to X; without it, a violation prints but does not corrupt the value.
  • Where — the specify block, in gate-level simulation; limits arrive via SDF.

The crux to keep: $setup is (data, clock); $hold is (clock, data) — swap them and the check silently verifies nothing.

The next sub-topic moves from synchronous data to asynchronous control: Chapter 18.3 $recovery, $removal & $recrem — the checks for reset/set release relative to the clock, and the reset-comes-out-in-X hazard.