Exercises — Sequential logic and always blocks
Before we start, one picture fixes the vocabulary we will use in every single problem: the clock edge, the old value, and the new value.

Level 1 — Recognition
Goal: can you read a block and name what hardware it is?
Exercise 1.1
State whether each block models combinational or sequential logic, and name the trigger:
// (a)
always @(*) y = a & b;
// (b)
always @(posedge clk) q <= d;
// (c)
always @(posedge clk or negedge rstn) begin
if (!rstn) q <= 0; else q <= d;
endRecall Solution 1.1
- (a) Combinational. Trigger: any change of a read signal (
aorb), because@(*)auto-builds the sensitivity list. No clock → no memory. - (b) Sequential. Trigger: the rising clock edge (
posedge clk). One flip-flop. - (c) Sequential with an active-low asynchronous reset. Trigger:
posedge clkfor normal capture,negedge rstnfor reset.rstnis in the list, so reset acts immediately, not on the clock. This is the reset style built step-by-step in D flip-flops and latches — reset that does not wait for the clock.
Exercise 1.2
Which assignment operator (= or <=) belongs in each of the two blocks above ((a) and (b)), and why in one sentence each?
Recall Solution 1.2
- (a) combinational → blocking
=. Order equals data-flow, so evaluating in program order is correct. - (b) sequential → non-blocking
<=. All right-hand sides must be read from old values so parallel flip-flops update together.
Level 2 — Application
Goal: write a small block that meets a spec.
Exercise 2.1
Write a D flip-flop q with a synchronous, active-high reset that forces q to 0.
Recall Solution 2.1
reg q; // 1-bit storage box for the flip-flop
always @(posedge clk) begin // rst NOT in the list → synchronous
if (rst) q <= 1'b0; // reset checked first = priority
else q <= d;
endWhy rst is not in the sensitivity list: synchronous reset only takes effect at a clock edge. Why if (rst) first: reset must dominate normal capture.
Exercise 2.2
Write a 4-bit up-counter count that resets asynchronously to 0 on active-high rst, else increments each clock.
Recall Solution 2.2
reg [3:0] count; // 4-bit box, holds 0..15
always @(posedge clk or posedge rst) begin
if (rst) count <= 4'b0000; // async → rst in the list
else count <= count + 1; // non-blocking: reads OLD count
endWhy <=: the right side reads the old count, adds 1, then commits — modelling one register that clocks once per edge. Start at 0000; after 15 edges it reads 1111 and the next edge wraps to 0000 (a 4-bit value rolls over modulo ).
Exercise 2.3
Predict the value of count from Exercise 2.2 after each of the first 6 clock edges following reset. (Report exactly 6 values — the state after edge 1 through after edge 6, not the pre-edge start.)
Recall Solution 2.3
Just before edge 1, count = 0 (reset value). Each edge adds 1, so the six requested values — the state after edges 1…6 — are:
In 4-bit binary: 0001, 0010, 0011, 0100, 0101, 0110. The pre-edge 0000 is the starting state, not one of the six reported outputs. The waveform below traces exactly these steps — read the red count line changing one tick after each rising edge.

Level 3 — Analysis
Goal: trace values through tricky blocking/non-blocking code.
Exercise 3.1
Before the edge, a = 1, b = 2, c = 3. Trace a, b, c after one clock edge for BOTH versions.
// Version N (non-blocking) // Version B (blocking)
always @(posedge clk) begin always @(posedge clk) begin
a <= b; a = b;
b <= c; b = c;
c <= a; c = a;
end endRecall Solution 3.1
Version N — non-blocking. All RHS read the old values a=1, b=2, c=3 first, then commit:
Result: a=2, b=3, c=1 — a clean 3-way rotation.
Version B — blocking. Executes top-to-bottom, each line sees the just-updated value:
a = b→a = 2b = c→b = 3c = a→c = 2(uses the newa, not old!)
Result: a=2, b=3, c=2 — the rotation is broken; 1 is lost.
The figure below puts both versions side by side: the black arrows in Version N all reach back to the old values, while the red arrow in Version B feeds the just-written a forward — that single red arrow is why the 1 disappears.

