always @(posedge clk), and understand precisely — not just as
a memorized rule — why sequential logic must use <= (non-blocking) while combinational
logic uses = (blocking). This is the single most consequential rule in practical Verilog.
A D flip-flop samples its input on a clock edge and holds that value until the next edge. In Verilog:
module dff (
input wire clk,
input wire d,
output reg q
);
always @(posedge clk)
q <= d;
endmodule
@(posedge clk) means "this block does something at the instant clk rises from
0 to 1, and at no other time." That's the entire definition of edge-triggered memory: everything else
about the module's behavior — what happens between edges — is "hold the last value," which is exactly
what a real flip-flop's physical storage does. Unlike always @(*), you write the sensitivity
explicitly here (posedge clk), because you specifically want "only on this edge," not "on
any change to anything read inside."
Real designs need a way to force known state at startup. Two common shapes — recognize both, and default to synchronous unless your course specifies otherwise:
always @(posedge clk) begin
if (rst)
q <= 1'b0;
else
q <= d;
end
Reset only takes effect on a clock edge. Simpler timing analysis; the common default in modern ASIC-oriented style guides.
always @(posedge clk or posedge rst) begin
if (rst)
q <= 1'b0;
else
q <= d;
end
Reset forces q to 0 immediately, independent of the clock. Common in many intro courses
and FPGA examples; note the extra or posedge rst in the sensitivity list.
<= exists at all
Here's the question worth sitting with: real hardware has no notion of "statement order" inside a clock
edge. Every flip-flop in a register bank samples its D input at the exact same physical instant. If your
RTL has to model "swap the contents of two registers on this clock edge" or "shift a value through a
chain of flip-flops," you need each flip-flop's new value to depend on the old values of
everything else — not on updates other statements in the same block already made. Blocking assignment
(=) can't express that; non-blocking (<=) exists specifically to express it.
= blocking | <= non-blocking | |
|---|---|---|
| When the update happens | Immediately — the very next statement sees the new value. | Deferred to the end of the current simulation time step. Every non-blocking assignment scheduled in this time step reads its right-hand side from values as they were before any of them updated. |
| Mental model | Ordinary software assignment. | "Everyone reads the old values first; then everyone updates at once" — exactly how real flip-flops behave on a clock edge. |
| Use for | Combinational always @(*) (Lesson 3). |
Sequential always @(posedge clk) (this lesson). |
Nothing makes this concrete like watching blocking assignment fail at something non-blocking handles correctly: swapping two registers on a clock edge.
always @(posedge clk) begin
a = b;
b = a;
end
Initial values before the clock edge.
always @(posedge clk) begin
a <= b;
b <= a;
end
Initial values before the clock edge.
Click Step twice to walk through one clock edge. With blocking assignment, statement 2
reads the value statement 1 just wrote — so both registers end up holding b's original
value; the swap is destroyed. With non-blocking, both right-hand sides are captured from the values that
existed before the edge, so the update that lands is a genuine swap, matching what two real
flip-flops sampling each other's old outputs would do.
always @(posedge clk)) → use <=.always @(*)) → use =.= and <= in the same always block.
The same collapsing failure shows up any time you model a chain of flip-flops with blocking assignment
— for instance a 2-stage shift register written as q1 = d; q2 = q1; inside one clocked
block. Because q1 is already updated by the time the second line runs, q2
ends up tracking d one cycle earlier than it should, silently collapsing two stages of
delay into one. It's the identical bug as the swap above, just with different symptoms — which is why
the fix is always the same guideline, not a special case.
dff module above and turn it into a 4-bit register: change d/q
to [3:0], add a synchronous active-high reset that clears q to
4'b0000. Then write a 2-stage shift register for a single bit
(d_in → q1 → q2, one clocked always block, non-blocking assignments) and trace by hand what
q1 and q2 hold after each of the first three clock edges, starting from
d_in = 1, 0, 1.
New terms — flip-flop, edge-triggered, blocking/non-blocking assignment, synchronous/asynchronous reset — are in the glossary.