HDL — Verilog & VHDL — Fundamentals
Overview showed a single combinational module (the mux) written two ways. This page covers the mechanics needed for everything after it: the three modeling styles Verilog supports (structural, dataflow, behavioral), how to describe sequential logic (the flip-flop from Digital Logic Fundamentals, now in code, sampling on a clock edge), and the single most common source of real HDL bugs — the difference between blocking (=) and non-blocking (<=) assignments inside an always block.
Analogy — Blocking vs. non-blocking assignment is the classic "swap two glasses of water" problem. If you pour glass A into glass B immediately, then pour (the now-mixed) glass B into glass A, you don't get a swap — you get the same liquid in both glasses. That's blocking assignment: each statement takes effect immediately, in order, so the second statement sees the first statement's already-updated result. Non-blocking assignment is doing the swap correctly with a third glass: you note down both original values first, then update both glasses at the same instant — no statement sees another statement's update within the same step. Getting this wrong is the single most common real bug new HDL engineers write, which is why it gets a full worked trace below.
The Three Modeling Styles
Sequential Logic in Verilog: The D Flip-Flop
Digital Logic Fundamentals introduced the flip-flop conceptually — a memory cell that samples its input only at a clock edge. Here is that exact behavior in Verilog:
Annotated Example — Tracing the Flip-Flop Over Five Clock Edges
Feed the sequence D = 1, 0, 1, 1, 0 into the flip-flop above, one value per rising clock edge, starting from Q = 0.
Verified programmatically by simulating the exact always @(posedge clk) Q <= D; behavior across all five edges — Q always ends up equal to whatever D was at the moment of that edge, never before or after. This is the foundation every register, counter, and state machine in RTL Design & Computer Architecture is built from — a chain of exactly this flip-flop behavior, wired together.
Blocking vs. Non-Blocking Assignment — The Classic Bug
Both always blocks below are meant to swap the values of a and b on every clock edge. Only one of them actually works.
Verified computationally by simulating both assignment orders exactly as shown — blocking assignment produces a=7, b=7 (both end up equal to the original b, confirming the swap fails); non-blocking assignment produces a=7, b=3 (a correct swap). The rule that follows directly from this: use non-blocking (<=) for sequential logic (anything inside always @(posedge clk)), and blocking (=) for combinational logic (anything inside always @(*)). Mixing them up inside sequential blocks is the single most common real bug this technology exists to prevent — and it's a question every VLSI technical interview asks in some form (see Interview tab).
Try It (2 Minutes)
A different always @(posedge clk) block uses blocking assignment to try to implement a simple 2-stage shift: a = b; b = c; (intending: on each clock edge, a takes b's old value, and b takes c's old value — a two-stage pipeline).
You should land on: blocking — a = b makes a=2, then b = c makes b=3 (c is unused/unread yet since nothing changes it here, but the point is a took the pre-statement b correctly since it was line 1) — in THIS particular case blocking happens to give a=2, b=3, matching intent, because the assignments don't chain into each other the way the swap example did. This is worth noticing precisely because it shows blocking assignment isn't always wrong — it's wrong specifically when a later statement in the same block reads a variable a prior statement in the same block just wrote (the swap case). The safe, general-purpose rule that avoids needing to reason about ordering at all: always use non-blocking assignment for sequential logic, which gives a=2, b=3 regardless of statement order, by design.

