Skip to content

Behavioral Modeling in Verilog

Introduction

Behavioral modeling is one of the most powerful ways to describe digital circuits in Verilog. It allows designers to express how a circuit behaves, rather than specifying the exact gate-level implementation. This style is highly readable, compact, and essential during the design and verification phase.

What is Behavioral Modeling?

Behavioral modeling describes the functionality of a circuit using high-level constructs such as if-else, case, loops, and procedural blocks (always, initial).

Instead of defining logic gates or connections, the designer specifies what the circuit should do under certain conditions.

Example: Behavioral 2-to-1 Multiplexer

module mux2to1 (input a, b, sel, output reg y);
  always @(*) begin
    if (sel)
      y = b;
    else
      y = a;
  end
endmodule

Here, we describe the behavior of the multiplexer using if-else, rather than gate-level primitives like and or or.

Let's look at the mux example once again using data-flow modeling.

module mux2to1 (input a, b, sel, output y);

assign y = sel ? a : b; // Using conditional assignment (data-flow construct)

endmodule

Procedural Blocks

Behavioral modeling in Verilog heavily relies on procedural blocks, mainly:

  • always : Repeated execution on events
  • initial : Executes once at simulation start

Example

module simple_behavioral;
  reg a, b, y;

  initial begin
    a = 0; b = 1;
    #10 a = 1; b = 0;
  end

  always @(*) begin
    y = a & b;
  end
endmodule

Here:

  • initial provides stimulus (simulation only)
  • always models combinational logic

Conditional Statements

Verilog supports if-else and case constructs similar to high-level programming languages.

If-Else

always @(*) begin
  if (sel == 0)
    y = a;
  else
    y = b;
end

Case

always @(*) begin
  case (sel)
    2'b00: y = a;
    2'b01: y = b;
    2'b10: y = c;
    2'b11: y = d;
  endcase
end
  • Use case for multi-way branching.
  • Use casez or casex to handle don’t-care (z or x) values.

Loops

Verilog supports procedural loops for repetitive operations.

Loop Type Description Example
for Iterative loop with counter for(i=0;i<8;i=i+1)
while Loop while condition true while(a < 10)
repeat Loop fixed number of times repeat(5)
forever Infinite loop (simulation) forever #5 clk = ~clk;

For Loop

integer i;
always @(*) begin
  sum = 0;
  for (i = 0; i < 8; i = i + 1)
    sum = sum + data[i];
end

