Procedural Blocks
Always block, initial block, sensitivity lists.
Verilog is a hardware description language but it also provides procedural constructs that allow designers to describe circuit behavior in a sequential, statement-by-statement manner. These procedural constructs are organized into blocks, and understanding them is the gateway to behavioral modeling of digital circuits including FSMs, arithmetic units, and control logic.
Core Concept: What Are Procedural Blocks
In Verilog, all logic is described either through continuous assignments using assign or through procedural blocks. A procedural block is a region of code that executes sequentially, meaning statements inside it run one after the other from top to bottom, unlike the parallel nature of hardware. The two types are the always block and the initial block.
The always block is the workhorse of behavioral modeling. It runs continuously throughout simulation. It does not start and stop; instead it waits for the condition in its sensitivity list to trigger, executes its body, and then waits again for the next trigger. This repeating behavior models how real hardware responds to changing inputs or clock edges.
The initial block runs exactly once, starting at simulation time zero. It has no sensitivity list and terminates when its last statement completes. It is primarily used in testbenches to apply stimulus, initialize memory, and control simulation time using delay constructs.
Sensitivity Lists: The Trigger Mechanism
The sensitivity list is written after the @ symbol in an always block. It specifies the signals that, when they change, trigger the block to execute. If a signal that affects the output is missing from the sensitivity list, the simulation will not model the hardware correctly, leading to synthesis-simulation mismatches.
For combinational logic, the sensitivity list should include all inputs. Rather than listing each one manually, Verilog 2001 introduced always @(*) which automatically includes every signal read inside the block. This is the recommended style for combinational always blocks and avoids accidental omissions.
For sequential logic, the sensitivity list uses edge descriptors. always @(posedge clk) triggers on the rising edge of the clock. always @(negedge clk) triggers on the falling edge. For asynchronous reset flip-flops, the reset signal is added as always @(posedge clk or posedge rst), meaning the block triggers on either a clock edge or a reset assertion.
Practical Understanding: Multiple Blocks Run in Parallel
A Verilog module can contain multiple always and initial blocks. All of them run concurrently with respect to each other. Each block is an independent process. This parallelism reflects the physical reality of hardware where multiple circuits operate simultaneously. Inside each block, however, statements execute sequentially.
This distinction is critical. If two always blocks assign to the same reg variable, the simulation result is unpredictable and depends on tool-specific scheduling. Such multiple-driver situations should be avoided in synthesizable design. In testbenches, it is more acceptable but still requires careful coordination.
Given:
A D flip-flop with synchronous active-high reset.
Clock: clk, Reset: rst, Input: d, Output: q (1-bit each)
Why this formula applies:
Flip-flop is sequential logic: triggered by posedge clk.
Reset is synchronous so it is checked inside the block, not in the sensitivity list.
Formula:
always @(posedge clk) block with synchronous reset
if (rst) q <= 0;
else q <= d;
Substitution:
module dff (
input wire clk, rst, d,
output reg q
);
always @(posedge clk) begin
if (rst)
q <= 1'b0;
else
q <= d;
end
endmodule
Calculation:
At posedge clk:
if rst=1 => q becomes 0 regardless of d
if rst=0 => q captures value of d
Final Answer:
At each rising clock edge, q is updated. Between edges, q holds its value.Exam Tip: always @(*) does not infer sequential logic by itself. Sequential behavior comes from posedge/negedge triggers. If a signal is missing from a manual sensitivity list, simulation may differ from synthesis. GATE often tests this with waveform-based questions.
Key Behavioral Rules for Procedural Blocks
- Variables assigned inside a procedural block must be of type reg (or integer, real, etc.), never wire.
- always blocks execute forever during simulation; initial blocks execute once and stop.
- always @(*) is the safest sensitivity list for combinational logic and is synthesis-friendly.
- For synchronous sequential logic, use posedge clk or negedge clk in the sensitivity list.
- For asynchronous reset, add posedge rst (or negedge rst) to the sensitivity list.
- Multiple always blocks in one module run in parallel, not in sequence.
- Initial blocks are simulation-only constructs and are not synthesizable.
Quick Revision
- always block: runs continuously, triggered by sensitivity list, synthesizable, used in RTL design.
- initial block: runs once at t=0, no sensitivity list, not synthesizable, used in testbenches.
- Sensitivity list: @(*) for combinational, @(posedge clk) for sequential.
- All always and initial blocks within a module run concurrently with each other.
- Statements inside one block run sequentially top to bottom.
- Signals assigned in procedural blocks must be reg type.
- Exam trap: missing signal in manual sensitivity list causes simulation-synthesis mismatch.
Verilog Procedural Blocks
Test your knowledge on this topic.
Q1.What is the consequence of an incomplete sensitivity list in combinational always blocks?
Related Articles
Block Statements
Begin-end, fork-join parallel blocks.
9 min read
Generate Blocks
Conditional and loop generate for scalable structures.
12 min read
FSM Behavioral Modeling
Mealy vs Moore coding styles.
11 min read
Loops in Verilog
For, while, repeat, forever loops.
8 min read
Timing Controls
Delay control #, event control @, wait statement.
4 min read