Maker.io main logo

Behavioral Modeling in Verilog - Part 19

10

2025-08-11 | By DWARAKAN RAMANATHAN

Introduction:

Behavioral modeling in Verilog describes circuit behavior at high abstraction levels using high-level structures similar to high-level languages like C. Structural or dataflow modeling is hardware, but not functionality. Behavioral modeling allows designers to describe circuits at a level of abstraction using variables, conditions, and loops to describe how signals flow through the design. Procedural blocks, like Always and Initial, are a key part of behavioral modeling. The blocks control how the system responds to control inputs and changing signals. For example, an always block is employed to represent flip-flops, counters, and state machines, and an initial block is typically employed for simulation-specific activities like initializing values or printing debugging messages.

To add variables to behavioral modeling, we predominantly employ reg and integer data types. reg type retains data from clock cycle to clock cycle and therefore is a significant part in sequential logic, and integer is easy to work with for loop counters and arithmetic. Behavioral modeling is robust in the sense that it can add decision-making and iteration features and thus make designs readable and manageable.

Block vs Non-Blocking Assignments

In Verilog, one of the most important things when it comes to behavioral modeling is to understand the Blocking and Non-blocking assignments. Whether an assignment is Blocking (=) or Non-Blocking (<=) completely determines the behavior and the order of execution of simulation.

The blocking assignments will execute immediately within the same simulation cycle and in a top-down manner. Which means that if a next statement is there in the same always block, then the next statement will not proceed until the current one completes.

Copy Code
always @(posedge clk) begin
 a = b + c; // Blocking assignment
 d = a * 2; // Executes after 'a' is updated 
end

In contrast to blocking assignments, non-blocking assignments don’t update the value immediately, but they are scheduled to take effect at the end of the simulation cycle. For sequential logic and flip-flops, this is the preferred approach since it does not leave any chance of unintended race conditions and ensures that all assignments within a clock cycle occur simultaneously.

Copy Code
always @(posedge clk) begin
 a <= b + c; // Non-blocking assignment
 d <= a * 2; // Uses previous value of 'a' 
end

If a=b+c and b+c updates a, d will use the old value of a as non-blocking assignments postpone their updates. For proper functioning of a sequential circuit, it is recommended to use <= in flip-flop based designs and = for combinational logic inside an always @(*) block.

Sequential vs Parallel Execution:

SystemVerilog supports parallel and sequential execution. Let's look at an example to understand them.

Sequential execution is when one statement gets executed at a time inside a begin-end block. Sequential execution can be useful to model current State Machines, Counters, and any circuits where the order of execution matters each and every time.

Copy Code
always @(posedge clk) begin
 a = b + c;
 d = a * 2; // This executes only after 'a' is assigned 
end

Parallel execution is when multiple always blocks will be executing concurrently, or values are being assigned by a different always block. This example is the best example to refer to if you get confused: updating b and c in parallel.

Copy Code
always @(posedge clk) a = b + c;
always @(posedge clk) d <= a * 2;

In the above example, a and d update independently, allowing for parallel processing. This concept is fundamental in designing pipelined and multi-threaded hardware architectures.

Control Flow Constructs:

With behavioral modeling, control flow constructs come into play, and hence Verilog starts looking a lot like other conventional programming languages, with respect to flexibility.

If-else Statements

This feature of a typical programming language allows some conditional execution based on the values of the signal, and it's a bitter fact. The following examples give you a clear idea of how If-else statements are used.

Copy Code
always @(posedge clk) begin
 if (enable) 
out <= data;
else
 out <= 0; 
end

Case Statements

The case statement is used when there are multiple things to do, and a single thing has to be selected. It's widely useful in Multiplexers and state machines.

Copy Code
always @(posedge clk) begin
 case(sel)
 2'b00: out = in0;
 2'b01: out = in1;
 2'b10: out = in2;
 2'b11: out = in3;
 default: out = 0;
 endcase
 end

Verilog Loop Constructs

Verilog also provides several different loop constructs, such as for, while, repeat, and forever, that are useful when we want to specify repeated behavior for hardware description or testbench.

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

While loop runs until some condition becomes true, repeat runs a defined number of times. The forever statement will be executed endlessly and is typically controlled by other mechanisms, like disable, wait, etc., in testbenches.

Event-Driven Simulation and Verilog Non-Hardware Constructs

Verilog simulations are event-driven by nature, meaning a change in signal values causes events to be triggered, which in turn causes blocks of code to be run (like always or initial). This simulation strategy makes the simulation process generally more efficient than other methodologies. Instead of re-running the entire simulation over again at each time step, only those parts of the design that have changed are re-evaluated. The initial block is the perfect place for all your testbench and non-synthesizable code. It executes once at the beginning of the simulation. The initial block in Verilog's procedural part is just like main () in C. When the simulation starts, initial blocks will be executed to model some initial events that must have occurred before time 0. You can apply test vectors and print output using various delay functions, like #, and also show debugging information using the $Display and $Monitor.

Verilog Initial block

Here we have a simple testbench leveraging the idea of Event-Driven and Non-Hardware Constructs:

Copy Code
module test;
reg clk, reset;
initial begin
clk = 0;
forever #5 clk = ~clk; // Clock toggles every 5 time units
end
initial begin
reset = 1;
#15 reset = 0;
#100 $finish;
end
initial begin
$monitor("Time = %0t, clk = %b, reset = %b", $time, clk, reset);
end
endmodule

These constructs (initial, #, $monitor) are extensions to behavioral modeling in use only for simulation, testing, and debugging purposes. Since they are not eligible for physical implementation (synthesizable), that means they won’t be translated into a hardware block. They are vital validation and verification activities, too.

Optimizing Simulation Performance

When our design is more complex and larger, one important issue is how to make the simulation faster and save more time when we are using behavioral modeling. Below are some points that may increase the performance of the simulation.

  • Remove unnecessary always blocks. Combining logic is possible to reduce sensitivity list complexity.
  • Use @(*) in combinatorial logic. Makes the simulator only evaluate when needed.
  • Avoid using delays (#) Synthesizable logic unnecessarily. Signals are needed in testbenches for time delays, but they are overhead in terms of final designs.
  • Break your testbench into more functions and tasks so that any test procedure can be reused easily.
  • To avoid the condition that will create spikes in the output, it is recommended to put a default: case on floating or undefined conditions on any specific output signal.

Conclusion:

Behavioral modeling in Verilog is a powerful way to design digital circuits at a high level of abstraction. It allows the creation of efficient, synthesized models and robust testbenches by utilizing procedural blocks for combinational and sequential execution, event-driven simulation, and non-hardware constructs, like initial and $display. Having a grasp of the principles of parallelism, correct types of assignment, and simulation optimization allows for faster development cycles with less design bugs. From implementing a processor to debugging a simple counter, behavioral modeling makes the HDL experience more intuitive and productive.

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.