In Verilog HDL, a hardware description language widely used in digital circuit design, the always block plays a crucial role. Instead of describing hardware behavior like software, Verilog represents circuits by defining “under what conditions signals change”. Among these, the always block is a fundamental construct used to describe what actions should be executed when certain conditions occur.
Why do we need the always block?
In Verilog, there are two main types of circuit behaviors you can describe:
Combinational logic: output changes immediately when inputs change
Sequential logic: output changes in sync with a clock signal or timing events
A simple assign statement cannot handle complex conditions or memory elements. This is where the always block comes in. For example, to describe conditional branching or flip-flop behavior, you need an always block with control structures such as if or case.
Common patterns of the always block
The always block has several common usage patterns depending on the circuit type being designed:
always @(*) → Used for combinational logic
always @(posedge clk) → Sequential logic triggered on the rising edge of the clock
always @(posedge clk or negedge rst) → Sequential logic with asynchronous reset or more complex control
Thus, understanding the always block, a core syntax of Verilog, is an essential first step for hardware designers.
Purpose of this article
This article provides a comprehensive guide to the always block in Verilog, covering basic syntax, practical usage, common pitfalls, and SystemVerilog extensions.
Learn the correct way to write always blocks
Understand why synthesis errors occur
Clarify the difference between = and <=
Avoid common beginner mistakes
We aim to make this a practical and easy-to-understand guide for anyone with such questions.
2. Basic Syntax and Types of always Blocks
Basic syntax of the always block
In Verilog, an always block repeatedly executes statements based on a specific sensitivity list. The basic syntax is:
always @(sensitivity list)
begin
// statements
end
The key part here is the “sensitivity list,” which defines which signals trigger execution when they change.
Using always @(*) for combinational logic
In combinational logic, the output must update immediately whenever inputs change. In this case, use @(*) as the sensitivity list.
always @(*) begin
if (a == 1'b1)
y = b;
else
y = c;
end
This means whenever a, b, or c changes, the always block executes and recalculates y.
Advantages of using @(*)
Automatically includes all referenced signals in the sensitivity list
Prevents mismatches between simulation and synthesis results
Using always @(posedge clk) for sequential logic
In sequential logic, state changes occur in sync with a clock signal. In this case, specify posedge clk in the sensitivity list.
always @(posedge clk) begin
q <= d;
end
Here, the value of d is latched into q at the rising edge of the clock. The operator <= represents a non-blocking assignment, which is the standard for sequential logic.
posedge vs negedge
posedge: triggered on the rising edge
negedge: triggered on the falling edge
Select the appropriate edge depending on the design requirements.
always @(posedge clk or negedge rst) with asynchronous reset
In more complex circuits, reset functionality is often required. A block with asynchronous reset can be written as:
always @(posedge clk or negedge rst) begin
if (!rst)
q <= 1'b0;
else
q <= d;
end
With this description, q is immediately reset when rst is low, otherwise it captures d on the clock edge.
Combinational vs Sequential Circuits
Type of Circuit
always to use
Behavior
Combinational
always @(*)
Output updates immediately based on inputs
Sequential
always @(posedge clk)
Operates in sync with the clock
3. Types of Assignments in always Blocks
Two assignment operators in Verilog
Inside a Verilog always block, you can use two different assignment operators:
=: Blocking assignment
<=: Non-blocking assignment
Misunderstanding these differences can lead to unexpected behavior and mismatches between simulation and synthesis, making this one of the most important points to learn.
Blocking assignment (=)
A blocking assignment executes sequentially, one statement after another, similar to software control flow.
always @(*) begin
a = b;
c = a;
end
Here, a = b executes first, and then c = a uses the updated value of a. The order of statements directly affects logic behavior.
Typical use cases
Control structures (if, case) in combinational logic
Logic that does not require state holding
Non-blocking assignment (<=)
A non-blocking assignment means that all statements are evaluated simultaneously and updated together, expressing hardware’s parallel nature.
always @(posedge clk) begin
a <= b;
c <= a;
end
Both a <= b and c <= a are evaluated at the same time and updated after the clock edge. Therefore, c receives the previous value of a.
Typical use cases
Sequential logic (registers, flip-flops)
Accurate state propagation across multiple signals
Blocking vs Non-blocking Assignments: Comparison
Feature
Blocking (=)
Non-blocking (<=)
Execution order
Sequential, one after another
Evaluated simultaneously, updated together
Typical usage
Combinational logic
Sequential logic
Update timing
Immediately applied
Applied after the clock edge
Common pitfalls
Unintended latch generation
Values not updated or propagated as expected
What happens if you mix them?
You should avoid mixing= and <= in the same block or on the same signal. For example:
always @(posedge clk) begin
a = b;
a <= c;
end
This code assigns to a twice using different methods, making the final stored value ambiguous, which may appear correct in simulation but fail in hardware.
Guidelines for usage
Use = inside always @(*) blocks (combinational)
Use <= inside always @(posedge clk) blocks (sequential)
Following this simple rule helps prevent many common mistakes.
4. Common Pitfalls and Best Practices with always Blocks
Mistakes in sensitivity lists
Incorrect sensitivity lists can cause hidden bugs
In Verilog, the sensitivity list (@(...)) must explicitly include all signals that trigger execution. Here’s an example where only part of the signals are included:
always @(a) begin
if (b)
y = 1'b1;
else
y = 1'b0;
end
This code does not respond to changes in b. As a result, when b changes, y will not update, causing a bug.
Solution: use @(*)
To avoid missing signals in the sensitivity list, use @(*) as follows:
always @(*) begin
if (b)
y = 1'b1;
else
y = 1'b0;
end
@(*) automatically includes all signals referenced in the block, improving both maintainability and reliability.
Unintended latch generation
Missing if/case branches creates latches
If not all cases assign values, the synthesis tool infers that the variable must “hold” its value, creating a latch:
always @(*) begin
if (enable)
y = d; // y is undefined when enable == 0
end
Even though it looks fine, a latch is inserted because y does not get updated when enable is 0.
Solution: always assign a value
always @(*) begin
if (enable)
y = d;
else
y = 1'b0; // y is always defined
end
By explicitly assigning a value in every case, you can prevent unwanted latches.
Overly complex conditionals
Complicated if or case statements can lead to undefined behavior or missing logic if not all conditions are covered.
Typical mistake: no default in a case statement
always @(*) begin
case(sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
// 2'b11 not handled → y may be undefined
endcase
end
Solution: add a default clause
always @(*) begin
case(sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
default: y = 1'b0; // safety net
endcase
end
Adding default ensures that outputs are always defined, improving design robustness.
Controlling multiple signals in one block
When controlling multiple signals in a single always block, assignment order and missing cases can create unintended dependencies. In complex designs, consider splitting logic into multiple always blocks for clarity and safety.
Summary of common pitfalls
Problem
Cause
Solution
Output not updating
Missing signals in sensitivity list
Use @(*) for automatic detection
Latch generated
Not all branches assign values
Always include else or default
Undefined behavior
Case statement missing conditions
Add default branch
Overly complex control
Too many signals in one block
Split into multiple always blocks
5. Extensions of always in SystemVerilog
always_comb: for combinational logic
Overview
always_comb works similarly to always @(*) but explicitly indicates combinational logic.
always_comb begin
y = a & b;
end
Main benefits
Automatically generates sensitivity list
Tools warn when unintended latches are inferred
Prevents conflicts with previously defined variables
Example (Verilog vs SystemVerilog)
// Verilog
always @(*) begin
y = a | b;
end
// SystemVerilog
always_comb begin
y = a | b;
end
always_ff: for sequential logic (flip-flops)
Overview
always_ff is designed for clock-driven sequential logic, requiring explicit edge conditions like posedge clk or negedge rst.
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= d;
end
Main benefits
Allows only non-blocking assignments (<=)
Tools check sensitivity list correctness
Code readability improves as it is clearly sequential
always_latch: for latch-based logic
Overview
always_latch is used when you intentionally describe latch behavior. However, in most designs, unintended latches should be avoided.
always_latch begin
if (enable)
q = d;
end
Points to note
If some branches skip assignment, a latch is explicitly created
Use only when latches are truly required
SystemVerilog usage summary
Construct
Purpose
Equivalent in Verilog
Features
always_comb
Combinational logic
always @(*)
Auto sensitivity list, latch detection
always_ff
Flip-flops
always @(posedge clk)
Clock-synchronous, safer assignments
always_latch
Latches
always @(*) with incomplete branches
Explicit latch design, error detection
SystemVerilog is becoming the standard
In modern development, SystemVerilog constructs are increasingly recommended for readability and safety. With better syntax checking, using always_ff and always_comb helps prevent “looks correct but doesn’t work” issues. Especially in large-scale or team-based projects, explicit constructs make design intent clear, improving code reviews and maintainability.
6. FAQ: Common Questions About the always Block
This section answers frequently asked questions about always blocks in Verilog and SystemVerilog, focusing on practical concerns that often come up in design projects. It covers common beginner to intermediate issues seen in real-world development.
Q1. Should I use if or case inside an always block?
A. It depends on the number and complexity of conditions:
For 2–3 simple conditions → if is easier to read
For multiple distinct states → case is clearer and expresses intent better
Using case also enforces the expectation of covering all possible cases, helping reduce mistakes.
Q2. What happens if I omit signals in the sensitivity list?
A. If the sensitivity list is incomplete, some signal changes won’t trigger the block, leaving outputs outdated. This may cause simulation vs synthesis mismatches. To prevent this, always use @(*) or SystemVerilog always_comb.
Q3. Why do unintended latches appear in my design?
A. If if or case statements don’t assign a value to a variable in every possible path, the synthesis tool infers that the value must be held, and creates a latch.
Bad example:
always @(*) begin
if (en)
y = d; // y is held when en == 0
end
Solution:
always @(*) begin
if (en)
y = d;
else
y = 1'b0; // always assigned
end
Q4. Can I mix = and <= in the same block?
A. Generally, no. Mixing blocking and non-blocking assignments in the same block, especially on the same signal, may cause simulations to work but hardware to fail.
Combinational logic → use = (blocking)
Sequential logic → use <= (non-blocking)
Rule of thumb:
Always use a consistent assignment style per signal.
Q5. What’s the difference between always_ff and always @(posedge clk)?
A. Functionally, they behave the same, but always_ff is safer and more readable.
Comparison
always @(posedge clk)
always_ff
Sensitivity
Must be manually specified
Checked automatically
Assignment errors
Blocking assignments may compile
Invalid assignments cause errors
Readability
May obscure circuit intent
Clearly indicates sequential logic
Q6. Is it okay to control multiple signals in one always block?
A. It’s possible, but if too many signals are included, debugging and maintenance become difficult. Consider splitting into multiple blocks when:
Each output behaves independently
You mix synchronous and asynchronous logic
Q7. What happens if I use <= in combinational logic?
A. It may still work in simulation, but during synthesis, it can create unexpected logic. Stick to blocking (=) assignments for combinational logic.
7. Conclusion
always blocks are the foundation of Verilog design
In Verilog hardware design, the always block is a powerful tool that allows you to describe both combinational and sequential circuits. It not only expands your design possibilities but also clarifies control flow and timing. For both beginners and professionals, always is essential knowledge.
Key takeaways
The differences and usage of always @(*) vs always @(posedge clk)
The distinction between = (blocking) and <= (non-blocking) assignments
How to avoid common mistakes such as latch generation and incomplete sensitivity lists
SystemVerilog extensions (always_comb, always_ff, always_latch) for safer design
Practical answers to common real-world questions (FAQ)
Precision determines quality
In hardware description, what you write is exactly what gets implemented. Even small mistakes can become hardware bugs. Since always is central to behavior, accuracy, correct assignment type, and complete condition coverage are critical.
Next steps: advancing to higher-level design
Once you master the always block, you can move on to:
Finite State Machine (FSM) design
Pipelining and streaming architectures
Developing IP cores and FPGA implementation
You can also broaden your skills by learning SystemVerilog and VHDL, making you adaptable across different design environments.
Final thoughts for hardware designers
In circuit design, it’s not just about “making it work.” What’s required is correct behavior, robustness for future changes, and clarity for team development.
Through this article, we hope you gained both fundamental knowledge of always blocks and an appreciation for safe, reliable design practices.