Funvizeo logo
  • Contents
      • Back
      • Verilog
      • SystemVerilog
      • UVM
      • Digital Basics
      • Verification
Most Popular
Verification
  Testbench Evolution
  Constraint Random Verification
  Verification Techniques
  Verification Plan
  Code Coverage

Verilog
  Data Types
  Basic Constructs
  Behavioral Modeling
  Gate Modeling
  Simulation Basics
  Design Examples
  Interview Questions

SystemVerilog
  Data Types
  Class
  Interface
  Constraints and more!
  Testbench Examples
  Interview Questions

UVM
  Sequences
  Testbench Components
  TLM Tutorial
  Register Model Tutorial
  Testbench Examples
  Interview Questions

Digital Fundamentals
  Binary Arithmetic
  Boolean Logic
  Karnaugh Maps
  Combinational Logic
  Sequential Logic

SystemVerilog Immediate Assertions

Immediate assertions are executed based on simulation event semantics and are required to be specified in a procedural block. It is treated the same way as the expression in a if statement during simulation.

The immediate assertion will pass if the expression holds true at the time when the statement is executed, and will fail if the expression evaluates to be false (X, Z or 0). These assertions are intended for use in simulation and is not suitable for formal verification. It can be used in both RTL code and testbench to flag errors in simulations.

Syntax


// Simple assert statement	
assert(<expression>);

// Assert statement with statements to be executed for pass/fail conditions
assert(<expression>) begin
	// If condition is true, execute these statements
end else begin
	// If condition is false, execute these statements
end

// Optionally give name for the assertion
[assert_name] : assert(<expression>);

Immediate Assertion in Design

Here is an example where the design has an immediate assertion to check that a push request to the FIFO does not come at a time when the FIFO is already full. If the expression within the assert statement evaluates to true, the first begin end block will be executed and if the expression evaluates to false, the else part will be evaluated. This is much like the if construct, with the difference that it is not required for the user to place display statement to flag the error.


module my_des (my_if _if);
  
  always @ (posedge _if.clk) begin
    if (_if.push) begin
    	// Immediate assertion and ensures that
    	// fifo is not full when push is 1
    	a_push: assert (!_if.full) begin
      		$display("[PASS] push when fifo not full");
    	end else begin
      		$display("[FAIL] push when fifo full !");
    	end
  	end
    
    if (_if.pop) begin
    	// Immediate assertion to ensure that fifo is not 
    	// empty when pop is 1
    	a_pop: assert (!_if.empty) begin
      		$display ("[PASS] pop when fifo not empty");
    	end else begin
      		$display ("[FAIL] pop when fifo empty !");
    	end
    end
  end
endmodule

Without such immediate assertions, one would need to duplicate the logic leading to that particular point using concurrent assertions that can take extra effort and resources.


interface my_if(input bit clk);
  logic pop;
  logic push;
  logic empty;
  logic full;
endinterface

module tb;
  bit clk;
  always #10 clk <= ~clk;
  
  my_if _if (clk);  
  my_des u0 (.*);
  
  initial begin
    for (int i = 0; i < 5; i++) begin
      _if.push  <= $random;
      _if.pop   <= $random;
      _if.empty <= $random;
      _if.full  <= $random;
      $strobe("[%0t] push=%0b full=%0b pop=%0b empty=%0b",
              $time, _if.push, _if.full, _if.pop, _if.empty);
      @(posedge clk);
    end
    #10 $finish;
  end             
endmodule

See that the time and line of assertion failure is displayed as *E .

 Simulation Log
ncsim> run
[0] push=0 full=1 pop=1 empty=1
ncsim: *E,ASRTST (./design.sv,13): (time 10 NS) Assertion tb.u0.a_pop has failed 
[FAIL] pop when fifo empty !
[10] push=1 full=0 pop=1 empty=1
[PASS] push when fifo not full
ncsim: *E,ASRTST (./design.sv,13): (time 30 NS) Assertion tb.u0.a_pop has failed 
[FAIL] pop when fifo empty !
[30] push=1 full=1 pop=1 empty=0
ncsim: *E,ASRTST (./design.sv,5): (time 50 NS) Assertion tb.u0.a_push has failed 
[FAIL] push when fifo full !
[PASS] pop when fifo not empty
[50] push=1 full=0 pop=0 empty=1
[PASS] push when fifo not full
[70] push=1 full=1 pop=0 empty=1
ncsim: *E,ASRTST (./design.sv,5): (time 90 NS) Assertion tb.u0.a_push has failed 
[FAIL] push when fifo full !
Simulation complete via $finish(1) at time 100 NS + 0
./testbench.sv:25     #10 $finish;
ncsim> exit

