VHDL · Chapter 5.4 · Sequential Statements & Processes
The if Statement
Inside a process, the if, elsif, and else statement is how you make decisions, and like its concurrent cousin it is a priority construct. The branches are tested top to bottom and the first true one wins. That priority is real hardware, so the order you write the branches is genuine precedence. Building on the process execution model, an if becomes a priority multiplexer for combinational logic or a conditional register update for clocked logic, and the rising-edge clock guard is simply an if wrapped around a register. This lesson covers the priority structure, the rule that a combinational if must assign every output on every path or it infers a latch, and when to reach for if versus a case statement for a balanced decode of one value.
Foundation14 min readVHDLif statementPriority MuxLatchRegistersSequential
1. Engineering intuition — decide by priority
An if ladder reads like a list of priorities: "if the most important condition holds, do this;
otherwise if the next holds, do that; otherwise the default." The hardware mirrors that ordering —
earlier conditions override later ones, exactly like the when/else priority mux from Module 4, but
now written as ordered statements inside a process. So when you write an if, you are describing a
precedence network, and the order you write the branches is that precedence.
2. Formal explanation — structure and the latch rule
if cond1 then
-- statements (highest priority)
elsif cond2 then
-- statements
else
-- default statements
end if;Conditions are boolean and tested in order; the first true branch executes and the rest are skipped.
Two rules matter. For combinational logic, every output must be assigned on every path —
omit an else (or a default) and some path leaves an output unassigned, which the execution model
holds at its old value: a latch (lesson 6.3). For clocked logic, an if without an else
is normal and correct — it means "update the register only when the condition holds; otherwise it
keeps its value" (a register naturally holds).
3. Production RTL — priority logic and registers
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all;
-- (A) Combinational priority: fault beats warn beats normal. Default-then-override = latch-free.
prio : process (all)
begin
level <= NORMAL; -- default assignment covers all paths
if fault = '1' then level <= CRIT;
elsif warn = '1' then level <= HIGH;
end if; -- no else needed: default already drove 'level'
end process;
-- (B) Clocked register with enable and synchronous reset (nested if; no else = holds).
reg : process (clk)
begin
if rising_edge(clk) then -- the clock guard is itself an if
if rst = '1' then q <= (others => '0');
elsif en = '1' then q <= d; -- update only when enabled
end if; -- else omitted → q holds (a register)
end if;
end process;What hardware does this become? (A) a priority multiplexer driving level, latch-free because the
default assigns it on every path; (B) a register with a clock enable and synchronous reset — the inner
if with no else correctly infers "hold when not enabled," which is what a flip-flop does.
4. Hardware interpretation — a priority mux
5. Simulation interpretation — branches in order
By the anchor's execution model, when the process wakes it evaluates the conditions top to bottom, takes the first true branch, runs its (zero-time) statements, and ignores the rest. Signal assignments inside the chosen branch are scheduled and take effect after the process suspends.
Combinational priority (fault beats warn) and a clocked enable register
8 cycles6. Debugging example — the missing-else latch
Expected: combinational logic. Observed: synthesis infers a latch on the output, and gate
simulation shows it holding stale values. Root cause: a combinational if left a path with the
output unassigned (no else, no default), so the execution model holds the old value — memory.
Fix: add a top-of-process default assignment (preferred) or a full else so every path drives
the output. Engineering takeaway: in a combinational process, an inferred latch almost always
means an if with an unassigned path — default-assign every output at the top, then override.
-- BUG: combinational if with no else → y unassigned when sel /= '1' → latch.
process (all) begin
if sel = '1' then y <= a; end if; -- no else → y holds → latch
end process;
-- FIX: default-then-override (latch-free).
process (all) begin
y <= b; -- default
if sel = '1' then y <= a; end if; -- override → pure 2:1 mux
end process;7. Common mistakes & what to watch for
- Combinational if with an unassigned path. Infers a latch. Default-assign outputs at the top, or
provide a full
else. - Confusing if-priority with parallel selection.
if/elsifis ordered precedence; for a balanced, value-based choice usecase(lesson 5.5). - Adding an unnecessary else in a clocked register. A clocked
ifwith noelsecorrectly means "hold"; forcing an else can accidentally clear the register. - Deeply nested ifs that hide intent. Long priority ladders are hard to read; a
caseor a refactor is often clearer. - Reading a signal assigned in a branch later in the same wake-up. It is still the old value (use a variable for immediate intermediates — anchor lesson).
8. Engineering insight
The if statement is priority made executable: branch order is precedence, and that single fact tells
you what mux network you are building. Combined with the discipline of default-then-override, it gives
concise, latch-free combinational logic; combined with a rising_edge(clk) guard, it expresses every
enabled/reset register you will write. Reach for if when the decision is naturally prioritised
(error-over-status, enable/reset gating) and for case when it is a balanced decode of one value —
the two cover all of process-based control logic.
9. Summary
The sequential if/elsif/else selects by priority — first true branch wins — synthesising to a
priority multiplexer (combinational) or a conditional register update (clocked). Branch order is
hardware precedence. A combinational if must assign every output on every path or it infers a latch
(use default-then-override); a clocked if without an else correctly holds the register.
10. Learning continuity
if selects by priority. When the decision is instead a balanced choice among the values of a single
expression, the right construct is the case statement — the next lesson — which synthesises to a
balanced multiplexer/decoder, requires exhaustive coverage with when others, and is the natural way
to write opcode decoders and, later, finite state machines.