SVTB
SVTB
SVTB
tb.sv
or
vlogan -sverilog
-f tb.list
Compile the design and testbench files using -ntb option to create
a simv.
For example:
vlogan -sverilog cpu.v tb.sv tb_util.sv
vhdlan mypackage.vhd
vhdlan tb_wrapper.vhd
A VHDL-Top
First, analyze VHDL, Verilog and SystemVerilog source code files in
a bottom-up approach. Use vhdlan for VHDL files, vlogan for Verilog
files and vlogan -sverilog for SystemVerilog files.
For example:
vlogan -sverilog tb.sv tb_util.sv
vhdlan mypackage.vhd
vhdlan tb_wrapper.vhd
vhdlan cpu.vhd top.vhd
Example:
scs -mhdl design_cfg
or
scs -mhdl TOP
Verilog-Top
The first steps are identical to a Verilog-top flow. Analyze all source
files in bottom-up order.
Example:
vlogan
vhdlan
vhdlan
vhdlan
-f filename
+define+macro_name=value
+incdir+directory_name
+libext+ext
-y directory_name
-timescale=time_unit/time_precision
Runtime Options
There are runtime options that were developed for OpenVera
testbenches that also work with SystemVerilog testbenches.
+ntb_random_seed=integer
Sets the seed value used by the top level random number
generator at the start of simulation. This option does not work for
the Verilog $random(seed) system function.
+ntb_solver_mode=1|2
Specifies the constraint solver mode for the randomize()
method:
1
The solver spends more pre-processing time in analyzing the
constraints, during the first call to randomize() on each class.
Subsequent calls to randomize() on that class are very fast.
2
The solver does minimal pre-processing, and analyzes the
constraint in each call to randomize(). Default is 2.
The randomize() method is described in Randomize Methods
on page 1-84.
+ntb_enable_solver_trace=0|1|2
Specifies the debugging mode when VCS executes the
randomize() method:
0
Disables tracing.
1
Enables tracing. This is the default.
2
Enables tracing with more verbose messages.
The randomize() method is described in Randomize Methods
on page 1-84.
+ntb_enable_solver_trace_on_failure[=0|1|2]
Enables a mode that displays trace information only when the
constraint solver fails to compute a solution, usually due to
inconsistent constraints.
0
Disables tracing.
1
Enables tracing. This is the default. This argument is the default
argument when you enter this option without an argument.
2
Enables tracing with more verbose messages and the analysis
narrows down to the smallest set of inconsistent constraints,
thus aiding the debugging process. This option with the 2
argument is the default condition when you dont enter this
option.
getc()
Returns the numerically specified character in the string.
bit1=string_name.getc(0);
toupper()
Returns a string with the lower case characters converted to upper
case.
string string1 = "abc";
string string2 = string1.toupper;
initial
begin
$display("string1 is \"%s\"",string1);
$display("string2 is \"%s\"",string2);
end
tolower()
Similar to the toupper method, this method returns a string with the
upper case characters converted to lower case.
compare() and icompare()
Compares strings and returns 0 if they match, and a value less than
0 or more than zero, depending on the order of the strings, if they
dont match. The icompare method doesnt see a difference between
upper case and lower case.
string
string
string
string
string1
string2
string3
string4
=
=
=
=
"abc";
"abc";
"xyz";
"ABC";
initial
begin
if (string1.compare(string2) == 0)
substr()
Returns a substring of the specified string. The arguments specify
the numbers of the characters in the specified string that begin and
end the substring.
string string1 = "abcdefgh";
string string2;
initial
begin
string2 = string1.substr(1,5);
$display("string2 = %s",string2);
end
=
=
=
=
=
x at 0
10 at 10
16 at 20
8 at 30
2 at 40
atoreal()
Returns a real number that is the decimal value of a string.
module m;
real r1;
string string1 = "1235/x0090";
initial
begin
r1 = string1.atoreal;
$display("r1 = 0%f",r1);
end
endmodule
itoa()
Stores the ASCII decimal representation of an integer in a string.
reg [63:0] r1 = 456;
string string1;
initial
begin
string1.itoa(123);
if (string1 == "123")
$display("string1 %s",string1);
string1.itoa(r1);
if (string1 == "456")
$display("string1 %s",string1);
end
hextoa()
hextoa(arg) returns the ASCII hexadecimal representation of the arg.
octtoa()
octtoa(arg) returns the ASCII octal representation of the arg.
bintoa()
bintoa(arg) returns the ASCII binary representation of the arg.
realtoa()
SystemVerilog Testbench Constructs
1-12
This example assigns the index 14 to integer i and prints out 14.
match()
The postmatch() method returns the string that is located just after
the string found by the last match() function call. The syntax is:
string string_variable.postmatch();
This example checks the Perl expressions given by string patt with
string str. It assigns the value 1234 to string str1 because of the
match to the expression [0-9]+. It assigns the value is a number.
to string str2 because of the match to the expression [a-zA-Z .]+.
Any number of additional Perl expressions can be listed in the patt
definition, and then called using sequential index numbers with the
backref() function.
Program Blocks
A program block contains the testbench for a design. In the default
implementation of SystemVerilog testbench constructs, all these
constructs must be in one program block. Multiple program blocks is
an LCA feature.
Requiring these constructs in a program block help to distinguish
between the code that is the testbench and the code that is the design.
Program blocks begin with the keyword program, followed by a name
for the program, followed by an optional port connection list, followed
by a semi colon (;). Program blocks end with the keyword
endprogram, for example:
program prog (input clk,output logic [31:0] data, output
logic ctrl);
logic dynamic_array [];
logic assoc_array[*];
int intqueue [$] = {1,2,3};
class classA;
function void vfunc(input in1, output out1);
.
.
.
endfunction
.
.
.
endclass
semaphore sem1 =new (2);
mailbox mbx1 = new();
reg [7:0] reg1;
covergroup cg1 @(posedge clk);
bit clk = 0;
logic [31:0] data;
logic ctrl;
module clkmod;
.
.
.
prog prog1 (clk,data,ctrl); // instance of the program
.
.
.
endmodule
class definitions
semaphores
SystemVerilog Testbench Constructs
1-19
mailboxes
concurrent assertions
coverage groups
l1 = 1 at 20
l1 = 1 at 40
simulation ends at 50
The final block executes in the last simulation time step at time 50.
A final block is the opposite of an initial block in that an initial block
begins execution in the first simulation time step and a final block
executes in the last simulation time step. Apart from the execution
time there are other important differences in a final block. A final
block is like a user-defined function call in that it executes in zero
simulation time and cannot contain the following:
delay specifications
event controls
wait statements
Arrays
Dynamic Arrays
Dynamic arrays are unpacked arrays with a size that can be set or
changed during simulation. The syntax for a dynamic array is as
follows:
data_type name [];
logic
longint
string
reg
shortint
class
byte
integer
enum
logicDA = new[100];
end
.
.
.
endprogram
= %0d",lDA1.size);
%0d", lDA1[1]);
%0d", lDA1[0]);
= %0d",lDA1.size);
VCS displays:
lDA1 size = 0lDA1[1] = 1
lDA1[0] = 0
lDA1 size = 2
lDA1[0]=0;
lDA2=lDA1;
$display("lDA2[1] = %0d", lDA2[1]);
$display("lDA2[0] = %0d", lDA2[0]);
$display("lDA2 size = %0d",lDA2.size);
end
endprogram
= 0
1
0
= 2
Associative Arrays
An associative array has a lookup table for the elements of its declared
data type. Its index is a data type which serves as the lookup key for
the table. This index data type also establishes an order for the
elements.
The syntax for declaring an associative array is as follows:
data_type array_id [index_type];
data_type array_id [* | string];
Where:
data_type
array_id
Wildcard Indexes
You can enter the wildcard character as the index.
data_type array_id [*];
Using the wildcard character permits entering any integral data type
as the index. Integral data types represent an integer (shortint,
int, longint, byte, bit, logic, reg, integer, and also packed
structs, packed unions, and enum.
program m;
bit [2:0] AA1[*];
int int1;
logic [7:0] log1;
initial begin
int1 = 27;
log1 = 42;
AA1[456] = 3'b101;
AA1[int1] = 3'b000;
AA1[log1] = 3'b111;
end
endprogram
// index is 27
// index is 42
String Indexes
A string index specifies that you can index the array with a string. You
specify a string index with the keyword string.
program p;
logic [7:0] a[string];
string string_variable;
initial begin
a["sa"] = 8;
a["bb"] = 15;
a["ec"] = 29;
a["d"] = 32;
a["e"] = 45;
a[string_variable] = 1;
end
endprogram
delete
Removes all entries from an array. If you specify an index, this
method removes the entry specified by the index.
exists
Returns a 1 if the specified entry exists.
first
Assigns the value of the smallest or alphabetically first entry in
the array. Returns 0 if the array is empty and returns 1 if the array
contains a value.
last
Assigns the value of the largest or alphabetically last entry in the
array. Returns 0 if the array is empty and returns 1 if the array
contains a value.
next
Finds the entry whose index is greater than the given index. If
there is a next entry, the index variable is assigned the index of
the next entry, and the function returns 1. Otherwise, variable is
unchanged, and the function returns 0
prev
Finds the entry whose index is smaller than the given index. If
there is a previous entry, the index variable is assigned the index
of the previous entry, and the function returns 1. Otherwise,
variable is unchanged, and the function returns 0.
The following example shows how to use these methods.
program p;
logic [7:0] a[string];
string s_index;
initial begin
a["sa"] = 8;
a["bb"] = 15;
a["ec"] = 29;
a["d"] = 32;
a["e"] = 45;
$display("number of entries = %0d",a.num);
if(a.exists("sa"))
$display("string \"sa\" is in a");
if(a.first(s_index))
begin
$display("the first entry is
\"%s\"",s_index);
do
$display("%s :
%0d",s_index,a[s_index]);
while (a.next(s_index));
end
if(a.last(s_index))
begin
$display("the last entry is
\"%s\"",s_index);
do
$display("%s :
%0d",s_index,a[s_index]);
while (a.prev(s_index));
end
a.delete;
$display("number of entries = %0d",a.num);
end
endprogram
e : 45
d : 32
bb : 15
number of entries = 0
Queues
A queue is an ordered collection of variables with the same data type.
The length of the queue changes during simulation. You can read any
variable in the queue, and insert a value anywhere in the queue.
The variables in the queue are its elements. Each element in the
queue has a number: 0 is the number of the first, you can specify the
last element with the $ (dollar sign) symbol. The following are some
examples of queue declarations:
logic logque [$];
This is a queue of elements with the logic data type.
int intque [$] = {1,2,3};
This is a queue of elements with the int data type. These
elements are initialized 1, 2, and 3.
string strque [$] = {"first","second","third","fourth"};
This is a queue of elements with the string data type. These
elements are initialized "first", "second", "third", and
"fourth".
You assign the elements to a variable using the element number, for
example:
string s1, s2, s3, s4;
initial
begin
s1=strque[0];
s2=strque[1];
s3=strque[2];
s4=strque[3];
$display("s1=%s s2=%s s3=%s s4=%s",s1,s2,s3,s4);
.
.
.
end
You also assign values to the elements using the element number,
for example:
int intque [$] = {1,2,3};
initial
begin
intque[0]=4;
intque[1]=5;
intque[2]=6;
$display("intque[0]=%0d intque[1]=%0d intque[2]=%0d",
intque[0],intque[1],intque[2]);
.
.
.
end
Removing elements from a queue are not yet supported, for example:
strque = strque [1:$];
intque = intque[0:$-1];
Queue Methods
There are the following built-in methods for queues:
size
Returns the size of a queue.
program prog;
int intque [$] = {1,2,3};
initial
begin
for (int i = 0; i < intque.size; i++)
$display(intque[i]);
end
endprogram
insert
Inserts new elements into the queue. This method takes two
arguments: the first is the number of the element, the second is
the new value.
program prog;
string strque [$] = {"first","second","third","forth"};
initial
begin
for (int i = 0; i < strque.size; i++)
$write(strque[i]," ");
$display(" ");
strque.insert(1,"next");
strque.insert(2,"somewhere");
for (int i = 0; i < strque.size; i++)
$write(strque[i]," ");
$display(" ");
end
endprogram
delete
Removes an element from the queue, specified by element
number. If you dont specify an element number, this method
deletes all elements in the queue.
string strque [$] = {"first","second","third"};
initial
begin
for (int i =0; i<strque.size; i++)
$write(strque[i]," ");
$display(" ");
strque.delete(1);
for (int i =0; i<strque.size; i++)
$write(strque[i]," ");
end
pop_front
Removes and returns the first element of the queue.
string strque [$] = {"first","second","third"};
string s1;
initial
begin
$write("the elements of strque are ");
for (int i =0; i<strque.size; i++)
SystemVerilog Testbench Constructs
1-34
$write(strque[i]," ");
$display("\ns1 before pop contains %s ",s1);
s1 = strque.pop_front;
$display("s1 after pop contains %s ",s1);
$write("the elements of strque are ");
for (int i =0; i<strque.size; i++)
$write(strque[i]," ");
end
pop_back
Removes and returns the last element in the queue.
program prog;
string strque [$] = {"first","second","third"};
string s1;
initial
begin
");
%s ",s1);
",s1);
");
2
1
3
2
The bit data type array named bit_array1 has three dimensions.
The first foreach loop iterates through the first two dimensions
11:10 and 9:8, to assign 8-bit values to these elements.
The second foreach loop displays the deposited values.
The $display system task displays the following:
bit_array1
bit_array1
bit_array1
bit_array1
[11][9]=99
[11][8]=88
[10][9]=90
[10][8]=80
The foreach loop also works with other types of arrays, such as this
example of a string array:
module test;
string words [2];
initial
begin
words [1] = "verification";
words [0] = "simulation";
foreach (words [j])
$display("string element number %1b",j,
"contains \"",words[j],"\"");
end
endmodule
The foreach loop also works with queues and dynamic and
associative arrays. The following is an example with a dynamic
array:
program test;
integer fixed_int_array[3] = {0, 1, 2};
integer dynamic_int_array[];
initial
begin
dynamic_int_array=new[3](fixed_int_array);
foreach (dynamic_int_array[dim1])
$display("dynamic_int_array [%1d]=%0d",
dim1,dynamic_int_array[dim1]);
end
endprogram
Description
sum()
product()
and()
or()
xor()
if(i)
$display("Randomization Success");
else
$display("Randomization Fails");
end
endprogram
Notes
1. Empty array - If the array over which the aggregate operation is
defined has size 0, then the behavior is as follows:
- array.sum(): The expression is substituted by 0.
- array.product(): The expression is substituted by 1.
For all other aggregate methods there is a runtime error if
reference to the aggregate operation is in an ON constraint block.
2. Array types - The array aggregate methods and the foreach loop
support the fixed size array, dynamic array, associative array, and
SmartQ types of arrays in every context.
Classes
The user-defined data type, class, is composed of data members of
valid SystemVerilog data types (known as properties) and tasks or
functions (known as methods) for manipulating the data members.
The properties and methods, taken together, define the contents and
capabilities of a class instance (also referred to as an object). Use
the class and endclass keywords to declare a class.
class B;
int q = 3;
function int send (int a);
send = a * 2;
endfunction
task show();
$display("q = %0d", q);
endtask
endclass
} struct1;
struct1 mystruct;
typedef struct packed { int intt1; logic [31:0] logg1; }
pstruct1;
typedef union packed {
pstruct1 u_pstruct1;
} p_union1;
endclass
The above two steps can be merged into one for instantiating a class
at the time of declaration:
class_name
handle_name = new();
For example:
B b1 = new;
Constructors
You can declare your own new() method. In such a case, the prototype
that you must follow is:
function new([arguments]);
// body of method
endfunction
handle_name = new([arguments]);
When called, new() will print the value passed to it as the value of
command.
When a class property has been initialized in its declaration, the
user-defined new() constructor can be used to override the initialized
value. The following example is a variant of the first example in this
section.
program P;
class A;
int q = 3; //q is initialized to 3
function new();
q = 4; //constructor overrides & assigns 4
endfunction
endclass
A a1;
initial
begin
a1 = new;
$display("The value of q is %0d.", a1.q);
end
endprogram
SystemVerilog Testbench Constructs
1-45
Packet p1; creates a variable, p1, that can hold the handle of an
object of class Packet. The initial default value of p1 is null. The
object does not yet exist, and p1 does not contain an actual handle,
until an instance of type Packet is created as shown below:
p1 = new(); //if no arguments are being passed, () can be
//omitted. e.g., p1=new;
In this case, there is still only one object. This single object can be
referred to with either variable, p1 or p2.
Static Properties
The static keyword is used to identify a class property that is shared
with all instances of the class. A static property is not unique to a
single object (that is, all objects of the same type share the property).
A static variable is initialized once.
Syntax
static data_type variable;
Example
program test;
class Parcel;
static int count = 2;
function new();
count++;
endfunction
endclass
initial begin
Parcel pk1 = new;
Parcel pk2 = new;
Parcel pk3 = new;
$display("%d Parcel",
$display("%d Parcel",
$display("%d Parcel",
pk3.count);
pk2.count);
pk1.count);
end
endprogram
Output
5 Parcel
5 Parcel
5 Parcel
class Xvdata_Class;
const int pi = 31412;//const variable
endclass
Xvdata_Class data;
initial begin
data = new();
data.pi = 42; //illegal and will generate
//compile time error
end
endprogram
The line, data.pi=42, is not valid and yields the following compile
time error message:
Error-[IUCV] Invalid use of 'const' variable
Variable 'pi' declared as 'const' cannot be used
in this context
"global_const.sv", 17: data.pi = 42;
program prog;
typedef enum {X, Y} A;
class C;
typedef enum {S1,S2,S3,S4} E; //enum declared inside class
rand A a1;
rand E e1;
extern function f1();
constraint c;
endclass
//out of class body method declared with the help of ::
//operator
function C::f1();
int i;
$display("Accessing enum label in constraint block
through :: operator");
repeat (5)
begin
i = this.randomize();
if(i)
$display("e1 = %s", this.e1.name);
else
$display("Randomization Failed");
end
// accessing enum label through :: operator in out
// of class body method
$display("Accessing enum label in function block
through :: operator");
i = this.randomize with {e1 != C::S2;};
if(i)
$display("e1 = %s", this.e1.name);
else
$display("Randomization Failed");
endfunction
// out of block constraint declaration accessing enum label
// through :: operator
constraint C::c { a1 == X; e1 inside {C::S1,C::S2,C::S3}; }
C c1
= new;
initial begin
c1.f1();
end
endprogram
Class Extensions
Subclasses and Inheritance
SystemVerilogs OOP implementation provides the capability of
inheriting all the properties and methods from a base class, and
extending its capabilities within a subclass. This concept is called
inheritance. Additional properties and methods can be added to this
new subclass.
For example, suppose we want to create a linked list for a class
Packet. One solution would be to extend Packet, creating a new
subclass that inherits the members of the parent class (see Class
Packet Example on page 68 for class Packet declaration).
class LinkedPacket extends Packet;
LinkedPacket next;
function LinkedPacket get_next();
get_next = next;
endfunction
endclass
Abstract classes
A group of classes can be created that are all derived from a
common abstract base class. For example, we might start with a
common base class of type BasePacket that sets out the structure of
packets but is incomplete; it would never be instantiated. It is
abstract. From this base class, a number of useful subclasses can
be derived: Ethernet packets, token ring packets, GPSS packets,
satellite packets. Each of these packets might look very similar, all
needing the same set of methods, but they would vary significantly
in terms of their internal details.We start by creating the base class
that sets out the prototype for these subclasses. Since it will not be
instantiated, the base class is made abstract by declaring the class
as virtual:
virtual class BasePacket;
endfunction
endclass
class EtherPacket extends BasePacket;
function integer send(bit[31:0] data)
// body of the function
...
endfunction
endclass
Polymorphism
The ability to call a variety of functions using the exact same interface
is called polymorphism.
Suppose we have a class called Packet. Packet has a task called
display(). All the derived classes of Packet will also have a display()
task, but the base version of display() does not satisfy the needs of
the derived classes. In such a case we want the derived class to
implement its own version of the display() task to be called.
To achieve polymorphism the base class must be abstract (defined
using the virtual identifier) and the method(s) of the class must be
defined as virtual. The abstract class (see page 52 for definition of
abstract class) serves as a template for the construction of derived
classes.
virtual class Packet;
extern virtual task display();
endclass
This example illustrates how the two classes derived from Packet
implement their own specific version of the display() method.
All derived classes can be referenced by a base class object. When
a derived class is referenced by the base class, the base class can
access the virtual methods within the derived class through the
handle of the base class.
program polymorphism;
//include class declarations of Packet, MyPacket
//and YourPacket here
MyPacket mp;
YourPacket yp;
Packet p; //abstract base class
initial
begin
mp = new;
yp = new;
p = mp; // mp referenced by p
p.display();// calls display in MyPacket
p = yp;
p.display();// calls display in YourPacket
end
endprogram
Output:
super keyword
The super keyword is used from within a derived class to refer to
properties of the parent class. It is necessary to use super when the
property of the derived class has been overridden, and cannot be
accessed directly.
program sample;
class Base;
integer p;
virtual task display();
$display("\nBase: p=%0d", p);
endtask
endclass
class Extended extends Base;
integer q;
task display();
super.display(); //refer to Base "display()"
$display("\nExtended: q=%0d\n", q);
endtask
endclass
Base b1;
Extended e1;
initial begin
b1 = new; // b1 points to instantiated object of Base
e1 = new; // e1 points to object of Extended
b1.p = 1; //property "p" of Base initialized to 1
b1.display(); //will print out "Base: p=1"
e1.p = 2; //"p" of Base is now 2
e1.q = 3; //"q" of Extended initialized to 3
e1.display(); //prints "Base: p=2 Extended: q=3"
end
endprogram
Base: p=2
Extended: q=3
Casting
$cast enables you to assign values to variables that might not
ordinarily be valid because of differing data type.
The following example involves casting a base class handle (b2),
which actually holds an extended class-handle (e1), at runtime back
into an extended class-handle (e2).
program sample;
class Base;
integer p;
virtual task display();
$write("\nBase: p=%0d\n", p);
endtask
endclass
class Extended extends Base;
SystemVerilog Testbench Constructs
1-58
integer q;
virtual task display();
super.display();
$write("Extended: q=%0d\n", q);
endtask
endclass
Base b1 = new(), b2;
Extended e1 = new(), e2;
initial begin
b1.p = 1;
$write("Shows the original base property p\n");
b1.display(); // Just shows base property
e1.p = 2;
e1.q = 3;
$write("Shows the new value of the base property, p,
and value of the extended property, q\n");
e1.display(); // Shows base and extended properties
// Have the base handle b2 point to the extended object
b2 = e1;
$write("The base handle b2 is pointing to the
Extended object\n");
b2.display(); // Calls Extended.display
// Try to assign extended object in b2 to extended handle e2
$write("Using $cast to assign b2 to e2\n");
if ($cast(e2, b2))
e2.display(); // Calls Extended.display
else
$write("cast_assign of b2 to e2
failed\n");
end
endprogram
Chaining Constructors
To understand the relational use of the terms base and super as
employed to refer to classes, consider the following code fragment:
class ClassA;
...
endclass
class Class_a extends ClassA;
...
endclass
class class_ab extends Class_a;
...
endclass
class Class_b extends ClassA;
...
endclass
Both Class_a and Class_b are extended from ClassA using the
extends keyword, making ClassA the base class for these two
subclasses. Class_ab extends from Class_a, making Class_a the
base class for the subclass, Class_ab. Both ClassA and Class_a are
super classes since they each have subclasses extended from them.
Figure 1-1
ClassA
Class_a
Class_ab
Base Class
Subclass
Class_b
Figure 1-2
Chaining Constructors
ClassA
Class_a
Class_ab
Base Class
Subclass
Class_b
local
protected
Accessing Properties
A property of an object can be accessed using the dot operator (.).
The handle name for the object precedes the dot, followed by the
qualifying property name (for example, address, command).
instance_name.property_name
Methods
Tasks or functions, known as methods, can be designated as
local, public, or protected. They are public by default.
Accessing Object Methods
An objects methods can be accessed using the dot operator (.). The
handle for the object precedes the dot, followed by the method.
program access_object_method;
class B;
int q = 3;
function int send (int a);
send = a * 2;
endfunction
task show();
$display("q = %0d", q);
endtask
endclass
initial begin
B b1;
//declare handle
b1 = new; // instantiate
b1.show(); //access show() of object b.1
$display("value returned by b1.send() = %0d",
b1.send(4));//access send()of object b.1
end
endprogram
Output of program
q = 3
value returned by b1.send() = 8
this keyword
The this keyword is used to unambiguously refer to properties or
methods of the current instance. For example, the following
declaration is a common way to write an initialization task:
program P;
class Demo;
integer x;
task new (integer x);
this.x = x;
endtask
endclass
endprogram
The x is now both a property of the class and an argument to the task
new(). In the task new(), an unqualified reference to x will be resolved
by looking at the innermost scope, in this case the subroutine
argument declaration. To access the instance property, we qualify it
with this to refer to the current instance.
class myclass;
.
.
.
typedef struct { int int1;
logic [7:0] log1;
} struct1;
struct1 mystruct1;
.
.
.
endclass
endprogram
Random Constraints
SystemVerilog has constructs for the random testing of a design and
a means of constraining the random testing to find hard to reach
corner cases.
Note:
To enable the new constraint features, use the -ntb_opts
new_constraints compile-time option.
Random Variables
You need variables to which VCS assigns random values. There are
two types of random variables and two different keywords to begin
their declarations:
rand
Specifies a standard random variable
randc
Specifies a random-cyclic variable.
The following is an example of a standard random variable
declaration:
rand bit [7:0] randbit1;
Variable randbit1 has eight bits so its possible values range from
0 to 255. The chances that VCS assigns any of these values is
precisely 1/256.
The following is an example of a random-cyclic variable declaration:
randc bit [1:0] randc1;
Variable randc1 has two bits so its possible values range from 3 to
0. VCS derives a random order, or permutation, of all the possible
values, and assigns the values in the permutation in a sequence.
If VCS assigns a value of 3, it wont assign 3 again until it first assigns
2, 1, and 0 (in no particular order), and after it assigns the permutation,
it derives the next permutation, beginning with any of the possible
values.
If this were a standard random variable, after VCS assigns the 3 value,
there is a 1/4 chance that VCS assigns the 3 again, but because it is
a random-cyclic variable, after VCS assigns the 3, there is no chance
that 3 will also be the next assigned value.
Random variables can only be declared in a class.
class Bus;
rand bit [15:0] addr;
rand bit [15:0] data;
.
.
.
endclass
Constraint Blocks
Constraints are specified in a constraint block. A constraint block can
be declared in the same class in which the random variables are
declared as well as in a class extended from the base class in which
the random variable is defined (see page 61 for definition of base
class) A constraint block begins with the keyword constraint.
program prog;
class myclass;
rand logic [1:0] log1,log2,log3;
randc logic [1:0] log4;
constraint c1 {log1 > 0;}
constraint c2 {log2 < 3;}
endclass
myclass mc = new;
initial
repeat (10)
if(mc.randomize()==1)
$display("log1 = %0d log2=%0d log3=%0d
log4=%0d",mc.log1, mc.log2,
mc.log3, mc.log4);
endprogram
log1
log1
log1
log1
log1
log1
log1
log1
log1
=
=
=
=
=
=
=
=
=
1
2
1
3
3
3
1
2
1
log2=2
log2=0
log2=1
log2=2
log2=2
log2=1
log2=1
log2=0
log2=0
log3=0
log3=1
log3=1
log3=1
log3=3
log3=0
log3=1
log3=2
log3=2
log4=0
log4=1
log4=3
log4=0
log4=3
log4=2
log4=1
log4=3
log4=0
External Declaration
You can declare a constraint block outside of a class declaration.
program prog1;
class cl1;
rand integer rint1, rint2, rint3;
constraint constr1;
endclass
constraint cl1::constr1 { rint1 < rint2; rint2 < rint3; }
endprogram
Inheritance
Constraints follow the same rules of inheritance as class variables,
tasks, and functions.
class myclass;
rand logic [1:0] log1,log2,log3;
randc logic [1:0] log4;
constraint c1 {log1 > 0;}
constraint c2 {log2 < 3;}
endclass
class myclass2 extends myclass;
constraint c2 {log2 < 2;}
endclass
myclass2 mc = new;
Set Membership
You can use the inside operator to specify a set of possible values
that VCS randomly applies to the random variable.
program prog;
class myclass;
rand int int1;
constraint c1 {int1 inside {1, [5:7], [105:107]};}
endclass
myclass mc = new;
initial
repeat (10)
if(mc.randomize()==1)
$display("int1=%0d",mc.int1);
endprogram
Constraint c1 specifies that the possible values for int1 are as follows:
is
is
is
is
is
is
is
is
1
100
1000
500
10
1
20
1
Weighted Distribution
You can use the dist operator to specify a set of values or ranges
of values, where you give each value or range of values a weight,
and the higher the weight, the more likely VCS is to assign that value
to the random variable.
If we modify the previous example as follows:
program prog;
class myclass;
rand int int1;
constraint c1 {int1 dist {[1:2] := 1, [6:7] :/ 5, 9
:=10};}
endclass
myclass mc = new;
int stats [1:9];
int i;
initial begin
for(i = 1; i < 10; i++) stats[i] = 0;
repeat (1700)
if(mc.randomize()==1) begin
stats[mc.int1]++;
//$display("int1=%0d",mc.int1);
end
for(i = 1; i < 10; i++)
$display("stats[%d] = %d", i, stats[i]);
end
endprogram
The repeat loop repeats 1700 times so that we have a large enough
sample. The following table shows the various values of int1 during
simulation:
value of int
1
2
3
4
5
6
7
8
9
105
98
0
0
0
248
251
0
998
1/17
1/17
2.5/17
2.5/17
10/17
Implications
An implication is an expression that must be true before VCS applies
the constraint.
program prog;
class Bus;
randc bit randcvar;
bit norandvar;
=
=
=
=
=
=
=
=
=
=
=
=
0
1
0
1
0
0
0
0
0
1
0
0
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
=
=
=
=
=
=
=
=
=
=
=
=
0
0
0
0
1
1
1
1
1
0
0
0
at
at
at
at
at
at
at
at
at
at
at
at
1
2
3
4
5
6
7
8
9
10
11
12
randcvar = 0 norandvar = 0 at 13
randcvar = 1 norandvar = 0 at 14
if else Constraints
An alternative to an implication constraint is the if else constraint.
The if else constraint allows a constraint or constraint set on the
else condition.
program prog;
class Bus;
randc bit [2:0] randcvar;
bit norandvar;
constraint c1 { if (norandvar == 1)
randcvar == 0;
else
randcvar inside {[1:3]};}
endclass
Bus bus = new;
initial
begin
bus.norandvar = 0;
#5 bus.norandvar = 1;
#5 bus.norandvar = 0;
#5 $finish;
end
initial
repeat (15)
#1 if (bus.randomize() ==1)
=
=
=
=
=
=
=
=
=
=
=
=
=
=
1
2
3
3
0
0
0
0
0
1
2
3
3
2
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
norandvar
=
=
=
=
=
=
=
=
=
=
=
=
=
=
0
0
0
0
1
1
1
1
1
0
0
0
0
0
at
at
at
at
at
at
at
at
at
at
at
at
at
at
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Global Constraints
Constraint expressions involving random variables from other objects
are called global constraints.
program prog;
class myclass;
rand bit [7:0] randvar;
endclass
class myextclass extends myclass;
rand myclass left = new;
rand myclass right = new;
constraint c1 {left.randvar <= randvar;
right.randvar <= randvar;}
endclass
endprogram
Variable Ordering
In an implication like the following:
constraint c1 { select -> data == 0;}
The select variable implies that data is 0, in this case, when select
is 1, data is 0.
The default behavior is for the constraint solver to solve for both
select and data at the same time. Sometimes you can assist the
constraint solver by telling it to solve for one variable first. You can
do this with another constraint block that specifies the order in which
the constraint solver solves the variables:
constraint c1 { select -> data == 0;}
constraint c2 ( solve select before data;}
Note:
Ordering also changes the distribution of values.
Randomize Methods
randomize()
Variables in an object are randomized using the randomize() class
method. Every class has a built-in randomize() method.
randomize()
Generates random values for active random variables, subject to
active constraints, in a specified instance of a class. This method
returns a 1 if VCS generates these random values, otherwise it
returns 0. Examples of the use of this method appear frequently
in previous code examples.
constraint con_size {
s > 0;
s < 50;
}
endclass
class Pkt;
integer header[7];
rand bit [7:0] payload[];
size sz;
function void pre_randomize();
/* Randomize the sz.s variable and instantiate
the dynamic array. */
integer res;
sz = new();
res = sz.randomize(); /* calling randomize()
on object sz of class size. Randomizes rand
variable s (constrained to >0 and <50 */
if(res==0)
$display("Randomization Failed");
else
begin
if((sz.s>0) && (sz.s<50))/* check for
size between 0 and 50*/
payload = new[sz.s];/* the new[] operator
allocates storage and initializes the
rand variable s in sz*/
else
$display("Failed to generate proper
size");
end
//end of outer else block
endfunction
function void post_randomize();
/* display size of payload dynamic array using
the size() built-in method*/
$display("payload.size = %d", payload.size);
endfunction
endclass
Pkt pkt;
integer success,i;
initial begin
pkt = new(); //instantiate pkt
for (int j = 0; j < 5; j++)
begin
success = pkt.randomize(); /*calling randomize
on object of class packet*/
if(success==0)
$display("Randomization Failed");
else
$display("Randomization Succeeded");
end // end of for loop
end // end of initial block
endprogram
Controlling Constraints
The predefined constraint_mode() method can be used either as a
task or a function. The constraint_mode() task controls whether a
constraint is active or inactive. The predefined constraint_mode()
function reports the current ON/OFF value for the specified variable.
All constraints are initially active.
Syntax:
task object[.constraint_identifier]::constraint_mode
(bit ON | OFF);
or
function int
object.constraint_identifier::constraint_mode();
else
$display("Error with constraint_mode");
generateRandomAddresses(`N);
// turn OFF "word_align" constraint in "bb"
bb.word_align.constraint_mode(0);
$display("=========one constraint ON =======");
generateRandomAddresses(`N);
// turn OFF all constraints in "bb"
bb.constraint_mode(0);
$display("=========both constraints OFF =======");
generateRandomAddresses(`N);
// turn ON "addr_range" constraint in "bb"
bb.addr_range.constraint_mode(1);
$display("=========one constraint ON =======");
generateRandomAddresses(`N);
// turn ON all constraint in "bb"
bb.constraint_mode(1);
$display("=========both constraints ON =======");
generateRandomAddresses(`N);
end
endprogram
Output of the above program:
=========both constraints ON =======
bb.addr =
14
bb.addr =
2
bb.addr =
4
bb.addr =
8
bb.addr =
2
=========one constraint ON =======
bb.addr =
2
bb.addr =
9
bb.addr =
9
bb.addr =
9
bb.addr =
3
=========both constraints OFF =======
bb.addr = 44051
bb.addr = 36593
bb.addr = 19491
bb.addr = 6853
bb.addr = 48017
=========one constraint ON =======
bb.addr =
11
bb.addr =
8
bb.addr =
15
bb.addr =
7
bb.addr =
4
=========both constraints ON =======
bb.addr =
2
bb.addr =
10
bb.addr =
12
bb.addr =
10
bb.addr =
8
generateRandomAddresses(`N);
// turn OFF "data" random variable in "bb"
bb.data.rand_mode(0);
$display("======one random variable ON ======");
generateRandomAddresses(`N);
// turn OFF all random variables in "bb"
bb.rand_mode(0);
$display("======both random variables OFF======");
generateRandomAddresses(`N);
// turn ON "data" constraint in "bb"
bb.data.rand_mode(1);
$display("======one random variable ON========");
generateRandomAddresses(`N);
// turn ON all random variable in "bb"
bb.rand_mode(1);
$display("======both random variables ON ======");
generateRandomAddresses(`N);
end
endprogram
bb.addr =
121,, bb.data =
219
bb.addr =
121,, bb.data =
219
bb.addr =
121,, bb.data =
219
bb.addr =
121,, bb.data =
219
========one random variable ON========
bb.addr =
121,, bb.data =
456
bb.addr =
121,, bb.data =
511
bb.addr =
121,, bb.data =
67
bb.addr =
121,, bb.data =
316
bb.addr =
121,, bb.data =
405
======both random variables ON =======
bb.addr =
18,, bb.data =
491
bb.addr =
231,, bb.data =
113
bb.addr =
118,, bb.data =
230
bb.addr =
46,, bb.data =
96
bb.addr =
155,, bb.data =
298
In-line Constraints
You can use the randomize() method and the with construct to
declare in-line constraints outside of the class for the random
variables, at the point where you call the randomize() method.
program prog;
class Bus;
rand bit [2:0]
endclass
bitrand1;
repeat (10)
begin
int1 = bus.randomize() with {bitrand1[1:0] ==
2'b00;};
if (int1 ==1) $display("bitrand1 = %0b",
bus.bitrand1);
end
endtask
initial
inline(bus);
endprogram
=
=
=
=
=
=
=
=
=
=
100
100
100
0
100
100
0
100
100
0
$urandom( seed )
a = 229,b=
Generation
a = 228,b=
Generation
a = 229,b=
175, c = 7, d = 99
of random number with seed 4 :
15, c = 254, d = 230
of random number with seed 3 :
175, c = 7, d = 99
$urandom_range()
The system function $urandom_range() generates random numbers
within a certain range. The syntax is:
$urandom_range( unsigned int maxval, unsigned int minval)
$srandom(2);
$srandom()
You can use the system function $srandom() to manually set the seed
for random number generation. The syntax is:
$srandom(seed)
Providing the same seed ensures that the same set of random values
are generated by the random number generator.
The following program demonstrates that the same random numbers
are generated for the same seed.
program test;
logic [3:0]
a, b;
initial begin
$display("Setting the seed as 2 through srandom");
$srandom(2);
repeat (2)
begin
a = $urandom();
b = $urandom();
$display("a = %0d, b = %0d", a, b);
end
// end of repeat block
$display("Changing the seed as 1 through srandom");
$srandom(1);
repeat (2)
begin
a = $urandom();
b = $urandom();
$display("a = %0d, b = %0d", a, b);
end
// end of repeat block
$display("Setting the seed once again to 2 through
srandom");
$srandom(2);
repeat (2)
begin
a = $urandom();
b = $urandom();
$display("a = %0d, b = %0d", a, b);
end
// end of repeat block
end
// end of initial block
endprogram
randcase Statements
The randcase construct specifies a block of statements with
corresponding weights attached to them. Weights can be any
expression. When a randcase statement occurs, one of the statement
with weights is executed at random. If the weight is an expression,
then it is evaluated each time the randcase statement is executed. If
the weight is zero, then that branch is not executed. The randcase
statements can be nested.
program test;
int a= 2;
logic [3:0] b;
//variables to keep count of how many time a particular case
//is executed in randcase
int count_2,count_c;
function f1();
randcase
a+3
10
endcase
endfunction
initial begin
repeat (15)
f1();
$display("Number of times a+3 called is %0d", count_2);
RSG Overview
Production Declaration
Production Controls
RSG Overview
The syntax for programming languages is often expressed in Backus
Naur Form (BNF) or some derivative thereof. Parser generators use
this BNF to define the language to be parsed. However, it is possible
to reverse the process. Instead of using the BNF to check that
existing code fits the correct syntax, the BNF can be used to
assemble code fragments into syntactically correct code. The result
is the generation of pseudo-random sequences of text, ranging from
sequences of characters to syntactically and semantically correct
assembly language programs.
SystemVerilogs implementation of a stream generator, the RSG, is
defined by a set of rules and productions encapsulated in a
randsequence block.
The general syntax to define a RSG code block is:
randsequence ([production_identifier])
production {production}
endsequence
Production Declaration
A language is defined in BNF by a set of production definitions. The
syntax to define a production is:
[function_datatype] production_identifier
[(tf_port_list)]:rs_rule{|rs_rule};
production_identifier
Is the reference name of the production definition.
tf_port_list
task/function port list.
rs_rule
The syntax for rs_rule is:
rs_production_list [:= weight_specification
[rs_code_block]]
rs_production_list
The syntax for rs_production_list is:
rs_prod{rs_prod} | rand join [(expression)]
production_item production_item {production_item}
weight_specification
The syntax for weight_specification is:
integral_number | ps_identifier | (expression)
{{data_declaration}{statement_or_null}}
The following table provides the syntax for the non-terminals within
rs_production_list, weight_specification, and rs_code_block, and the
non-terminals therein.
non-terminal
Syntax
rs_prod
rs_code_block
rs_if_else
{{data_declaration}}{statement_or_null}}
if(expression) product_item [else production_item]
see also if-else Statements on page 1-106
repeat(expression) production_item
see also repeat Loops on page 1-108
case(expression) rs_case_item {rs_case_item} endcase
see also case Statements on page 1-107
expression{,expression} : production_item | default [:]
production_item;
see also case Statements on page 1-107
production_identifier[(list_of_arguments)]
rs_repeat
rs_case
re_case_item
production_item
Production Controls
SystemVerilog provides several mechanisms that can be used to
control productions: weights for randomization, if-else statements,
case statements, and repeat loops.
if-else Statements
case Statements
repeat Loops
break Statement
weight_specification
The syntax for weight_specification is:
integral_number | ps_identifier | (expression)
if-else Statements
A production can be conditionally referenced using an if-else
statement.
The syntax to declare an if-else statement within a production
definition is:
if(expression) product_item [else production_item]
expression
Can be any valid SystemVerilog expression that evaluates to a
boolean value.
If the conditional evaluates to true, the first production item is selected.
If it evaluates to false, the second production item is selected. The
else statement can be omitted. If it is omitted, a false evaluation
ignores the entire if statement. The following is an example of a
production definition with an if-else statement.
assembly_block : if (nestingLevel > 10) seq_block else
any_block;
case Statements
A general selection mechanism is provided by the case statement.
The syntax to declare a case statement within a production definition
is:
case(expression)
rs_case_item {rs_case_item}
endcase
expression
Is evaluated. The value of the expression is successively
checked, in the order listed, against each rs_case_item. The
production corresponding to the first matching case found is
executed, and control is passed to the production definition
whose name is in the case item with the matching case
expression. If other matches exist, they are not executed. If no
case item value matches the evaluated primary expression and
there is no default case, nothing happens.
rs_case_item
Can be any valid SystemVerilog expression or comma-separated
list of expressions. Expressions separated by commas allow
multiple expressions to share the same statement block. The
syntax is:
expression{,expression}: production_item | default [:]
production_item;
A case statement must have at least one case item aside from the
default case, which is optional. The default case must be the last
item in a case statement. The following is an example of a production
definition using a case statement:
repeat Loops
The repeat loop is used to loop over a production a specified number
of times.
The syntax to declare a repeat loop within a production definition is:
repeat(express) production_item
expression
Can be any valid SystemVerilog expression that evaluates to a
non-negative integer, including functions that return a numeric
value.
The expression is evaluated when the production definition is
executed. The value specifies how many times the corresponding
production item is executed. The following is an example of a
production definition using a repeat loop.
randsequence()
...
seq_block : repeat (random() ) integer_instruction;
...
endsequence
break Statement
The break statement is used to terminate a randsequence block.
Syntax
break;
return Statement
The return statement is used to interrupt the execution of the current
production. After the current production is aborted, the execution
continues on the next item in the production from which the call is
made.
Syntax
return;
Aspect-Oriented Extensions in SV
In SV, AOP is supported by a set of directives and constructs that
need to be processed before compilation. Therefore, an SV program
with these Aspect oriented directives and constructs would need to
be processed as per the definition of these directives and constructs
in SV to generate an equivalent SV program that is devoid of aspect
extensions, and consists of traditional SV. Conceptually, AOP is
implemented as pre-compilation expansion of code.
This chapter explains how AOE in SV are directives to SV compiler
as to how the pre-compilation expansion of code needs to be
performed.
In SV, an aspect extension for a class can be defined in any scope
where the class is visible, except for within another aspect extension.
That is, aspect extensions can not be nested.
An aspect oriented extension in SV is defined using a new top-level
extends directive. Terms aspect and extends directive have been
used interchangeably throughout the document. Normally, a class is
extended through derivation, but an extends directive defines
modifications to a pre-existing class by doing in-place extension of
the class. in-place extension modifies the definition of a class by
adding new member fields and member methods, and changing the
behavior of earlier defined class methods, without creating any new
subclasse(s). That is, SVs Aspect-Oriented Extensions change the
original class definition without creating subclasses. These changes
affect all instances of the original class that was extended by AOEs.
Introduction
Declaration of a new property, or the definition of a new method,
a new constraint, or a new coverage group within the extends
directive scope adds (or introduces) the new symbol into the
original class definition as a new member. Such declaration/
definition is called an introduction.
Advice
An advice is a construct to specify code that affects the behavior
of a member method of the class by weaving the specified code
into the member method definition. This is explained in more
detail later. The advice item is said to be an advice to the affected
member method.
Hide list:
Some items within an extends directive, such as a virtual method
introduction, or an advice to virtual method may not be
permissible within the extends directive scope depending upon
the hide permissions at the place where the item is defined. A
hide list is a construct whose placement and arguments within
the extends directive scope controls the hide permissions. There
could be multiple hide lists within an extends directive.
Syntax:
extends_directive ::=
extends extends_identifier
(class_identifier)[dominate_list];
extends_item_list
endextends
dominate_list ::=
dominates(extends_identifier
{,extends_identifier});
extends_item_list ::=
extends_item {extends_item}
extends_item ::=
class_item
| advice
| hide_list
class_item ::=
class_property
| class_method
| class_constraint
| class_coverage
| enum_defn
advice ::= placement procedure
placement ::=
before
| after
| around
procedure ::=
| optional_method_specifiers task
task_identifier(list_of_task_proto_formals);
| optional_method_specifiers function
function_type
function_identifier(list_of_function_proto_formals)
endfunction
advice_code ::= [stmt] {stmt}
aspects are added to the class definition. It also determines the order
in which advices defined in the aspects are woven into the class
method definitions thus affecting the behavior of a class method.
Rules for determination of precedence among aspects are explained
later in Precedence on page 1-125.
class_property
Refers to an item that can be parsed as a property of a class.
class_method
Refers to an item that can be parsed as a class method.
class_constraint
Refers to an item that can be parsed as a class constraint.
class_coverage
Refers to an item that can be parsed as a coverage_group in a
class.
advice_code
Specifies to a block of statements.
statement
Is an SV statement.
procedure_prototype
A full prototype of the target procedure. Prototypes enable the advice
code to reference the formal arguments of the procedure.
opt_method_specifiers
Refers to a combination of protection level specifier (local, or
protected), virtual method specifier (virtual), and the static method
specifier (static) for the method.
task_identifier
Name of the task.
function_identifier
Name of the function.
function_type
Data type of the return value of the function.
list_of_task_proto_formals
List of formal arguments to the task.
list_of_function_proto_formals
List of formal arguments to the function.
placement
Specifies the position at which the advice code within the advice is
woven into the target method definition. Target method is either the
class method, or some other new method that was created as part of
the process of weaving, which is a part of pre-compilation expansion
of code. The overall details of the process of weaving are explained
in Pre-compilation Expansion details. The placement element could
SystemVerilog Testbench Constructs
1-118
The following table shows the rest of the steps involved in weaving
of the advice for each type of placement element (before, after, and
around).
Table 1-2
Placement Elements
Element
Description
before
after
around
Within an extends directive, you can specify only one advice can be
specified for a given placement element and a given method. For
example, an extends directive may contain a maximum of one
before, one after, and one around advice each for a class method
Packet::foo of a class Packet, but it may not contain two before
advices for the Packet::foo.
Target method:
task myTask();
$display("Executing original code\n");
endtask
Advice:
before task myTask ();
$display("Before in aoe1\n");
endtask
Note that the SV language does not impose any restrictions on the
names of newly created methods during pre-compilation expansion,
such as mytask_before . Compilers can adopt any naming
conventions such methods that are created as a result of the
weaving process.
Example 1-1
Target method:
task myTask();
$display("Executing original code\n");
endtask
Advice:
after task myTask ();
$display("Before in aoe1\n");
endtask
Target method:
task myTask();
$display("Executing original code\n");
endtask
Advice:
around task myTask ();
$display("Around in aoe1\n");
endtask
Advice:
around task myTask ();
proceed;
$display("Around in aoe1\n");
endtask
5. Weaving of all advices in the input program are weaved into their
respective class methods as per the precedence order.
These steps are described in more detail in the following sections.
Precedence
Precedence is specified through the dominate_list (see
dominate_list on page 1-116) There is no default precedence
across files; if precedence is not specified, the tool is free to weave
code in any order. Within a file, dominance established by
dominate_lists always overrides precedence established by the
order in which extends directives are coded. Only when the
precedence is not established after analyzing the dominate lists of
directives, is the order of coding used to define the order of
precedence.
Within an extends directive there is an inherent precedence between
advices. Advices that are defined later in the directive have higher
precedence that those defined earlier.
Precedence does not change the order between adding of
introductions and weaving of advices in the code. Precedence
defines the order in which introductions to a class are added to the
class, and the order in which advices to methods belonging to a
class are woven into the class methods.
Example 1-3
// Beginnning of file Input.vr
program top ;
initial begin
packet p;
p = new();
p.send();
end
endprogram
class packet;
...
// Other member fields/methods
...
task send();
$display("Sending data\n");
endtask
endclass
extends aspect_1(packet) dominates (aspect_2, aspect_3);
after task send();
// Advice 1
$display("Aspect_1: send advice after\n");
endtask
endextends
extends aspect_2(packet);
after task send() ;
// Advice 2
$display("Aspect_2: send advice after\n");
endtask
endextends
extends aspect_3(packet);
around task send();
$display("Aspect_3:
proceed;
$display("Aspect_3:
endtask
before task send();
$display("Aspect_3:
endtask
endextends
// Advice 3
Begin send advice around\n");
End send advice around\n");
// Advice 4
send advice before\n");
Weaving of advices
An input program may contain several aspect extensions for any or
each of the different class definitions in the program. Weaving of
advices needs to be carried out for each class method for which an
advice is specified.
Weaving of advices in the input program consists of weaving of
advices into each such class method. Weaving of advices into a
class method A is unrelated to weaving of advices into a different
class method B, and therefore weaving of advices to various class
methods can be done in any ordering of the class methods.
For weaving of advices into a class method, all the advices
pertaining to the class method are identified and ordered in the order
of increasing precedence in a list L. This is the order in which these
advices are woven into the class method thereby affecting the
run-time behavior of the method. The advices in list L are woven in
the class method as per the following steps. Target method is
initialized to the class method.
a. Advice A that has the lowest precedence in L is woven into the
target method as explained earlier. Note that the target method
may either be the class method or some other method newly
created during the weaving process.
b. Advice A is deleted from list L.
c. The next advice on list L is woven into the target method. This
continues until all the advices on the list have been woven into
list L.
It would become apparent from the example provided later in this
section how the order of precedence of advices for a class method
affects how advices are woven into their target method and thus the
relative order of execution of advice code blocks. Before and after
advices within an aspect to a target method are unrelated to each
SystemVerilog Testbench Constructs
1-128
other in the sense that their relative precedence to each other does
not affect their relative order of execution when a method call to the
target method is executed. The before advices code block executes
before the target method code block, and the after advice code block
executes after the target method code block. When an around
advice is used with a before or after advice in the same aspect, code
weaving depends upon their precedence with respect to each other.
Depending upon the precedence of the around advice with respect
to other advices in the aspect for the same target method, the around
advice either may be woven before all or some of the other advices,
or may be woven after all of the other advices.
As an example, weaving of advices 1, 2, 3, 4 specified in aspect
extensions in Example 1-3 leads to the expansion of code in the
following manner. Advices are woven in the order of increasing
precedence {2, 3, 4, 1} as explained earlier.
Example 1-4
// Beginnning of file Input.vr
program top ;
packet p;
p = new();
p.send_Created_a();
endprogram
class packet;
...
// Other member fields/methods
...
task send();
p$display("Sending data\n);
endtask
task send_Created_a();
send();
send_after_Created_b();
endtask
task send_after_Created_b();
This Example 1-4 shows what the input program looks like after
weaving advice 2 into the class method. Two new methods
send_Created_a and send_after_Created_b are created in the
process and the instances of method call to the target method
packet::send are modified, such that the code block from advice 2
executes after the code block of the target method packet::send.
Example 1-5
// Beginnning of file Input.vr
program top;
packet p;
p = new();
p.send_around_Created_c();
endprogram
class packet;
...
// Other member fields/methods
...
task send();
$display("Sending data\n);
endtask
task send_Created_a();
send();
send_after_Created_b();
endtask
task send_after_Created_b();
$display("Aspect_2: send advice after\n");
endtask
task send_around_Created_c();
$display("Aspect_3: Begin send advice around\n");
send_Created_a();
$display("Aspect_3: End send advice around\n");
endtask
endclass
extends aspect_1(packet) dominates (aspect_2, aspect_3);
after task send();
// Advice 1
$display("Aspect_1: send advice after\n");
endtask
endextends
extends aspect_3(packet);
before task send();
// Advice 4
$display("Aspect_3: send advice before\n");
endtask
endextends
// End of file Input.sv
This Example 1-5 shows what the input program looks like after
weaving advice 3 into the class method. A new method
send_around_Created_c is created in this step and the instances of
method call to the target method packet::send_Created_a are
modified, such that the code block from advice 3 executes around
the code block of method packet::send_Created_a. Also note that
the proceed statement from the advice code block is replaced by a
endtask
endextends
// End of file Input.sv
This Example 1-6 shows what the input program looks like after
weaving advice 4 into the class method. A new method
send_before_Created_d is created in this step and a call to it is
added as the first statement in the target method
packet::send_around_Created_c. Also note that the outcome would
have been different if advice 4 (before advice) was defined earlier
than advice 3 (around advice) within aspect_3, as that would have
affected the order of precedence of advice 3 and advice. In that
scenario the advice 3 (around advice) would have weaved around
the code block from advice 4 (before advice), unlike the current
outcome.
Example 1-7
// Beginnning of file Input.vr
program top;
packet p;
p = new();
p.send_Created_f();
endprogram
class packet;
...
// Other member fields/methods
...
task send();
$display("Sending data\n);
endtask
task send_Created_a();
send();
send_Created_b();
endtask
task send_after_Created_b();
This Example 1-7 shows the input program after weaving of all four
advices {2, 3, 4, 1}. New methods send_after_Created_e and
send_Created_f are created in the last step of weaving and the
instances of method call to packet::send_around_Created_c were
replaced by method call to packet::send_Created_f.
When executed, output of this program is:
Aspect_3: send advice before
Aspect_3: Begin send advice around
Sending data
Aspect_2: send advice after
Aspect_3: End send advice around
Aspect_1: send advice after
Example 1-8
// Begin file input.vr
program top;
foo f;
f = new();
f.myTask();
endprogram
class foo;
int i;
task myTask();
printf("Executing original code\n");
endtask
endclass
extends aoe1 (foo);
around task myTask();
proceed;
printf("around in aoe1\n");
endtask
endextends
extends aoe2 (foo) dominates (aoe1);
around task myTask();
proceed;
printf("around in aoe2\n");
endtask
endextends
// End file input.sv
The extends directive in Example 1-9 sets the x parameter inside the
foo() task to 99 before the original code inside of foo() executes.
Actual argument to foo() is not affected, and is not set unless
passed-by-reference using ref.
Example 1-10
// Begin file example.sv
program top ;
packet p;
p = new();
$display(Output is: %d\n, p.bar());
endprogram
class packet ;
function integer bar();
bar = 5;
$display(Point 1: Value = %d\n, bar);
endfunction
endclass
extends myaspect(packet);
after function integer bar();
$display(Point 2: Value = %d\n, bar);
bar = bar + 1;
// Stmt A
$display(Point 3: Value = %d\n, bar);
endfunction
endextends
// End file example.sv
hide_list details
The hide_list item of an extends_directive specifies the
permission(s) for introductions to hide symbols, and/or advice to
modify local and protected methods. By default, an introduction does
not have permission to hide symbols that were previously visible in
the target scope, and it is an error for an extension to introduce a
symbol that hides a global or super-class symbol.
The hide_list option contains a comma-separated list of options such
as:
Example 1-11
class pbase;
virtual task print();
$display("Im pbase\n");
endtask
endclass
class packet extends pbase;
task foo();
$display(); //Call the print task
endtask
endclass
extends myaspect(packet);
hide(virtuals); // Allows permissions to
// hide pbase::print task
virtual task print();
$display("Im packet\n);
endtask
endextends
Examples
constraint con1 {
myfield == 4;
}
endextends
Example 1-18
// Begin file example.sv
program test;
packet p;
p = new();
p.setLen(5000);
p.send();
p.setLen(10000);
p.send();
endprogram
class packet;
integer len;
task setLen( integer i);
len = i;
}
task send();
$display("Sending data\n);
endtask
endclass
extends myaspect(packet);
around task send();
if (len < 8000){
proceed;
}
else{
$display("Dropping packet\n");
}
endtask
endextends
// End file example.sv
This Example 1-18 also demonstrates how the around advice code
can reference properties such as len in the packet object p. When
executed the output of the above example is,
Sending data
Dropping packet
end
endprogram
In this program there are two initial blocks, labeled by the label on
their begin-end blocks, initial1 and intital2.
The program has a semaphore named sem1 that starts with two keys,
as specified with the semaphore keyword and new() method.
If it were not for initial2, initial1 would do the following:
1. Take a key at simulation time 1 (using the get method).
2. Return a key at time 7 (using the put method).
3. Take a key again at time 8 (using the get method).
If it were not for initial1, initial2 would do the following:
1. Take two keys at simulation time 5 (using the get method).
2. Return one key at time 10 (using the put method).
However both initial blocks contend for a limited number of keys that
they need in order to finish executing, in taking keys that the other
needs, they interrupt each others processing. The $display system
tasks display the following:
initial1 takes 1 key at 1
initial1 returns 1 key at 7
inital2 takes 2 keys at 7
inital2 returns 1 key at 12
initial1 takes 1 key at 12
The initial block initial2 could be rewritten to use the try_get method
to see if a certain number of keys are available, for example:
initial
begin:initial2
#5 if(sem1.try_get(2))
begin
sem1.get(2);
$display("inital2 takes 2 keys at %0t",$time);
end
#5 sem1.put(1);
$display("inital2 returns 1 key at %0t",$time);
end
endprogram
Semaphore Methods
Semaphores have the following built-in methods:
new (number_of_keys)
You use this method with the semaphore keyword. It specifies
the initial number of keys in the semaphore.
put(number_of_keys)
Increments the number of keys in the semaphore.
get(number_of_keys)
Decrements the number of keys in the semaphore. If there arent
the specified number of keys in the semaphore, VCS halts
simulation of the process (initial block, task, etc.) until there the
put method in another process increments the number of keys
to the sufficient number.
try_get (number_of_keys)
Decrements the number of keys in the semaphore. If there arent
the specified number of keys in the semaphore, this method
returns a 0. If the semaphore has the specified number of keys,
this method returns 1. After returning the value, VCS executes
the next statement.
Mailboxes
Mailboxes are FIFO containers for messages that are expressions.
Note:The SystemVerilog 3.1a LRM specifies that you can specify a
maximum number of messages that a mailbox can hold, but this
feature isnt implemented yet.
program prog;
mailbox mbx = new ();
int i,j;
int k = 10;
initial
begin
repeat(3)
begin
#5 mbx.put(k);
i = mbx.num();
$display("No. of msgs in mbx = %0d at %0t",i,$time);
k = k + 1;
end
i = mbx.num();
repeat (3)
begin
#5 $display("No. of msgs in mbx = %0d j = %0d at
%0t",i,j,$time);
mbx.get(j);
i = mbx.num();
end
end
endprogram
of
of
of
of
of
of
msgs
msgs
msgs
msgs
msgs
msgs
in
in
in
in
in
in
mbx
mbx
mbx
mbx
mbx
mbx
=
=
=
=
=
=
1
2
3
3
2
1
at 5
at 10
at 15
j = 0 at 20
j = 10 at 25
j = 11 at 30
Mailbox Methods
Mailboxes use the following methods:
new()
Along with the mailbox keyword, declares a new mailbox. You
cannot yet specify the maximum number of messages with this
method.
num()
Returns the number of messages in the mailbox.
put(expression)
Puts another message in the mailbox.
get(variable)
Assigns the value of the first message to the variable. VCS
removes the first message so that the next message becomes
the first method. If the mailbox is empty, VCS suspends simulation
of the process (initial block, task, etc.) until a put method put a
message in the mailbox.
try_get(variable)
Assigns the value of the first message to the variable. If the
mailbox is empty, this method returns the 0 value. If the message
is available, this method returns a non-zero value. After returning
the value, VCS executes the next statement.
peek(variable)
Assigns the value of the first message to the variable without
removing the message. If the mailbox is empty, VCS suspends
simulation of the process (initial block, task, etc.) until a put
method put a message in the mailbox.
try_peek(variable)
Assigns the value of the first message to the variable without
removing the message. If the mailbox is empty, this method
returns the 0 value. If the message is available, this method
returns a non-zero value. After returning the value, VCS executes
the next statement.
Events
SystemVerilog has a number of extensions to named events. These
extensions are as follows:
Waiting for an Event
Persistent Trigger
Merging Events
Reclaiming Named Events
Event Comparison
initial
t1;
initial
@(t1.evt1) $display("t1.evt1 happened at %0t",$time);
endprogram
Persistent Trigger
The triggered property persists on a named event throughout the
time step when it is triggered, preventing a race condition, for
example, when a named event is triggered and is evaluated in an
event control during the same time step.
program prog;
event evt1,evt2;
initial
-> evt1;
initial
begin
wait (evt1.triggered);
$display("evt1 triggered");
end
initial
fork
-> evt2;
begin
wait (evt2.triggered);
$display("evt2 occurred");
end
join
endprogram
Merging Events
You can assign a SystemVerilog named event to another named
event. When you do, they alias each other and when VCS executes
a line calling for the triggering of one of these events, VCS triggers
both named events.
program prog;
event evt1, evt2, evt3;
initial
begin
evt2 = evt3;
evt1 = evt3;
#2 -> evt1;
end
// this is an alias
// this is an alias
initial
#1 @ (evt1) $display("evt1 triggerred");
initial
#1 @ (evt2) $display("evt2 triggerred");
initial
#1 @ (evt3) $display("evt3 triggerred");
endprogram
IMPORTANT:
When you merge events, the merger takes effect only in
subsequent event controls or wait statements.
In this example, the merging occurred at time 0, the event controls
at time 1, and the triggering of the events at time 2.
$display("evt1 occurred");
end
endprogram
Event Comparison
You can use the equality and inequality operators to see if named
events are aliased to each other or have been assigned the null value,
for example:
program prog;
event evt1, evt2, evt3;
initial
begin
evt1 = evt2;
if (evt1 == evt2)
$display("evt1
if (evt1 === evt2)
$display("evt1
if (evt1 != evt3)
$display("evt1
if (evt3 != null)
$display("evt3
end
endprogram
== evt2");
=== evt2");
!= evt3");
!= null");
== evt2
=== evt2
!= evt3
!= null
Clocking Blocks
A clocking block encapsulates a group of signals that are sampled or
synchronized by a common clock. It defines the timing behavior of
those signals with respect to the associated clock. Consequently,
timing and synchronization details for these signals is separate from
the structural, functional, and procedural elements of the testbench.
This enables synchronous events, input sampling, and synchronous
drives to be written without explicitly using clocks or specifying timing.
Clocking blocks can be declared inside a program block or inside an
interface.
clocking_identifier
Name of the clocking block being declared.
clocking_event
Event that acts as the clock for the clocking block (for example:
posedge, negedge of a clocking signal):
@(posedge clk)
or
@(clk)
Note:
Program signals cannot be used inside a clocking event
expression.
clocking_dir
Direction of the signal: input, output or inout. If you specify more
than one clocking_dir, they must be in the order input...output:
input clocking_skew output clocking_skew
clocking_skew
Determines how long before the synchronized edge the signal is
sampled, or how long after the synchronized edge the signal is
driven. A clocking_skew can consist of an edge identifier and a
delay control, just an edge identifier, or just the delay control. The
edge identifiers are posedge and negedge. The edge can be
specified only if the clocking event is a singular clock (that is, a
simple edge of a single signal like @(posedge clk), @(clk),
@(negedge top.clk), etc.).The delay control is introduced by "#"
followed by the delay value. The following are examples of legal
clocking_skews:
input #0 i1;
output negedge #2 i2;
input #1 output #2;
Note:
Time literals (e.g., #10ns and #2ns) are not supported in this
release.
The skew for an input signal is implicitly negative (that is, sampling
occurs before the clock event). The skew for an output signal is
implicitly positive (that is, the signal is driven after the clock event).
Note:
#1step is the default input skew unless otherwise specified.
However, an explicit #1step skew is not yet supported.
signal_identifier
Identifies a signal in the scope enclosing the clocking block
declaration, and declares the name of a signal in the clocking
block. Unless a hierarchical_expression is used, both the signal
and the clocking_item names shall be the same. For example:
input #1 i1;
= top.i2;
reg out3;
reg out1;
reg clk = 0;
p1 p(out3,clk,out1);
assign out1 = out3;
initial forever begin
clk = 0;
#10;
clk = 1;
#10;
end
endmodule
program p1(output reg out3,input logic clk,input reg in );
@(cb.out1);
$display($time,,,cb.out1);
end
endprogram
input signal
sampled here
output signal
driven here
Clock
For more details see section 15.3 of the SystemVerilog LRM 3.1a.
Hierarchical Expressions
Every signal in a clocking block is associated with a program port or
a cross module reference.
As described when defining hierarchical_identifier, the hierarchical
path is assigned to the defined in the clocking block.
clocking cb2 @ (negedge clk);
input #0 b = top.q;
endclocking
endclocking
Clocking blocks that use the same clock can share the same
synchronization event. For example:
program test( input bit clk_1, input reg [15:0] address,
input reg [15:0] data );
default clocking data @(posedge clk_1);
input data;
endclocking
clocking address @(posedge clk_1);
input address;
endclocking
The clocking event of the cb1 clocking block can be used to wait for
that particular event:
@(cb1);
clocking_identifier
Name of a clocking block.
Note:
You can specify only one default clocking block in a program,
module, or interface. VCS issues a compilation error if you specify
more than one default clocking block.
For example:
program test( input bit clk, input reg [15:0] data );
default clocking bus @(posedge clk);
input data;
endclocking
initial begin
## 5;
if ( bus.data == 10 )
## 1;
else
##2;
end
endprogram
Cycle Delays
Cycle delays can be used to postpone or delay the execution by a
specified number of clock cycles or clocking events. The term cycle
refers to the clock associated with the default clocking block.The ##
operator is used to specify cycle delay.
Syntax:
## integral_number | integer | (expression)
expression
Note:
VCS issues a compilation error if you use a cycle delay without
specifying a default clocking block for the current module,
interface, or program.
Input Sampling
All inputs and inouts of a clocking block are sampled at the
clocking_event for that block. The following is skew related behavior
involving regions:
When the skew is #0, the signal value in the Observed region
corresponds to the value sampled.
When the skew is not #0, then the signal value at the Postponed
region of the timestep skew time-units prior to the clocking event
corresponds to the value sampled.
When the skew is #1step, the signal value in the Preponed region
corresponds to the value sampled.
The last sampled value of signal replaces the signal when the signal
appears in an expression.
Note:
See section 14.3 of the SystemVerilog LRM 3.1a for definitions of
Observed, Postponed and Preponed regions.
Synchronous Events
The event control operator, @, is used for explicit synchronization.
This operator causes a process to wait for a particular event (that is,
signal value change, or a clocking event) to occur.
Syntax
@ (expression);
expression
denotes clocking block input or inout, or a slice, which may include
dynamic indices. The dynamic indices are evaluated when
@(expression) executes.
For examples, see pages 189-190 of the SystemVerilog LRM3.1a
Synchronous Drives
The output (or inout) signals defined in a clocking block are used to
drive values onto their corresponding signals in the DUT at a specified
time. That is, the corresponding signal changes value at the indicated
clocking event as indicated by the output skew.
Note: For the syntax for specifying a synchronous drive, see section
15.14 of the SystemVerilog LRM 3.1a.
Consider the following clocking block and synchronous drives:
clocking cb1 @(posedge clk);
default output #2;
input #2 output #0 a = a1;
output b = b1;
endclocking
initial
begin
@ (cb1); //synchronising with clocking event
cb1.a <= 0; //drive at first posedge
cb1.b <= 0;
//drive after skew on first posedge
##2 cb1.a <= 1;
##1 cb1.b <= 1; //drive after 3 clock cycles
end
sequence seq ;
@(ck1) a ##1 b ;
endsequence
A: assert property (@(ck1) a ##1 b) ;
N: assert property (seq) ;
In this example the clocking block named ck1 is specified a the clock
signal in the sequence and the two assertions. The clocking event in
the clocking block, posedge clk, becomes the clock signal.
Here property prop1 is declared in the clocking block named ck1. The
clock signal for the property is posedge clk. You enter the property
in an assertion by specifying the clocking block and then the property.
Note:
- assert, assume, and cover statements cannot appear in
clocking blocks.
- assert, assume, and cover statements can appear in
programs on an early availability basis.
In an expect statement you specify a clock signal and have the
option of specifying an edge for clocking events and delays, just like
assert and cover statements, but these are not followed by a
sequence, instead there is just a clock delay and an expression. There
are action blocks that execute based on the truth or falsity of the
expression. The clock delay can be a range of clocking events, and
VCS evaluates the expression throughout that range. You can specify
that the clock delay and evaluation of the expression must repeat a
number of times (you cant both have a range of clocking events and
also use repetition).
Where:
e1:
Is an instance name for the expect statement. You can use any
unique name you want, followed by a colon (:).
expect
else
begin
bit1=0;
$display("failure at %0t in %m\n",$time);
end
e2: expect (@(posedge clk) ##[1:9] in1 && in2)
begin
bit1=1;
$display("success at %0t in %m\n",$time);
end
else
begin
bit1=0;
$display("failure at %0t in %m\n",$time);
end
e3: expect (@(posedge clk) ##1 in1 && in2 [*5])
begin
bit1=1;
$display("success at %0t in %m\n",$time);
end
else
begin
bit1=0;
$display("failure at %0t in %m\n",$time);
end
end
endprogram
Virtual Interfaces
A Virtual Interface (VI) allows a variable to be a reference to an actual
instance of an interface. VCS classifies such a variable with the Virtual
Interface data type. Here is an example:
interface SBus;
logic req, grant;
endinterface
module m;
SBus sbus1();
SBus sbus2();
.
.
.
endmodule
program P;
virtual SBus bus;
initial
begin
bus = m.sbus1;
Scope of Support
VCS supports virtual interface declarations in the following locations:
Variables with the virtual interface data type can be either of the
following:
The semantic meaning is the same as in the example above with the
difference that sb is now a reference only to a portion of Sbus and
1
2
@(x.d); $display($time,,x.d);
@(x.d); $display($time,,x.d);
1
2
Clocking Block
Similarly to modport, a clocking block inside interface instance can
be referred to by a virtual variable:
interface SyncBus(input bit clk);
wire w;
clocking cb @(posedge clk);
output w;
endclocking
endinterface
....
program P;
virtual SyncBus vi;
...
initial vi.cb.w <= 1;
...
endprogram
Event Expression/Structure
Consider SyncBus as defined in the section Clocking Block .
task wait_on_expr(virtual SyncBus vi1, virtual SyncBus vi2);
@(posedge (vi1.a & vi2.a))
$display(vi1.b, vi2.b);
endtask
Null Comparison
We support:
NULL assignment
virtual vi = 0;
Comparison of vi variables
By definition, vi1 == vi2 iff they refer to the same instance of an
interface (or both NULL).
Coverage
The VCS implementation of SystemVerilog supports the
covergroup construct. Covergroups are specified by the user. They
allow the system to monitor values and transitions for variables and
signals. They also enable cross coverage between variables and
signals.
VCS collects all the coverage data during simulation and generates
a database. VCS provides a tool to read the database and generate
text or HTML reports.
The enumerated data type colors, with members named red, blue,
and yellow (whose default values are 0, 1, and 2).
The keyword bins specifies one or more bins for coverage data.
The name some_name is an identifier for all the bins. It is the root
bin name.
The range of value follows the equal sign = and is in the nested
set of curly braces { }. This range is 1 through 20. The range is
always specified as lowest_value:highest_value.
Coverage point data will have 20 bins, the first named some_name_1
and the last named some_name_20.
You can specify different bins for different value ranges, for example:
coverpoint data
{
bins first_ten = {[1:10]};
bins second_ten = {[11:20]};
}
Here the coverage information about when the coverage point data
has the values 1 to 10 is in the bin named first_ten, and the information
about when data has the values from 11 to 20 is in the bin named
second_ten.
You can specify a default bin with the default keyword, for example:
coverpoint data
{
bins bin1 = {[1:5]};
bins bin2 = {[6:10]};
bins bin3 = default;
}
In this example coverage information about when data has the values
1-10 is in bins bin1 and bin2, information about all other values is in
bin3.
You can specify a list of value ranges for example:
coverpoint data
{
bins bin1 = {[0:3],5,7,[9:10]};
bins bin2 = {4,6,8};
bins bin3 = default;
}
{
bins bin1 = {[low:high]};
}
endgroup
cg1 cg1_1 = new(0,10);
In covergroup cg1 there are two coverpoints labeled bit1 and bit2. In
addition, there is the following:
1. the bit1Xbit2 identifier for the cross.
There are 16 cross coverage bins, one for each possible combination.
In this example, the respective cross coverage bins, bin1, bin2, and
bin3 receive data whenever the corresponding right hand side binsof
expressions are satisfied. For example, bin1 receives data when
any bin of cp1 whose value range overlaps with the range [0:7]
receives data. In this case bin1 receive data whenever bin
lowcp1vals of coverpoint cp1 receives data.
Similarly, cross bin, bin2 receives data whenever either bin
hicp1vals, of coverpoint cp1, receives data, or bin hicp2vals, of
cover point cp2, receives data. Cross bin, bin3 receives data if any
bin of cover point cp1, whose value range overlaps with the range
[0:1], receives data, and, for the same sample event occurrence, any
bin of cover point cp2, whose value range overlaps with the range
[0:3], also receives data. In this example, cross bin bin3 receives
data when bin lowcp1vals, of cover point cp1, receives data, and
bin lowcp2vals, of cover point cp2, also receives data.
Coverage Options
You can specify options for the coverage of a covergroup with the
type_option.option=argument keyword and argument for
specifying options, for example:
covergroup cg2 @(negedge clk);
type_option.weight = 3;
type_option.goal = 99;
type_option.comment = "Comment for cg2";
cp3 : coverpoint bit3;
cp4 : coverpoint bit4;
endgroup
type_option.comment = "string";
A comment in the report on the covergroup.
You can also apply these option to coverage points, for example:
covergroup cg1 @(posedge clk);
cp1 : coverpoint bit1
{
type_option.weight = 333;
type_option.goal = 50;
type_option.comment = "Comment for bit1";
}
cp2 : coverpoint bit2;
endgroup
instance_name.option.at_least=integer
Specifies the minimum number of hits in a bin for VCS to consider
the bin covered.
instance_name.option.auto_bin_max=integer
Specifies the maximum number of bins for a coverage point when
you dont define the bins for a coverage point.
instance_name.option.detect_overlap=boolean
The boolean argument is 1 or 0. When boolean is 1, VCS
displays a warning message when there is an overlap between
the range list or transitions list of two bins for the coverage point.
instance_name.option.name[=string]
This option is used to specify a name for the covergroup instance.
If a name is not specified, a name is automatically generated.
instance_name.option.per_instance=boolean
The boolean argument is 1 or 0. When boolean is 1, VCS keeps
track of coverage data for the instance.
get_coverage()
get_inst_coverage()
SystemVerilog Testbench Constructs
1-195
set_inst_name(string)
sample()
stop()
start()
get_coverage()
Calculates the coverage number for the covergroup type (see
page 193 for definition). Return type: real.
Below is an example of using get_coverage() to calculate the
coverage number of a covergroup:
program test();
reg clk = 0;
reg [2:0] var = 3'b001;
class A;
covergroup covType @(clk); //covergroup, covType, defined
//in class A,
cp1: coverpoint var {
bins s0 = {[ 0 : 2]} ;
bins s1 = { 3 };
bins s2 = { 4 };
bins s3 = { 5 };
bins s4 = { 6 };
bins s5 = { 7 };
}
endgroup
function new;
covType = new(); //instantiate the embedded covergroup
endfunction
endclass
A A_inst;
initial begin
get_inst_coverage()
Calculates the coverage number for coverage information related
to the covergroup instance. Return type: real.
program test();
reg clk = 0;
reg [2:0] var = 3'b001;
covergroup covType (input integer param1) @(clk);
cp1: coverpoint var {
bins s0 = { [ 0 : param1] } ;
bins s1 = { 3 };
bins s2 = { 4 };
bins s3 = { 5 };
}
endgroup
covType cov1;
initial begin
repeat (5) begin
#5 clk = ~clk;
var = var + 1;
$display("var=%b coverage=%f\n", var,
cov1.get_inst_coverage());
end
end
initial
cov1 = new(2);
endprogram
var=101 coverage=-1.000000
var=110 coverage=-1.000000
set_inst_name(string)
The instance name is set to string. Return type: void.
In the example below, cov1 is the name of an instance that is of type
covType, declared as such (covType cov1; cov1 = new(2);).
The name is changed to new_cov1 after calling
set_inst_name("new_cov1").
program test();
reg clk = 0;
reg [2:0] var = 3'b001;
covergroup covType (input integer param1) @(clk);
cp1: coverpoint var {
bins s0 = { [ 0 : param1] } ;
bins s1 = { 3 };
}
endgroup
covType cov1;
initial
begin
cov1 = new(2);
$display("Original instance name is %s\n",
cov1.option.name);
cov1.set_inst_name("new_cov1");//change instance name
$display("Instance name after calling set_inst_name()
is %s\n", cov1.option.name);
end
endprogram
sample()
sample() triggers sampling of the covergroup instance. Return
void.
program test();
reg clk = 0;
reg [2:0] var = 3'b001;
covergroup covType ();
cp1: coverpoint var {
bins s0 = { 0 };
bins s1 = { 1 };
bins s2 = { 2 };
bins s3 = { 3 };
bins s4 = { 4 };
bins s5 = { 5 };
bins s6 = { 6 };
bins s7 = { 7 };
}
endgroup
covType cov1;
initial
cov1 = new();
initial begin
repeat (10) begin
#10 clk = ~clk;
var = var + 1;
end
end
initial begin
repeat (40) begin
#3 cov1.sample();
end
end
SystemVerilog Testbench Constructs
1-200
endprogram
stop()
When called, collecting of coverage information is stopped for that
instance. Return type: void. See the get_coverage(), stop(), start()
example on page 201.
start()
When called, collecting of coverage information resumes for that
instance. Return type: void. See the get_coverage(), stop(), start()
example on page 201.
get_coverage(), stop(), start() example
program test();
reg clk = 0;
reg [2:0] var = 3'b001;
covergroup covType (input integer param1) @(clk);
cp1: coverpoint var {
bins s0 = { [ 0 : param1] } ;
bins s1 = { 3 };
bins s2 = { 4 };
bins s3 = { 5 };
bins s4 = { 6 };
bins s5 = { 7 };
}
endgroup
covType cov1;
initial begin
repeat (10) begin
#5 clk = ~clk;
var = var + 1;
$display("var=%b covergae=%f\n", var,
cov1.get_coverage());
end
end
initial
begin
cov1 = new(2);
cov1.stop();
cov1.option.weight = 5;
#30 cov1.start();
end
endprogram
OUTPUT:
var=010 covergae=0.000000
var=011 covergae=0.000000
var=100 covergae=0.000000
var=101 covergae=0.000000
var=110 covergae=0.000000
var=111 covergae=0.000000
var=000 covergae=16.666666
var=001 covergae=33.333332
var=010 covergae=33.333332
var=011 covergae=33.333332
an HTML file
Expected
Covered
Percent
Goal
Weight
Total
3
3
100.00
cp1
3
3
100.00
100
1
---------------------------------------------------------Summary for variable cp1
Expected Covered Percent
User Defined Bins
3
3
100.00
User Defined Bins for cp1
Bins name
auto_yellow
auto_blue
auto_red
count at least
2
1
2
1
4
1
The coverage point, in this case variable my_color, reached all its
possible values.
There were four hits for bin auto_red, this means that during
simulation, there were four clocking events (rising edge on signal
clk) where VCS detected that the value of the variable my_color
was red.
The report has links to other .html reports. Click on them to see the
appropriate information.
Please refer to the Unified Report generator for more details
Table 1-3
Database
pci_test
pci_test_gen_1
pci_test_gen_2
pci_test_gen_n
You can disable this method of ensuring database backup and force
VCS MX to always overwrite an existing coverage database. To do
this, use the following system task::
$coverage_backup_database_file (flag );
In order to not save the coverage data to a database file (for example,
if there is a verification error), use the following system task:
$coverage_save_database (flag );
The commands above direct VCS to find the coverage data for the
specified instance name in the database, and load it into the coverage
instance.
In the Example 1-2, there is a SystemVerilog class MyClass with an
embedded covergroup covType. Two objects obj1 and obj2 are
instantiated, each with the embedded covergroup covType. VCS
will find the coverage information for the coverage instance
obj1:covType from the database file Run1, and load this coverage
data into the newly instantiated obj1 object. Note that the object
obj2 will not be affected as part of this load operation.
Example 1-20
class MyClass;
integer m_e;
covergroup covType @m_e;
cp1 : coverpoint m_e;
endgroup
endclass
...
MyClass obj1 = new;
$covgLoadInstFromDbTest(obj1,"Run1");
MyClass obj2 = new;
Note:
The compile time or runtime options -cm_dir and -cm_name will
over write the calls to coverage_set_test_database_name
and loading coverage data tasks.
-cm_dir directory_path_name
-cm_name filename
associative Array
dynamic Array
smart Queue
string
event
class
Use Model
The $vcsmemprof() task can be called from the CLI or the UCLI
interface. The syntax for $vcsmemprof() is as follows:
$vcsmemprof("filename", "w|a+");
filename
Name of the file where the memory profiler dumps the report.
w | a+
w and a+ designate the mode in which the file is opened. Specify
w for writing and a+ for appending to an existing file.
UCLI Interface
Compile-time
The dynamic memory profiler is enabled only if you specify +dmprof
on the VCS compile-time command line:
vcs -ntb -sverilog +dmprof dut_filename.v testbench_filename.sv \
[-debug | -debug_all]
Note:
Use the -sverilog compile-time option is used when compiling
SystemVerilog code.
Runtime
At runtime, invoke $vcsmemprof() from the UCLI command line
prompt as follows:
simv -ucli //Invokes the ucli prompt
ucli>call {$vcsmemprof("memprof.txt", "w|a+")}
CLI Interface
Compile-time
The dynamic memory profiler is enabled only if you specify +dmprof
on the VCS compile-time command line:
Note:
Use the -sverilog compile-time option is used when compiling
SystemVerilog code.
Runtime
At runtime, when you invoke $vcsmemprof() from the CLI command
line prompt you can make the call to $vcsmemprof() at any point
during the simulation:
simv -s //Invokes the cli prompt
cli_0>$vcsmemprof("memprof.txt", "w|a+")
The memory profiler reports the memory consumed by all the active
instances of the different dynamic data types. As noted above, the
memory profiler report is dumped in the filename specified in the
$vcsmemprof() call.
Incremental Profiling
Each invocation of $vcsmemprof() appends the profiler data to the
user specified file. The time at which the call is made is also reported.
This enables you to narrow down the search for any memory issue.
integer arr1[*];
arr1 = new[500];
delay(5);
}
task t2() {
integer arr2[*];
arr2 = new[500];
delay(10);
}
program main {
fork
{
t1();
}
{
t2();
}
join all
}
3. Program View
Reports the dynamic memory consumed in each SV program in
the system.
4. Program To Construct View
a. Task-Function-Thread section
Reports the total dynamic memory in each active task, function
and thread in this module/program.
b. Class Section
Reports the total dynamic memory consumed in each class in
this module/program.
c. Dynamic data Section
Reports the total memory consumed in each of dynamic
testbench data types - associative arrays, dynamic arrays,
queues, events, strings, in the module/program.
5. Module To Construct View:
Same as Program To Construct View.
Example:
import "DPI" context task c_task(input int addr);
program top;
initial c_task(1000);
initial c_task(2000);
endprogram
#include <svdpi.h>
void c_task(int addr) {
...
}
vcs -sverilog top.sv c_task.c
Example:
import "DPI" task c_test(int addr);
initial c_task(1000);
export "DPI" task sv_add;
function int sv_add(input int a, input int b);
sv_add = a+b;
endtask
#include <svdpi.h>
extern void sv_add(int, int);
void c_task(int addr) {
...
sv_add(addr, 10);
...
}
vcs -sverilog top.sv c_task.c
Note:
You can export all SystemVerilog functions except class member
functions.
Limitations
In the current implementation of the DPI, import and export functions
are supported with some restrictions on the following data types:
Include Files
The SystemVerilog 3.1a LRM defines the C layer for the DPI. The
C-layer of the DPI provides two include files:
svGetNameFromScope
svAckDisabledState
svGetCallerInfo
svIsDisabledState
SystemVerilog File
This SV code calls C_test(), which then calls the blocking APB_Write
task in SystemVerilog. The APB_Write task has a simple semaphore
to ensure unique access.
import "DPI" context task c_test(input int base_addr);
program top;
semaphore bus_sem = new(1);
export "DPI" task apb_write;
task apb_write(input int addr, data);
bus_sem.get(1);
#10 $display("VLOG : APB Write : Addr = %x, Data = %x
", addr, data);
#10 ifc.addr <= addr;
#10 ifc.data <= data;
bus_sem.put(1);
endtask
initial begin
fork
c_test(32'h1000);
c_test(32'h2000);
join
end
endprogram