The Verilog if-else-if statement decides which statements run based on one or more conditions. An if runs its block when its condition is true (non-zero), an optional else runs when it is false (zero, x or z), and else if adds further conditions that are checked in order until one is true.

12 min read | Beginner Level

What You'll Learn

  • The syntax of if, if-else and if-else-if, and when begin/end is needed
  • What each form synthesizes into: latch, flip-flop, enable or priority logic
  • Why the order of an else if chain sets the priority, and when synthesis removes it
  • How to avoid dangling-else, unreachable-branch and latch bugs

Syntax

Each if or else branch controls exactly one statement. To put more than one statement in a branch, group them with begin and end. Like all procedural statements, if-else can only be used inside an always or initial block. See Verilog Conditional Statements for how it compares with the conditional operator and case.


if ([expression])
	Single statement

// Use "begin" and "end" blocks for more than 1 statements
if ([expression]) begin
	Multiple statements
end

// Use else to execute statements for which expression is false
if ([expression]) begin
	Multiple statements
end else begin
	Multiple statements
end

// if-else-if style to check for more expressions if the previous one doesn't match
if ([expression 1])
	Single statement
else if ([expression 2]) begin
	Multiple Statements
end else
	Single statement

Components:

  • [expression] - Any expression. A non-zero value is true; zero, x and z are false (required)
  • begin ... end - Groups several statements into one branch; optional for a single statement
  • else if - Checked only when every earlier condition was false; any number can be chained (optional)
  • else - Runs when no earlier condition was true (optional)

Conditions are evaluated from top to bottom, and only the first true branch runs. Every later branch is skipped, even if its condition is also true. This is what gives an if-else-if chain its priority.

Hardware Implementation

What an if statement synthesizes into depends on two things: whether the always block is combinational or clocked, and whether every path assigns the output.

Block typeEvery path assigns the output?Hardware
always @(*) or level-sensitiveYesCombinational mux
always @(*) or level-sensitiveNoLatch
always @(posedge clk)YesFlip-flop with a mux on D
always @(posedge clk)NoFlip-flop that holds, usually with a clock enable

if without else

In a level-sensitive block, an if without an else leaves q unassigned when en is 0. q must then keep its old value, so synthesis infers a latch that is transparent while en is high. See D Latch for how latches work.


module des (  input en,
              input d,
              output reg q);

    // Level-sensitive block: runs whenever en or d changes
    always @ (en or d)
        if (en)          // When en is 1, q follows d
            q = d;
        // No else: when en is 0, q is not assigned and keeps its value,
        // so synthesis infers a latch

endmodule
Synthesized schematic of an if statement without else: a latch with en as its gate and d as its data input

In combinational logic, an unintended latch is a bug.

If the block is meant to be combinational, add the else branch or assign a default value at the top of the block. Only leave out the else when you want a latch.

if with else

In a clocked block, the same structure builds flip-flops. Here every path assigns q, and the block runs only on the rising clock edge, so the result is a D flip-flop with a synchronous reset.


module dff (	input clk,
							input rstn,
							input d,
							output reg q);

	always @ (posedge clk) begin
		if (! rstn)      // Reset has priority: checked first
			q <= 0;
		else             // Otherwise capture d at the clock edge
			q <= d;
	end
endmodule

Synthesis result: A D flip-flop with a small gate in front of its D input that forces 0 when rstn is low. See D Flip-Flop Async Reset for the synchronous and asynchronous reset styles.

Synthesized schematic of a D flip-flop with synchronous reset logic on its D input

Quick Check

Q: In the dff module, would removing the else and its assignment create a latch?

Show Answer

No. The block is clocked, so q is a flip-flop either way. Without the else, q simply holds its value at clock edges where rstn is 1. Missing branches create latches only in level-sensitive blocks. The modified design would never load d, though, so a synthesis tool may reduce q to a constant 0.

if else if

This 4-bit counter uses an if-else-if chain inside the reset's else branch. When mode is 1 it counts up, when it is 2 it counts down, and for any other value no branch runs, so q holds.


module des ( input [1:0] mode,
             input clk,
            input rstn,
            output reg [3:0] q);

  always @ (posedge clk) begin
    if (! rstn)                // Highest priority: synchronous reset
      q <= 0;
    else begin
      if (mode == 1)           // Count up
        q <= q + 1;
      else if (mode == 2)      // Count down
        q <= q - 1;
      // mode 0 or 3: no branch runs, so q holds its value
    end
  end
