The short answer: Inside always and initial blocks, Verilog uses if-else and case for decisions, and for, while, repeat and forever for loops. In synthesisable RTL, an if-else chain builds priority logic, a case statement builds a multiplexer, and a for loop is unrolled into repeated hardware, so its bounds must be constants. In combinational blocks, every output must be assigned on every path, or synthesis infers a latch.
These statements look exactly like C, which is why it’s easy to forget that each one turns into hardware. In this post I show what each one builds and the habits that keep it clean. It combines my older posts on conditional statements and loops.
- What each statement builds
- if-else
- case, casez and casex
- Avoiding latches
- Loops
- Habits that help
Table of Contents
Part 1: What each statement builds

Part 2: if-else
always @(*) begin
if (rst) next = 4'd0; // highest priority
else if (load) next = din;
else if (en) next = count + 1'b1;
else next = count; // final else: no latch
end
The first true condition wins, so the order is the priority. That’s exactly right here: reset beats load, load beats enable. If your conditions can never be true at the same time, a case statement often describes the intent better.
Part 3: case, casez and casex
always @(*) begin
case (sel)
2'd0: y = a;
2'd1: y = b;
2'd2: y = c;
default: y = d; // covers 2'd3 and any x/z in simulation
endcase
end
casez treats z or ? as don’t-care, which is handy for priority decoders:
casez (req)
4'b1???: grant = 4'b1000;
4'b01??: grant = 4'b0100;
4'b001?: grant = 4'b0010;
4'b0001: grant = 4'b0001;
default: grant = 4'b0000;
endcase
Avoid casex: it also treats x as don’t-care, so an unknown select in simulation silently matches an item and hides a bug.
Part 4: Avoiding latches
In an always @(*) block, if any output isn’t assigned on some path (an if without else, a case without default), the tool must remember the old value, which means a latch. The simplest fix is to assign defaults first:
always @(*) begin
y = 1'b0; // defaults
err = 1'b0;
case (state)
IDLE: y = start;
RUN: y = 1'b1;
ERR: err = 1'b1;
endcase
end
In clocked blocks, a missing else is fine: the register simply keeps its value, which is what a flip-flop does anyway.
Part 5: Loops
| Loop | Synthesisable? | Typical use |
|---|---|---|
| for | Yes, with constant bounds | Bit-level operations, generating repeated logic |
| repeat (n) | In testbenches; constant n only in RTL | Repeat stimulus a fixed number of times |
| while | Testbench only, in practice | Wait for a condition |
| forever | Testbench only | Clock generation, monitors |
// count the 1s in a vector (combinational)
integer i;
always @(*) begin
ones = 0;
for (i = 0; i < 16; i = i + 1)
ones = ones + data[i];
end
// testbench
initial begin
repeat (10) @(posedge clk); // wait 10 clocks
forever #5 clk = ~clk; // (normally in its own block)
end
A synthesisable for loop doesn’t run over time; it describes 16 adders’ worth of logic all at once. If you need something to happen over several clock cycles, use a counter and a state machine instead.
Part 6: Habits that help
- Always add a default to case statements in RTL, even if all values are covered.
- Keep if-else chains short. Long chains become long timing paths.
- Use case for decoding selects and FSM states, if-else for real priority.
- Name loop variables clearly and declare them as integer (or genvar for generate loops).
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
Previous in my Verilog series: Blocking vs non-blocking assignments. Next: Writing combinational logic.
FAQ
What is the difference between if-else and case in Verilog?
if-else gives priority to the first true condition; case compares one expression against several values and usually builds a parallel mux.
Why does my case statement create a latch?
Because some output isn’t assigned for some value of the select. Add a default and assign every output in every branch, or set defaults before the case.
Are for loops synthesisable in Verilog?
Yes, if the bounds are constants. The loop is unrolled into parallel hardware at elaboration.
Should I use casex?
No. It can hide x values in simulation. Use casez with ? when you need don’t-care bits.