Immediate Assertion in Testbench

Assume a class called Packet is created and randomized. However this example has a constraint error and randomization will fail. But, the failure will be displayed as a warning message and if the user is not careful enough the test may display incorrect behavior and may even appear to pass.


class Packet;
  rand bit [7:0] addr;
  
  constraint c_addr { addr > 5; addr < 3; }
endclass

module tb;
  initial begin
    Packet m_pkt = new();
    
    m_pkt.randomize();
  end
endmodule
 Simulation Log
ncsim> run
    m_pkt.randomize();
                  |
ncsim: *W,SVRNDF (./testbench.sv,11|18): The randomize method call failed. The unique id of the failed randomize call is 0.
Observed simulation time : 0 FS + 0
ncsim: *W,RNDOCS: These constraints contribute to the set of conflicting constraints:

  constraint c_addr { addr > 5; addr < 3; } (./testbench.sv,4)
ncsim: *W,RNDOCS: These variables contribute to the set of conflicting constraints:

rand variables:
       addr [./testbench.sv, 2]

ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit

Instead an immediate assertion can be placed on the randomization method call to ensure that the return value is always 1, indicating the randomization is successful. If the assertion fails, it prompts the user to first look at the failure thereby reducing debug efforts.


class Packet;
  rand bit [7:0] addr;
  
  constraint c_addr { addr > 5; addr < 3; }
endclass

module tb;
  initial begin
    Packet m_pkt = new();
    
    assert(m_pkt.randomize());
  end
endmodule

Simulator assigns a generated name for the assertion if the user has not specified one.

 Simulation Log
ncsim> run
    assert(m_pkt.randomize());
                         |
ncsim: *W,SVRNDF (./testbench.sv,11|25): The randomize method call failed. The unique id of the failed randomize call is 0.
Observed simulation time : 0 FS + 0
ncsim: *W,RNDOCS: These constraints contribute to the set of conflicting constraints:

  constraint c_addr { addr > 5; addr < 3; } (./testbench.sv,4)
ncsim: *W,RNDOCS: These variables contribute to the set of conflicting constraints:

rand variables:
       addr [./testbench.sv, 2]

ncsim: *E,ASRTST (./testbench.sv,11): (time 0 FS) Assertion tb.unmblk1.__assert_1 has failed 
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit

In a similar way, assert can be used with any expression that evaluates to true or false within a procedural block.

UVM Pool

uvm_pool implements a parameterized, class-based dynamic associative array and can be allocated on demand, and stored by reference. This also can return handle to a global pool which can then be used to share items between different verification components.

Declaration


class uvm_pool #(type KEY = int, T = uvm_void) extends uvm_object;

Read more: UVM Pool

SystemVerilog Clocking Blocks Part II

Clocking blocks allow inputs to be sampled and outputs to be driven at a specified clock event. If an input skew is mentioned for a clocking block, then all input signals within that block will be sampled at skew time units before the clock event. If an output skew is mentioned for a clocking block, then all output signals in that block will be driven skew time units after the corresponding clock event.

Read more: SystemVerilog Clocking Blocks Part II

SystemVerilog fork join_any

In a simple SystemVerilog fork join, the main thread waits until all the child threads have finished execution. This means the fork will hang the simulation if any of the child threads run forever and never complete. SystemVerilog also provides a variation to the original with a fork and join_any.

A fork and join_any will allow the main thread to resume execution of further statements that lie after the fork, if any one of the child threads complete. If five threads are launched, the main thread will resume execution only when any one of the five threads finish execution. Once the main thread resumes operation, the remaining four threads that were launched will continue to run in the background.

Syntax


	fork
		// Thread 1
		// Thread 2
		// ...
		// Thread N
	join_any

fork join_any Example


module tb;
	initial begin
      $display ("[%0t] Main Thread: Fork join going to start", $time);
		fork
          print (20, "Thread1_0");
          print (30, "Thread1_1");
          print (10, "Thread2");              
        join_any
      $display ("[%0t] Main Thread: Fork join has finished", $time);
	end
  
  // Note that this task needs to be automatic
  task automatic print (int _time, string t_name);
    #(_time) $display ("[%0t] %s", $time, t_name);
  endtask
endmodule
 Simulation Log
ncsim> run
[0] Main Thread: Fork join going to start
[10] Thread2
[10] Main Thread: Fork join has finished
[20] Thread1_0
[30] Thread1_1
ncsim: *W,RNQUIE: Simulation is complete.

Nested fork join_any


