3.5.6 · D3HDL & Digital Design Flow

Worked examples — RTL (register transfer level) design

2,679 words12 min readBack to topic

The scenario matrix

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 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 ()
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:


Figure — RTL (register transfer level) design

The figure above is the timeline every example reads off: whistle at , output appears later, logic settles later, and the value must be steady before the next whistle. The leftover gap is the slack.


Example 1 — Setup PASS with positive slack · cell A

Forecast: guess before reading — is the slack positive, zero, or negative? Roughly how big?

  1. Add the demand of the path. 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.
  2. Compare to the supply . We have ns available, the path needs ns. Why this step? Setup is literally "does the whistle give enough time?" — a supply vs demand comparison.
  3. Compute slack. ns positive. Why this step? Slack is the leftover margin; positive means safe.

Verify: ✓, so it passes. Units: all nanoseconds, slack ns. Sanity: we could shave ns off (run faster) before this path breaks.


Example 2 — Setup FAIL, then fix by pipelining · cell B

Forecast: guess the slack sign at ns, then guess whether cutting the logic in half rescues it.

  1. Test the original path. Demand ns, supply ns. Why? Same supply-vs-demand law.
  2. Slack 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.
  3. Pipeline: cut in half. Insert one register in the middle so each stage does ns of logic. Why this step? Each half now sees demand ns.
  4. Re-test. Slack 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 ns (fail), pipelined stage demand ns, pipelined slack ns (pass). Units ns throughout. Sanity: halving removed ns of demand, which more than covered the ns deficit.


Example 3 — Hold check on a short path · cell C

Forecast: hold has no in it. Guess pass or fail, and guess whether can rescue it.

  1. Compute the earliest arrival of new data. ns after the edge. Why? Hold asks: how soon can the new value show up at the receiver? The fastest route decides.
  2. Compare to the hold requirement. Need ns. We have only ns. Why? If new data arrives before the register finished capturing the old value, it corrupts the capture.
  3. → HOLD FAIL by ns. Why can't help: never appears in . Slowing the clock changes nothing.
  4. Fix: add a delay buffer of ns on the short path so arrival becomes ns. Why this step? We slow down the too-fast path (add ) — the only lever hold responds to.

Verify: arrival ns, requirement ns, deficit ns. Add ns buffer → arrival ns (exactly meets). Units ns.


Example 4 — The word problem: a real counter's max speed · cell G

Forecast: guess whether is closer to 100 MHz or 1 GHz before computing.

  1. Find the minimum period. ns. Why? The counter can go no faster than its worst path allows to settle.
  2. Invert to frequency. . Why this tool — reciprocal? Frequency is cycles per second; period is seconds per cycle. They are exact inverses, so is the only conversion.
  3. Compute. Hz MHz. Why the powers of ten? ns s; its reciprocal is Hz, and Hz MHz.

Verify: ns, MHz. Units: . Sanity: ns a third of a GHz MHz — matches. Closer to 100 MHz than 1 GHz, as the 3 ns adder dominates.


Example 5 — Zero logic + blocking bug: a shift register · cells D & E

// 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 old q0 (a real 2-deep shift) or the new q0 (collapsed to 1 stage)? Guess before step 3.

  1. Note the logic depth is zero. Between registers there's a plain wire, so (case D). Why this matters: the register-to-register path is a pure baton pass — timing is trivial (), but the ordering semantics of = vs <= still bite.
  2. Trace the blocking version in order. q0 = d runs first, so q0 is now d. Then q1 = q0 reads the already-updated q0, so q1 = d too. Why? Blocking = executes top-to-bottom, using values as they've been updated within this pass.
  3. Result: both registers hold d. The 2-deep shift collapsed into 1 stage — the old q0 was lost.
  4. Fix with non-blocking <=.
    always @(posedge clk) begin
      q0 <= d;
      q1 <= q0;   // reads OLD q0 (pre-edge), then all latch together
    end
    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 old q0, 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.


Example 6 — Register-count trap: which wires become flip-flops? · cell F

wire [15:0] sum  = a + b;         // combinational
wire [15:0] prod = sum * c;       // combinational
reg  [15:0] y;
always @(posedge clk) y <= prod;  // sequential: only THIS latches

Forecast: guess the flip-flop count — 80, 48, or 16?

  1. List every named signal. a, b, c (inputs), sum, prod, y. Why? We must decide, per signal, "does this remember across a clock?"
  2. 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.
  3. 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 (only y). The adder + multiplier synthesize as gates (combinational logic), not registers. The claimed 80 is , off by counting 4 non-registers. Units: flip-flops (bits of state).


Example 7 — The exam twist: two paths, one critical · cell H

Forecast: guess which path is critical for setup, and guess whether the same path matters for hold.

  1. Setup uses the SLOWEST path (max ). P2 at ns is the critical path. Why? Setup fails if any path hasn't settled; the slowest is the binding one.
  2. Compute setup demand on P2. ns. Supply ns → slack ns → FAIL by ns. Why? The slow path overruns the whistle.
  3. Hold uses the FASTEST path (min ). The quick route arrives at 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.
  4. Compare to hold. Need ns; have ns → HOLD FAIL by 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 ns, slack ns (fail). Hold arrival ns vs ns → deficit ns (fail). Fix setup by pipelining P2; fix hold by adding ns delay on the fast path. Units ns.


Example 8 — The exact-limit degenerate case (slack = 0) · cell A edge

Forecast: is slack positive, exactly zero, or negative? And is zero slack safe?

  1. Demand. ns. Why? Same setup sum.
  2. Slack. ns — exactly zero. Why this is the boundary: the data becomes steady at the instant before the edge — no earlier.
  3. Judge it. Zero slack passes on paper but leaves no margin for temperature, voltage, or aging variation. Why care? Real silicon drifts; a -slack path fails in the field. Designers keep a positive guard band.

Verify: demand ns , slack 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 (critical path). Which extreme governs hold? ::: The fastest path — smallest . Can a slower clock fix a hold violation? ::: No — is absent from ; add delay instead. A pure register-to-register wire has ::: Zero (case D); only 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

Flashcards

Setup slack formula?
; positive passes, negative fails.
Hold constraint?
— independent of .
Fastest clock of a path?
.