techmore.in

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:

  1. initial blocks
  2. always 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:

verilog
initial begin // Statements end

Example:

verilog
module 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:

verilog
always @(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:

verilog
always @* begin // Combinational logic end

Example:

verilog
module 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:

verilog
always @(posedge clock or negedge reset) begin // Sequential logic end

Example:

verilog
module 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:

verilog
always @* 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:

verilog
always @(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 sequentiallyStatements execute concurrently
Used in combinational logicUsed in sequential logic
Immediate update of variablesUpdate 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:

verilog
always @(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:

verilog
always @(posedge clk) begin if (reset) q <= 0; else q <= d; end

case Example:

verilog
always @* begin case (sel) 2'b00: out = in0; 2'b01: out = in1; 2'b10: out = in2; 2'b11: out = in3; endcase end

Loop Example:

verilog
integer i; always @(posedge clk) begin for (i = 0; i < 8; i = i + 1) begin mem[i] <= data[i]; end end