Verilog · Chapter 14.5 · Behavioural Modeling
Multiway Branching in Verilog — if/else, case & Latch Avoidance
Branching is how behavioural logic makes decisions, and Verilog gives you two constructs. The if and else form checks conditions in order and is inherently a priority structure, where the first true branch wins. The case form selects among the values of an expression and is inherently a parallel structure of mutually exclusive branches. Both build the multiplexers and decoders of combinational logic and the decision logic of state machines. They also share one hazard that dominates this section, which is latch inference. A combinational block must assign every output on every path, and an if without an else, or a case without a default, leaves a path where the output holds its old value, which synthesis turns into an unintended latch. This page surveys both constructs, the priority versus parallel distinction, and above all the discipline of avoiding latches.
Foundation15 min readVerilogif elsecaseLatchCombinationalRTL Design
Chapter 14 · Section 14.5 · Behavioural Modeling
1. The Engineering Problem
An engineer writes a combinational block that conditionally drives an output:
always @(*)
if (en) y = d; // assigns y only when en is 1 — no elseWhen en is 0, y is not assigned — so it must hold its previous value, which means it needs memory. Synthesis infers an unintended latch (a storage element) in logic that was meant to be purely combinational. The block "works" (it holds the old value) but it is a latch, not a wire — a classic, silent bug that lint flags as "latch inferred."
The fix is to assign y on every path:
always @(*)
if (en) y = d;
else y = 8'h00; // every path now assigns y → no latch ✓A combinational
alwaysblock must assign every output on every path — anifwithout anelse, or acasewithout adefault, leaves an unassigned path that infers an unintended latch.
Multiway branching — if/else and case — is how behavioural logic decides, and latch avoidance is the discipline that keeps it combinational. This page surveys both constructs and the latch rule; the sub-topics drill each.
2. Mental Model — if Is Priority, case Is Parallel; Both Must Be Complete
Visual A — if (priority) vs case (parallel)
if/else vs case
data flow3. if/else — Priority Branching
if/else if/else checks conditions in order — the first true branch executes:
always @(*)
if (sel_a) y = a; // highest priority
else if (sel_b) y = b;
else y = c; // else ensures every path assigns y (no latch)- It is priority:
sel_ais checked first;sel_bonly ifsel_ais false. The textual order is the priority order (like the conditional-operator chain, 10.11). - The
elseensuresyis assigned even when no condition holds — preventing a latch. - Use
if/elsewhen conditions have a natural priority (an error overrides normal operation, an interrupt takes precedence). (Drilled in 14.5.1.)
4. case — Parallel Branching
case selects among the values of an expression:
always @(*)
case (op)
2'b00: y = a + b;
2'b01: y = a - b;
2'b10: y = a & b;
default: y = a | b; // default covers all other values (no latch)
endcase- It is parallel: the cases are (normally) mutually exclusive, so it synthesizes to a parallel mux/decoder, not a priority cascade.
- The
defaultcovers every value not listed explicitly — preventing a latch (and handling unexpected inputs). - Use
casewhen selecting on a value: an opcode, an FSM state, a mux select. (Drilled in 14.5.2, includingcasez/casex.)
5. Latch Avoidance — The Core Discipline
The rule that dominates this section: in a combinational always @(*) block, assign every output on every path. A missing else or default leaves a path where an output is unassigned, so it must hold its previous value — which requires memory — and synthesis infers a latch.
// LATCH — y not assigned when en=0:
always @(*) if (en) y = d;
// FIX 1 — complete the branches:
always @(*) if (en) y = d; else y = 0;
// FIX 2 — default assignment at the top:
always @(*) begin
y = 0; // default first
if (en) y = d; // override when en — y always has a value
endBoth fixes guarantee complete assignment. The default-at-the-top pattern (Fix 2) is especially robust: assign every output a default value at the start of the block, then override in the branches — so no path can leave an output unassigned, regardless of how complex the branching gets. Latch inference is a watched bug; lint flags it, and the discipline of complete assignment eliminates it. (Sequential blocks @(posedge clk) intentionally hold state, so this is a combinational-block rule.)
6. The Sub-Topics
| § | Sub-topic | Covers |
|---|---|---|
| 14.5.1 | If / Else Statements | if/else if/else — priority branching, nesting, latch from missing else |
| 14.5.2 | Case Statements | case/casez/casex — parallel branching, default, the casex x-matching hazard |
| 14.5.3 | Multiway Advanced Techniques | don't-cares, full_case/parallel_case pragmas (and their dangers), priority vs parallel |
7. Common Misconceptions
"if and case are interchangeable." Partly false. Both branch, but if/else is priority (ordered) and case is parallel (mutually exclusive). The choice affects the synthesized structure (priority cascade vs parallel mux) and readability — use if for prioritized conditions, case for value selection.
"A combinational block can skip assigning an output sometimes." False. Skipping an assignment on any path infers a latch. Combinational logic assigns every output on every path.
"Latches are fine." Misleading. Unintended latches are bugs — they create timing hazards and were almost never intended in synchronous design. Synchronous design uses flip-flops (clocked), not level-sensitive latches; an inferred latch usually means a missing else/default.
8. Debugging Lab
One multiway-branching debug post-mortem
9. Interview Q&A
10. Summary
Multiway branching makes decisions in behavioural logic:
if/else— priority — conditions in order, first true wins; a priority-mux cascade.case— parallel — select among an expression's values; a parallel mux/decoder.- Latch avoidance — a combinational block must assign every output on every path; a missing
else/defaultinfers an unintended latch. Fix by completing the branches or assigning defaults at the top.
The discipline this section instils: complete assignment in combinational blocks — every output, every path — and use if for priority, case for value selection.
The sub-topics drill each: Chapter 14.5.1 If / Else Statements (priority branching), 14.5.2 Case Statements (parallel branching, casez/casex), and 14.5.3 Multiway Advanced Techniques (don't-cares, synthesis pragmas). After branching, the chapter covers iteration: 14.6 Loops.
Related Tutorials
- always & initial Blocks — Chapter 14.1; the combinational blocks where latch avoidance applies.
- Conditional Operator — Chapter 10.11; the dataflow mux these branches parallel.
- Blocking & Non-Blocking Assignments — Chapter 14.3; the assignments inside these branches.
- Dataflow Practical Examples — Chapter 13.2; muxes and decoders at the dataflow level.