endmodule

Synthesis result: The tool builds an incrementer and a decrementer, a mux that picks between them, and four flip-flops. Because q holds for two of the four mode values, the tool can use flip-flops with a clock enable (CE) pin, enabling them only when mode is 1 or 2 or reset is active, instead of feeding q back through a mux.

Synthesized schematic of the 2-bit mode up/down counter, with incrementer, decrementer, select logic and enabled flip-flops

Compare it with a version where mode is 1 bit and the chain ends in an else:


module des ( input mode,
             input clk,
            input rstn,
            output reg [3:0] q);

  always @ (posedge clk) begin
    if (! rstn)
      q <= 0;
    else begin
      if (mode)                // mode = 1: count up
        q <= q + 1;
      else                     // mode = 0: count down
        q <= q - 1;
      // Every path assigns q, so q changes on every clock edge
    end
  end

endmodule

Now q is updated on every clock edge, so no hold condition and no clock enable are needed. Synthesis builds plain flip-flops with a mux in front of D that selects the incremented or decremented value.

Synthesized schematic of the 1-bit mode up/down counter, with a mux selecting the incremented or decremented value into plain flip-flops

Where is if-else-if Used?

Hardware timers in microcontrollers are built from counters like the one above. A mode register selects whether the timer counts up, counts down or is stopped, and an if-else-if chain in the counter's clocked block implements each mode. Reset is placed first so that it overrides every mode.

Priority in an if-else-if Chain

Because only the first true branch runs, an if-else-if chain describes a priority structure. As written, it is a cascade of 2 to 1 muxes: the first condition controls the mux nearest the output, and each later condition sits one stage further back.

Whether that priority stays in the hardware depends on whether the conditions can be true at the same time:

  • Conditions that can overlap, such as if (req[3]) ... else if (req[2]) ...: two of them can be true together, so the order changes the result. Synthesis keeps the priority. This is how a priority encoder is built.
  • Mutually exclusive conditions, such as if (sel == 2'b00) ... else if (sel == 2'b01) ...: only one can ever be true, so the order cannot change the result. Synthesis removes the priority and builds a parallel mux, the same as a case statement. See Verilog 4 to 1 Multiplexer.

PPA Impact: In a long chain with overlapping conditions, the last condition passes through every mux stage, so the delay grows with the number of branches. Put the most timing-critical signal in the first branch, or restructure long chains into a case statement when the conditions are mutually exclusive.

Pipeline Control Priority

The control logic of a processor pipeline register is usually written as one if-else-if chain: reset first, then flush (for a mispredicted branch or an exception), then stall, and finally the normal update. Several of these signals can be active in the same cycle, and the chain's order defines which one wins, for example a flush always overrides a stall.

Quick Check

Q: if (a > 5) y = 1; else if (a > 10) y = 2; else y = 3;. For which values of a is y = 2?

Show Answer

None. Any value greater than 10 is also greater than 5, so the first branch always catches it. The second branch is unreachable. Checking a > 10 first fixes it.

Examples

These simulation examples show how begin/end decides which statements belong to each branch.

if without else for single statement


module tb;
  integer a = 10;

  initial begin
    if (a == 10)      // if block can have only one statement in it
      $display ("a is found to be 10");
    $display ("Always executed regardless of value of a");   // This statement is outside if block because
  end
endmodule
Output
$ /usr/bin/vvp simulation +random_seed+1788676158
a is found to be 10
Always executed regardless of value of a

Without begin/end, only the first $display belongs to the if. The second one runs every time, even though the indentation is the same.

if without else for multiple statements


module tb;
  integer a = 10;

  initial begin
    if (a == 10) begin       // if block has begin end keywords, and can support multiple statements
       $display ("a is found to be 10");
       $display ("Its good to get 10");

       // Anything else can be done here until the "end" keyword
    end
    $display ("Always executed regardless of value of a");   // Statement is outside the if block, because of the closing "end" for the "begin" in if
  end
endmodule
Output
$ /usr/bin/vvp simulation +random_seed+187329118
a is found to be 10
Its good to get 10
Always executed regardless of value of a

if-else for single statement


module tb;
  integer a = 9;

  initial begin
    if (a == 10)
       $display ("a is found to be 10");    // Is executed when "if" expression is True
    else
       $display ("a is NOT 10 :(");         // Is executed when "if" expression is false
  end
