Verilog Procedural Blocks
In Verilog, procedural blocks are used to define sequential and conditional execution of code, allowing you to describe complex behavior in hardware designs. The two main types of procedural blocks in Verilog are:
initial
blocksalways
blocks
Each of these blocks has its specific use cases, and together, they allow for both sequential and combinational logic descriptions in a hardware model.
1. Initial Blocks
The initial
block is used for one-time execution of statements. It starts executing at simulation time 0
and runs only once. This is commonly used for:
- Assigning initial values to registers.
- Defining testbenches.
- Initializing memory or variables.
Syntax:
veriloginitial begin // Statements end
Example:
verilogmodule initial_example; reg [3:0] counter; initial begin counter = 4'b0000; // Initialize counter to zero #10 counter = 4'b1010; // After 10 time units, assign 1010 end endmodule
Key Points:
- One-time execution: The
initial
block runs only once at the start of simulation. - Use cases: Typically used for initialization purposes, testbenches, or defining stimuli.
2. Always Blocks
The always
block is used for continuous execution and can describe both sequential and combinational logic depending on how it is used. It re-evaluates the block whenever there is a change in its sensitivity list.
Syntax:
verilogalways @(sensitivity_list) begin // Statements end
Types of always
Blocks:
There are two primary types of always
blocks, based on how the sensitivity list is defined.
2.1 Combinational Logic (always @*
)
This type of always
block is used to model combinational logic, where the outputs depend only on the current inputs, without any storage of past states. The @*
sensitivity list automatically includes all signals used inside the block, avoiding incomplete sensitivity lists.
Syntax:
verilogalways @* begin // Combinational logic end
Example:
verilogmodule combinational_example ( input wire a, b, c, output reg y ); always @* begin y = (a & b) | c; // Combinational logic end endmodule
Key Points:
- Combinational logic: Outputs depend on the current state of inputs.
- No storage: No past state is retained.
- Automatic sensitivity list: The
@*
ensures that all inputs affecting the block are included in the sensitivity list.
2.2 Sequential Logic (always @(posedge clock)
or @(negedge clock)
)
This type of always
block is used to model sequential logic, where the output depends on the current inputs as well as the past state. It typically uses clock edges to synchronize the behavior of flip-flops and registers.
Syntax:
verilogalways @(posedge clock or negedge reset) begin // Sequential logic end
Example:
verilogmodule sequential_example ( input wire clk, input wire rst_n, input wire d, output reg q ); always @(posedge clk or negedge rst_n) begin if (!rst_n) q <= 0; // Reset logic else q <= d; // Flip-flop logic end endmodule
Key Points:
- Sequential logic: Describes flip-flops, registers, and counters.
- Edge-triggered: Sensitive to rising (
posedge
) or falling (negedge
) edges of the clock or reset signals. - Storage of state: Retains past state information, typical for registers.
3. Blocking vs Non-blocking Assignments
Inside procedural blocks, you can assign values using either blocking (=
) or non-blocking (<=
) assignments.
3.1 Blocking Assignments (=
)
Blocking assignments execute statements in sequence. The next statement is executed only after the current one is completed.
- Usage: Often used in combinational logic (
always @*
blocks).
Example:
verilogalways @* begin a = b + 1; c = a + 2; // This will use the updated value of a end
3.2 Non-blocking Assignments (<=
)
Non-blocking assignments allow multiple statements to execute in parallel. The right-hand side is evaluated immediately, but the assignment happens at the end of the current time step.
- Usage: Commonly used in sequential logic (
always @(posedge clk)
blocks) to describe flip-flop behavior.
Example:
verilogalways @(posedge clk) begin a <= b + 1; c <= a + 2; // This will use the old value of a end
Key Differences:
Blocking (= ) | Non-blocking (<= ) |
---|---|
Statements execute sequentially | Statements execute concurrently |
Used in combinational logic | Used in sequential logic |
Immediate update of variables | Update at the end of the time step |
4. Sensitivity List
The sensitivity list in an always
block specifies the signals that trigger the execution of the block. You can use specific signals like posedge clk
, or you can use @*
for combinational logic, which automatically includes all input signals.
Example:
verilogalways @(posedge clk or negedge reset) begin // Triggered on clock rising edge or reset falling edge end
In this example, the block is triggered on the rising edge of the clock or the falling edge of the reset signal.
5. Control Flow Statements in Procedural Blocks
Within initial
and always
blocks, you can use control flow statements such as if
, case
, for
, while
, and repeat
to control the execution of your Verilog code.
if-else
Example:
verilogalways @(posedge clk) begin if (reset) q <= 0; else q <= d; end
case
Example:
verilogalways @* begin case (sel) 2'b00: out = in0; 2'b01: out = in1; 2'b10: out = in2; 2'b11: out = in3; endcase end
Loop Example:
veriloginteger i; always @(posedge clk) begin for (i = 0; i < 8; i = i + 1) begin mem[i] <= data[i]; end end