Exercise 3.2
This mux compiles and simulates, yet the synthesizer warns about an inferred latch. Find the bug and its consequence.
always @(*) begin
if (sel == 2'b00) y = a;
else if (sel == 2'b01) y = b;
else if (sel == 2'b10) y = c;
// sel == 2'b11 : nothing assigned
endRecall Solution 3.2
When sel == 2'b11, no branch assigns y. Verilog interprets "keep the last value" as memory, so the synthesizer infers a latch on y — an unwanted 1-bit storage element in what should be pure combinational logic. In Combinational logic and assign statements the same job is done safely with a single assign and a full ternary, which is impossible to leave incompletely specified.
Fix (either works):
always @(*) begin
y = a; // default at top, then override
if (sel == 2'b01) y = b;
else if (sel == 2'b10) y = c;
else if (sel == 2'b11) y = 1'b0;
endNow y is assigned on every path → no latch.
Level 4 — Synthesis
Goal: build a multi-stage design from a word spec.
Exercise 4.1
Design a 3-stage shift register: input din enters, and on each clock the data shifts q1 → q2 → q3. Write it, and explain why <= is mandatory here.
Recall Solution 4.1
reg q1, q2, q3; // three 1-bit boxes, one per stage
always @(posedge clk) begin
q1 <= din; // reads OLD din
q2 <= q1; // reads OLD q1
q3 <= q2; // reads OLD q2
endWhy <= is mandatory: each line must read the pre-edge value of the previous stage. With <=, all three RHS are sampled from old values, then committed together — so a bit truly moves one stage per edge. If you used blocking =, line 2 would see the new q1 and the whole register would collapse into a single stage.
Exercise 4.2
For the shift register above, din over 5 consecutive cycles is 1, 0, 1, 1, 0. All of q1,q2,q3 start at 0. Give (q1,q2,q3) after each of the 5 edges.
Recall Solution 4.2
Each edge: q1←din, q2←old q1, q3←old q2. Start (0,0,0).
| edge | din | q1 | q2 | q3 |
|---|---|---|---|---|
| 1 | 1 | 1 | 0 | 0 |
| 2 | 0 | 0 | 1 | 0 |
| 3 | 1 | 1 | 0 | 1 |
| 4 | 1 | 1 | 1 | 0 |
| 5 | 0 | 0 | 1 | 1 |
Notice the first 1 (from edge 1) reaches q3 exactly on edge 3 — three stages, three cycles of delay. The waveform below shows this: follow the red highlight tracking that first 1 marching one stage to the right per clock.

Exercise 4.3
Build a 2-state FSM: a toggle flip-flop state that flips between 0 and 1 every clock when en=1, and holds when en=0. Async active-high reset to 0.
Recall Solution 4.3
reg state; // 1-bit box holding the FSM state
always @(posedge clk or posedge rst) begin
if (rst) state <= 1'b0;
else if (en) state <= ~state; // flip
else state <= state; // hold (explicit is clearest)
endWhy the explicit else state <= state: it documents the hold. In a clocked block the omission is safe (a flop naturally holds), unlike the combinational latch case in 3.2. This one-flip-flop toggle is the smallest example of the state-register pattern generalised in Finite State Machines in Verilog, where the same clocked block updates a multi-bit state.
Level 5 — Mastery
Goal: reason about timing, wrap-around, and mismatch at once.
Exercise 5.1
A counter count is 3 bits wide and increments every clock (no reset). It currently reads 6 (110). What does it read after 1 edge? After 3 edges? Explain the wrap.
Recall Solution 5.1
A 3-bit register counts modulo .
- After 1 edge: →
111. - After 3 edges: →
001.
Why the wrap: 7 + 1 = 8 needs 4 bits, but only 3 exist, so the top bit is discarded, leaving 000. Counting continues 0,1,... — this is exactly arithmetic modulo 8.
Exercise 5.2
Two students write the same logic two ways. Student A uses always @(a or b) (forgets c); Student B uses always @(*). Both intend y = a & b & c. Explain what each simulates and what each synthesizes, and who is correct.
// Student A // Student B
always @(a or b) always @(*)
y = a & b & c; y = a & b & c;Recall Solution 5.2
- Student A: the sensitivity list is missing
c. Simulation: the block only re-runs whenaorbchanges; a change incalone leavesystale (wrong). Synthesis: the synthesizer reads the body, seesc, and builds the full AND gate anyway. → the block behaves one way in the simulator and another in silicon, the classic hazard catalogued in Synthesis vs simulation mismatch. - Student B:
@(*)auto-includesa,b,c. Simulation and synthesis agree. ✅ - Correct: Student B. Always use
@(*)for combinational logic so no signal is ever forgotten.
Exercise 5.3
Final boss. In a posedge clk block you write q <= q + 1;. Explain, using the "old value" idea, why this does not create an infinite feedback loop even though q appears on both sides.
Recall Solution 5.3
The right side q + 1 is evaluated using the old value of q (the value before the edge). That result is then committed to the left q once, at the end of the edge. The block does not re-run just because q changed — as the parent note on always blocks stresses, an edge-triggered block is event-driven and wakes only on the next posedge clk. So each edge adds exactly one; no runaway loop. Contrast a combinational always @(*) q = q + 1;, where q changing would re-trigger the block → an illegal combinational loop. The clock edge is precisely what tames the feedback.
Recall Master checklist (reveal after all 5 levels)
Clocked → <=. Combinational → = with @(*). Async reset → in the sensitivity list, checked first. Every combinational branch assigns every output (else latch). One register → one always. N-bit register wraps modulo . always is event-driven, not a loop.