Every RTL problem lands in one of these case classes. Think of it as a checklist: if we solve one example per row, no scenario can surprise you.
#
Case class
The question it asks
Degenerate / edge version
A
Setup pass (positive slack)
Does logic settle before the edge with room to spare?
slack =0 exactly (right at the limit)
B
Setup fail (negative slack)
Clock too fast — how much too fast?
fix by pipelining
C
Hold check
Does new data arrive too early and corrupt the capture?
zero logic between registers (tcomb,min=0)
D
Zero combinational logic
Register directly wired to register (a shift)
pure baton pass, no computation
E
Blocking vs non-blocking
Does = vs <= change the hardware built?
the swap / shift bug
F
Combinational vs register count
How many flip-flops does this actually build?
a wire that looks stored but isn't
G
Real-world word problem
Traffic-light / counter spec → clock frequency
limiting: what's the fastest it can run?
H
Exam twist
Two paths, pick the critical one; then hold on the short one
both constraints active at once
The eight examples below cover A through H, one each, and Example 5 covers D+E together (the shift register is the cleanest place to see both).
Symbols used everywhere below — earn them once:
The figure above is the timeline every example reads off: whistle at t, output appears tcq later, logic settles tcomb later, and the value must be steady tsetup before the next whistle. The leftover gap is the slack.
Forecast: guess before reading — is the slack positive, zero, or negative? Roughly how big?
Add the demand of the path.tcq+tcomb,max+tsetup=0.2+1.5+0.3=2.0 ns.
Why this step? This sum is the minimum period the path can survive. It's the total time from one whistle to when the data is safely steady before the next.
Compare to the supply T. We have T=3.0 ns available, the path needs 2.0 ns.
Why this step? Setup is literally "does the whistle give enough time?" — a supply vs demand comparison.
Compute slack.3.0−2.0=1.0 ns positive.
Why this step? Slack is the leftover margin; positive means safe.
Verify: 2.0≤3.0 ✓, so it passes. Units: all nanoseconds, slack =+1.0 ns. Sanity: we could shave 1.0 ns off T (run faster) before this path breaks.
Forecast: guess the slack sign at T=1.6 ns, then guess whether cutting the logic in half rescues it.
Test the original path. Demand =0.2+1.5+0.3=2.0 ns, supply T=1.6 ns.
Why? Same supply-vs-demand law.
Slack =1.6−2.0=−0.4 ns → FAIL.Why this matters: a negative slack means the data is still settling when the whistle blows — garbage gets latched. You cannot fix this by clocking faster.
Pipeline: cut tcomb in half. Insert one register in the middle so each stage does 1.5/2=0.75 ns of logic.
Why this step? Each half now sees demand =tcq+0.75+tsetup=0.2+0.75+0.3=1.25 ns.
Re-test. Slack =1.6−1.25=+0.35 ns → PASS.
Why it works: shorter logic between registers = less to settle per clock. Throughput stays high; only latency grows by one clock.
Verify: original slack =−0.4 ns (fail), pipelined stage demand =1.25 ns, pipelined slack =+0.35 ns (pass). Units ns throughout. Sanity: halving tcomb removed 0.75 ns of demand, which more than covered the 0.4 ns deficit.
Forecast: hold has noT in it. Guess pass or fail, and guess whether T can rescue it.
Compute the earliest arrival of new data.tcq+tcomb,min=0.2+0.15=0.35 ns after the edge.
Why? Hold asks: how soon can the new value show up at the receiver? The fastest route decides.
Compare to the hold requirement. Need ≥thold=0.5 ns. We have only 0.35 ns.
Why? If new data arrives before the register finished capturing the old value, it corrupts the capture.
0.35<0.5 → HOLD FAIL by 0.15 ns.Why T can't help:T never appears in tcq+tcomb,min≥thold. Slowing the clock changes nothing.
Fix: add a delay buffer of ≥0.15 ns on the short path so arrival becomes ≥0.5 ns.
Why this step? We slow down the too-fast path (add tcomb,min) — the only lever hold responds to.
Verify: arrival =0.35 ns, requirement 0.5 ns, deficit =0.15 ns. Add 0.15 ns buffer → arrival =0.50 ns =thold (exactly meets). Units ns.
Forecast: guess whether fmax is closer to 100 MHz or 1 GHz before computing.
Find the minimum period.Tmin=tcq+tcomb,max+tsetup=0.25+2.4+0.35=3.0 ns.
Why? The counter can go no faster than its worst path allows to settle.
Invert to frequency.fmax=1/Tmin.
Why this tool — reciprocal? Frequency is cycles per second; period is seconds per cycle. They are exact inverses, so f=1/T is the only conversion.
Compute.fmax=3.0×10−9s1=3.33×108 Hz =333.3 MHz.
Why the powers of ten?3.0 ns =3.0×10−9 s; its reciprocal is ≈3.33×108 Hz, and 108 Hz =100 MHz.
Verify: Tmin=3.0 ns, fmax≈333.3 MHz. Units: 1/s=Hz. Sanity: 1/3 ns → a third of a GHz =333 MHz — matches. Closer to 100 MHz than 1 GHz, as the 3 ns adder dominates.
// Intended: q1 <- old q0, q0 <- d (a true shift)// BUGGY (blocking):always @(posedge clk) begin q0 = d; // q0 updated first q1 = q0; // q1 now sees the NEW q0 = d, not the old q0!end
Forecast: after the blocking version, does q1 get the oldq0 (a real 2-deep shift) or the newq0 (collapsed to 1 stage)? Guess before step 3.
Note the logic depth is zero. Between registers there's a plain wire, so tcomb,max=0 (case D).
Why this matters: the register-to-register path is a pure baton pass — timing is trivial (T≥tcq+tsetup), but the ordering semantics of = vs <= still bite.
Trace the blocking version in order.q0 = d runs first, so q0 is now d. Then q1 = q0 reads the already-updatedq0, so q1 = d too.
Why? Blocking = executes top-to-bottom, using values as they've been updated within this pass.
Result: both registers hold d. The 2-deep shift collapsed into 1 stage — the old q0 was lost.
Fix with non-blocking <=.
always @(posedge clk) begin q0 <= d; q1 <= q0; // reads OLD q0 (pre-edge), then all latch togetherend
Why it works:<= reads all right-hand sides using pre-edge values, then updates simultaneously — exactly how real flip-flops fire in parallel. Now q1 correctly gets the oldq0, giving a genuine 2-deep shift.
Verify: feed d = 1,0,0,.... Blocking: clock1 → q0=1,q1=1; clock2 → q0=0,q1=0 (the 1 never reappears at q1 one cycle late → NOT a shift). Non-blocking: clock1 → q0=1,q1=0; clock2 → q0=0,q1=1 (the 1 arrives at q1 one cycle later → correct shift). See the flip-flop model. Units: none, it's logic values.
wire [15:0] sum = a + b; // combinationalwire [15:0] prod = sum * c; // combinationalreg [15:0] y;always @(posedge clk) y <= prod; // sequential: only THIS latches
Forecast: guess the flip-flop count — 80, 48, or 16?
List every named signal.a, b, c (inputs), sum, prod, y.
Why? We must decide, per signal, "does this remember across a clock?"
Apply the register test to each. A signal becomes a register only if it is assigned inside @(posedge clk). Only y is.
Why this tool — the register test? From the synthesis rule in the parent: combinational wire/assign become gates, not memory.
Count.sum and prod are pure combinational wires → 0 flip-flops. a,b,c are input ports → not stored here. y is 16 bits → 16 flip-flops.
Why the student is wrong: they counted wires as storage; only clocked signals hold state.
Verify: flip-flops =16 (only y). The adder + multiplier synthesize as gates (combinational logic), not registers. The claimed 80 is 5×16, off by counting 4 non-registers. Units: flip-flops (bits of state).
Forecast: guess which path is critical for setup, and guess whether the same path matters for hold.
Setup uses the SLOWEST path (max tcomb). P2 at 1.9 ns is the critical path.
Why? Setup fails if any path hasn't settled; the slowest is the binding one.
Compute setup demand on P2.0.2+1.9+0.3=2.4 ns. Supply T=2.2 ns → slack =2.2−2.4=−0.2 ns → FAIL by 0.2 ns.
Why? The slow path overruns the whistle.
Hold uses the FASTEST path (min tcomb). The quick route arrives at tcq+tcomb,min=0.2+0.1=0.3 ns.
Why this switch? Setup asks "is the slow one done in time?"; hold asks "does the fast one arrive too soon?" — opposite extremes of the same register pair.
Compare to hold. Need ≥thold=0.4 ns; have 0.3 ns → HOLD FAIL by 0.1 ns.
Why both fail at once: a design can violate setup (slow path) and hold (fast path) simultaneously — they are independent constraints.
Verify: setup demand on P2 =2.4 ns, slack =−0.2 ns (fail). Hold arrival =0.3 ns vs 0.4 ns → deficit 0.1 ns (fail). Fix setup by pipelining P2; fix hold by adding 0.1 ns delay on the fast path. Units ns.
Forecast: is slack positive, exactly zero, or negative? And is zero slack safe?
Demand.0.3+2.4+0.3=3.0 ns.
Why? Same setup sum.
Slack.3.0−3.0=0.0 ns — exactly zero.
Why this is the boundary: the data becomes steady at the instanttsetup before the edge — no earlier.
Judge it. Zero slack passes on paper but leaves no margin for temperature, voltage, or aging variation.
Why care? Real silicon drifts; a 0-slack path fails in the field. Designers keep a positive guard band.
Verify: demand =3.0 ns =T, slack =0.0 ns. Passes exactly, zero margin. Units ns.
Recall Quick self-test across the matrix
Which extreme (slowest / fastest path) governs setup? ::: The slowest path — largest tcomb,max (critical path).
Which extreme governs hold? ::: The fastest path — smallest tcomb,min.
Can a slower clock fix a hold violation? ::: No — T is absent from tcq+tcomb,min≥thold; add delay instead.
A pure register-to-register wire has tcomb=? ::: Zero (case D); only tcq+tsetup constrains setup.
Does every named wire become a flip-flop? ::: No — only signals assigned in @(posedge clk).
Related: RTL (register transfer level) design · HDL & Digital Design Flow · Verilog syntax and semantics · Finite State Machines · Clock domains and metastability