Combinational vs. Sequential always Blocks
Every rule of thumb mentioned so far — use @(*) and blocking assignment for combinational logic, use @(posedge clk) and nonblocking assignment for sequential logic, always include a default — is a consequence of one underlying fact: synthesis tools infer what hardware an always block becomes purely from its sensitivity list and assignment pattern, not from any comment or intention you had while writing it. This page makes that inference process explicit.
Two block shapes, two hardware outcomes
// Combinational: output depends only on current inputs
always @(*) begin
y = a & b;
end
// Sequential: output updates only at a clock edge, otherwise holds
always @(posedge clk) begin
q <= d;
end
These look almost identical in structure — an always block with one assignment inside — but describe fundamentally different hardware: the first is pure combinational logic (an AND gate, full stop); the second is a D flip-flop with genuine memory, holding q's value across every cycle where no clock edge occurs. The sensitivity list is what tells the synthesis tool which one you mean, which is exactly why getting it wrong doesn't produce a warning so much as a different circuit than you wrote the code to describe.
The three-way outcome for always blocks
Synthesis tools infer one of three things from any given always block, and the input that decides which is whether every signal assigned inside the block is given a value on every possible path through the code:
| Every assigned signal covered on every path? | Sensitivity list | Result |
|---|---|---|
| Yes | @(*) (all inputs) | Pure combinational logic |
| No | @(*) (all inputs) | Combinational logic plus an inferred latch for the uncovered signal |
| — | @(posedge/negedge clk) | Sequential logic (flip-flops) |
The middle row is the trap already previewed in Conditional and Case Statements: a case (or if) that doesn't assign a signal on every branch leaves that signal's synthesized "hardware" with no defined value for the uncovered case — and the only physically sensible thing to build is a latch that holds the last value. This happens regardless of whether you intended a latch, which is why it's called unintentional latch inference, and why it's treated as a bug to eliminate rather than a valid design choice most of the time.
always @(*) begin
if (enable)
y = a;
// no else — what should y do when enable is 0?
end
Here, y has no assignment when enable is 0 — synthesis infers a latch (transparent while enable is high, holding while low) to make that "keep the last value" behavior physically real:
a ──────┐
▼
┌─────┐
│ D │
enable ─┤ LATCH│──── y
└─────┘
This is exactly the D latch from Digital Design's Latches & Flip-Flops page, inferred whether or not you meant to describe one. Adding else y = 0; (or any defined fallback) removes the ambiguity and the latch along with it.
The complete rule set
always blocks predictable- Combinational block →
@(*)sensitivity, blocking (=) assignment, every assigned signal covered on every path (adefault/elsefor every branch). - Sequential block →
@(posedge clk)(ornegedge) sensitivity, nonblocking (<=) assignment, for every signal in the block. - Never mix combinational and sequential intent in the same
alwaysblock — if a block needs both a clock edge and combinational-style logic, that's the shape of a register with combinational next-state logic feeding it, which is exactly the structure FSM Coding Styles covers next as two (or three) separate, correctly-typed blocks rather than one mixed block. - Never assign the same signal from two different
alwaysblocks. Synthesis tools generally reject this outright (two blocks driving the same target is a real conflict, the same as two output pins physically tied together), and even where a tool tolerates it, the resulting behavior depends on block evaluation order — a source of the least deterministic bugs in Verilog.
Why this matters more than any other single rule in this topic
Every gate-level, dataflow, and behavioral example since Gate-Level Modeling has, in effect, been building toward this page — the point where "does this code simulate correctly" and "does this code synthesize to the hardware I meant" can diverge. A design that simulates perfectly but silently contains an inferred latch, or a flip-flop with an incompletely specified reset value, is the single most common way working-in-simulation RTL turns into broken-in-hardware RTL. Lint tools catch most of these mechanically — but knowing the underlying rule is what lets you read why a lint warning fired, rather than treating it as an opaque tool complaint.
What's next
With the combinational/sequential inference rules settled, the next page applies them to the most common structured use of sequential logic: finite state machines, coded in Verilog using the 1-process, 2-process, and 3-process styles — each one a specific, deliberate arrangement of exactly the always-block rules covered on this page.