Skip to content

Verilog · Chapter 18.4 · Timing Checks

$width, $period, $nochange & $skew in Verilog — Waveform-Integrity Checks

Some timing checks judge a signal against its own shape instead of comparing data to a clock. The width check confirms a pulse is wide enough, so a runt clock or reset pulse is caught. The period check confirms a clock is not running faster than its cells allow. The nochange check confirms a signal stays stable across a window, which latches and gated paths require. The skew check confirms two related signals arrive close enough together. Three of these fire when something is too small, while skew is the odd one out and fires when a separation is too large. This page groups all four and explains what each one guards, the glitch-rejection threshold, and the comparison direction that trips people up.

Intermediate13 min readVerilogTiming Checks$width$period$skew

Chapter 18 · Section 18.4 · Timing Checks

1. The Engineering Problem

A gated clock develops a sliver of a pulse — a few hundred picoseconds wide — and a downstream flop behaves erratically. There is no data setup or hold violation; the data is fine. The problem is the clock's own shape: the pulse is too narrow for the flop to act on reliably.

not-every-timing-bug-is-data-vs-clock.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // Some timing requirements are about a SIGNAL'S OWN WAVEFORM:
   //   - is the clock's high phase wide enough?         → $width
   //   - is the clock period long enough?               → $period
   //   - did a control stay stable across a gate window? → $nochange
   //   - did two related clocks arrive close enough?     → $skew
   //
   // None of these compares data to a clock. They judge a signal's shape,
   // or the separation between two signals.

Setup/hold (18.1–18.2) and recovery/removal (18.3) all measure one signal against the clock. But a real cell also requires its clock to have a minimum high time, a minimum period, and sometimes that a control input not change during a sensitive window. These are waveform-integrity checks.

$width, $period, $nochange, and $skew judge a signal's own waveform — or the separation between two signals — rather than data against a clock. Three of them fire when something is too small; $skew fires when a separation is too large.

2. Mental Model — Judge the Shape, Not the Data-to-Clock Gap

3. $width — Minimum Pulse Width

$width checks that a pulse — from one edge to the next opposite edge of the same signal — is at least limit wide.

width.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // $width(reference_event, limit, [threshold], [notifier]);
   $width(posedge clk, 2.0, 0.1, notifier);
   //     ^edge        ^min  ^glitch-reject  ^notifier
  • Measures from the reference edge (posedge clk) to the next opposite edge (the following negedge) — i.e., the high pulse width. A negedge reference would check the low pulse.
  • Fires when that pulse is narrower than limit — a runt pulse the cell cannot act on reliably.
  • The threshold argument is glitch rejection: pulses narrower than threshold are treated as glitches and not reported as width violations (they are assumed filtered, not real edges). So with 2.0, 0.1, a pulse between 0.1 and 2.0 is a violation; below 0.1 it is ignored as a glitch. This separates "a real but too-narrow pulse" from "noise."
  • Typical use: minimum clock high/low time, minimum asynchronous reset pulse width.

4. $period — Minimum Clock Period

$period checks that the time from one edge to the next same-direction edge is at least limit.

period.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // $period(reference_event, limit, [notifier]);
   $period(posedge clk, 5.0, notifier);
   //      ^edge        ^min period  ^notifier
  • Measures from the reference edge to the next edge of the same direction (posedge to posedge) — one full cycle.
  • Fires when the period is shorter than limit — the clock is running faster than the cell's characterized maximum frequency.
  • Where $width guards the pulse, $period guards the cycle. A clock can have a legal high time and still violate the period if its low time is too short.

5. $nochange — Stability Across a Window

$nochange checks that a signal does not change during a window defined relative to a reference event.

nochange.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // $nochange(reference_event, data_event, start_offset, end_offset, [notifier]);
   $nochange(posedge clk, d, 0, 0, notifier);
   //        ^reference   ^data ^start ^end  ^notifier
  • The data_event (d) must remain stable in the window from reference + start_offset to reference + end_offset.
  • Fires when d changes inside that window.
  • Its natural home is level-sensitive logic: a transparent latch requires its data to be stable while the latch is open; a gated path requires its control to hold steady across the gating window. $nochange expresses "this must not move during this interval," which setup/hold (edge-relative) cannot.