module tb;
	initial begin
      $display ("[%0t] Main Thread: Fork join going to start", $time);
		fork
			fork
              print (20, "Thread1_0");
              print (30, "Thread1_1");
            join_any
          print (10, "Thread2");              
        join_any
      $display ("[%0t] Main Thread: Fork join has finished", $time);
	end
  
  // Note that this task has to be automatic
  task automatic print (int _time, string t_name);
    #(_time) $display ("[%0t] %s", $time, t_name);
  endtask
endmodule
 Simulation Log
ncsim> run
[0] Main Thread: Fork join going to start
[10] Thread2
[10] Main Thread: Fork join has finished
[20] Thread1_0
[30] Thread1_1
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit

SystemVerilog foreach loop

SystemVerilog arrays are data structures that allow storage of many values in a single variable. A foreach loop is only used to iterate over such arrays and is the easiest and simplest way to do so.

Syntax

The foreach loop iterates through each index starting from 0. If there are multiple statements within the foreach loop, they have to be enclosed with begin and end keywords like all other procedural blocks.


	foreach(<variable>[<iterator>])
		// Single statement
		
	foreach(<variable>[<iterator>]) begin
		// Multiple statements
	end

Example #1: Single dimensional Arrays


module tb;
	int array[5] = '{1, 2, 3, 4, 5};
	int sum;
	
	initial begin
		// Here, "i" is the iterator and can be named as anything you like
		// Iterate through each element from index 0 to end using a foreach
		// loop.
		foreach (array[i])
			$display ("array[%0d] = %0d", i, array[i]);
		
		// Multiple statements in foreach loop requires begin end
		// Here, we are calculating the sum of all numbers in the array
		// And because there are 2 statements within foreach there should
		// be a begin-end
		foreach (array[l_index]) begin
			sum += array[l_index];
			$display ("array[%0d] = %0d, sum = %0d", l_index, array[l_index], sum);
		end
	end
endmodule
 Simulation Log
ncsim> run
array[0] = 1
array[1] = 2
array[2] = 3
array[3] = 4
array[4] = 5
array[0] = 1, sum = 1
array[1] = 2, sum = 3
array[2] = 3, sum = 6
array[3] = 4, sum = 10
array[4] = 5, sum = 15
ncsim: *W,RNQUIE: Simulation is complete.

Note that foreach is just a shorter version to the following for loop:


	for (int i = 0; i < $size(array); i++) begin
		// Statements inside the for loop
	end

Example #2: Multidimensional Arrays


module tb;
  int md_array [5][2] = '{'{1,2}, '{3,4}, '{5,6}, '{7,8}, '{9,10}};
	
	initial begin
      // First iterate through the first dimension using "i"
      foreach (md_array[i])
        // For each element in first dimension "i", iterate through the
        // second dimension using "j"
        foreach (md_array[i][j])
          $display("md_array[%0d][%0d] = %0d", i, j, md_array[i][j]);      
	end
endmodule
 Simulation Log
ncsim> run
md_array[0][0] = 1
md_array[0][1] = 2
md_array[1][0] = 3
md_array[1][1] = 4
md_array[2][0] = 5
md_array[2][1] = 6
md_array[3][0] = 7
md_array[3][1] = 8
md_array[4][0] = 9
md_array[4][1] = 10
ncsim: *W,RNQUIE: Simulation is complete.

Click here to learn more about other loops in SystemVerilog !

  1. SystemVerilog for loop
  2. Verilog Testbench Simulation
  3. Verilog Display Tasks
  4. Verilog Syntax
  5. Verilog Blocking & Non-Blocking

Page 30 of 68

  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
Interview Questions
  Verilog Interview Set 1
  Verilog Interview Set 2
  Verilog Interview Set 3
  Verilog Interview Set 4
  Verilog Interview Set 5

  SystemVerilog Interview Set 1
  SystemVerilog Interview Set 2
  SystemVerilog Interview Set 3
  SystemVerilog Interview Set 4
  SystemVerilog Interview Set 5

  UVM Interview Set 1
  UVM Interview Set 2
  UVM Interview Set 3
  UVM Interview Set 4
Related Topics
  Digital Fundamentals
  Verilog Tutorial

  Verification
  SystemVerilog Tutorial
  UVM Tutorial
Latest in Verilog
  • Verilog $random
  • Verilog VCD Dump
  • Verilog VCD
  • Verilog Namespace
  • Verilog $stop $finish
Latest in SystemVerilog
  • SystemVerilog `define Macro
  • SystemVerilog Callback
  • SystemVerilog Interview Questions Set 10
  • SystemVerilog Interview Questions Set 9
  • SystemVerilog Interview Questions Set 8
Latest in UVM
  • UVM Callback
  • UVM Singleton Object
  • UVM Component [uvm_component]
  • UVM Object [uvm_object]
  • UVM Root [uvm_root]
© 2025 Funvizeo
Terms and Conditions