Verilog’s for loop is not iterative in time like in software languages such as C—it executes within zero simulation time when used in procedural blocks (always or initial). This means the loop simply repeats statements during simulation elaboration or within a single time step. To create time-based iteration, delays (#10, for example) must be explicitly added inside the loop.

When used in synthesizable RTL, for loops are unrolled by the synthesizer. Each iteration represents a piece of replicated hardware rather than a sequential operation. Therefore, loop bounds must be static (known at compile time), or synthesis will fail. For example:

for (i = 0; i < 8; i = i + 1)
  assign y[i] = a[i] & b[i];

Another key point is the loop index variable—it must be declared as an integer or genvar depending on context. In procedural code, integer is fine; in generate blocks, genvar is required for synthesis.

Nested for loops are allowed as long as they loop boundaries are well defined.

While Loop

In simulation, the while loop is evaluated continuously within the current simulation time step. This means it can easily cause infinite loops if the condition never becomes false. For example:

integer i = 0;
while (i < 10) begin
  $display("i = %0d", i);
  i = i + 1;
end

executes ten times instantly (without advancing time), since no delay (#) is used inside the loop. To model time progression, explicit delays must be added:

while (i < 10) begin
  #10 i = i + 1;
end

From a synthesis perspective, while loops are typically not synthesizable unless the loop iteration count is determinable at compile time. Synthesis tools require fixed iteration bounds to map loops into hardware. If the termination condition depends on runtime signals, the synthesizer cannot predict how many hardware copies to generate, causing errors. Hence, for hardware description, for loops or generate blocks are preferred over while.

Repeat Loop

The repeat loop in Verilog is a convenient control structure used to execute a block of code a fixed number of times. Unlike for or while loops, the repeat statement does not require explicit initialization or update expressions—only a single integer expression specifying how many times to iterate. For example:

integer i = 0;
repeat (5) begin
  #10 i = i + 1;
  $display("Iteration %0d, Time=%0t", i, $time);
end

This loop executes five times, incrementing i with a 10-time unit delay between iterations.

From a synthesis standpoint, repeat loops are generally non-synthesizable, as they are primarily intended for simulation and verification.

Forever Loop

The forever loop in Verilog is a special looping construct that repeats a block of code indefinitely until the simulation ends or an external event interrupts it. For example:

forever begin
  #5 clk = ~clk;
end

This snippet is one of the most common uses of the forever loop—it continuously toggles a clock signal every 5 time units, effectively generating a free-running clock for simulation.

Since it represents endless behavior, the forever loop is not synthesizable.

Sequential Logic using Behavioral Modeling

Sequential circuits depend on clock events and previous states.

Example: D Flip-Flop (Behavioral)

module dff (input clk, d, reset, output reg q);
  always @(posedge clk or posedge reset) begin
    if (reset)
      q <= 0;
    else
      q <= d;
  end
endmodule

Combinational Logic using Behavioral Modeling

Combinational logic produces output based only on current inputs.

Example: 4-to-1 Multiplexer

always @(*) begin
  case (sel)
    2'b00: y = a;
    2'b01: y = b;
    2'b10: y = c;
    2'b11: y = d;
  endcase
end
  • Uses blocking assignment (=) for immediate updates
  • Sensitivity list uses @(*) to include all signals automatically

Tasks and Functions

Tasks and functions allow modular design and code reuse in behavioral models.

Task

  • Can have multiple outputs
  • Can include time controls
task display_values;
  input [7:0] data;
  begin
    $display("Time=%0t, Data=%b", $time, data);
  end
endtask

Call inside an always block:

always @(*) display_values(a);

Function

  • Returns a single value
  • Cannot include delays or event controls
function [7:0] add;
  input [7:0] a, b;
  begin
    add = a + b;
  end
endfunction

Call:

y = add(a, b);

More details about task and function : Task and Function

Behavioral Modeling Examples

Example 1: Counter

module counter #(parameter WIDTH=4) (
  input clk, reset, enable,
  output reg [WIDTH-1:0] count
);
  always @(posedge clk or posedge reset) begin
    if (reset)
      count <= 0;
    else if (enable)
      count <= count + 1;
  end
endmodule

Example 2: Simple ALU

module alu (
  input [3:0] a, b,
  input [2:0] sel,
  output reg [4:0] y
);
  always @(*) begin
    case (sel)
      3'b000: y = a + b;
      3'b001: y = a - b;
      3'b010: y = a & b;
      3'b011: y = a | b;
      3'b100: y = a ^ b;
      default: y = 0;
    endcase
  end
endmodule

Example 3: Clock generator for simulation

module tb;
  reg clk;

  initial clk = 0; // Initializing the clock at time t=0

  always #5 clk = ~clk; // Using always and delay, changing clock state every 5ns.

  initial begin
    $monitor("At t = %0d, clk = %0b", $time, clk);
    #50 $finish;
  end

endmodule

Behavioral Modeling in Testbenches

Behavioral modeling is also the foundation for testbench design. You can describe stimulus, monitor responses, and verify functionality without hardware details.

module tb_alu;
  reg [3:0] a, b;
  reg [2:0] sel;
  wire [4:0] y;

  alu uut (.a(a), .b(b), .sel(sel), .y(y));

  initial begin
    a = 4'd5; b = 4'd3;
    sel = 3'b000; #10;
    sel = 3'b001; #10;
    sel = 3'b010; #10;
    $finish;
  end

  initial begin
    $monitor("Time=%0t sel=%b a=%d b=%d y=%d", $time, sel, a, b, y);
  end
endmodule

Synthesizable vs Non-Synthesizable Behavioral Code

Feature Synthesizable Non-Synthesizable
if, case, for Yes Yes
# delays No Yes
$display, $monitor No Yes
initial FPGA only Yes
always @(posedge clk) Yes Yes
File operations No Yes

Synthesizable behavioral code can be converted into real hardware logic by synthesis tools. Non-synthesizable constructs are used for simulation and verification only. More details about Synthesizable and Non-synthesizable code.

Advantages of Behavioral Modeling

  1. High-level abstraction – Easier to understand and debug
  2. Faster development – Quicker prototyping and testing
  3. Compact code – Less verbose than gate-level descriptions
  4. Supports parameterization – Easy to scale and modify
  5. Reusability – Modular design via tasks and functions

Summary

Aspect Description Example
Procedural Blocks always, initial always @(*) y = a & b;
Conditional if, case if(sel) y=b; else y=a;
Loops for, while, repeat for(i=0;i<8;i=i+1)
Sequential Uses clock always @(posedge clk)
Combinational Uses sensitivity list always @(*)
Tasks/Functions Reusable code task display(); ... endtask
Simulation Tools $display, $monitor For debugging