6. $skew — Maximum Separation (the Direction Flip)

$skew checks that two related signals arrive within limit of each other — it is a maximum check, the opposite direction from everything else in the chapter.

skew.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // $skew(reference_event, data_event, limit, [notifier]);
   $skew(posedge clk1, posedge clk2, 0.5, notifier);
   //    ^reference    ^data         ^max  ^notifier
  • Fires when the gap between the two events is greater than limit — the signals drifted too far apart.
  • Compare the others: $setup/$hold/$width/$period fire when an interval is less than a limit (too small); $skew fires when it is more than the limit (too large). This reversal of the comparison is the single most common confusion with $skew.
  • Typical use: bounding the skew between two clocks (or two related control signals) that downstream logic assumes arrive together. (Variants $timeskew and $fullskew refine when the check evaluates and which signal leads; they share $skew's "too far apart" semantics.)

Visual A — what each check guards, and which direction it fires

Four waveform-integrity checks — three minimums and one maximum

data flow
Four waveform-integrity checks — three minimums and one maximum$width — pulsefires if pulse TOONARROW$period — cyclefires if period TOOSHORT$nochange —stable windowfires if signalCHANGES in window$skew —separationfires if gap TOO LARGE
$width, $period, and $nochange are minimum/stability checks — they fire when a pulse, period, or stable window is too small (or a forbidden change occurs). $skew is the odd one out: a maximum check that fires when two signals are too far apart. Same family, opposite comparison.

7. The "Fires When" Summary

CheckMeasuresFires whenDirection
$widthone edge → next opposite edge (a pulse)pulse shorter than limitminimum
$periodone edge → next same edge (a cycle)period shorter than limitminimum
$nochangea signal across a reference-relative windowsignal changes in the windowstability
$skewgap between two signalsgap larger than limitmaximum

Reading the table: the first three guard against something being too small or unstable; $skew alone guards against something being too spread out. Keep the direction straight and these four are simple.

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

Like every timing check, these live in the specify block (17.4), run in gate-level simulation on a delay-annotated netlist, accept a notifier (18.5) that forces affected outputs to X, and get their limits from the cell library via SDF. The specify block declares the check; SDF supplies the minimum width, minimum period, and skew numbers characterized for the real silicon.

9. Common Mistakes

  1. Treating $skew like the others. $skew fires when the gap is too large; $setup/$hold/$width/$period fire when it is too small. Inverting that misreads every skew result (§6, §7).
  2. Confusing the $width threshold with the limit. The limit is the minimum legal width; the threshold is glitch rejection (pulses below it are ignored, not flagged). They are different arguments with different jobs (§3).
  3. Using $width where $period is meant (or vice versa). $width guards a single pulse (edge to opposite edge); $period guards a full cycle (edge to same edge). A clock can pass one and fail the other (§3, §4).
  4. Reaching for setup/hold to express a stability window. A "must not change during this interval" requirement on level-sensitive logic is $nochange, not an edge-relative setup/hold (§5).
  5. Expecting them to synthesise. All are simulation-only (Chapter 17 overview).

10. Debugging Lab

The runt clock pulse that $width caught

Pitfall — a combinational gated clock produces a sub-minimum-width pulse
Buggy Code
// Clock gating done the naive way: AND the clock with an enable so the
// downstream flops only toggle when 'en' is high (a low-power technique).
module bad_gating (input clk, input en, output gclk);
  assign gclk = clk & en;     // combinational clock gate — the bug
endmodule

// The flops fed by gclk carry the usual minimum clock-high-width check:
//     $width(posedge gclk, 2.0, 0.1, notifier);
//
// Problem: 'en' is produced by combinational logic and can change at ANY
// time — including WHILE clk is high. If 'en' falls in the middle of clk's
// high phase, gclk is chopped: it went high with clk, then 'en' yanks it
// low early — producing a RUNT pulse far narrower than 2.0 ns.
//
//   clk : __|‾‾‾‾‾‾|__|‾‾‾‾‾‾|__
//   en  : ‾‾‾‾|___________________   (falls mid-high)
//   gclk: __|‾|________|‾‾‾‾‾‾|__    (first pulse is a sliver)
//                ↑ $width(posedge gclk, 2.0, ...) FIRES — runt pulse.
//
// The flops clocked by that sliver may not toggle reliably; in sim the
// $width violation forces X. The engineer blames the flops or the clock
// source — but the data paths are fine; the CLOCK'S SHAPE is illegal.
Symptom

Flops on a gated clock behave erratically and a $width violation fires on the gated clock, even though no data setup/hold check fails. The ungated clock is clean. The problem appears only when the enable happens to change while the clock is high.

Root Cause

The clock is being gated COMBINATIONALLY — 'gclk = clk & en' — with an enable that can change at any time. When 'en' deasserts while 'clk' is high, it chops the clock pulse short, creating a RUNT pulse much narrower than the cell's minimum clock-high width. $width(posedge gclk, 2.0, ...) measures that pulse (posedge to the next negedge), finds it below 2.0 ns, and fires. (If the sliver were below the 0.1 ns threshold it would be treated as a glitch and ignored — but here it is a real, too-narrow pulse, exactly what $width exists to catch.)

This is a waveform-integrity failure, not a data-vs-clock one: the data is fine; the clock's own SHAPE is illegal because the enable gated it at the wrong moment. The flop cannot reliably act on a clock pulse that violates its minimum width.

Fix
// Fix the GATING so the enable can only change while the clock is LOW —
// then it can never chop a high pulse. The standard solution is a latch-
// based integrated clock gate (ICG): a low-active latch samples 'en' while
// clk is low and holds it stable through the high phase.
module good_gating (input clk, input en, output gclk);
  reg en_latched;
  // Transparent while clk is low; holds en stable while clk is high.
  always @(*) if (!clk) en_latched = en;
  assign gclk = clk & en_latched;   // en_latched is stable during high phase
endmodule

// Now the enable is sampled only when clk is low, so it never changes
// during clk's high phase — gclk's high pulses are always full width and
// $width no longer fires.
//
// Lesson: $width catches a runt pulse, but the FIX is in how the waveform
// is generated (proper clock gating), not in the check. Never gate a clock
// with a combinational enable that can move while the clock is high.

11. Interview Q&A

12. Exercises

Exercise 1 — Which check?

Choose the check for each: (a) the clock high time must be at least 1.5 ns; (b) the clock must not run faster than 500 MHz; (c) two clocks must arrive within 0.3 ns of each other; (d) a latch's data must not change while it is transparent.

Exercise 2 — Direction of fire

For each, say whether the check fires when the interval is too small or too large: $width, $period, $skew.

Exercise 3 — Limit vs threshold

In $width(posedge clk, 2.0, 0.1, notifier), what happens to a 0.05 ns pulse? A 1.0 ns pulse? A 3.0 ns pulse?

Exercise 4 — Explain the runt pulse

A combinational gated clock gclk = clk & en fires a $width violation. Explain why, and give the design fix.

13. Summary

$width, $period, $nochange, and $skew are the waveform-integrity checks — they judge a signal's own shape, or two signals' separation, not data against a clock:

  • $width — minimum pulse width (edge to opposite edge); fires when too narrow; takes a glitch-rejection threshold.
  • $period — minimum clock period (edge to same edge); fires when too short.
  • $nochangestability across a reference-relative window; fires when the signal changes in it (level-sensitive logic).
  • $skewmaximum separation between two signals; fires when the gap is too large — the direction flip versus all the others.
  • Where — the specify block, in gate-level simulation; limits from SDF; notifier forces X.

The crux to keep: three of these fire when something is too small or unstable; $skew fires when a separation is too large — same family, opposite comparison.

The final sub-topic of the chapter explains what every fired check actually does: Chapter 18.5 Violation Behaviour — Notifiers & X-Propagation — how a notifier forces an output to X, how that X spreads, and how to debug a netlist-wide X flood back to one early violation.