endmodule
Output
$ /usr/bin/vvp simulation +random_seed+1031342321
a is NOT 10 :(

if-else for multiple statements


module tb;
  integer a = 9;

  initial begin
    if (a == 10) begin
       $display ("a is found to be 10");              // Is executed when "if" expression is True
       // Can have more additional statements here

    end else begin
       $display ("a is NOT 10 :(");                   // Is executed when "if" expression is false
       $display ("Why is a not 10 ?");
       // Can have more additional statements here
    end
  end
endmodule
Output
$ /usr/bin/vvp simulation +random_seed+1922538095
a is NOT 10 :(
Why is a not 10 ?

Conditional Checks in Testbenches

Verification testbenches use if-else inside initial blocks and tasks in the same way as these examples: compare a design output with an expected value, print a pass message in one branch and an error with the mismatching values in the other, and count failures for a final report.

Common Beginner Mistakes

Mistake #1: Dangling else attaching to the wrong if

Wrong:


if (en)
  if (load) q <= d;
else          // Intended for "if (en)", but belongs to "if (load)"
  q <= 0;

Right:


if (en) begin
  if (load) q <= d;
end else
  q <= 0;

Why? An else always pairs with the nearest preceding if that has no else, regardless of indentation. In the wrong version, q clears when en is 1 and load is 0, and nothing happens when en is 0. Wrapping the inner if in begin/end forces the pairing you intended.

Mistake #2: Forgetting begin/end around multiple statements

Wrong: if (start) count <= 0; busy <= 1;
Right: if (start) begin count <= 0; busy <= 1; end

Why? Only the first statement after if is conditional. busy <= 1 runs every time, as the first simulation example shows. The code compiles without a warning.

Mistake #3: Ordering overlapping conditions wrongly

Wrong: if (a > 5) ... else if (a > 10) ...
Right: if (a > 10) ... else if (a > 5) ...

Why? When conditions overlap, the first true one wins. A broad condition placed before a narrower one makes the narrower branch unreachable. Synthesis tools usually remove that logic silently, so review the order whenever conditions can overlap.

Mistake #4: Missing final else in combinational logic

Wrong: always @(*) if (sel == 0) y = a; else if (sel == 1) y = b; with a 2-bit sel
Right: End the chain with else y = 0;, or assign y a default before the chain

Why? When sel is 2 or 3, no branch assigns y, so synthesis infers a latch to hold it. In a combinational block, every if-else-if chain needs a final else or a default assignment.

Practice Exercise

Challenge: Shift Register with Prioritized Controls

Write a module shreg8 with an 8-bit register q, a clock clk, and three active-high controls with this priority: rst (clear to 0) highest, then load (load 8-bit input d), then shift (shift left by one, filling bit 0 with 0). If none are active, q holds. What happens when load and shift are both 1?

Hint

Write one if-else-if chain in an always @(posedge clk) block, in priority order. A final else is not needed, because a clocked block holds its value.

Solution

module shreg8 (
  input            clk,
  input            rst,     // Highest priority: clear
  input            load,    // Middle priority: parallel load
  input            shift,   // Lowest priority: shift left
  input      [7:0] d,
  output reg [7:0] q
);

  always @(posedge clk) begin
    if (rst)
      q <= 8'h00;               // Clear
    else if (load)
      q <= d;                   // Load wins over shift
    else if (shift)
      q <= {q[6:0], 1'b0};      // Shift left, fill bit 0 with 0
    // No else: q holds when no control is active
  end

endmodule

When load and shift are both 1, load is checked first, so q takes d and the shift is ignored. The three controls can overlap, so synthesis keeps this priority. It builds a priority mux chain in front of the flip-flops, plus a clock enable that is active when any control is set.

Summary

In this tutorial, you learned:

  • An if runs its branch when the condition is non-zero; zero, x and z count as false
  • Each branch controls one statement unless the statements are grouped with begin/end
  • An else pairs with the nearest unpaired if, regardless of indentation
  • In a level-sensitive block, an if without else infers a latch
  • In a clocked block, a missing branch simply holds the flip-flop's value, often through a clock enable
  • An if-else-if chain is a priority structure; synthesis keeps the priority only when the conditions can overlap
  • Place reset first and order overlapping conditions from most to least specific
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5