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 .
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
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.
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
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;
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.
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
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
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 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
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
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 !