- 1 1. Introduction
- 2 2. Basic Syntax and Types of always Blocks
- 3 With this description, q is immediately reset when rst is low, otherwise it captures d on the clock edge.
- 4 3. Types of Assignments in always Blocks
- 5 4. Common Pitfalls and Best Practices with always Blocks
- 6 5. Extensions of always in SystemVerilog
- 7 6. FAQ: Common Questions About the always Block
- 7.1 Q1. Should I use if or case inside an always block?
- 7.2 Q2. What happens if I omit signals in the sensitivity list?
- 7.3 Q3. Why do unintended latches appear in my design?
- 7.4 Q4. Can I mix = and <= in the same block?
- 7.5 Q5. What’s the difference between always_ff and always @(posedge clk)?
- 7.6 Q6. Is it okay to control multiple signals in one always block?
- 7.7 Q7. What happens if I use <= in combinational logic?
- 8 7. Conclusion
1. Introduction
What is the role of the always
block in Verilog?
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 logicalways @(posedge clk)
→ Sequential logic triggered on the rising edge of the clockalways @(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 edgenegedge
: 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
=
insidealways @(*)
blocks (combinational) - Use
<=
insidealways @(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 @(*)
vsalways @(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.