3.5.3 · D5HDL & Digital Design Flow
Question bank — Sequential logic and always blocks
Read the waveforms first



True or false — justify
always @(posedge clk) is a loop that keeps running as fast as possible.
False. It is event-driven: it executes its body exactly once per rising clock edge, then sleeps until the next edge. The word "always" describes availability to react, not repetition.
An always @(*) block with no clock in its sensitivity list can never create memory.
False. If an output is not assigned in every branch, the synthesizer infers a latch to hold the old value — accidental memory, even though you wrote no clock.
Non-blocking <= assigns its left-hand sides immediately, one after another, like software.
False. That describes blocking
=. Non-blocking evaluates all right-hand sides using old values first, then updates all left-hand sides together — modelling parallel flip-flops.A single D flip-flop coded with blocking = will always simulate wrong.
False (subtle). One isolated flop may simulate fine; the danger appears with multiple registers where ordering leaks new values between them. It is still a bad habit because it invites mismatch the moment you add a second register.
always @(posedge clk or posedge rst) describes a synchronous reset.
False. Putting
rst in the sensitivity list makes it asynchronous — the block wakes on rst rising independently of the clock. A synchronous reset keeps only clk in the list.always @(posedge clk or negedge rst_n) is an error because reset should be posedge.
False.
negedge rst_n is a perfectly standard active-low async reset: the flop clears when rst_n falls to 0, matching the common convention where the reset line rests at 1 and is pulsed low. Inside the body you then write if (!rst_n) q <= 0;.Combinational logic can count.
False. Counting requires remembering the previous value, which pure combinational logic cannot do — its output depends only on current inputs. You need a clocked flip-flop to store the running count.
You may drive the same reg from two different always blocks to combine behaviours.
False. Two drivers on one register create a race / multiple-driver error. One register must have exactly one
always block that owns it.Using <= inside a combinational always @(*) is just as safe as =.
False — and here is the concrete failure. Consider
always @(*) begin temp <= a; y = temp; end. With <=, temp updates at the end of the time-step, so y reads the old temp on this pass — simulation shows a one-delta-cycle stale y. Synthesis, however, has no notion of delta cycles: it wires y directly from the new temp. The two disagree → a sim/synth mismatch. Blocking = keeps in-block order = data flow, so both agree. Rule: combinational → =.Spot the error
What is wrong here, and what hardware results?
always @(posedge clk) begin q2 = q1; q3 = q2; end (intended 2-stage shift)
Blocking
= updates q2 first, so line 2 reads the new q2 (= q1). The two stages collapse into one — you get a single flop, not a shift. Fix: use <=.Spot the trap: always @(sel) begin if (sel) y = b; else y = a; end
The sensitivity list misses
a and b. Simulation only re-evaluates when sel changes, so y goes stale when a or b change, while synthesis builds a real mux → mismatch. Fix: always @(*).always @(*) begin if (en) y = d; end — what does synthesis infer?
An inferred latch: when
en is 0 there is no assignment, so y must "remember" its previous value. Usually a bug. Fix: add else y = 0; or a default y = 0; at the top.always @(posedge clk) begin if (rst) q <= 0; end — reset works, but normal operation doesn't. What's missing?
There is no
else q <= d;, so q is only ever cleared, never loaded. The flop captures nothing. Add the else branch for normal capture.always @(posedge clk or rst) — why might the tool complain or behave oddly?
rst is listed as a level, not an edge. Async control signals must be given as posedge rst (or negedge rst_n for active-low) so the block reacts to the transition, matching the flip-flop's async set/reset port.Two always blocks both write count. What error class is this?
A multiple-driver / race condition — the value of
count becomes ambiguous. Consolidate into one block that fully owns count.Why questions
Why check if (rst) before else q <= d rather than after?
Reset must have priority: it should override normal capture. Checking it first makes reset dominate whenever it is active, which is the intended safety behaviour.
Why must an asynchronous reset appear in the sensitivity list, but a synchronous one must not?
Async reset must act the instant
rst changes, so the block needs to wake on that edge — hence it's in the list. Sync reset should only take effect on a clock edge, so it stays out and is tested inside the clocked body.Why do many designs use an active-low async reset negedge rst_n instead of active-high?
Convention and safety: a line that rests high and asserts low is naturally held reset by a pull-down during power-up glitches, and matches most vendor cells. Functionally it is identical to active-high — only the asserted polarity and the
if (!rst_n) test differ.Why does non-blocking <= correctly model a rank of flip-flops clocking together?
Real flops all sample their inputs at the same instant using pre-edge values, then update simultaneously.
<= reproduces this by reading every RHS first, then writing every LHS — no line sees another's new value.Why is always @(*) preferred over manually listing @(a or b or c)?
The
* auto-includes every signal read in the block, so you can never forget one. A hand-written list that misses a signal causes stale simulation output and a synthesis mismatch.Why does a mux use always @(*) and not always @(posedge clk)?
A mux has no memory; it must respond instantly to any input change. Triggering on a clock edge would wrongly delay and register its output.
Why is a shared clock ("drumbeat") what lets millions of gates cooperate reliably?
Because all storage updates happen only at the same clock edge, every stage sees a well-defined, stable value — removing timing chaos and making the whole system's state advance in lockstep.
Edge cases
A clocked block only ever assigns a signal inside if (rst), never in the else. Latch or flop?
Still a proper edge-triggered flip-flop (it lives in
@(posedge clk)), but a broken one — it can be reset yet never loaded. The concern here is missing function, not a latch. Inferred latches come from @(*) blocks, not clocked ones.always @(negedge clk) q <= d; — is this legal, and what does it build?
Legal — it builds a flip-flop that samples on the falling () edge instead of the rising edge. Valid, but mixing edge polarities across a design complicates timing; keep it deliberate.
For an active-low async reset, why is the test if (!rst_n) and not if (rst_n)?
Because "active-low" means the reset is asserted when the line is 0.
!rst_n is true exactly when rst_n == 0, so that branch fires during reset — matching the negedge rst_n that woke the block.What happens at the very first clock edge if a flop has no reset and d is unknown?
q captures whatever d holds, which in simulation is often x (unknown) and propagates. This is why real designs use a reset to force a known starting state before relying on outputs.Both posedge clk and posedge rst fire in the same instant — which branch does the code take?
In simulation the
if (rst) branch wins because it is tested first, so reset appears to dominate. But this exact-coincidence outcome is not guaranteed by the Verilog LRM — it depends on the simulator's event scheduling and, in silicon, on real setup/hold timing near the reset-release edge. Never rely on it: use a proper reset-synchronizer so reset de-assertion is clean. See Clocking and timing constraints.An always @(*) block reads a signal but assigns nothing on one path and the output is later re-driven elsewhere — is a latch still inferred?
If the same block leaves the output unassigned on some path, yes, that block infers a latch. The fix is a default assignment at the top so every path defines the output.
If a design needs the reset to be ignored until the next clock tick (e.g. to meet tight timing), which reset style?
A synchronous reset —
rst is left out of the sensitivity list and tested inside @(posedge clk), so it only takes effect on a clock edge. See Clocking and timing constraints for the timing trade-off.Recall One-line summary of the traps
Question ::: Nearly every trap reduces to three rules: (1) clocked → <=; (2) combinational → always @(*) with =; (3) in a combinational block, give every output a default at the top so every path assigns it — otherwise you get a race, a stale signal, an inferred latch, or a sim/synth mismatch.