0% found this document useful (0 votes)
33 views265 pages

SV Notes

Uploaded by

Shubham R Ghodke
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33 views265 pages

SV Notes

Uploaded by

Shubham R Ghodke
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 265

SystemVerilog

Introduction:

SystemVerilog is an extension of Verilog.

Gateway Design Automation introduced Verilog as an evolutionary HDL


in 1985.

Verilog language stemmed primarily from two earlier languages,

 HILO-2
 Occam parallel-processing language

In 1990, Cadence placed the Verilog language in the public domain,


and Open Verilog International (OVI) formed to manage the language.

Cadence released the Verilog-XL user manual as the basis for the
first Language Reference Manual. This manual became known as OVI
Verilog 1.0.

In 1993, OVI released Verilog 2.0 to the IEEE, and in 1995 this
became IEEE Std. 1364.

The IEEE working group released a revised standard in March of 2002,


known as IEEE 1364-2001.

A revised version was released in 2003, known as IEEE 1364-2001


Revision C.

In 1998, the original developers of Verilog and HILO-2 formed Co-Design


Automation and created Superlog Language.

Superlog’s goal is to integrate verification features into the Verilog


language and create the first hardware design and verification language.

The new LRM for extensions to Verilog received the


name SystemVerilog 3.0, which Accellera approved as a standard in
June 2002.
Concurrently, Synopsys announced that it was donating several
verification technologies to the SystemVerilog effort.

The donations included testbench constructs based on Vera, OpenVera


assertions, Synopsys’ VCS DirectC simulation interface to C and C++, and
a coverage application programming interface that provides links to
coverage metrics.

On 2 June 2003, Accellera announced that its board and technical


committee members had approved the SystemVerilog 3.1

In 2005, SystemVerilog was adopted as IEEE Standard 1800-2005

In 2009, the standard was merged with the base Verilog (IEEE 1364-2005)
standard, creating IEEE Standard 1800-2009.

Error corrections and clarification of a few aspects of IEEE Std 1800-2009


lead to the release of IEEE Standard 1800-20012.

The current version is the IEEE standard 1800-2017.

SystemVerilog can be divided into two distinct based on its roles,

 SystemVerilog for design is an extension of Verilog-2005


 SystemVerilog for verification
Evolution of SystemVerilog
SystemVerilog Components

SystemVerilog language is a combination of concepts of multiple


languages.

SystemVerilog language components are,

 Concepts of Verilog HDL


 Testbench constructs based on Vera
 OpenVera assertions
 Synopsys’ VCS DirectC simulation interface to C and C++
 A coverage application programming interface that provides links
to coverage metrics
SystemVerilog language components
SystemVerilog for Verification

Testbench or Verification Environment is used to check the functional


correctness of the Design Under Test (DUT) by generating and driving a
predefined input sequence to a design, capturing the design output and
comparing with-respect-to expected output.

The verification environment can be written by using SystemVerilog


concepts.

SystemVerilog TestBench
About TestBench

TestBench or Verification environment is a group of classes or


components. where each component is performing a specific operation.
i.e, generating stimulus, driving, monitoring, etc. and those classes will
be named based on the operation.

TestBench Components

Name Type Description


Defines the pin level activity generated by agent (to
drive to DUT through the driver) or the activity has
transaction class
to be observed by agent (Placeholder for the activity
monitored by the monitor on DUT signals)
Generates the stimulus (create and randomize the
generator class
transaction class) and send it to Driver
Receives the stimulus (transaction) from a generator
driver class and drives the packet level data inside the
transaction into pin level (to DUT
Observes pin level activity on interface signals and
monitor class converts into packet level which is sent to the
components such as scoreboard
An agent is a container class, which groups the
agent class class’s (generator, driver, and monitor) specific to an
interface or protocol
Receives data items from monitors and compares
them with expected values.
scoreboard class
Expected values can be either golden reference
values or generated from the reference model
The environment is a container class for grouping
environment class
higher level components like agent’s and scoreboard
The test is responsible for,

 Configuring the testbench


test program  Initiate the testbench components
construction process
 Initiate the stimulus driving

This is the topmost file, which connects the DUT and


TestBench. It consists of DUT, Test and interface
testbench_top class
instances, the interface connects the DUT and
TestBench
TestBench Hierarchy
SV TB Hierarchy
SystemVerilog TestBench Architecture

SV TestBench block diagram

Structural Data Types


wire and reg

The initial state

reg is x unknown, wire is z.


Behavioral Data Types
integer, real, and time

integer Syntax:

integer integer_variable_name;

Syntax

real real_variable_name;

Default value of integer type variable is “x” and Default value


of real type variable is “0”.

Examples:

integer a[0:64] ; // an array of 65 integer values

real float_v ; // a variable to store real value

Syntax

time time_variable_name;

parameter size = 16 ;

logic
it Can be driven by continuous assignments, gates, and modules in
addition to being a variable. declaration:

bit – Unsigned
byte, shortint, int, longint – Signed
unsigned two-state types,
bit single_bit ; // unsigned single bit
bit [31:0] 32_bit ; // 32-bit unsigned integer
signed two-state types,
int integer; // 32-bit signed integer
byte 8_bit ; // 8-bit signed integer
shortint 16_bit ; // 16-bit signed integer
longint 64_bit ; // 64-bit signed integer
unsigned from signed two-state types,
int unsigned integer; // 32-bit unsigned integer
byte unsigned 8_bit; // 8-bit unsigned integer
shortint unsigned 16_bit; // 16-bit unsigned integer
longint unsigned 64_bit; // 64-bit unsigned integer

Void Data Types


This type can be specified as the return type of functions, indicating no
return value.

String
A string data type is variable size, it is a dynamically allocated array of
bytes.
string declaration examples:
string s1 = "Hellow World";
string s2 = {"Hi"," ",s1};
bit [31:0] b = 128;
string s3 = b; // sets 128 to s3

module string_datatype;
//declaration
string s1 = "Hello World";
string s2 = {"Hi,"," ",s1};
bit[31:0]b= 128;
string s3 = b; // sets 128 to s3
initial begin
//display values
$display("String 1 s1 = %0s",s1);
$display("String 2 s2 = %0s",s2);
$display("String 3 s3 = %0d bit b = %0d",s3,b);
end
endmodule

EVENT

event declaration examples,

event e1;
event e2;
event done;

User Defined

The user can define a new type using typedef, as in C. This can then be
instantiated as,

SystemVerilog enum data type


An enumerated type defines a set of named values

enum { red, green, blue, yellow, white, black } Colors;

In the following example value is set for red = 0, blue = 4, white = 10.

enum { red=0, green, blue=4, yellow, white=10, black } Colors;

syntax error.

enum { red=0, green=0, blue=4, yellow, white=5, black=6 } Colors

Defining new data types as enumerated types

A type name can be given so that the same type can be used in many
places.

typedef enum {GOOD, BAD} pkt_type;

pkt_type pkt_a; // named type


enum methods

Method Description
first() returns the value of the first member of the enumeration
last() returns the value of the last member of the enumeration
next() returns the value of next member of the enumeration
next(N) returns the value of next Nth member of the enumeration
prev() returns the value of previous member of the enumeration
prev(N) returns the value of previous Nth member of the enumeration
num() returns the number of elements in the given enumeration
name() returns the string representation of the given enumeration value

module enum_datatype;
//declaration
enum { red, green, blue, yellow, white, black } Colors;

//display members of Colors


initial begin
Colors = Colors.first;

for(int i=0;i<6;i++) begin


$display("Colors :: Value of %0s \t is = %0d",Colors.name,Colors);
Colors = Colors.next;
end
end
endmodule

module enum_datatype;
//declaration
enum { red=0, green, blue=4, yellow, white=10, black } Colors;

//display members of Colors


initial begin
Colors = Colors.first;

for(int i=0;I<6;i++) begin


$display("Colors :: Value of %0s \t is = %0d",Colors.name,Colors);
Colors = Colors.next;
end
end
endmodule

module enum_datatype;
//declaration
enum { red=0, green=1, blue=4, yellow, white=5, black=6 } Colors;

initial begin
Colors = Colors.first;
for(int i=0;i<6;i++)
$display("Colors :: Value of %0s is = %0d",Colors.name(),Colors);
end
endmodule

module enum_datatype;
//declaration
typedef enum {GOOD, BAD} pkt_type;
pkt_type pkt_a;
pkt_type pkt_b;

initial begin
pkt_a = GOOD;
pkt_b=BAD;

if(pkt_a == GOOD)
$display("pkt_a is GOOD packet");
else
$display("pkt_a is BAD packet");

if(pkt_b == GOOD)
$display("pkt_b is GOOD packet");
else
$display("pkt_b is BAD packet");
end
endmodule

module enum_datatype;
//declaration
typedef enum { red=0, green, blue=4, yellow, white=10, black } colors;

enum { a, b, c, d, e, f, g } alphabets;
colors first_set;
colors second_set;

initial begin

first_set = first_set.first();
$display("first_set first color is \t %0s, \t Value
= %0d", first_set.name(),first_set);
first_set = first_set.last();
$display("first_set last color is \t %0s, \t Value
= %0d", first_set.name(),first_set);

second_set = first_set;
$display("second_set color is \t %0s, \t Value
= %0d", second_set.name(),second_set);
second_set =second_set.prev(2);
$display("second_set color is \t %0s, \t Value
= %0d", second_set.name(),second_set);
second_set =second_set.next(2);
$display("second_set color is \t %0s, \t Value
= %0d", second_set.name(),second_set);

$display("Number of members in alphabets is


\t %0d",alphabets.num());
$display("Default First members in alphabets is \t %0s , \t value
is %0d",alphabets.name(),alphabets);
alphabets=alphabets.next;
$display("Next members in alphabets is \t %0s , \t value
is %0d",alphabets.name(),alphabets);
alphabets=alphabets.last;
$display("Last members in alphabets is \t %0s , \t value
is %0d",alphabets.name(),alphabets);
alphabets=alphabets.prev(3);
$display("3rd members from last in alphabets is \t %0s , \t value
is %0d",alphabets.name(),alphabets);
end
endmodule

Array
An array is a collection of variables, all of the same type, and accessed
using the same name plus one or more indices.
int array1 [6]; //fixed size single dimension array
int array2 [5:0]; //fixed size single dimension array
int array3 [3:0][2:0]; //fixed size multi dimension array
bit [7:0] array4[2:0]; //unpacked array declaration
bit [2:0][7:0] array5; //packed array declaration
bit [2:0][7:0] array6 [3]; //mixed packed and unpacked array

Single dimensional array

int array1 [6]; //Compact declaration


int array2 [5:0]; // Verbose declaration

Multidimensional array

Multidimensional arrays are also known as an array of arrays.


TWO-DIMENSIONAL ARRAY

int arr[2][3];

This array has total 2*3 = 6 elements.

THREE-DIMENSIONAL ARRAY

int arr[2][2][2];

This array has total 2*2*2 = 8 elements.

TWO-DIMENSIONAL ARRAY DECLARATION

int array3 [2:0][3:0];

The data in a two-dimensional array is stored in a tabular form as shown


in the below diagram.

array assignment

array1 = '{0,1,2,3,4,5};
array2 = '{0,1,2,3,4,5};
array3 = '{ '{0,1,2,3},'{4,5,6,7},'{8,9,10,11}};
Fixed Size Array Example
This example shows array declaration and array manipulation using for
and foreach loop.

module fixedsize_array;
//declaration of array’s
int array1[6]; //single dimension array
int array2[5:0]; //single dimension array
int array3[2:0][3:0]; //multi dimension array
int array4[4:0];

initial begin
//array initialization
array1 = '{0,1,2,3,4,5};
array2 = '{0,1,2,3,4,5};
array3 = '{'{0,1,2,3},'{4,5,6,7},'{8,9,10,11}};

//displaying array elements


$display("-------displaying array1-------");
foreach(array1[i]) $display("\t array1[%0d] = %0d",i,array1[i]);

$display("-------displaying array2-------");
for(int i=0;i<6;i++) $display("\t array2[%0d]= %0d",i,array2[i]);

$display("-------displaying array3-------");
foreach(array3[i,j]) $display("\t array3[%0d][%0d] = %0d",i,j,array3[i][j]);

$display("-------displaying uninitialized array4-------");


for(int i=0;i<5;i++) $display("\t array4[%0d] = %0d",i,array4[i]);
end
endmodule
Packed and Unpacked array in SystemVerilog

 The term packed array is used to refer to the dimensions declared


before the data identifier name
 The term unpacked array is used to refer to the dimensions
declared after the data identifier name

bit [7:0] temp_var; // packed array of bit types


real temp_var [7:0]; // unpacked array of real types
Packed array example
bit [2:0] [7:0] array5;

The below diagram shows storing packed array as a contiguous set of


bits.

UnPacked array

 Unpacked arrays can be of any data type.


 Unpacked arrays shall be declared by specifying the element
ranges after the identifier name.
 An unpacked array may or may not be so represented as
a contiguous set of bits.

Unpacked array example

bit [7:0] array4[2:0];


Dynamic array Syntax

data_type array_name [ ];
Dynamic array methods

new[ ] –> allocates the storage.


size( ) –> returns the current size of a dynamic array.
delete( ) –> empties the array, resulting in a zero-sized array.

Example:

//declaration
bit [7:0] d_array1[ ];
int d_array2[ ];

//memory allocation
d_array1 = new[4]; //dynamic array of 4 elements
d_array2 = new[6]; //dynamic array of 6 elements

//array initialization
d_array1 = {0,1,2,3};
foreach(d_array2[j]) d_array2[j] = j;
resize the dynamic array

//Change the length of the array after declaration/initialization


d_array1 = new[10]; //dynamic array of 10 elements
//Allocate 6 new elements and retain values of 4 elements.
d_array1 = new[10](d_array1);

Delete the dynamic array

//delete array
d_array1.delete;
module dynamic_array;
//dynamic array declaration
bit [7:0] d_array1[];
int d_array2[];

initial begin
$display("Before Memory Allocation");
$display("\tSize of d_array1 %0d",d_array1.size());
$display("\tSize of d_array2 %0d",d_array2.size());

//memory allocation
d_array1 = new[4];
d_array2 = new[6];
$display("After Memory Allocation");
$display("\tSize of d_array1 %0d",d_array1.size());
$display("\tSize of d_array2 %0d",d_array2.size());

//array initialization
d_array1 = {0,1,2,3};
foreach(d_array2[j]) d_array2[j] = j;

$display("--- d_array1 Values are ---");


foreach(d_array1[i]) $display("\td_aaray1[%0d] = %0d",i, d_array1[i]);
$display("---------------------------------");

$display("--- d_array2 Values are ---");


foreach(d_array2[i]) $display("\td_aaray2[%0d] = %0d",i, d_array2[i]);
$display("---------------------------------");
end

endmodule

Associative array

 Associative array Stores entries in a sparse matrix


 Associative arrays allocate the storage only when it is used, unless
like in the dynamic array we need to allocate memory before
using it
 In associative array index expression is not restricted to integral
expressions, but can be of any type
 An associative array implements a lookup table of the elements of
its declared type. The data type to be used as an index serves as
the lookup key and imposes an ordering

When the size of the collection is unknown or the data space is sparse,
an associative array is a better option.

Dynamic arrays are useful for contiguous collections of variables whose


number changes dynamically.

Array Declaration
data_type array_name [ index_type ];

where:
data_type – data type of the array elements.
array_name – name of the associative array.
index_type – data-type to be used as an index, or *.
* indicates the array is indexed by any integral expression of arbitrary
size.

Array Example

int a_array1[*] ; // associative array of integer (unspecified index)


bit [31:0] a_array2[string]; // associative array of 32-bit, indexed by string
ev_array [myClass]; //associative array of event,indexed by class

SystemVerilog Associative array


Associative Array Methods

Method Description
num() returns the number of entries in the associative array
delete(index) removes the entry at the specified index.exa_array.delete(index)
exists(index) returns 1 if an element exists at the specified index else returns
0
first(var) assigns the value of first index to the variable var
last(var) assigns the value of last index to the variable var
next(var) assigns the value of next index to the variable var
prev(var) assigns the value of previous index to the variable var

Associative Array Examples


num(), first() and last() method’s

Example-1 : Associative Array


Declaration, num(), first() and last() method’s.

module associative_array;
//array declaration
int a_array[*];
int index;

initial begin
//allocating array and assigning value to it
repeat(3) begin
a_array[index] = index*2;
index=index+4;
end

//num() –Associative array method


$display("\tNumber of entries in a_array is %0d",a_array.num());
$display("--- Associative array a_array entries and Values are ---");
foreach(a_array[i]) $display("\ta_array[%0d] \t = %0d",i,a_array[i]);
$display("--------------------------------------------------------");

//first()-Associative array method


a_array.first(index);
$display("\First entry is \t a_array[%0d] = %0d",index,a_array[index]);
//last()-Associative array method
a_array.last(index);
$display("\Last entry is \t a_array[%0d] = %0d",index,a_array[index]);
end
endmodule

Queue in SystemVerilog

A queue is a variable-size, ordered collection of homogeneous elements.

 like a dynamic array, queues can grow and shrink


 queue supports adding and removing elements anywhere

Queues are declared using the same syntax as unpacked arrays, but
specifying $ as the array size. In queue 0 represents the first, and
$ representing the last entries.

A queue can be bounded or unbounded.

 bounded queue – queue with the number of entries limited or


queue size specified
 unbounded queue – queue with unlimited entries or queue size
not specified

Queue Declaration

data_type queue_name[$];

where:
data_type – data type of the queue elements.
queue_name – name of the queue.

Queue Declaration Example


bit queue_1[$]; // queue of bits (unbound queue)
int queue_2[$]; // queue of int
byte queue_3[$:255]; // queue of byte (bounded queue with 256 entries)
string queue_4[$]; // queue of strings
Queue Initialization

queue_1 = {0,1,0,1};
queue_4 = {“Red”,"Blue”,"Green”};
Unbounded Queue

SystemVerilog queue
Bounded Queue

SystemVerilog Bounded queue


Queue Methods

Method Description
size() returns the number of items in the queue
insert() inserts the given item at the specified index position
delete() deletes the item at the specified index position
push_front() inserts the given element at the front of the queue
push_back() inserts the given element at the end of the queue
pop_front() removes and returns the first element of the queue
pop_back() removes and returns the last element of the queue
Queue Methods Example
Unbounded Queue Declaration, Initialization, Size, Insert and Delete
Method

This example shows the declaration and usage Queue methods.

module queues_array;
//declaration
bit [31:0] queue_1[$]; //unbounded queue
string queue_2[$];

initial begin
//Queue Initialization:
queue_1 = {0,1,2,3};
queue_2 = {"Red","Blue","Green"};

//Size-Method
$display("----- Queue_1 size is %0d -----",queue_1.size());
foreach(queue_1[i]) $display("\tqueue_1[%0d] = %0d",i,queue_1[i]);
$display("----- Queue_2 size is %0d -----",queue_2.size());
foreach(queue_2[i]) $display("\tqueue_2[%0d] = %0s",i,queue_2[i]);

//Insert-Method
queue_2.insert(1,"Orange");
$display("----- Queue_2 size after inserting Orange is %0d -----
",queue_2.size());
foreach(queue_2[i]) $display("\tqueue_2[%0d] = %0s",i,queue_2[i]);

//Delete Method
queue_2.delete(3);
$display("----- Queue_2 size after Delete is %0d -----",queue_2.size());
foreach(queue_2[i])$display("\tqueue_2[%0d] = %0s",i,queue_2[i]);
end

endmodule

Queue, push_front(), push_back(), pop_front() and pop_back() Method


SystemVerilog Queue Operations
module queues_array;
//declaration
bit [31:0] queue_1[$];
int lvar;

initial begin
//Queue Initialization:
queue_1 = {0,1,2,3};

//Size-Method
$display("\tQueue_1 size is %0d",queue_1.size());

//Push_front Method
queue_1.push_front(22);
$display("\tQueue_1 size after push_front is %0d",queue_1.size());

//Push_back Method
queue_1.push_back(44);
$display("\tQueue_1 size after push_back is %0d",queue_1.size());

//Pop_front Method
lvar = queue_1.pop_front();
$display("\tQueue_1 pop_front value is %0d",lvar);

//Pop_back Method
lvar = queue_1.pop_back();
$display("\tQueue_1 pop_back value is %0d",lvar);
end
endmodule
Bounded queue declaration and accessing

The number of entries of the bounded queue is limited, push_back to


the bounded queue (after the queue full condition) will not impact any
changes to the queue. push_front to the bounded queue (after the
queue full condition) will delete the last entry from queue and stores a
new entry in the 0th index of the queue.

SystemVerilog Bounded Queue Operations


module queues_array;
//declaration
int queue[$:2];
int index;
int temp_var;

initial begin
//Queue Initialization:
queue = {7,3,1};

$display("Queue elements are,");


$display("\tqueue = %p",queue);
queue.push_back(10);

$display("After push_back Queue elements are,");


$display("\tqueue = %p",queue);

queue.push_front(10);

$display("After push_front Queue elements are,");


$display("\tqueue = %p",queue);
end

endmodule

Blocking Assignment

Blocking assignment statements execute in series order. Blocking


assignment blocks the execution of the next statement until the
completion of the current assignment execution.

Blocking assignment example

In Below Example, a and b is initialized with value 10 and 15 respectively,


after that b is being assigned to a (a value will become 15), and value 20
is assigned to b. After assignment value of a = 15 and b=20.

module blocking_assignment;
//variables declaration
int a,b;
initial begin
$display("-----------------------------------------------------------------");
//initializing a and b
a = 10;
b = 15;

//displaying initial value of a and b


$display("\tBefore Assignment :: Value of a is %0d",a);
$display("\tBefore Assignment :: Value of b is %0d",b);

a = b;
b = 20;

$display("\tAfter Assignment :: Value of a is %0d",a);


$display("\tAfter Assignment :: Value of b is %0d",b);
$display("-----------------------------------------------------------------");
end
endmodule
Blocking assignment example-2

In Below Example, a and b are initialized with value 10 and 15


respectively, after that b is being assigned to a (a value will become 15),
and value 20 is assigned to b. After assignment value of a = 15 and b = 20.

module blocking_assignment;
//variables declaration
int a,b;
int x,y;
initial begin
//initializing a and b
a = 10;
b = 15;

x = a + b;
y = a + b + x;

$display("-----------------------------------------------------------------");
$display("\tValue of x is %0d",x);
$display("\tValue of y is %0d",y);
$display("-----------------------------------------------------------------");
end
endmodule

nonblocking assignment

 non-blocking assignment statements execute in parallel


 In the non-blocking assignment, all the assignments will occur at
the same time. (during the end of simulation timestamp)
Nonblocking assignment example

In the below example,


a and b are initialized with values 10 and 15 respectively, after that b is
being assigned to a (a value will become 15), and value 20 is assigned to
b.
After assignment values expected in a and b are 15 and 20 respectively.
but these values will get assigned only after the simulation time-stamp.

module nonblocking_assignment;

//variables declaration
int a,b;

initial begin //initial block will get executed at starting of simulation


$display("-----------------------------------------------------------------");
//initializing a and b
a = 10;
b = 15;

//displaying initial value of a and b


$display("\tBefore Assignment :: Value of a is %0d",a);
$display("\tBefore Assignment :: Value of b is %0d",b);

a <= b;
b <= 20;

$display("\tAfter Assignment :: Value of a is %0d",a);


$display("\tAfter Assignment :: Value of b is %0d",b);
$display("-----------------------------------------------------------------");
end

final begin //final block will get executed at end of simulation


$display("-----------------------------------------------------------------");
$display("\tEnd of Simulation :: Value of a is %0d",a);
$display("\tEnd of Simulation :: Value of b is %0d",b);
$display("-----------------------------------------------------------------");
end
endmodule
In the below example,
a and b are initialized with value 10 and 15 respectively.
x<=a+b and y<=a+b+x
value of x is sum of a (10) and b (15). -> x=10+15=25.
value of y is sum of a (10) ,b(15) and x (0) -> became at current
simulation time-stamp value of x=0.
new value will get assigned at the end of current time stamp, and new
value will be available only after the current time-stamp). therefore
y=10+15+0=25;

module nonblocking_assignment;
//variables declaration
int a,b;
int x,y;

initial begin
//initializing a and b
a = 10;
b = 15;

x <= a + b;
y <= a + b + x;

$display("-----------------------------------------------------------------");
$display("\tValue of x is %0d",x);
$display("\tValue of y is %0d",y);
$display("-----------------------------------------------------------------");
end

final begin
$display("-----------------------------------------------------------------");
$display("\tEnd of Simulation :: Value of x is %0d",x);
$display("\tEnd of Simulation :: Value of y is %0d",y);
$display("-----------------------------------------------------------------");
end

endmodule

unique if
Unique if evaluates all the conditions parallel.

In the following conditions simulator issue a run time error/warning,

 More than one condition is true


 No condition is true or final if doesn’t have corresponding else

Unique if example’s

In the below example,


More than one condition is true.

value of a=10, b=20 and c=40. conditions a<b and a<c are true,

Therefore on execution, simulator issue a run time warning.


“RT Warning: More than one condition match in ‘unique if’ statement.”

module unique_if;
//variables declaration
int a,b,c;

initial begin
//initialization
a=10;
b=20;
c=40;

unique if ( a < b ) $display("\t a is less than b");


else if ( a < c ) $display("\t a is less than c");
else $display("\t a is greater than b and c");
end
endmodule

In below example,
No condition is true and final if doesn’t have corresponding else.
value of a=50, b=20 and c=40, conditions a<b and a<c are false,

Therefore on execution, simulator issue a run time warning.


“RT Warning: No condition matches in ‘unique if’ statement.”
module unique_if;
//variables declaration
int a,b,c;

initial begin
//initialization
a=50;
b=20;
c=40;

unique if ( a < b ) $display("\t a is less than b");


else if ( a < c ) $display("\t a is less than c");
end

endmodule

Unique if example 3

In below example, value of a=50, b=20 and c=40.


conditions a<b and a<c are false, so else part is true, there is no
simulator run time warning.

module unique_if;

//variables declaration
int a,b,c;

initial begin
//initialization
a=50;
b=20;
c=40;

priority if ( a < b ) $display("\t a is less than b");


else if ( a < c ) $display("\t a is less than c");
else $display("\t a is greater than b and c");
end

endmodule

priority if
Priority if evaluates all the conditions in sequential order.

In the following conditions simulator issue a run time error/warning

 No condition is true or final if doesn’t have corresponding else

priority if examples

In the below example,


No condition is true or final if doesn’t have corresponding else.
value of a=50,b=20 and c=40. conditions a<b and a<c are false,

therefore simulator issue a run time warning.


“RT Warning: No condition matches in ‘priority if’ statement.”

module priority_if;
//variables declaration
int a,b,c;

initial begin
//initialization
a=50;
b=20;
c=40;

priority if ( a < b ) $display("\t a is less than b");


else if ( a < c ) $display("\t a is less than c");
end
endmodule
priority if example 2

In the below example,


value of a=10,b=20 and c=40.
conditions a<b and a<c are true, as it is priority based, simulator
considers the first match. therefore there will be no simulator warning
message.

module priority_if;

//variables declaration
int a,b,c;
initial begin
//initialization
a=10;
b=20;
c=40;

priority if ( a < b ) $display("\t a is less than b");


else if ( a < c ) $display("\t a is less than c");
else $display("\t a is greater than b and c");
end

endmodule

do while loop

A do while loop is a control flow statement that allows code to be


executed repeatedly based on a given condition.

do while loop syntax

do begin
// statement -1
...
// statement -n
end
while(condition);

In do-while,

 the condition will be checked after the execution of statements


inside the loop
 the condition can be any expression.
SystemVerilog do while loop

do-while is similar to while loop but in case of while loop execution of


statements happens only if the condition is true. In a do while,
statements inside the loop will be executed at least once even if the
condition is not satisfied.

do while loop example

module do_while;
int a;

initial begin
$display("-----------------------------------------------------------------");

do
begin
$display("\tValue of a=%0d",a);
a++;
end
while(a<5);

$display("-----------------------------------------------------------------");
end
endmodule

do while loop example 2


module do_while;
int a;

initial begin
$display("-----------------------------------------------------------------");

do
begin
$display("\tValue of a=%0d",a);
a++;
end
while(a>5);

$display("-----------------------------------------------------------------");
end
endmodule

while loop SystemVerilog

A while loop is a control flow statement that allows code to be executed


repeatedly based on a given condition.

while loop syntax

while(condition) begin
// statement -1
...
// statement -n
end

In a while,

 Execution of statements within the loop happens only if the


condition is true
SystemVerilog while loop
while loop example

module while_loop;
int a;

initial begin
$display("-----------------------------------------------------------------");

while(a<5)
begin
$display("\tValue of a=%0d",a);
a++;
end
$display("-----------------------------------------------------------------");
end
endmodule
while loop example 2

module while_loop;
int a;
initial begin
$display("-----------------------------------------------------------------");

while(a>5)
begin
$display("\tValue of a=%0d",a);
a++;
end
$display("-----------------------------------------------------------------");
end
endmodule

foreach loop

SystemVerilog foreach specifies iteration over the elements of an array.


the loop variable is considered based on elements of an array and the
number of loop variables must match the dimensions of an array.

foreach loop syntax

foreach(<variable>[<iterator>]]) begin
//statement - 1
...
//statement - n
end

Foreach loop iterates through each index starting from index 0.

foreach loop example

below example shows,


foreach loop in the single dimensional array.

module for_loop;
int a[4];
initial begin
$display("-----------------------------------------------------------------");
foreach(a[i]) a[i] = i;
foreach(a[i]) $display("\tValue of a[%0d]=%0d",i,a[i]);

$display("-----------------------------------------------------------------");
end
endmodule
foreach multidimensional array
Below example shows how to use the foreach loop in a multidimensional
array.

module for_loop;
int a[3][2];

initial begin
$display("-----------------------------------------------------------------");
foreach(a[i,j]) a[i][j] = i+j;
foreach(a[i,j]) $display("\tValue of a[%0d][%0d]=%0d",i,j,a[i][j]);
$display("-----------------------------------------------------------------");
end
endmodule
for loop

SystemVerilog for loop is enhanced for loop of Verilog.

In Verilog,

 the control variable of the loop must be declared before the loop
 allows only a single initial declaration and single step assignment
within the for a loop

SystemVerilog for loop allows,

 declaration of a loop variable within the for loop


 one or more initial declaration or assignment within the for loop
 one or more step assignment or modifier within the for loop

for loop syntax

for(initialization; condition; modifier) begin


//statement - 1
...
//statement - n
end

Initialization: executed first, and only once. This allows the user to
declare and initialize loop control variables.
Condition: the condition is evaluated. If it is true, the body of the loop is
executed, else the flow jumps to the statement after the ‘for’ loop.

Modifier: at the end of each iteration it will be executed, and execution


moves to Condition.

for loop example

Below example shows the declaration of a loop variable within the for
loop.

module for_loop;
initial begin
$display("-----------------------------------------------------------------");
for(int i=0;i<5;i++) $display("\t Value of i = %0d",i);
$display("-----------------------------------------------------------------");
end
endmodule
multiple initializations in for loop

Below example shows the declaration and initialization of two


variables i and j in for loop.

module for_loop;

initial begin
$display("-----------------------------------------------------------------");

for ( int j=0,i=4;j<8;j++) begin


if(j==i) $display("\tValue j equals to Value of i. j=%0d i=%0d",j,i);
end

$display("-----------------------------------------------------------------");
end

endmodule
multiple modifiers in for loop

Below example shows the use of two modifiers j++ and i– within the for
loop.
module for_loop;

initial begin
$display("-----------------------------------------------------------------");

for ( int j=0,i=7;j<8;j++,i--) begin


$display("\tValue j=%0d Value of i=%0d",j,i);
end

$display("-----------------------------------------------------------------");
end

endmodule
repeat and forever loop
repeat loop

repeat will execute the statements within the loop for a loop variable
number of times.

if the loop variable is N, then the statements within the repeat block will
be executed N number of times.

repeat loop syntax

repeat(<variable>) begin
//statement - 1
...
//statement - n
end

statements 1-n will be executed for a variable value number of times.

repeat loop example

In the below example,


repeat loop value is 4, so the statements within the repeat loop will be
executed for 4 times.

module repeat_loop;
int a;
initial begin
$display("-----------------------------------------------------------------");

repeat(4) begin
$display("\tValue of a=%0d",a);
a++;
end
$display("-----------------------------------------------------------------");
end
endmodule
forever loop

As the name says forever loop will execute the statements inside the
loop forever.
It can be said as indefinite iteration.

forever loop syntax

forever begin
//statement - 1
...
//statement - n
end
forever loop example

module forever_loop;
int a;
initial begin
$display("-----------------------------------------------------------------");

forever begin
$display("\tValue of a=%0d",a);
a++;
#5;
end

$display("-----------------------------------------------------------------");
end
initial begin
#20 $finish;
end
endmodule

break and continue

break

The execution of a break statement leads to the end of the loop.

break shall be used in all the loop constructs (while, do-while, foreach,
for, repeat and forever).

syntax

break;
break in while loop

module break_in_while_loop;
int i;

initial begin
$display("-----------------------------------------------------------------");
i = 8;

while(i!=0)
begin
$display("\tValue of i=%0d",i);
if(i == 4)
begin
$display("\tCalling break,");
break;
end
i--;
end

$display("-----------------------------------------------------------------");
end
endmodule

break in do while loop

module break_in_do_while_loop;
int i;

initial begin
$display("-----------------------------------------------------------------");
i = 8;

do begin
$display("\tValue of i=%0d",i);
if(i == 4) begin
$display("\tCalling break,");
break;
end
i--;
end
while(i!=0);

$display("-----------------------------------------------------------------");
end
endmodule

break in a foreach loop


module foreach_loop_break;
int a[4];

initial begin
$display("-----------------------------------------------------------------");

foreach(a[i]) a[i] = i;
foreach(a[i]) begin
$display("\tValue of a[%0d]=%0d",i,a[i]);
if(i == 2) begin
$display("\tCalling break,");
break;
end
end

$display("-----------------------------------------------------------------");
end
endmodule
break in for loop

In below example,
when the loop value equals 4, the break is called this leads to the end of
the loop.

module break_in_loop;

initial begin
$display("-----------------------------------------------------------------");

for(int i=0;i<8;i++) begin


$display("\tValue of i=%0d",i);
if(i == 4) begin
$display("\tCalling break,");
break;
end
end

$display("-----------------------------------------------------------------");
end
endmodule
break in repeat loop

module repeat_loop_break;
int i;

initial begin
$display("-----------------------------------------------------------------");
repeat(5) begin
$display("\tValue of i=%0d",i);
if(i == 2) begin
$display("\tCalling break,");
break;
end
i++;
end

$display("-----------------------------------------------------------------");
end
endmodule
break in forever loop

module forever_loop_break;
int i;

initial begin
$display("-----------------------------------------------------------------");
i = 5;
forever begin
$display("\tValue of i=%0d",i);
if(i == 2) begin
$display("\tCalling break,");
break;
end
i++;
end

$display("-----------------------------------------------------------------");
end
endmodule
Continue in SystemVerilog

Execution of continue statement leads to skip the execution of


statements followed by continue and jump to next loop or iteration
value.

syntax

continue;
Continue example

In below example,

when ever the loop value is with in 3 to 6, continue statement will be


executed, this leads to skip the execution of display statement after the
continue.

module continue_in_loop;

initial begin
$display("-----------------------------------------------------------------");

for(int i=0;i<8;i++) begin

if((i > 2) && (i < 7))begin


$display("\t\tCalling continue,");
continue;
end

$display("\t\tAfter Continue\t:: Value of i=%0d",i);


end

$display("-----------------------------------------------------------------");

end

endmodule
event control

Any change in a variable or net can be detected using the @ event


control.

A change of any bits of a multi-bit variable shall trigger the event control.
SystemVerilog adds an iff qualifier to the @ event control.

event control example

In the below example,


always block will get executed at any change in variables value within
the always block.

module event_ctrl;
bit [2:0] a,b;

//always block will be executed at any value change in a and b


always @(*)
begin
$display($time,"\tValue of a=%0d, b=%0d",a,b);
end

initial begin
#2 a=5;
#3 b=2;
#2 a=1;
#1 b=7;
#2;
$finish;
end
endmodule

event control example 2

In below example,
always block will be executed at every posedge of the clk signal.

module event_ctrl;
bit clk;
always #2 clk = ~clk;
//always block will be executed at every posedge of clk signal
always @(posedge clk)
begin
$display($time,"\tInside always block");
end

initial begin
#20 $finish;
end
endmodule

event control example 3

In below example,
always block will be executed at every negedge of the clk signal.

module event_ctrl;
bit clk;
always #2 clk = ~clk;

//always block will be executed at every negedge of clk signal


always @(negedge clk)
begin
$display($time,"\tInside always block");
end

initial begin
#20 $finish;
end
endmodule

iff in event control example

In the below example,


block-1 will be executed at the posedge of clk if reset is equals to ‘0’.
block-2 will be executed at every posedge and negedge of the clk signal.

module event_ctrl;
bit clk;
bit reset;
always #2 clk = ~clk;

//at posedge of clk if reset is equals to '0',always block will be executed


always @(posedge clk iff reset == 0)
begin :block-1
$display($time,"\tInside always block");
end :block-1

//always block will be executed at every posedge and negedge of clk signal
always @(posedge reset or negedge reset)
begin :block-2
$display($time,"\tReset Value = %0d",reset);
end :block-2

initial begin
#40 $finish;
end
initial begin
reset = 1;
#7 reset = 0;
#8 reset = 1;
#5 reset = 0;
end
endmodule

fork join

Fork-Join will start all the processes inside it parallel and wait for the
completion of all the processes.
SystemVerilog Fork Join
fork join example

In below example,

fork block will be blocked until the completion of process-1 and Process-
2.

Both process-1 and Process-2 will start at the same time, Process-1 will
finish at 5ns and Process-2 will finish at 20ns. fork-join will be unblocked
at 20ns.

module fork_join;

initial begin
$display("-----------------------------------------------------------------");
fork
//-------------------
//Process-1
//-------------------
begin
$display($time,"\tProcess-1 Started");
#5;
$display($time,"\tProcess-1 Finished");
end

//-------------------
//Process-2
//-------------------
begin
$display($time,"\tProcess-2 Started");
#20;
$display($time,"\tProcess-2 Finished");
end
join
$display($time,"\tOutside Fork-Join");
$display("-----------------------------------------------------------------");
$finish;
end
endmodule
fork join_any

Fork-Join_any will be unblocked after the completion of any of the


Processes.

SystemVerilog Fork Join any


fork join any example
In the below example,
fork block will be blocked until the completion of any of the Process
Process-1 or Process-2.

Both Process-1 and Process-2 will start at the same time, Process-1 will
finish at 5ns and Process-2 will finish at 20ns. fork-join_any will be
unblocked at 5ns.

module fork_join;
initial begin
$display("-----------------------------------------------------------------");
fork
//Process-1
begin
$display($time,"\tProcess-1 Started");
#5;
$display($time,"\tProcess-1 Finished");
end

//Process-2
begin
$display($time,"\tProcess-2 Started");
#20;
$display($time,"\tProcess-2 Finished");
end
join_any

$display($time,"\tOutside Fork-Join");
$display("-----------------------------------------------------------------");
end
endmodule

fork join_none

As in the case of Fork-Join and Fork-Join_any fork block is blocking, but


in case of Fork-Join_none fork block will be non-blocking.
Processes inside the fork-join_none block will be started at the same
time, fork block will not wait for the completion of the Process inside the
fork-join_none.

SystemVerilog Fork Join none


fork join none example

In the below example,

The fork will start Process-1 and Process-2 at the same time, and it will
come out of the block. Process-1 and Process-2 will be executed until the
completion.

module fork_join_none;
initial begin
$display("-----------------------------------------------------------------");
fork
//Process-1
begin
$display($time,"\tProcess-1 Started");
#5;
$display($time,"\tProcess-1 Finished");
end
//Process-2
begin
$display($time,"\tProcess-2 Startedt");
#20;
$display($time,"\tProcess-2 Finished");
end
join_none

$display($time,"\tOutside Fork-Join_none");
$display("-----------------------------------------------------------------");
end
endmodule
wait fork

wait fork; causes the process to block until the completion of all
processes started from fork blocks.

wait fork example

In the below example,

after the completion of Process-1 (i.e, after 5ns) fork-join_any will get
unblocked, the $finish will get called and it ends the simulation.
The simulation will get ended in the middle of the execution of process-2,
this can be avoided with the use of wait-fork.

The problem in this example is overcome in example-2 with the use


of wait fork;

module wait_fork;

initial begin
$display("-----------------------------------------------------------------");
fork
//Process-1
begin
$display($time,"\tProcess-1 Started");
#5;
$display($time,"\tProcess-1 Finished");
end
//Process-2
begin
$display($time,"\tProcess-2 Started");
#20;
$display($time,"\tProcess-2 Finished");
end
join_any
$display("-----------------------------------------------------------------");
$finish; //ends the simulation
end
endmodule
wait fork example 2

In the below example,

wait fork will wait for the completion of the second thread in the fork-
join_any.
for better understanding compare the result of Example-1 and Example-
2

module wait_fork;

initial begin
$display("-----------------------------------------------------------------");
fork
//Process-1
begin
$display($time,"\tProcess-1 Started");
#5;
$display($time,"\tProcess-1 Finished");
end

//Process-2
begin
$display($time,"\tProcess-2 Started");
#20;
$display($time,"\tProcess-2 Finished");
end
join_any
wait fork; //waiting for the completion of active fork threads
$display("-----------------------------------------------------------------");
$finish; //ends the simulation
end
endmodule
disable fork

disable fork; causes the process to kill/terminate all the active processes
started from fork blocks.

disable fork example

In the below example,

On execution of the disable fork, all the active process will get
terminated.
Process-2 of fork-1, Process-1, and Process-2 of fork-2 will get
terminated.

module disable_fork;

initial begin
$display("-----------------------------------------------------------------");

//fork-1
fork
//Process-1
begin
$display($time,"\tProcess-1 of fork-1 Started");
#5;
$display($time,"\tProcess-1 of fork-1 Finished");
end

//Process-2
begin
$display($time,"\tProcess-2 of fork-1 Started");
#20;
$display($time,"\tProcess-2 of fork-1 Finished");
end
join_any
//fork-2
fork
//Process-1
begin
$display($time,"\tProcess-1 of fork-2 Started");
#5;
$display($time,"\tProcess-1 of fork-2 Finished");
end
//Process-2
begin
$display($time,"\tProcess-2 of fork-2 Started");
#20;
$display($time,"\tProcess-2 of fork-2 Finished");
end
join_none

disable fork;

$display("-----------------------------------------------------------------");
$display($time,"\tAfter disable-fork");
$display("-----------------------------------------------------------------");
end
endmodule
Example-2

In the below example,

sub_process started from process-2 will get terminated during the


execution of disable fork.

module disable_fork;
initial begin
$display("-----------------------------------------------------------------");

fork
//Process-1
begin
$display($time,"\tProcess-1 of fork-1 Started");
#5;
$display($time,"\tProcess-1 of fork-1 Finished");
end
//Process-2
begin
sub_process();
end
join_any
disable fork;

$display("-----------------------------------------------------------------");
$display($time,"\tAfter disable-fork");
$display("-----------------------------------------------------------------");
end
//Sub-Process
task sub_process;
$display($time,"\tSub-Process Started");
#10;
$display($time,"\tSub-Process Finished");
endtask
endmodule
tasks

Tasks and Functions provide a means of splitting code into small parts.

A Task can contain a declaration of parameters, input arguments, output


arguments, in-out arguments, registers, events, and zero or more
behavioral statements.

SystemVerilog task can be,

 static
 automatic

Static tasks

Static tasks share the same storage space for all task calls.

Automatic tasks

Automatic tasks allocate unique, stacked storage for each task call.

SystemVerilog allows,

 to declare an automatic variable in a static task


 to declare a static variable in an automatic task
 more capabilities for declaring task ports
 multiple statements within task without requiring a begin…end or
fork…join block
 returning from the task before reaching the end of the task
 passing values by reference, value, names, and position
 default argument values
 the default direction of argument is input if no direction has been
specified
 default arguments type is logic if no type has been specified

task examples
task arguments in parentheses

module sv_task;
int x;

//task to add two integer numbers.


task sum(input int a,b,output int c);
c = a+b;
endtask

initial begin
sum(10,5,x);
$display("\tValue of x = %0d",x);
end
endmodule

functions

A Function can contain declarations of range, returned type, parameters,


input arguments, registers, and events.

 A function without a range or return type declaration returns a


one-bit value
 Any expression can be used as a function call argument
 Functions cannot contain any time-controlled statements, and
they cannot enable tasks

Functions can return only one value


SystemVerilog function can be,

 static
 automatic

Static Function

Static functions share the same storage space for all function calls.

Automatic Function

Automatic functions allocate unique, stacked storage for each function


call.

SystemVerilog allows,

 to declare an automatic variable in static functions


 to declare the static variable in automatic functions
 more capabilities for declaring function ports
 multiple statements within a function without requiring a
begin…end or fork…join block
 returning from the function before reaching the end of the
function
 Passing values by reference, value, names, and position
 default argument values
 default arguments type is logic if no type has been specified.
 function output and inout ports
 the default direction of argument is input if no direction has been
specified.

function examples
function arguments in parentheses

module sv_function;
int x;
//function to add two integer numbers.
function int sum(input int a,b);
sum = a+b;
endfunction

initial begin
x=sum(10,5);
$display("\tValue of x = %0d",x);
end
endmodule
function arguments in declarations and mentioning directions

module sv_function;
int x;

//function to add two integer numbers.


function int sum;
input int a,b;
sum = a+b;
endfunction
initial begin
x=sum(10,5);
$display("\tValue of x = %0d",x);
end
endmodule
function with return value with the return keyword

In the below example,


arguments in declarations and directions, return value is specified using
the return statement.

module sv_function;
int x;

//function to add two integer numbers.


function int sum;
input int a,b;
return a+b;
endfunction

initial begin
x=sum(10,5);
$display("\tValue of x = %0d",x);
end
endmodule
Void function

The example below shows usage of void function, void


function,(function with no return value)

module sv_function;
int x;
//void function to display current simulation time
function void current_time;
$display("\tCurrent simulation time is %0d",$time);
endfunction

initial begin
#10;
current_time();
#20;
current_time();
end
endmodule
discarding function return value

The function return value must be assigned to a variable or used in an


expression.

Calling a function without return value assigned to a variable can result


in a warning message. SystemVerilog void data type is used to discard a
function’s return value without any warning message.

module sv_function;
int x;
//function to add two integer numbers.
function int sum;
input int a,b;
return a+b;
endfunction

initial begin
$display("Calling function with void");
void'(sum(10,5));
end
endmodule
function call as an expression

module sv_function;
int x;
//function to add two integer numbers.
function int sum;
input int a,b;
return a+b;
endfunction
initial begin
x = 10 + sum(10,5);
$display("\tValue of x = %0d",x);
end
endmodule

Task and Function argument passing

SystemVerilog provides below means for passing arguments to functions


and tasks,

 argument pass by value


 argument pass by reference
 argument pass by name
 argument pass by position

also, functions and tasks can have default argument values.

argument pass by value

In argument pass by value,

the argument passing mechanism works by copying each argument into


the subroutine area.

if any changes to arguments within the subroutine, those changes will


not be visible outside the subroutine.

argument pass by value example

Variables x and y are passed as an argument in the function call sum,


changes to the argument x within the function is not visible outside.
module argument_passing;
int x,y,z;
//function to add two integer numbers.
function int sum(int x,y);
x = x+y;
return x+y;
endfunction

initial begin
x = 20;
y = 30;
z = sum(x,y);
$display("-----------------------------------------------------------------");
$display("\tValue of x = %0d",x);
$display("\tValue of y = %0d",y);
$display("\tValue of z = %0d",z);
$display("-----------------------------------------------------------------");
end
endmodule

argument pass by reference

In pass by reference, a reference to the original argument is passed to


the subroutine.

As the argument within a subroutine is pointing to an original argument,


any changes to the argument within subroutine will be visible outside.

To indicate argument pass by reference, the argument declaration is


preceded by keyword ref.

Any modifications to the argument value in a pass by reference can


be avoided by using const keyword before ref, any attempt in changing
the argument value in subroutine will lead to a compilation error.

argument pass by reference example

variables x and y are passed as an argument in the function call sum,


changes to the argument x within the function, is visible outside.
module argument_passing;
int x,y,z;

//function to add two integer numbers.


function int sum(ref int x,y);
x = x+y;
return x+y;
endfunction

initial begin
x = 20;
y = 30;
z = sum(x,y);
$display("-----------------------------------------------------------------");
$display("\tValue of x = %0d",x);
$display("\tValue of y = %0d",y);
$display("\tValue of z = %0d",z);
$display("-----------------------------------------------------------------");
end
endmodule
Any modifications to the argument value in a pass by reference can be
avoided by using const keyword before ref, any attempt in changing the
argument value in subroutine will lead to a compilation error.

ARGUMENT PASS BY REFERENCE WITH THE CONST KEYWORD

variables x and y are passed as an argument in the function call sum, as


arguments are mentioned as const, changes to the argument x within
the function leads to a compilation error.

module argument_passing;
int x,y,z;
//function to add two integer numbers.
function int sum(const ref int x,y);
x = x+y;
return x+y;
endfunction

initial begin
x = 20;
y = 30;
z = sum(x,y);
$display("-----------------------------------------------------------------");
$display("\tValue of x = %0d",x);
$display("\tValue of y = %0d",y);
$display("\tValue of z = %0d",z);
$display("-----------------------------------------------------------------");
end
endmodule

default argument values

The default value can be specified to the arguments of the subroutine.

In the subroutine call, arguments with a default value can be omitted


from the call.

if any value is passed to an argument with a default value, then the new
value will be considered.

an argument with default value example

variables x, y and z of the subroutine has a default value of 1,2 and 3


respectively, in the function call value is passed only for z. x and y will
take the default value.

module argument_passing;
int q;

//function to add three integer numbers.


function int sum(int x=5,y=10,z=20);
return x+y+z;
endfunction

initial begin
q = sum( , ,10);
$display("-----------------------------------------------------------------");
$display("\tValue of z = %0d",q);
$display("-----------------------------------------------------------------");
end
endmodule

argument pass by name

In argument pass by name, arguments can be passed in any order by


specifying the name of the subroutine argument.

value to the second argument is passed first by specifying the argument


name.

module argument_passing;
int x,y,z;

function void display(int x,string y);


$display("\tValue of x = %0d, y = %0s",x,y);
endfunction

initial begin
display(.y("Hello World"),.x(2016));
end
endmodule
A class is a user-defined data type that includes data (class properties),
functions and tasks that operate on data.

functions and tasks are called as methods, both are members of the
class.classes allow objects to be dynamically created, deleted, assigned
and accessed via object handles.

Class Declaration
Class declaration example

The below class has one data property x and two methods set and get.

class sv_class;
//class properties
int x;

//method-1
task set(int i);
x = i;
endtask

//method-2
function int get();
return x;
endfunction
endclass
Class Instance and Object Creation
Class declaration/Class Instance

As we know the class is a data type, Declaring class type variable is


similar to declaring other variables.

sv_class class_1;
the above statement shows the declaration of variable class_1 with the
type sv_class.
In other words, variable class_1 can contain a handle to an instance of
the class sv_class.

Object Creation

Class properties and methods can be accessed only after creating the
object.

class_1 = new();

the above statement will create an object and assign its handle to
class_1.

sv_class class_1 = new();


the above statement will do both declarations of variable and object
creation.
Accessing class properties and methods

Class properties and methods can be accessed by using object names


followed by property or method name.

class_1.set(10); //calling set method to set value 10 to x


$display("Vlaue of x = %0d",class_1.get(););
Class example

class sv_class;
//class properties
int x;

//method-1
task set(int i);
x = i;
endtask

//method-2
function int get();
return x;
endfunction
endclass

module sv_class_ex;
sv_class class_1; //Creating Handle

initial begin
sv_class class_2 = new(); //Creating handle and Object
class_1 = new(); //Creating Object for the Handle
//Accessing Class methods
class_1.set(10);
class_2.set(20);
$display("\tclass_1 :: Value of x = %0d",class_1.get());
$display("\tclass_2 :: Value of x = %0d",class_2.get());
end
endmodule
SystemVerilog this keyword

this keyword is used to refer to class properties. this keyword is used to


unambiguously refer to class properties or methods of the current
instance. this is a pre-defined class handle referring to the object from
which it is used, calling this.variable means object.variable.

 this keyword shall only be used within non-static class methods


 this keyword refers to the object handle in which it is invoked

this keyword example


In the example below,

The addr, data, write and pkt_type are the property of both class and an
argument to the function new, as the name in both are same.this will
lead to an ambiguity in assignment and values will not be assigned
properly.

class packet;

//class properties
bit [31:0] addr;
bit [31:0] data;
bit write;
string pkt_type;

//constructor
function new(bit [31:0] addr,data,bit write,string pkt_type);
addr = addr;
data = data;
write = write;
pkt_type = pkt_type;
endfunction

//method to display class prperties


function void display();
$display("---------------------------------------------------------");
$display("\t addr = %0h",addr);
$display("\t data = %0h",data);
$display("\t write = %0h",write);
$display("\t pkt_type = %0s",pkt_type);
$display("---------------------------------------------------------");
endfunction

endclass

module sv_constructor;
packet pkt;

initial begin
pkt = new(32'h10,32'hFF,1,"GOOD_PKT");
pkt.display();
end

endmodule

Class Constructors

The new function is called as class constructor.

On calling the new method it allocates the memory and returns the
address to the class handle.

SystemVerilog Class Constructor

 The new operation is defined as a function with no return type


 every class has a built-in new method, calling the constructor of
class without the explicit definition of the new method will invoke
the default built-in new method
 specifying return type to the constructor shall give a compilation
error (even specifying void shall give a compilation error)
 The constructor can be used for initializing the class properties. In
case of any initialization required, those can be placed in the
constructor and It is also possible to pass arguments to the
constructor, which allows run-time customization of an object.

SystemVerilog Constructor for Initialization


Class Constructor example

class packet;
//class properties
bit [31:0] addr;
bit [31:0] data;
bit write;
string pkt_type;
//constructor
function new();
addr = 32'h10;
data = 32'hFF;
write = 1;
pkt_type = "GOOD_PKT";
endfunction

//method to display class prperties


function void display();
$display("---------------------------------------------------------");
$display("\t addr = %0d",addr);
$display("\t data = %0h",data);
$display("\t write = %0d",write);
$display("\t pkt_type = %0s",pkt_type);
$display("---------------------------------------------------------");
endfunction
endclass

module sv_constructor;
packet pkt;
initial begin
pkt = new();
pkt.display();
end
endmodule

Static class members

Class members can be created with the keyword static. class members
with the keyword static are called as static class members. the class can
have static properties and static methods (functions and tasks). a single
copy of static variables is shared across multiple instances.

Static Properties

 The class can have multiple instances, each instance of the class
will be having its own copy of variables.
 Sometimes only one version of a variable is required to be shared
by all instances. These class properties are created using the
keyword static.

Syntax

static <data_type> <property_name>;


Static Methods

Static methods are the same as static properties,


 a static method can access only static properties of the class and
access to the non-static properties is illegal and lead to a
compilation error.
 Static methods cannot be virtual

Note:
Static class properties and methods can be used without creating an
object of that type.

Syntax

static task/function <method_name>;


Static properties example

In the below example,


The class has the variable packet_id, which is the unique ID assigned to
the packet;
Static variable no_of_pkts_created, no_of_pkts_created will get
incremented on every object creation.

no_of_pkts_created is assigned to packet_id.

class packet;

//class properties
byte packet_id;

//static property to keep track of number of pkt's created


static byte no_of_pkts_created;

//constructor
function new();
//incrementing pkt count on creating an object
no_of_pkts_created++;
packet_id = no_of_pkts_created;
endfunction

//method to display class prperties


function void display();
$display("--------------------------------------");
$display("\t packet_id = %0d",packet_id);
$display("--------------------------------------");
endfunction
endclass

module static_properties;
packet pkt[3];

initial begin
foreach(pkt[i]) begin
pkt[i] = new();
pkt[i].display();
end
end
endmodule

Static method example


Below example shows the declaration of a static function.
class packet;

//class properties
byte packet_id;

//static property to keep track of number of pkt's created


static byte no_of_pkts_created;

//constructor
function new();
//incrementing pkt count on creating an object
no_of_pkts_created++;
packet_id = no_of_pkts_created;
endfunction

//method to display class prperties


function void display();
$display("--------------------------------------");
$display("\t packet_id = %0d",packet_id);
$display("--------------------------------------");
endfunction
endclass

module static_properties;
packet pkt[3];

initial begin
foreach(pkt[i]) begin
pkt[i] = new();
pkt[i].display();
end
end
endmodule

STATIC METHOD TRYING TO ACCESS A NON-STATIC VARIABLE

In the below example,


The static function tries to access the non-static variable of the class,
which leads to a compilation error.

class packet;
byte packet_id;

//static property to keep track of number of pkt's created


static byte no_of_pkts_created;

//constructor
function new();
//incrementing pkt count on creating an object
no_of_pkts_created++;
endfunction

//Static method to display class prperties


static function void display_packets_created();
$display("--------------------------------------");
$display("\t Packet Id is %0d",packet_id);
$display("\t %0d packets created.",no_of_pkts_created);
$display("--------------------------------------");
endfunction
endclass
module static_properties;
packet pkt[3];

initial begin
foreach(pkt[i]) begin
pkt[i] = new();
end
pkt[0].display_packets_created();
end
endmodule

ACCESSING STATIC CLASS PROPERTIES WITHOUT CREATING AN OBJECT

In the below example,

Static class variable (no_of_pkts_created) and Static class function


(display_packets_created) is accessed with class handle which is not
constructed (new() is not done).

class packet;

//static property to keep track of number of pkt's created


static byte no_of_pkts_created;

//constructor
function new();
//incrementing pkt count on creating an object
no_of_pkts_created++;
endfunction

//Static method to display class prperties


static function void display_packets_created();
$display("--------------------------------------");
$display("\t %0d packets created.",no_of_pkts_created);
$display("--------------------------------------");
endfunction
endclass

module static_properties;
packet pkt[3];
packet p;

initial begin
foreach(pkt[i]) begin
pkt[i] = new();
end

//Accesing static Variable with class handle p


$display("--------------------------------------");
$display("\t %0d packets created.",p.no_of_pkts_created);
$display("--------------------------------------");

//Accesing static Method with class handle p


p.display_packets_created();
end
endmodule

Class Assignment

Object will be created only after doing new to an class handle,

packet pkt_1;

pkt_1 = new();

packet pkt_2;

pkt_2 = pkt_1;

In the above piece of code,

 an object is created only for pkt_1, pkt_2 is just a handle to the


packet
 pkt_1 is assigned to the pkt_2. so only one object has been
created, pkt_1 and pkt_2 are two handles both are pointing to the
same object
 As both the handles are pointing to the same object any changes
made with respect to pkt_1 will reflect on pkt_2
SystemVerilog Class Assignment
Class assignment example

The below example shows an assigning the object handle to another


handle and accessing class properties with it.

class packet;
//class properties
bit [31:0] addr;
bit [31:0] data;
bit write;
string pkt_type;
//constructor
function new();
addr = 32'h10;
data = 32'hFF;
write = 1;
pkt_type = "GOOD_PKT";
endfunction

//method to display class properties


function void display();
$display("---------------------------------------------------------");
$display("\t addr = %0d",addr);
$display("\t data = %0h",data);
$display("\t write = %0d",write);
$display("\t pkt_type = %0s",pkt_type);
$display("---------------------------------------------------------");
endfunction
endclass

module class_assignment;
packet pkt_1;
packet pkt_2;

initial begin
pkt_1 = new();
$display("\t**** calling pkt_1 display ****");
pkt_1.display();
//assigning pkt_1 to pkt_2
pkt_2 = pkt_1;
$display("\t**** calling pkt_2 display ****");
pkt_2.display();
//changing values with pkt_2 handle
pkt_2.addr = 32'hAB;
pkt_2.pkt_type = "BAD_PKT";

//changes made with pkt_2 handle will reflect on pkt_1


$display("\t**** calling pkt_1 display ****");
pkt_1.display();
end
endmodule
Shallow Copy

An object will be created only after doing new to a class handle,

packet pkt_1;
pkt_1 = new();
packet pkt_2;
pkt_2 = new pkt_1;
In the last statement, pkt_2 is created and class properties were copied
from pkt_1 to pkt_2, this is called as “shallow copy”.

Shallow copy allocates the memory, copies the variable values and
returns the memory handle.

In shallow copy, All of the variables are copied across: integers, strings,
instance handles, etc.

Note::
Objects will not be copied, only their handles will be copied.

to perform the full or deep copy, the custom method can be added.

SystemVerilog Class Shallow copy


SystemVerilog Shallow Copy Limitation
Shallow copy example

In the below example,

packet class has the properties of bit type and object type
(address_range).
after the shallow copy addr, data and handle to are were copied. As it is
shallow copy any changes on pkt_2. are will reflect in pkt_1.ar (because
pkt_2.ar and pkt_1.ar will point to the same object).
//-- class ---
class address_range;
bit [31:0] start_address;
bit [31:0] end_address ;
function new(); start_address = 10;
end_address = 50;
endfunction
endclass

//-- class ---


class packet;
//class properties
bit [31:0] addr;
bit [31:0] data;
address_range ar; //class handle

//constructor
function new();
addr = 32'h10;
data = 32'hFF;
ar = new(); //creating object
endfunction
//method to display class properties
function void display();
$display("---------------------------------------------------------");
$display("\t addr = %0h",addr);
$display("\t data = %0h",data);
$display("\t start_address = %0d",ar.start_address);
$display("\t end_address = %0d",ar.end_address);
$display("---------------------------------------------------------");
endfunction
endclass

// -- module ---
module class_assignment;
packet pkt_1;
packet pkt_2;

initial begin
pkt_1 = new(); //creating pkt_1 object
$display("\t**** calling pkt_1 display ****");
pkt_1.display();

pkt_2 = new pkt_1; //creating pkt_2 object and copying pkt_1


to pkt_2
$display("\t**** calling pkt_2 display ****");
pkt_2.display();

//changing values with pkt_2 handle


pkt_2.addr = 32'h68;
pkt_2.ar.start_address = 60;
pkt_2.ar.end_address = 80;
$display("\t**** calling pkt_1 display after changing pkt_2
properties ****");

//changes made to pkt_2.ar properties reflected on pkt_1.ar,


so only handle of the object get copied, this is called shallow copy
pkt_1.display();
$display("\t**** calling pkt_2 display after changing pkt_2
properties ****");
pkt_2.display(); //
end
endmodule

Difference between Assignment and Shallow Copy

assignment vs shallow copy

deep copy

SystemVerilog deep copy copies all the class members and its nested
class members. unlike in shallow copy, only nested class handles will be
copied. In shallow copy, Objects will not be copied, only their handles
will be copied. to perform a full or deep copy, the custom method needs
to be added.
In the custom method, a new object is created, all the class properties
will be copied to a new handle and the new handle will be returned.

SystemVerilog Deep Copy


deep copy example

In the below example, the copy method is added in each class.


whenever the copy method is called, it will create the new object and
copies all the class properties to a new object handle and return the new
object handle.

//-- class ---


class address_range;
bit [31:0] start_address;
bit [31:0] end_address ;

function new();
start_address = 10;
end_address = 50;
endfunction
//copy method
function address_range copy;
copy = new();
copy.start_address = this.start_address;
copy.end_address = this.end_address;
return copy;
endfunction
endclass

//-- class ---


class packet;
//class properties
bit [31:0] addr;
bit [31:0] data;
address_range ar; //class handle

//constructor
function new();
addr = 32'h10;
data = 32'hFF;
ar = new(); //creating object
endfunction

//method to display class prperties


function void display();
$display("---------------------------------------------------------");
$display("\t addr = %0h",addr);
$display("\t data = %0h",data);
$display("\t start_address = %0d",ar.start_address);
$display("\t end_address = %0d",ar.end_address);
$display("---------------------------------------------------------");
endfunction

//copy method
function packet copy();
copy = new();
copy.addr = this.addr;
copy.data = this.data;
copy.ar = ar.copy;//calling copy function of tr
return copy;
endfunction
endclass

// -- module ---
module class_assignment;
packet pkt_1;
packet pkt_2;
initial begin
pkt_1 = new(); //creating pkt_1 object
$display("\t**** calling pkt_1 display ****");
pkt_1.display();
pkt_2 = new(); //creating pkt_2 object
$display("\t**** calling pkt_2 display ****");
pkt_2.display();
pkt_2 = pkt_1.copy(); //calling copy method
//changing values with pkt_2 handle
pkt_2.addr = 32'h68;
pkt_2.ar.start_address = 60;
pkt_2.ar.end_address = 80;
$display("\t**** calling pkt_1 display after changing pkt_2
properties ****");
pkt_1.display();
$display("\t**** calling pkt_2 display after changing pkt_2
properties ****");
pkt_2.display();
end
endmodule

Parameterized Classes

Parameterized classes are same as the parameterized modules in the


verilog. parameters are like constants local to that particular class.
The parameter value can be used to define a set of attributes in class.
default values can be overridden by passing a new set of parameters
during instantiation. this is called parameter overriding.
//---- class ----
Classpacket
#(parameter int ADDR_WIDTH = 32,DATA_WIDTH = 32);
bit [ADDR_WIDTH-1:0] address;
bit [DATA_WIDTH-1:0] data ;

function new();
address = 10;
data = 20;
endfunction
endclass

packet pkt; –> creates pkt handle with default ADDR_WIDTH and
DATA_WIDTH values.
The default parameter value can be overridden when the class is
instantiated.

packet #(32,64) pkt; –> creates pkt handle with ADDR_WIDTH = 32 and
DATA_WIDTH = 64.

Pass a data type to a class

class packet #(parameter type T = int);


T address;
T data ;

function new();
address = 10;
data = 20;
endfunction
endclass

packet pkt; –> address and data type is int


packet #(bit [31:0]) pkt; –> address and data type is bit [31:0]

SystemVerilog Inheritance

SystemVerilog Inheritance. Inheritance is an OOP concept that allows


the user to create classes that are built upon existing classes. The new
class will be with new properties and methods along with having access
to all the properties and methods of the original class. Inheritance is
about inheriting base class members to the extended class.

 New classes can be created based on existing classes, this is


referred to as class inheritance
 A derived class by default inherits the properties and methods of
its parent class
 An inherited class is called a subclass of its parent class
 A derived class may add new properties and methods, or modify
the inherited properties and methods
 Inheritance allows re-usability. i.e. derived class by default
includes the properties and methods, which is ready to use
 If the class is derived from a derived class, then it is referred to as
Multilevel inheritance

Inheritance Terminology
Parent Class

 It’s an existing class;


 The class whose features are inherited
 The parent class is also known as a base class, superclass
Child Class

 It’s an extended class;


 The class that inherits the other class is known as subclass
 The child class is also known as an extended class, derived class,
subclass

Inheritance Example

Parent class properties are accessed using child class handle, i.e child
class will have (inherit) parent class properties and methods.

In the below example,


parent_class is base class and child_class is written by extending the
parent_class.
so child_class is derived from a base class, and it inherits the properties
of the parent class.

Though the addr is not declared in child_class, it is accessible. because it


is inherited from the parent class.

class parent_class;
bit [31:0] addr;
endclass

class child_class extends parent_class;


bit [31:0] data;
endclass

module inheritence;
initial begin
child_class c = new();
c.addr = 10;
c.data = 20;
$display("Value of addr = %0d data = %0d",c.addr,c.data);
end
endmodule

Polymorphism in SystemVerilog
Polymorphism means many forms. Polymorphism in SystemVerilog
provides an ability to an object to take on many forms.

Method handle of super-class can be made to refer to the subclass


method, this allows polymorphism or different forms of the same
method.

How, many forms of a method can be made by referring to the subclass


method?
will see with an example,

Polymorphism example

Let’s write the base_class with a method display();

class base_class;
virtual function void display();
$display("Inside base class");
endfunction
endclass

Writing three extended classes of base_class, with display method


overridden in it.

class ext_class_1 extends base_class;


function void display();
$display("Inside extended class 1");
endfunction
endclass
class ext_class_2 extends base_class;
function void display();
$display("Inside extended class 2");
endfunction
endclass

class ext_class_3 extends base_class;


function void display();
$display("Inside extended class 3");
endfunction
endclass

Create an object of each extended class,

ext_class_1 ec_1 = new();


ext_class_2 ec_2 = new();
ext_class_3 ec_3 = new();

Declare an array of a base class,

base_class b_c[3];
Assign extend class handles to base class handles.
b_c[0] = ec_1;
b_c[1] = ec_2;
b_c[2] = ec_3;

Call the display method using base_class handle,

b_c[0].display();
b_c[1].display();
b_c[2].display();

In the above method calls,


Though all the methods are called using base_class handle, different
methods are getting called. this shows the many forms of the same
method, this is called polymorphism.

Complete code,
// base class
class base_class;
virtual function void display();
$display("Inside base class");
endfunction
endclass

// extended class 1
class ext_class_1 extends base_class;
function void display();
$display("Inside extended class 1");
endfunction
endclass

// extended class 2
class ext_class_2 extends base_class;
function void display();
$display("Inside extended class 2");
endfunction
endclass

// extended class 3
class ext_class_3 extends base_class;
function void display();
$display("Inside extended class 3");
endfunction
endclass

// module
module class_polymorphism;

initial begin

//declare and create extended class


ext_class_1 ec_1 = new();
ext_class_2 ec_2 = new();
ext_class_3 ec_3 = new();

//base class handle


base_class b_c[3];
//assigning extended class to base class
b_c[0] = ec_1;
b_c[1] = ec_2;
b_c[2] = ec_3;

//accessing extended class methods using base class handle


b_c[0].display();
b_c[1].display();
b_c[2].display();
end

endmodule

Overriding class member example

In below example,

The parent class has the method display().


display() method is re-defined in the child class, which will override the
parent class method.
c is the handle to the child class, because of override calling c.display will
call display method of the child class, not the parent class.

class parent_class;
bit [31:0] addr;

function display();
$display("Addr = %0d",addr);
endfunction
endclass

class child_class extends parent_class;


bit [31:0] data;
function display();
$display("Data = %0d",data);
endfunction
endclass

module inheritence;
initial begin
child_class c=new();
c.addr = 10;
c.data = 20;
c.display();
end
endmodule

Super keyword

The super keyword is used in a derived class to refer to the members of


the parent class.

 When class members are overridden in the derived class, It is


necessary to use the super keyword to access members of a parent
class
 With super keyword, it is allowed to access the class members of
parent class which is only one level up

If the method of the parent class is overridden in the child class, then
using the ‘super’ keyword parent class method can be accessed from the
child class.

Super keyword example


Super Keyword

In below example,

The parent class has the method ‘display’.


Implementing the method display in the child class will override the
parent class method.
By calling super.display() from child class, the display method of the
parent class can be accessed.

class parent_class;
bit [31:0] addr;

function display();
$display("Addr = %0d",addr);
endfunction
endclass

class child_class extends parent_class;


bit [31:0] data;

function display();
super.display();
$display("Data = %0d",data);
endfunction

endclass

module inheritence;
initial begin
child_class c=new();
c.addr = 10;
c.data = 20;
c.display();
end
endmodule

Casting

In Manufacturing, Casting is a process in which liquid metal is converted


into the desired object. Similarly, SystemVerilog casting means the
conversion of one data type to another datatype. During value or
variable assignment to a variable, it is required to assign value or
variable of the same data type. Some situations need assignment of
different data type, in such situations, it is necessary to convert data
type and assign. Otherwise, the assignment of different data type results
in a compilation error. The method of data type conversion is
called casting.
In systemVerilog, there are two types of casting,

 Static casting
 Dynamic casting

Static casting

 SystemVerilog static casting is not applicable to OOP


 Static casting converts one data type to another compatible data
types (example string to int)
 As the name says ‘Static’, the conversion data type is fixed
 Static casting will be checked during compilation, so there won’t
be run-time checking and error reporting
 Casting is applicable to value, variable or to an expression
 A data type can be changed by using a cast ( ‘ ) operation
 The value/variable/expression to be cast must be enclosed in
parentheses or within concatenation or replication braces

Static casting example

In the below example,


the real type is converted into int type. i.e multiplication of two real
numbers results in real value, the result is converted into int and then
assigned to a variable of int type.
Note: the casting is applied to expression here.

module casting;

real r_a;
int i_a;

initial begin

r_a = (2.1 * 3.2);

//real to integer conversion


i_a = int'(2.1 * 3.2); //or i_a = int'(r_a);

$display("real value is %f",r_a);


$display("int value is %d",i_a);
end
endmodule

Dynamic casting

 Dynamic casting is used to, safely cast a super-class pointer


(reference) into a subclass pointer (reference) in a class hierarchy
 Dynamic casting will be checked during run time, an attempt to
cast an object to an incompatible object will result in a run-time
error
 Dynamic casting is done using the $cast(destination, source)
method
 With $cast compatibility of the assignment will not be checked
during compile time, it will be checked during run-time

Let’s see how we can use the casting,


It is always legal to assign a child class variable to a variable of a class
higher in the inheritance tree (parent class).
parent_class = child_class; //allowed

It is never legal to directly assign a super-class (parent class) variable to a


variable of one of its subclasses (child class).
child_class = parent_class; //not-allowed

However, it is legal to assign a super-class (parent class) handle to a


subclass (child class) variable if the super-class (parent class) handle
refers to an object of the given subclass(child class).
parent_class = child_class ;
child_class = parent_class; //allowed because parent_class is pointing to
child_class.

Though parent_class is pointing to the child_class, we will get


a compilation error saying its not compatible type for the assignment.

This we can over come by make use of $cast method, i.e,

$cast(child_class,parent_class);

Why is it called as dynamic casting?

In the above parent class assignment with child class example. type of
parent class is changing dynamically i.e on declaration it is of parent
class type, on child class assignment it is of child class type.
Parent class handle during $cast execution is considered for the
assignment, so it referred to as dynamic casting.

Dynamic Casting examples


assigning child class handle to parent class handle

class parent_class;
bit [31:0] addr;
function display();
$display("Addr = %0d",addr);
endfunction
endclass

class child_class extends parent_class;


bit [31:0] data;
function display();
super.display();
$display("Data = %0d",data);
endfunction
endclass

module inheritence;
initial begin
parent_class p=new();
child_class c=new();
c.addr = 10;
c.data = 20;
p = c; //assigning child class handle to parent class handle
c.display();
end
endmodule

assigning parent class handle to child class handle

This assignment is invalid, it leads to a compilation error.

class parent_class;
bit [31:0] addr;
function display();
$display("Addr = %0d",addr);
endfunction
endclass

class child_class extends parent_class;


bit [31:0] data;

function display();
super.display();
$display("Data = %0d",data);
endfunction
endclass

module inheritence;
initial begin
parent_class p=new();
child_class c=new();
c.addr = 10;
c.data = 20;
c = p; //assigning child class handle to parent class handle
c.display();
end
endmodule

assigning parent class handle to child class handle

assigning parent class handle (which is pointing to child class handle) to


child class handle, leads to compile error.

class parent_class;
bit [31:0] addr;

function display();
$display("Addr = %0d",addr);
endfunction
endclass

class child_class extends parent_class;


bit [31:0] data;
function display();
super.display();
$display("Data = %0d",data);
endfunction
endclass

module inheritence;
initial begin
parent_class p;
child_class c=new();
child_class c1;
c.addr = 10;
c.data = 20;
p = c; //p is pointing to child class handle c.
c1 = p; //type check fails during compile time.
c1.display();
end
endmodule

Use of $cast or casting

In the above example, assigning parent class handle (which is pointing to


child class handle) to child class handle is valid but compilation error is
observed.
During the compile time, as the handle of p is of parent class type which
leads to compile error.

With the use of $cast(), type check during compile time can be skipped.

class parent_class;
bit [31:0] addr;

function display();
$display("Addr = %0d",addr);
endfunction
endclass

class child_class extends parent_class;


bit [31:0] data;

function display();
super.display();
$display("Data = %0d",data);
endfunction
endclass

module inheritence;
initial begin
parent_class p;
child_class c=new();
child_class c1;
c.addr = 10;
c.data = 20;

p = c; //p is pointing to child class handle c.


$cast(c1,p); //with the use of $cast, type chek will occur
during runtime

c1.display();
end
endmodule

Data hiding and Encapsulation

The technique of hiding the data within the class and making it available
only through the methods, is known as encapsulation.
Because it seals the data (and internal methods) safely inside the
“capsule” of the class, where it can be accessed only by trusted
users (i.e., by the methods of the class).

Access Control

By default all the members and methods of a class are accessible from
anywhere using the object handle, sometimes this could corrupt the
class members values, which should not be touched at all.
Access control rules that restrict the members of a class from being used
outside the class, this is achieved by prefixing the class members with
the keywords,

 local
 protected

local class members

External access to the class members can be avoided by declaring


members as local.
Any violation could result in a compilation error.

SYNTAX:
local integer x;

Local Class members examples


ACCESSING LOCAL VARIABLE OUTSIDE THE CLASS ( NOT ALLOWED )

In below example,
The local variable declared inside the class is trying to access from
outside the class by using object handle.
As the variable is declared as local, which leads to a compilation error.

class parent_class;
local bit [31:0] tmp_addr;

function new(bit [31:0] r_addr);


tmp_addr = r_addr + 10;
endfunction

function display();
$display("tmp_addr = %0d",tmp_addr);
endfunction
endclass

// module
module encapsulation;
initial begin
parent_class p_c = new(5);

p_c.tmp_addr = 20; //Accessing local variable outside the class


p_c.display();
end
endmodule

ACCESSING LOCAL VARIABLE WITHIN THE CLASS ( ALLOWED )

In the below example, The local variable declared inside the class is
being accessed inside the class. as it is allowed, no compilation error is
observed.

class parent_class;
local bit [31:0] tmp_addr;

function new(bit [31:0] r_addr);


tmp_addr = r_addr + 10;
endfunction

function display();
$display("tmp_addr = %0d",tmp_addr);
endfunction
endclass

// module
module encapsulation;
initial begin
parent_class p_c = new(5);
p_c.display();
end
endmodule

ACCESSING LOCAL VARIABLE WITHIN THE CLASS ( ALLOWED )

In the below example, The local variable declared inside the class is
being accessed inside the class. as it is allowed, no compilation error is
observed.

class parent_class;
local bit [31:0] tmp_addr;

function new(bit [31:0] r_addr);


tmp_addr = r_addr + 10;
endfunction

function display();
$display("tmp_addr = %0d",tmp_addr);
endfunction
endclass

// module
module encapsulation;
initial begin
parent_class p_c = new(5);
p_c.display();
end
endmodule

ACCESSING A PROTECTED VARIABLE IN THE EXTENDED CLASS


( ALLOWED )

In the below example, The protected variable declared inside the class is
being accessed inside the extended class. as it is allowed, no compilation
error is observed.

class parent_class;
protected bit [31:0] tmp_addr;

function new(bit [31:0] r_addr);


tmp_addr = r_addr + 10;
endfunction

function display();
$display("tmp_addr = %0d",tmp_addr);
endfunction
endclass

class child_class extends parent_class;


function new(bit [31:0] r_addr);
super.new(r_addr);
endfunction

function void incr_addr();


tmp_addr++;
endfunction
endclass

// module
module encapsulation;
initial begin
child_class c_c = new(10);

c_c.incr_addr(); //Accessing protected variable in extended


class
c_c.display();
end
endmodule

Abstract Class

SystemVerilog class declared with the keyword virtual is referred to as


an abstract class.

 An abstract class sets out the prototype for the sub-classes.


 An abstract class cannot be instantiated, it can only be derived.
 An abstract class can contain methods for which there are only a
prototype and no implementation, just a method declaration.

Abstract Class SystemVerilog


Abstract class Syntax

virtual class abc;


//Class defination
endclass
Abstract Class Examples
Instantiating virtual class

In the below example, Creating an object of a virtual class. An abstract


class can only be derived, creating an object of a virtual class leads to a
compilation error.

//abstract class
virtual class packet;
bit [31:0] addr;
endclass
module virtual_class;
initial begin
packet p;
p = new();
end
endmodule

Deriving virtual class

In the below example, An abstract class is derived and written extend


the class and creating it.

//abstract class
virtual class packet;
bit [31:0] addr;
endclass

class extended_packet extends packet;


function void display;
$display("Value of addr is %0d", addr);
endfunction
endclass

module virtual_class;
initial begin
extended_packet p;
p = new();
p.addr = 10;
p.display();
end
endmodule

Virtual Methods in SystemVerilog

SystemVerilog Methods declared with the keyword virtual are referred


to as virtual methods.

Virtual Methods,

 Virtual Functions
 Virtual Tasks
Virtual Functions

A function declared with a virtual keyword before the function keyword


is referred to as virtual Function

Virtual Task

Task declared with a virtual keyword before the task keyword is referred
to as virtual task

About Virtual Method

In a virtual method,
If the base_class handle is referring to the extended class, then the
extended class method handle will get assigned to the base class handle.

In the below explanation, extended_class is an extended class of


base_class.

base_class b_c;
extended_class e_c;

Considering both the class’s has the method display().

assigning e_c to b_c,

b_c = e_c;
On calling b_c.display()

 if display() method in base_class is virtual, then extended class


display method will get called
 if display() method in base_class is non-virtual, then base class
display method will get called

Virtual function syntax

virtual function function_name;

//Function definition
endfunction
Virtual task syntax
virtual task task_name;
//task definition
endtask
Virtual Method Examples
Method without virtual keyword

In the below example,


the method inside the base class is declared without a virtual keyword,
on calling method of the base class which is pointing to the extended
class will call the base class method.

class base_class;

function void display;


$display("Inside base_class");
endfunction

endclass

class extended_class extends base_class;

function void display;


$display("Inside extended class");
endfunction

endclass

module virtual_class;
initial begin
base_class b_c;
extended_class e_c;

e_c = new();
b_c = e_c;

b_c.display();
end
endmodule
Scope Resolution Operator ::

The class scope operator :: is used to specify an identifier defined within


the scope of a class.

 Classes and other scopes can have the same identifiers


 The scope resolution operator uniquely identifies a member of a
particular class

Class Resolution operator allows access to static members (class


properties and methods) from outside the class, as well as access to
public or protected elements of super classes from within the derived
classes.

Scope resolution operator example

In the Below example,


A static member of the class is accessed outside the class by using class
resolution operator ::

//class
class packet;
bit [31:0] addr;
static bit [31:0] id;

function display(bit [31:0] a,b);


$display("Values are %0d %0d",a,b);
endfunction
endclass

module sro_class;
int id=10;
initial begin
packet p;
p = new();
packet::id = 20;
p.display(packet::id,id);
end
endmodule

SystemVerilog External Methods

If the definition of the method written outside the body of the class then
the method is called an external method.

 external function. The definition of the function written outside


the class body is referred to as an external function
 external task. The definition of the task written outside the class
body is referred to as an external task
 to do this, need to declare the method (Function/Task) with
an extern keyword in the class body along with
 any qualifiers (local, protected or virtual)
 full argument list
 The extern qualifier indicates that the body of the method (its
implementation) is to be found outside the class declaration
 Before the method name, the class name should be specified with
a class resolution operator to specify to which class the method
corresponds to.

Note:

 Number of arguments, arguments name and argument type


should match between method declaration and method definition

External function example

In the example below,


The function display is declared inside the class with the extern keyword,
and the definition of the function is written outside the class.

//class with extern function


class packet;
bit [31:0] addr;
bit [31:0] data;

//function declaration - extern indicates out-of-body declaration


extern virtual function void display();
endclass

//function implementation outside class body


function void packet::display();
$display("Addr = %0d Data = %0d",addr,data);
endfunction

module extern_method;
initial begin
packet p;
p = new();
p.addr = 10;
p.data = 20;
p.display();
end
endmodule

External task example

In the example below,


The task display is declared inside the class with the extern keyword, and
the definition of the task is written outside the class.

//class with extern function


class packet;
bit [31:0] addr;
bit [31:0] data;

//task declaration - extern indicates out-of-body declaration


extern virtual task display();
endclass

//task implementation outside class body


task packet::display();
$display("Addr = %0d Data = %0d",addr,data);
endtask

module extern_method;
initial begin
packet p;
p = new();
p.addr = 10;
p.data = 20;
p.display();
end
endmodule

typedef class

A typedefis used to provide a forward declaration of the class.


In some cases, the class needs to be instantiated before the class
declaration. In these kinds of situations, the typedef is used to provide a
forward declaration of the class.

typedef syntax

typedef class class_name;


typedef examples
Without typedef

In the below example,


There are two classes c1 and c2.
c2 is instantiated inside c1 and c1 inside c2. Both classes need the handle
of each other. As execution will happen in sequential order.
Dependency between both the classes leads to a compilation error.

//class-1
class c1;
c2 c; //using class c2 handle before declaring it.
endclass

//class-2
class c2;
c1 c;
endclass

module typedef_class;
initial begin
c1 class1;
c2 class2;
$display("Inside typedef_class");
end
endmodule

With typedef

The compilation error of the above example can be avoided by using a


typedef.

typedef class c2;


//class-1
class c1;
c2 c; //using class c2 handle before declaring it.
endclass

//class-2
class c2;
c1 c;
endclass

module typedef_class;
initial begin
c1 class1;
c2 class2;
$display("Inside typedef_class");
end
endmodule

randomization in SystemVerilog

Randomization is the process of making something random;


SystemVerilog randomization is the process of generating random values
to a variable. Verilog has a $random method for generating the random
integer values. This is good for randomizing the variables alone, but it is
hard to use in case of class object randomization. for easy randomization
of class properties, SystemVerilog provides rand keyword and
randomize() method.
random variables

The class variables which get random values on randomization are called
random variables. In order to make variables as random variables, Class
variables need to be declared using the rand and randc type-modifier
keywords.

Following types can be declared as rand and randc,

 singular variables of any integral type


 arrays
 arrays size
 object handle’s

rand keyword

Variables declared with the rand keyword are standard random variables.
Their values are uniformly distributed over their range.

rand bit [3:0] addr;

addr is a 4-bit unsigned integer with a range of 0 to 15. on


randomization this variable shall be assigned any value in the range 0 to
15 with equal probability.

randc keyword

randc is random-cyclic. For the variables declared with the randc


keyword, on randomization variable values don’t repeat a random value
until every possible value has been assigned.

randc bit wr_rd;

In order to randomize the object variables, the user needs to call


randomize() method.

example:
object.randomize();
randomization example
In the example below,
Two variables addr1 and addr2 of same bit type are declared as rand and
randc respectively, observe the randomized values of addr1 and addr2.

addr1 – takes the random value on every randomization


addr2 – takes the random value on every randomization, but takes
random value until every possible value has been assigned

//class
class packet;
rand bit [2:0] addr1;
randc bit [2:0] addr2;
endclass

module rand_methods;
initial begin
packet pkt;
pkt = new();
repeat(10) begin
pkt.randomize();
$display("\taddr1 = %0d \t addr2
= %0d",pkt.addr1,pkt.addr2);
end
end
endmodule

Disable randomization

The variable declared without rand or randc will not get random values
on randomization. what if for some reason it is required not to generate
a random value for a random variable. Yes, it is possible to disable the
randomization of a variable by using the systemverilog randomization
method rand_mode.

rand_mode method

The rand_mode() method is used to disable the randomization of a


variable declared with the rand/randc keyword.

 rand_mode(1) means randomization enabled


 rand_mode(0) means randomization disabled
 The default value of rand_mode is 1, i.e enabled
 Once the randomization is disabled, it is required to make
rand_mode(1) enable back the randomization
 rand_mode can be called as SystemVerilog method, the
randomization enables/disable status of a variable can be obtained
by calling varirble.rand_mode().
 the rand_mode method returns 1 if randomization is enabled else
returns 0

rand_mode syntax

<object_hanlde>.<variable_name>.rand_mode(enable);
//enable = 1, randomization enable
//enable = 0, randomization disable
randomization disable examples
without randomization disable

In the below example,


The class packet has random variables addr and data, on randomization,
these variables will get random value.

class packet;
rand byte addr;
rand byte data;
endclass

module rand_methods;
initial begin
packet pkt;
pkt = new();
//calling randomize method
pkt.randomize();

$display("\taddr = %0d \t data = %0d",pkt.addr,pkt.data);


end
endmodule

randomization disable for a class variable

In the below example,


The class packet has random variables addr and data, randomization is
disabled for a variable addr, on randomization only data will get random
value. The addr will not get any random value.
rand_mode() method is called in a display to know the status.

class packet;
rand byte addr;
rand byte data;
endclass

module rand_methods;
initial begin
packet pkt;
pkt = new();

//disable rand_mode of addr variable of pkt


pkt.addr.rand_mode(0);

//calling randomize method


pkt.randomize();

$display("\taddr = %0d \t data = %0d",pkt.addr,pkt.data);

$display("\taddr.rand_mode() = %0d \t data.rand_mode()


= %0d",pkt.addr.rand_mode(),pkt.data.rand_mode());
end
endmodule

randomization disable for all class variable


In the below example,
randomization for all the class variable is disabled by calling
obj.rand_mode(0);

class packet;
rand byte addr;
rand byte data;
endclass

module rand_methods;
initial begin
packet pkt;
pkt = new();

$display("\taddr.rand_mode() = %0d \t data.rand_mode()


= %0d",pkt.addr.rand_mode(),pkt.data.rand_mode());

//disable rand_mode of object


pkt.rand_mode(0);

//calling randomize method


pkt.randomize();

$display("\taddr = %0d \t data = %0d",pkt.addr,pkt.data);

$display("\taddr.rand_mode() = %0d \t data.rand_mode()


= %0d",pkt.addr.rand_mode(),pkt.data.rand_mode());
end
endmodule

Randomization Methods

SystemVerilog randomization provides a built-in method randomize. The


randomize() method generates random values for all the active random
variables of an object, subject to the active constraints. Variables
declared with the rand keyword will get random values on the
object.randomize() method call.The randomize() method returns 1 if the
randomization is successful i.e on randomization it’s able to assign
random values to all the random variables, otherwise, it returns 0.
randomize method associated with below callbacks,

 pre_randomize
 post_randomize

pre randomize and post randomize methods

 On calling randomize(), pre_randomize() and post_randomize()


functions will get called before and after the randomize call
respectively
 Users can override the pre_randomize() and post_randomize()
functions

pre_randomize

the pre_randomize function can be used to set pre-conditions before the


object randomization.

For example, Users can implement randomization control logic in


pre_randomize function. i.e randomization enable or disable by using
rand_mode() method.

post_randomize

the post_randomization function can be used to check and perform


post-conditions after the object randomization.

For example, Users can override the randomized values or can print the
randomized values of variables.

randomization method examples


Implementing pre and post randomize methods

In the below example,


pre and post randomized methods are implemented in the class, on
calling obj.randomize() pre and post will get called.

class packet;
rand bit [7:0] addr;
randc bit [7:0] data;
//pre randomization function
function void pre_randomize();
$display("Inside pre_randomize");
endfunction

//post randomization function


function void post_randomize();
$display("Inside post_randomize");
$display("value of addr = %0d, data = %0d",addr,data);
endfunction
endclass

module rand_methods;
initial begin
packet pkt;
pkt = new();
pkt.randomize();
end
endmodule

randomization control from pre_randomize method

In the example below,


Paket has two variables, addr, and wr_rd.

assuming wr_rd = 0 read operation.


wr_rd = 1 write operation.

In order to perform write followed by reading to the same addr,


randomization of addr is controlled based on the previous
randomization value of wr_rd. this controlling is done in pre_randomize()
function.

//class
class packet;
rand bit [7:0] addr;
randc bit wr_rd;
bit tmp_wr_rd;

//pre randomization function - disabling randomization of addr,


//if the prevoius operation is write.
function void pre_randomize();
if(tmp_wr_rd==1) addr.rand_mode(0);
else addr.rand_mode(1);
endfunction

//post randomization function - store the wr_rd value to


tmp_wr_rd
//and display randomized values of addr and wr_rd
function void post_randomize();
tmp_wr_rd = wr_rd;
$display("POST_RANDOMIZATION:: Addr = %0h,wr_rd
= %0h",addr,wr_rd);
endfunction
endclass

module rand_methods;
initial begin
packet pkt;
pkt = new();

repeat(4) pkt.randomize();
end
endmodule

Constrained randomization

As the name says random variable will get random value on


randomization. In some situations it is required to control the values
getting assigned on randomization, this can be achieved by writing
constraints. By writing constraints to a random variable, the user can get
specific value on randomization. constraints to a random variable shall
be written in constraint blocks.

Constraint blocks
 Constraint blocks are class members like tasks, functions, and
variables
 Constraint blocks will have a unique name within a class
 Constraint blocks consist of conditions or expressions to limit or
control the values for a random variable
 Constraint blocks are enclosed within curly braces { }
 Constraint blocks can be defined inside the class or outside the
class like extern methods, constraint block defined outside the class
is called as extern constraint block

Constraint block syntax

constraint <constraint_block_name> { <condition/expression>;


...
<condition/expression>; }
Constraint block examples

constraint addr_range { addr > 5; }

where, addr_range is constraint block name


addr is constrained in such a way that on randomization addr will get a
value greater than 5.

Constraint block inside the class

In the below example,


constraint block is defined inside the class.

class packet;
rand bit [3:0] addr;

constraint addr_range { addr > 5; }


endclass

module constr_blocks;
initial begin
packet pkt;
pkt = new();
repeat(10) begin
pkt.randomize();
$display("\taddr = %0d",pkt.addr);
end
end
endmodule

Constraint block outside the class

In the below example,


constraint block is declared inside the class and defined outside the class.

class packet;

rand bit [3:0] addr;


//constraint block declaration
constraint addr_range;
endclass

//constraint implementation outside class body


constraint packet::addr_range { addr > 5; }

module extern_constr;
initial begin
packet pkt;
pkt = new();

repeat(10) begin
pkt.randomize();
$display("\taddr = %0d",pkt.addr);
end
end
endmodule

Constraint Inheritance

Like class members, constraints also will get inherited from parent class
to child class. Constraint blocks can be overridden by writing constraint
block with the same name as in parent class.
In the example below,
Constraint to an addr > 5 of the parent class is overridden with
constraint addr < 5 in child class.

class packet;
rand bit [3:0] addr;
constraint addr_range { addr > 5; }
endclass

class packet2 extends packet;


constraint addr_range { addr < 5; } //overriding constraint of
parent class
endclass

module const_inhe;
initial begin
packet pkt1;
packet2 pkt2;

pkt1 = new();
pkt2 = new();

$display("------------------------------------");
repeat(5) begin
pkt1.randomize();
$display("\tpkt1:: addr = %0d",pkt1.addr);
end

$display("------------------------------------");
repeat(5) begin
pkt2.randomize();
$display("\tpkt2:: addr = %0d",pkt2.addr);
end
$display("------------------------------------");
end
endmodule

Constraint inside SystemVerilog


During randomization, it might require to randomize the variable within
a range of values or with inset of values or other than a range of values.
this can be achieved by using constraint inside operator.With
SystemVerilog inside operator, random variables will get values specified
within the inside block.

 values within the inside block can be variable, constant or range


 the inside block is written with an inside keyword followed by
curly braces {}

constraint addr_range { addr inside { ... }; }

 the range is specified by [ ]

constraint addr_range { addr inside { [5:10]}; }

 set of values are specified by ‘comma’,

constraint addr_range { addr inside { 1,3,5,7,9}; }

 it is allowed to mix range and set of values

constraint addr_range { addr inside {1,3,[5:10],12,[13:15]}; }

 if the value needs to be outside the range, then it can be specified


as inverse (!) of inside

constraint addr_range { addr !(inside {[5:10]}); }

 Other random variables can be used in inside block

rand bit [3:0] start_addr;


rand bit [3:0] end_addr;
rand bit [3:0] addr;
constraint addr_range { addr inside {[start_addr:end_addr]}; }
constraint inside example
In the example below,
addr_1 will get random value within the range start_addr and end_addr,

class packet;
rand bit [3:0] addr;
rand bit [3:0] start_addr;
rand bit [3:0] end_addr;

constraint addr_1_range { addr inside {[start_addr:end_addr]}; }


endclass

module constr_inside;
initial begin
packet pkt;
pkt = new();
$display("------------------------------------");
repeat(3) begin
pkt.randomize();
$display("\tstart_addr = %0d,end_addr
= %0d",pkt.start_addr,pkt.end_addr);
$display("\taddr = %0d",pkt.addr);
$display("------------------------------------");
end
end
endmodule

inverted inside example

In the example below,


addr will get the random value outside the range start_addr and
end_addr. this is done by using negation or invert operator (!)

class packet;
rand bit [3:0] addr;
rand bit [3:0] start_addr;
rand bit [3:0] end_addr;

constraint addr_1_range { !(addr inside {[start_addr:end_addr]}); }


endclass
module constr_inside;
initial begin
packet pkt;
pkt = new();
$display("------------------------------------");
repeat(3) begin
pkt.randomize();
$display("\tstart_addr = %0d,end_addr
= %0d",pkt.start_addr,pkt.end_addr);
$display("\taddr = %0d",pkt.addr);
$display("------------------------------------");
end
end
endmodule

dist Constraint in SystemVerilog

Constraint provides control on randomization, from which the user can


control the values on randomization. it would be good if it’s possible to
control the occurrence or repetition of the same value on
randomization.yes its possible, with dist operator, some values can be
allocated more often to a random variable. this is called a weighted
distribution. dist is an operator, it takes a list of values and weights,
separated by := or :/ operator.

weighted distribution

As the name says, in weighted distribution weight will be specified to the


values inside the constraint block. Value with the more weight will get
allocated more often to a random variable.

syntax

value := weight or
value :/ weight

Value – desired value to a random variable


weight – indicates how often the value needs to be considered on
randomization

 The values and weights can be constants or variables,


 value can be single or a range
 the default weight of an unspecified value is := 1
 the sum of weights need not be a 100

The := operator assigns the specified weight to the item, or if the item is
a range, specified weight to every value in the range.
addr dist { 2 := 5, [10:12] := 8 };

for addr == 2 , weight 5


addr == 10, weight 8
addr == 11, weight 8
addr == 12, weight 8

The :/ operator assigns the specified weight to the item, or if the item is
a range, specified weight/n to every value in the range. where n is the
number of values in the range.

addr dist { 2 :/ 5, [10:12] :/ 8 };

for addr == 2 , weight 5


addr == 10, weight 8/3
addr == 11, weight 8/3
addr == 12, weight 8/3
weighted distribution constraint examples
RANDOMIZATION WITH DIST OPERATOR

In the example below,


On randomization, the possibility of ‘addr’ is getting the value of 10 is
more than 7 and 2. this is because of weight specified to get value 10 is
more than the other two values.

class packet;
rand bit [3:0] addr;

constraint addr_range { addr dist { 2 := 5, 7 := 8, 10 := 12 }; }


endclass

module constr_dist;
initial begin
packet pkt;
pkt = new();
$display("------------------------------------");
repeat(10) begin
pkt.randomize();
$display("\taddr = %0d",pkt.addr);
end
$display("------------------------------------");
end
endmodule

DIFFERENCE BETWEEN := AND :/ DIST OPERATOR

In the below example,


addr_1=2 weight=5, and addr_1=10 weight=8, addr_1=11 weight=8,
addr_1=12 weight=8
addr_1=2 weight=5, and addr_1=10 weight=8/3=2.66, addr_1=11
weight=2.66, addr_1=12 weight=2.66

class packet;
rand bit [3:0] addr_1;
rand bit [3:0] addr_2;

constraint addr_1_range { addr_1 dist { 2 := 5, [10:12] := 8 }; }


constraint addr_2_range { addr_2 dist { 2 :/ 5, [10:12] :/ 8 }; }
endclass

module constr_dist;
initial begin
packet pkt;
pkt = new();

$display("------------------------------------");
repeat(10) begin
pkt.randomize();
$display("\taddr_1 = %0d",pkt.addr_1);
end
$display("------------------------------------");
$display("------------------------------------");
repeat(10) begin
pkt.randomize();
$display("\taddr_2 = %0d",pkt.addr_2);
end
$display("------------------------------------");
end
endmodule

Implication constraints and if else constraints in SystemVerilog


Implication constraints

The implication operator can be used to declaring conditional relations


between two variables. implication operator is denoted by the symbol ->.
The implication operator is placed between the expression and
constraint.

expression -> constraint

If the expression on the LHS of implication operator (->) is true, then the
only constraint on the RHS will be considered.

Implication constraint example

In the example below,


If addr_range == small, then addr will get a value less than 8.
i.e implication operator is used to mention condition between the two
variables addr_range and addr.

class packet;
rand bit [3:0] addr;
string addr_range;
constraint address_range { (addr_range == "small") -> (addr <
8);}
endclass

module constr_implication;
initial begin
packet pkt;
pkt = new();
pkt.addr_range = "small";
$display("------------------------------------");
repeat(4) begin
pkt.randomize();
$display("\taddr_range = %s addr
= %0d",pkt.addr_range,pkt.addr);
end
$display("------------------------------------");
end
endmodule

if else constraints

if else block allows conditional executions of constraints. If the


expression is true, all the constraints in the first constraint/constraint-
block must be satisfied, otherwise all the constraints in the optional else
constraint/constraint-block must be satisfied.

if else constraints example

Above an example of implication operator usage is written with if else


condition.

In the example below,


If addr_range == small, then addr will get a value less than 8.
class packet;
rand bit [3:0] addr;
string addr_range;

constraint address_range { if(addr_range == "small")


addr < 8;
else
addr > 8;
}
endclass

module constr_if_else;
initial begin
packet pkt;
pkt = new();
pkt.addr_range = "small";
$display("------------------------------------");
repeat(3) begin
pkt.randomize();
$display("\taddr_range = %s addr
= %0d",pkt.addr_range,pkt.addr);
end
$display("------------------------------------");

pkt.addr_range = "high";
$display("------------------------------------");
repeat(3) begin
pkt.randomize();
$display("\taddr_range = %s addr
= %0d",pkt.addr_range,pkt.addr);
end
$display("------------------------------------");
end
endmodule

SystemVerilog foreach loop Constraint Blocks


foreach constraint

SystemVerilog supports using the foreach loop inside a constraint block.


using the foreach loop within the constraint block will make easy to
constrain an array. The foreach loop iterates over the elements of an
array, so constraints with the foreach loop are called Iterative
constraints.
the foreach constraint will be applicable to an array with one or more
than one element. so it’s required to specify or constrain the size of the
dynamic array.

constraint constraint_name { foreach ( variable[iterator] ) variable[itera


tor] <..conditions..> }
foreach loop constraint example

In the below example,


addr and data are the two dynamic arrays, the first size of the arrays is
constrained and then the values for each array element are constrained
using a foreach loop.

Constraining array sizes,


constraint asize { addr.size < 4; }
constraint dsize { data.size == addr.size; }

Constraining array elements,


constraint avalues { foreach ( addr[i] ) addr[i] inside {4,8,12,16}; }
constraint dvalues { foreach ( data[j] ) data[j] > 4 * j; }

class packet;
rand byte addr [];
rand byte data [];

constraint avalues { foreach( addr[i] ) addr[i] inside {4,8,12,16}; }


constraint dvalues { foreach( data[j] ) data[j] > 4 * j; }
constraint asize { addr.size < 4; }
constraint dsize { data.size == addr.size; }
endclass

module constr_iteration;
initial begin
packet pkt;
pkt = new();

$display("------------------------------------");
repeat(2) begin
pkt.randomize();
$display("\taddr-size = %0d data-size
= %0d",pkt.addr.size(),pkt.data.size());
foreach(pkt.addr[i]) $display("\taddr = %0d data
= %0d",pkt.addr[i],pkt.data[i]);
$display("------------------------------------");
end
end
endmodule

Disable Constraints
Constraints in a class can be disabled using the constraint_mode method
call. By default all the constraints will be enabled, during the
randomization constraint solver will not consider the disabled
constraints. the constraint disables method is similar to rand_mode()
method.

constraint_mode() method

The constraint_mode() method can be used to disable any particular


constraint block.

 constraint_mode(1) means constraint block is enabled


 constraint_mode(0) means constraint block is disabled
 default value of constraint_mode is 1, i.e enabled
 once the constraint block is disabled, it is required to make
constraint_mode(1) enable back the constraint block
 constraint_mode can be called as like SystemVerilog method,
which returns the enable/disable status of a constraint block

constraint_mode syntax

<object_hanlde>.<constraint_block_name>.constraint_mode(enable);
//enable == 1, constraint block enable
//enable == 0, constraint block disable
constraint disable examples
without constraint disable

In the below example,


The constraint is enabled by default, so on randomization constraint
solver will consider the constraint and assigns the random value to
variable addr as per the constraint is written.

class packet;
rand bit [3:0] addr;

constraint addr_range { addr inside {5,10}; }


endclass

module static_constr;
initial begin
packet pkt;
pkt = new();

pkt.randomize();
$display("\taddr = %0d",pkt.addr);
end
endmodule

with constraint disabled

The constraint is disabled by using the constraint_mode method, so on


randomization constraint solver will not consider the constraint.

class packet;
rand bit [3:0] addr;

constraint addr_range { addr inside {5,10,15}; }


endclass

module static_constr;
initial begin
packet pkt;
pkt = new();

$display("Before Constraint disable");


repeat(2) begin //{
pkt.randomize();
$display("\taddr = %0d",pkt.addr);
end //}

//disabling constraint
pkt.addr_range.constraint_mode(0);

$display("After Constraint disable");


repeat(2) begin //{
pkt.randomize();
$display("\taddr = %0d",pkt.addr);
end //}
end
endmodule

calling constraint_mode method

In the below example,


Constrain_mode is called as method to see to the enable/disable status
of constraint.

class packet;
rand bit [3:0] addr;

constraint addr_range { addr inside {5,10,15}; }


endclass

module static_constr;
initial begin
packet pkt;
pkt = new();

$display("Before Constraint disable");


$display("Value of constraint mode
= %0d",pkt.addr_range.constraint_mode());
pkt.randomize();
$display("\taddr = %0d",pkt.addr);

//disabling constraint
pkt.addr_range.constraint_mode(0);

$display("After Constraint disable");


$display("Value of constraint mode
= %0d",pkt.addr_range.constraint_mode());
pkt.randomize();
$display("\taddr = %0d",pkt.addr);

end
endmodule

Static Constraints in SystemVerilog


SystemVerilog static constraints are similar to static class properties. A
constraint block can be defined as static by including the static keyword
in its definition. constraint block with the static keyword followed by
constraint keyword is called as a static constraint. the static constraint is
shared across all the class instances.

 only the mode change of static constraint will get affected in all
the instances of a class
 mode change is enable or disable of a constraint by
constrain_mode() method
 a static class can be enabled or disabled by any object handle of its
class, mode change with one object handle will reflect all other
objects of same class type

Static constraint syntax

static constraint constraint_name { ....; }


Static constraint examples
turn off non-static constraint

In the below example,


The class packet has two instances pkt1 and pkt2. constraint addr_range
is defined to control the value of random variable addr.
On randomization, pkt1.addr and pkt2.addr will take value based on the
constraint.
The constraint is disabled using a pkt2 handle. As constraint is non-static
only for pkt2 constraint will get disabled. On randomization, pkt1.addr
will get value based on the constraint.

class packet;
rand bit [7:0] addr;

constraint addr_range { addr == 5; }


endclass

module static_constr;
initial begin
packet pkt1;
packet pkt2;
pkt1 = new();
pkt2 = new();

$display("Before disabling constraint");


pkt1.randomize();
$display("\tpkt1.addr = %0d",pkt1.addr);
pkt2.randomize();
$display("\tpkt2.addr = %0d",pkt2.addr);

pkt2.addr_range.constraint_mode(0);

$display("After disabling constraint");


pkt1.randomize();
$display("\tpkt1.addr = %0d",pkt1.addr);
pkt2.randomize();
$display("\tpkt2.addr = %0d",pkt2.addr);
end

turn off static constraint

The example below is the same as the above example, the only change is
the constraint is made as static.
The constraint is disabled by using one of the object handles. as the
constraint is static in nature, the constraint will get disabled for both the
objects.

class packet;
rand bit [7:0] addr;

static constraint addr_range { addr == 5; }


endclass

module static_constr;
initial begin
packet pkt1;
packet pkt2;
pkt1 = new();
pkt2 = new();

$display("Before disabling constraint");


pkt1.randomize();
$display("\tpkt1.addr = %0d",pkt1.addr);
pkt2.randomize();
$display("\tpkt2.addr = %0d",pkt2.addr);

pkt2.addr_range.constraint_mode(0);

$display("After disabling constraint");


pkt1.randomize();
$display("\tpkt1.addr = %0d",pkt1.addr);
pkt2.randomize();
$display("\tpkt2.addr = %0d",pkt2.addr);
end
endmodule

Inline Constraints in SystemVerilog


Constraints will be written inside the class. inline constraint allows the
user to add extra constraints to existing constraints written inside the
class. inline constraints will be written outside the class i.e along with
the randomize method call.

 the inline constraint is written using with keyword


 during randomization, constraint solver will consider both inline
constraints and constraints written inside the class
 the inline constraint will not override the constraints written
inside the class
 the inline constraint shouldn’t conflict with the constraint written
inside the class, else it will lead to randomization failure
 for example, constraint inside the class written as var < 5,
and inline constraint written as var > 5

Inline constraint Syntax

object.randomize() with { .... };


Inline constraint examples
Only inline constraint

In the below example,


Class doesn’t have constraints defined in it. the inline constraint is used
to constrain the variable addr.
class packet;
rand bit [3:0] addr;
endclass

module inline_constr;
initial begin
packet pkt;
pkt = new();

repeat(2) begin
pkt.randomize() with { addr == 8;};
$display("\taddr = %0d",pkt.addr);
end
end
endmodule

Constraint inside the class and inline constraint

In the below example,


addr and data are the two random variables. The constraint for data is
written inside the class, and the inline constraint is written for addr.

Conclusion: Both class and inline constraints are considered during


randomization.

class packet;
rand bit [3:0] addr;
rand bit [3:0] data;

constraint data_range { data > 0;


data < 10; }
endclass

module inline_constr;
initial begin
packet pkt;
pkt = new();
repeat(2) begin
pkt.randomize() with { addr == 8;};
$display("\taddr = %0d data = %0d",pkt.addr,pkt.data);
end
end
endmodule

In the below example,


The addr is the random variable. constraint inside the class written as
addr < 5, and inline constraint written as addr > 5.

Conclusion: Conflict between the class and inline constraints leads to


randomization failure.

class packet;
rand bit [3:0] addr;

constraint addr_range {addr < 5;};


endclass

module inline_constr;
initial begin
packet pkt;
pkt = new();
repeat(2) begin
pkt.randomize() with { addr > 5;};
$display("\taddr = %0d",pkt.addr);
end
end
endmodule

Class and inline constraints for the same random variable

In the below example,


The addr is the random variable. constraint inside the class written as
addr between 6:12, and inline constraint is written as addr == 8.

Conclusion: Constraint solver considers both class and inline constraints.

class packet;
rand bit [3:0] addr;
constraint addr_range {addr inside {[6:12]};};
endclass

module inline_constr;
initial begin
packet pkt;
pkt = new();
repeat(2) begin
pkt.randomize() with { addr == 8;};
$display("\taddr = %0d",pkt.addr);
end
end
endmodule

Functions in Constraints

In some cases constraint can’t be expressed in a single line, in such cases


function call can be used to constrain a random variable. calling the
function inside the constraint is referred to as function in constraints.

 The function will be written outside the constraint block


 Constraint logic shall be written inside the function as function
definition and function call shall be placed inside the constraint
block
 Functions shall be called before constraints are solved, and their
return values shall be treated as state variables.

constraint constraint_name { var = function_call(); };


Functions in constraints example

In the below example,


The function is called inside the constraint.

class packet;
rand bit [3:0] start_addr;
rand bit [3:0] end_addr;

constraint start_addr_c { start_addr == s_addr(end_addr); }

function bit [3:0] s_addr(bit [3:0] e_addr);


if(e_addr < 4)
s_addr = 0;
else
s_addr = e_addr - 4;
endfunction

endclass

module func_constr;
initial begin
packet pkt;
pkt = new();
repeat(3) begin
pkt.randomize();
$display("\tstart_addr = %0d end_addr
=",pkt.start_addr,pkt.end_addr);
end
end
endmodule

SystemVerilog Soft Constraints

SystemVerilog constraints declared with the keyword soft is called as


soft constraints. any conflict between class constraint and inline
constraint leads to a randomization failure, from this it is clear that it is
not possible to override the class constraint by inline constraint. Some
test scenarios demand to override the constraints, this can be done by
writing a soft keyword in class constraint.

A soft constraint is a constraint on a random variable, which allows


overriding the constraint.

constraint c_name { soft variable { condition }; }


real-time use of soft constraint

This is one of the situations where soft constraints are useful.


Lets Consider, In a verification testbench transaction class constraint is
written to generate normal stimulus. For error stimulus generation,
error testcase need to have an inline constraint that may conflict with
the constraint defined inside the class. In this situation, we have only the
option to change the constraint defined inside the transaction class, but
changing the constraint in the transaction class will reflect in all other
test cases. so this kind of problem can be avoided using soft constraints.

soft constraint examples


Conflict between constraints

In the example below,


In the class packet, the addr variable is constrained to greater than 6 and
the same addr variable is constrained to less than 6 in the inline
constraint. that means expecting the value of addr to be less than and
greater than 6, this is not possible and leads to a randomization failure.
this problem is resolved in the next example using soft constraint.

class packet;
rand bit [3:0] addr;
constraint addr_range { addr > 6; }
endclass

module soft_constr;
initial begin
packet pkt;
pkt = new();

repeat(2) begin
pkt.randomize() with { addr < 6;};
$display("\taddr = %0d",pkt.addr);
end
end
endmodule
Using soft constraint

In the example below,


a previous example problem is solved using soft constraints,
Constraint declared inside the class will get suppressed by inline
constraints.

class packet;
rand bit [3:0] addr;
constraint addr_range { soft addr > 6; }
endclass

module soft_constr;
initial begin
packet pkt;
pkt = new();

repeat(2) begin
pkt.randomize() with { addr < 6;};
$display("\taddr = %0d",pkt.addr);
end
end
endmodule

Unique Constraint

SystemVerilog constraint defined with the keyword unique is called as a


unique constraint. On randomization, unique values to set of variables or
unique elements to an array can be generated by using unique
constraints.

Unique constraint allows us to,

 Generate unique values across the variables


 Generate unique elements in an array (Fixed Size Array, Dynamic
Array, Associative array and Queue)

constraint c_name { unique {variable's/array}; }

Below example shows,

 Generation of random unique values to the variables var_1, var_2


and var_3
 Generation of random unique elements to an array array

Unique constraint example


Unique elements

In the below example,


On randomization unique values to a variable var_1, var_2, var_3 can be
obtained by writing unique constraints. Also, a unique constraint is
written to an array to get unique array elements.

class unique_elements;
rand bit [3:0] var_1,var_2,var_3;
rand bit [7:0] array[6];
constraint varis_c {unique {var_1,var_2,var_3};}
constraint array_c {unique {array};}

function void display();


$display("var_1 = %p",var_1);
$display("var_2 = %p",var_2);
$display("var_3 = %p",var_3);
$display("array = %p",array);
endfunction
endclass

program unique_elements_randomization;
unique_elements pkt;

initial begin
pkt = new();
pkt.randomize();
pkt.display();
end
endprogram

unique array of elements example

In below example,
Unique elements for an array is generated by using a unique keyword.
Also considered that the value of elements is less than 10.

class unique_elements;
rand bit [31:0] array[10];

constraint array_c {unique {array};


foreach(array[i]) array[i] < 10;}

function void display();


$display("array = %p",array);
endfunction
endclass

program unique_elements_randomization;
unique_elements pkt;

initial begin
pkt = new();
pkt.randomize();
pkt.display();
end

endprogram

Bidirectional Constraints

SystemVerilog constraints are solved bidirectionally, which means


constraints on all random variables will be solved parallel.

Consider a constraint example,

constraint c_name { if(a == 0) b == 1;


else b == 0; }

We see that ‘b’ is dependent on ‘a’.


but constraint solver see’s it as ‘a’ is dependent on ‘b’ and ‘b’ is
dependent on ‘a’.
i.e if ‘b’ is inline constrained as ‘1’, in order to satisfy, ‘a’ should take the
value ‘0’.

As constraints are considered from all the aspects, SystemVerilog


constraints are called as bidirectional constraints.

Bidirectional constraint example

In the example below,

Value of a should be sum of b and c, b should be greater than 6 and c


should be less than 8. so in this case constraint solver will choose a value
to ‘a’ in such a way that it should be sum of b and c, also ‘b’ and ‘c’
should satisfies their constraint.

class packet;
rand bit [3:0] a;
rand bit [3:0] b;
rand bit [3:0] c;

constraint a_value { a == b + c; }
constraint b_value { b > 6; }
constraint c_value { c < 8; }
endclass

module bidirectional_constr;
initial begin
packet pkt;
pkt = new();
repeat(5) begin
pkt.randomize();
$display("Value of a = %0d \tb = %0d \tc
=%0d",pkt.a,pkt.b,pkt.c);
end
end
endmodule

Bidirectional constraint example 2

In below example,

Generation of value for b is depending on value of a.


i.e if(a == 0) b = 1;

this condition can be re-written as,


if(b == 1) a = 0;

but there is a constraint for ‘a’ that value for ‘a’ should be always ‘1’. so
‘b’ should not take value of ‘1’ (to satisfy constraint if(a == 0) b == 1;)

What if we make ‘b’ value as ‘1’ with inline constraint?


See the below example for the answer.
class packet;
rand bit a;
rand bit b;

constraint a_value { a == 1; }
constraint b_value { if(a == 0) b == 1;
else b == 0; }
endclass

module bidirectional_const;
initial begin
packet pkt;
pkt = new();
pkt.randomize() with { b == 1; };
$display("Value of a = %0d \tb = %0d",pkt.a,pkt.b);
end
endmodule

Solve Before Constraints

Solve before is the constraint property. solve before is used inside the
constraint block to specify the order of constraint solving. If the variables
are dependent, due to the bidirectional nature of constraints value of
one variable will influence the value of another variable.

 solve before constraints are used to force the constraint solver to


choose the order in which constraints are solved.
 constraint solver will give equal weight-age to all the possible
values. i.e On multiple randomization solver should assign all the
possible values.

class pakcet;
rand bit a;
rand bit [3:0] b;

constraint a_b { (a == 1) -> b == 0; }


endclass

Possible value of,


a -> 0 and 1; // 2 possible values
b -> 0 to 15; // 16 possible values

As ‘b’ value ranges 0:15, Probability of getting b == 0 is very less.


so it will impact the value of a, i.e if value of b != 0, which will lead to
value of ‘a’ to become ‘0’.

What if in some situations it is required to generate a value of ‘a’ to ‘1’


more frequently. by using ‘solve before’ it can be done.

Writing below constraint will direct solver to solve ‘a’ first, so more
frequently a will take value of 1.

constraint sab { solve a before b;}


Solve before constraint example
without solve before

The below example is the example explained above,


this example is without solve before constraint, so we can see the
simulation result that the occurrence of value a == 1 is less.

class packet;
rand bit a;
rand bit [3:0] b;

constraint a_b { (a == 1) -> b == 0; }


endclass

module inline_constr;
initial begin
packet pkt;
pkt = new();
repeat(10) begin
pkt.randomize();
$display("\tValue of a = %0d, b = %0d",pkt.a,pkt.b);
end
end
endmodule
with solve before

this example is to solve before constraint, so we can see the simulation


result that the occurrence of value a == 1 is more than without solve
before constraint.

Random System Functions

$urandom( )

The system function $urandom provides a mechanism for generating


pseudorandom numbers. The function returns a new 32-bit random
number each time it is called. The number shall be unsigned.

variable = $urandom(seed);

The seed is an optional argument that determines the sequence of


random numbers generated. The seed can be an integral expression. for
a particular seed, the same value will get generated.

bit [31:0] addr1;


bit [31:0] addr2;
bit [64:0] addr3;
bit [31:0] data;
addr1 = $urandom();
addr2 = $urandom(89);
addr3 = {$urandom(),$urandom()};
data = $urandom * 6;
$random( )

$random() is same as $urandom() but it generates signed numbers.

$urandom_range( )

The $urandom_range() function returns an unsigned integer within a


specified range.
$urandom_range( int unsigned maxval, int unsigned minval = 0 );addr1
= $urandom_range(30,20);
addr2 = $urandom_range(20); //takes max value as '0'
addr3 = $urandom_range(20,30); //considers max value as '30' and min
value as '20'
random system methods examples

In the example below,


Shows usage of $urandom and $urandom_range.

module system_funcations;
bit [31:0] addr1;
bit [31:0] addr2;
bit [64:0] addr3;
bit [31:0] data;
initial begin
addr1 = $urandom();
addr2 = $urandom(89);
addr3 = {$urandom(),$urandom()};
data = $urandom * 6;

$display("addr1=%0d, addr2=%0d, addr3=%0d,


data=%0d",addr1,addr2,addr3,data);

addr1 = $urandom_range(30,20);
addr2 = $urandom_range(20); //takes max value as '0'
addr3 = $urandom_range(20,30); //considers max value as '30'
and min value as '20'
$display("addr1=%0d, addr2=%0d,
addr3=%0d",addr1,addr2,addr3);
end
endmodule
Semaphore

Semaphore is a SystemVerilog built-in class, used for access control to


shared resources, and for basic synchronization.

A semaphore is like a bucket with the number of keys. processes using


semaphores must first procure a key from the bucket before they can
continue to execute, All other processes must wait until a sufficient
number of keys are returned to the bucket.

Imagine a situation where two processes try to access a shared memory


area. where one process tries to write and the other process is trying to
read the same memory location. this leads to an unexpected result. A
semaphore can be used to overcome this situation.

Semaphore syntax

semaphore semaphore_name;
Semaphore methods

Semaphore is a built-in class that provides the following methods,

 new(); Create a semaphore with a specified number of keys


 get(); Obtain one or more keys from the bucket
 put(); Return one or more keys into the bucket
 try_get(); Try to obtain one or more keys without blocking

new( );

The new() method is used to create the Semaphore.

semaphore_name = new(numbers_of_keys);
 the new method will create the semaphore with number_of_keys
keys in a bucket; where number_of_keys is integer variable.
 the default number of keys is ‘0’
 the new() method will return the semaphore handle or null if the
semaphore cannot be created

put( );

The semaphore put() method is used to return key/keys to a semaphore.

semaphore_name.put(number_of_keys); or semaphore_name.put();

When the semaphore_name.put() method is called, the specified


number of keys are returned to the semaphore. The default number of
keys returned is 1.

get( );
The semaphore get() method is used to get key/keys from a semaphore.
semaphore_name.get(number_of_keys); or semaphore_name.get();

When the semaphore_name.get() method is called,

 If the specified number of keys are available, then the method


returns and execution continues
 If the specified number of keys are not available, then the process
blocks until the keys become available
 The default number of keys requested is 1

try_get();

The semaphore try_get() method is used to procure a specified number


of keys from a semaphore, but without blocking.

semaphore_name.try_get(number_of_keys); or semaphore_name.try_g
et();

When the semaphore_name.try_get() method is called,

 If the specified number of keys are available, the method returns


1 and execution continues
 If the specified number of keys are not available, the method
returns 0 and execution continues
 The default number of keys requested is 1

Semaphore examples
two processes accessing the same resource

In the example below,


semaphore sema is created with the 1 key, two processes are accessing
the display method at the same time, but only one process will get the
semaphore key and the other process will wait till it gets the key.

module semaphore_ex;
semaphore sema; //declaring semaphore sema
initial begin
sema=new(1); //creating sema with '1' key
fork
display(); //process-1
display(); //process-2
join
end

//display method
task automatic display();
sema.get(); //getting '1' key from sema
$display($time,"\tCurrent Simulation Time");
#30;
sema.put(); //putting '1' key to sema
endtask
endmodule

Semaphore with 4 keys

In the example below,


Creating semaphore with ‘4’ keys.

module semaphore_ex;
semaphore sema; //declaring semaphore sema
initial begin
sema=new(4); //creating sema with '4' keys
fork
display(); //process-1
display(); //process-2
join
end
//display method
task automatic display();
sema.get(4); //getting '4' keys from sema
$display($time,"\tCurent Simulation Time");
#30;
sema.put(4); //putting '4' keys to sema
endtask
endmodule

Semaphore Examples
Semaphore access with 2 keys

In the example below,


Creating semaphore with ‘4’ keys. ‘2’ keys are required to get access to
the method.

At the same time, two processes will get access to the method and the
other process will be blocked until the one other process puts the key.

module semaphore_ex;
semaphore sema; //declaring semaphore sema

initial begin
sema=new(4); //creating sema with '4' keys
fork
display(); //process-1
display(); //process-2
display(); //process-3
join
end

//display method
task automatic display();
sema.get(2); //getting '2' keys from sema
$display($time,"\tCurrent Simulation Time");
#30;
sema.put(2); //putting '2' keys to sema
endtask
endmodule

Putting back more keys

In the example below,


Creating semaphore with ‘1’ key, putting more number of keys back to
the semaphore.

module semaphore_ex;
semaphore sema; //declaring semaphore sema

initial begin
sema=new(1); //creating sema with '1' keys
fork
display(1); //process-1
display(2); //process-2
display(3); //process-3
join
end

//display method
task automatic display(int key);
sema.get(key); //getting 'key' number of keys
from sema
$display($time,"\tCurrent Simulation Time,
Got %0d keys",key);
#30;
sema.put(key+1); //putting 'key' number of keys
to sema
endtask
endmodule

the process with more than 1 key

In the example below, Creating a semaphore with the ‘4’ keys, the
method will be blocked until it gets enough keys.
module semaphore_ex;
semaphore sema; //declaring semaphore sema

initial begin
sema=new(4); //creating sema with '4' keys
fork
display(2); //process-1
display(3); //process-2
display(2); //process-3
display(1); //process-4
join
end

//display method
task automatic display(int key);
sema.get(key); //getting 'key' number of keys
from sema
$display($time,"\tCurrent Simulation Time,
Got %0d keys",key);
#30;
sema.put(key); //putting 'key' number of keys to
sema
endtask
endmodule

using try_get

In the example below,


Creating semaphore with ‘4’ key, try_get() will check for the keys if the
keys are not available simulation will proceed(non-blocking).

module semaphore_ex;
semaphore sema; //declaring semaphore sema

initial begin
sema=new(4); //creating sema with '4' keys
fork
display(4); //process-1
display(4); //process-2
join
end

//display method
task automatic display(int key);
sema.try_get(key); //getting 'key' number of keys
from sema
$display($time,"\tCurrent Simulation Time,
Got %0d keys",key);
#30;
sema.put(key); //putting 'key' number of keys to
sema
endtask
endmodule

SystemVerilog Mailbox

A mailbox is a communication mechanism that allows messages to be


exchanged between processes. The process which wants to talk to
another process posts the message to a mailbox, which stores the
messages temporarily in a system defined memory object, to pass it to
the desired process.

Based on the sizes mailboxes are categorized as,

 bounded mailbox
 unbounded mailbox

A bounded mailbox is with the size defined. mailbox becomes full when
on storing a bounded number of messages. A process that attempts to
place a message into a full mailbox shall be suspended until enough
space becomes available in the mailbox queue.

Unbounded mailboxes are with unlimited size.

Mailbox types

There are two types of mailboxes,

 Generic Mailbox
 Parameterized mailbox
Generic Mailbox (type-less mailbox)

The default mailbox is type-less. that is, a single mailbox can send and
receive data of any type.

mailbox mailbox_name;
Parameterized mailbox (mailbox with particular type)

Parameterized mailbox is used to transfer a data of particular type.

mailbox#(type) mailbox_name;
Mailbox Methods
SystemVerilog Mailbox is a built-in class that provides the following
methods. these are applicable for both Generic and Parameterized
mailboxes
new(); - Create a mailbox
put(); - Place a message in a mailbox
try_put(); - Try to place a message in a mailbox
without blocking
get(); or peek();- Retrieve a message from a mailbox
num(); - Returns the number of messages in
the mailbox
try_get(); or try_peek(); - Try to retrieve a message
from a mailbox without blocking
new( );

Mailboxes are created with the new() method.

mailbox_name = new(); // Creates unbounded


mailbox and returns mailbox handle

mailbox_name = new(m_size); //Creates bounded


mailbox with size m_size and returns mailbox
handle ,where m_size is integer variable
SystemVerilog Mailbox example

In the example below,


Mailbox is used for communication between generator and driver.
 Process-1(Generator class) will generate (created and randomize)
the packet and put into the mailbox mb_box
 Process-2(Driver class) gets the generated packet from the
mailbox and display the fields

//------
-------------------------------------------------------------------
// Packet
//-------------------------------------------------------------------------
class packet;
rand bit [7:0] addr;
rand bit [7:0] data;

//Displaying randomized values


function void post_randomize();
$display("Packet::Packet Generated");
$display("Packet::Addr=%0d,Data=%0d",addr,data);
endfunction
endclass

//-------------------------------------------------------------------------
//Generator - Generates the transaction packet and send to
driver
//-------------------------------------------------------------------------
class generator;
packet pkt;
mailbox m_box;
//constructor, getting mailbox handle
function new(mailbox m_box);
this.m_box = m_box;
endfunction
task run;
repeat(2) begin
pkt = new();
pkt.randomize(); //generating packet
m_box.put(pkt); //putting packet into mailbox
$display("Generator::Packet Put into Mailbox");
#5;
end
endtask
endclass

//-------------------------------------------------------------------------
// Driver - Gets the packet from generator and display's the
packet items
//-------------------------------------------------------------------------
class driver;
packet pkt;
mailbox m_box;

//constructor, getting mailbox handle


function new(mailbox m_box);
this.m_box = m_box;
endfunction

task run;
repeat(2) begin
m_box.get(pkt); //getting packet from mailbox
$display("Driver::Packet Received");
$display("Driver::Addr=%0d,Data=%0d\n",pkt.addr,pkt.data);
end
endtask
endclass

//-------------------------------------------------------------------------
// tbench_top
//-------------------------------------------------------------------------
module mailbox_ex;
generator gen;
driver dri;
mailbox m_box; //declaring mailbox m_box

initial begin
//Creating the mailbox, Passing the same handle to generator
and driver,
//because same mailbox should be shared in-order to
communicate.
m_box = new(); //creating mailbox

gen = new(m_box); //creating generator and passing mailbox


handle
dri = new(m_box); //creating driver and passing mailbox
handle
$display("------------------------------------------");
fork
gen.run(); //Process-1
dri.run(); //Process-2
join
$display("------------------------------------------");
end
endmodule

SystemVerilog Events

Events are static objects useful for synchronization between the process.
Events operations are of two staged processes in which one process will
trigger the event, and the other processes will wait for an event to be
triggered.

 Events are triggered using -> operator or ->> operator


 wait for an event to be triggered using @ operator or

 wait() construct

SystemVerilog events act as handles to synchronization queues. thus,


they can be passed as arguments to tasks, and they can be assigned to
one another or compared.
Event triggering
-> operator

Named events are triggered via the -> operator. Triggering an event
unblocks all processes currently waiting on that event.

->> operator
Non-blocking events are triggered using the ->> operator.

Waiting for event trigger


@ operator

wait for an event to be triggered is via the event control operator, @.

@(event_name.triggered);

 The @ operator blocks the calling process until the given event is
triggered.
 For a trigger to unblock a process waiting on an event, the waiting
process must execute the @ statement before the triggering process
executes the trigger operator, ->

Note: If the trigger executes first, then the waiting process remains
blocked.

wait operator

If the event triggering and waiting for event trigger with @ operator
happens at the same time, @ operator may miss detecting the event
trigger.

Whereas wait(); construct will detect the event triggering.


wait(event_name.triggered);
wait_order();

The wait_order construct is blocking the process until all of the specified
events are triggered in the given order (left to right). event trigger with
out of order will not unblock the process.

Example:

wait_order(a,b,c);

Blocks the process until events a, b, and c trigger in the order a –> b –> c.
If the events trigger out of order, a run-time error is generated.

Example:
wait_order( a, b, c ) else $display( "Error: events out
of order" );

In this example, the fail statement specifies that upon failure of the
construct, a user message is displayed, but without an error being
generated.

Example:

bit success;
wait_order( a, b, c ) success = 1; else success = 0;

In this example, the completion status is stored in the variable success,


without an error being generated.

Merging events

When one event variable is assigned to another, the two become


merged. Thus, executing -> on either event variable affects processes
waiting on either event variable.

Event examples
the event waiting with @ operator

The below example shows the event triggering and waiting for the event
trigger.

module events_ex;
event ev_1; //declaring event ev_1

initial begin
fork
//process-1, triggers the event
begin
#40;
$display($time,"\tTriggering The Event");
->ev_1;
end

//process-2, wait for the event to trigger


begin
$display($time,"\tWaiting for the Event to
trigger");
@(ev_1.triggered);
$display($time,"\tEvent triggered");
end
join
end
endmodule

trigger first and then waiting for a trigger

In the below example,


event triggering happens first and then the waiting for trigger happens.
As the waiting happens later, it will be blocking, so the statements after
the wait for the trigger will not be executed.

module events_ex;

event ev_1; //declaring event ev_1

initial begin
fork
//process-1, triggers the event
begin
#40;
$display($time,"\tTriggering The Event");
->ev_1;
end

//process-2, wait for the event to trigger


begin
$display($time,"\tWaiting for the Event to
trigger");
#60;
@(ev_1.triggered);
$display($time,"\tEvent triggered");
end
join
end
initial begin
#100;
$display($time,"\tEnding the Simulation");
$finish;
end
endmodule

events examples
trigger and wait for an event at the same time

In the example below,


event triggering and waiting for the event trigger will happen at the
same time.
wait(); the operator is used to detects the event triggering.

module events_ex;
event ev_1; //declaring event ev_1

initial begin
fork
//process-1, triggers the event
begin
$display($time,"\tTriggering The Event");
->ev_1;
end

//process-2, wait for the event to trigger


begin
$display($time,"\tWaiting for the Event to
trigger");
wait(ev_1.triggered);
$display($time,"\tEvent triggered");
end
join
end
endmodule

trigger and wait for an event at the same time


In the example below,
event triggering and waiting for the event trigger will happen at the
same time.@() operator is used to detecting the event trigger. but as
triggering and waiting happens at the same time, @() operator will not
detect the event. this can be solved by using wait() operator;

module events_ex;
event ev_1; //declaring event ev_1

initial begin
fork
//process-1, triggers the event
begin
$display($time,"\tTriggering The Event");
->ev_1;
end

//process-2, wait for the event to trigger


begin
$display($time,"\tWaiting for the Event to
trigger");
@(ev_1.triggered);
$display($time,"\tEvent triggered");
end
join
endendmodule

wait_order example

In the example below,


There are three events ev_1, ev_2 and ev_3. the wait_order(); is used to
see to the order in which the events are triggered.

module events_ex;
event ev_1; //declaring event ev_1
event ev_2; //declaring event ev_2
event ev_3; //declaring event ev_3

initial begin
fork
//process-1, triggers the event ev_1
begin
#6;
$display($time,"\tTriggering The Event ev_1");
->ev_1;
end
//process-2, triggers the event ev_2
begin
#2;
$display($time,"\tTriggering The Event ev_2");
->ev_2;
end
//process-3, triggers the event ev_3
begin
#8;
$display($time,"\tTriggering The Event ev_3");
->ev_3;
end
//process-4, wait for the events to trigger in
order of ev_2,ev_1 and ev_3
begin
$display($time,"\tWaiting for the Event's to
trigger");
wait_order(ev_2,ev_1,ev_3)
$display($time,"\tEvent's triggered Inorder");
else
$display($time,"\tEvent's triggered Out-Of-
Order");
end
join
end
endmodule

wait_order example

In the example below,


There are three events ev_1, ev_2 and ev_3. the wait_order(); is used to
see to the order in which the events are triggered.
But events are triggered in out of order, leads to execution of else part
of wait_order().

module events_ex;
event ev_1; //declaring event ev_1
event ev_2; //declaring event ev_2
event ev_3; //declaring event ev_3

initial begin
fork
//process-1, triggers the event ev_1
begin
#6;
$display($time,"\tTriggering The Event ev_1");
->ev_1;
end

//process-2, triggers the event ev_2


begin
#2;
$display($time,"\tTriggering The Event ev_2");
->ev_2;
end

//process-3, triggers the event ev_3


begin
#1;
$display($time,"\tTriggering The Event ev_3");
->ev_3;
end
//process-4, wait for the events to trigger in
order of ev_2,ev_1 and ev_3
begin
$display($time,"\tWaiting for the Event's to
trigger");
wait_order(ev_2,ev_1,ev_3)
$display($time,"\tEvent's triggered Inorder");
else
$display($time,"\tEvent's triggered Out-Of-
Order");
end
join
end
endmodule

Program Block

The Program construct provides a race-free interaction between the


design and the testbench, all elements declared within the program
block will get executed in the Reactive region. Non-blocking assignments
within the module are scheduled in the active region, initial blocks
within program blocks are scheduled in the Reactive region.

Statements within a program block (scheduled in the Reactive region)


that are sensitive to changes in design signals declared in modules
(scheduled in the active region), an active region is scheduled before the
reactive region this avoids the race condition between testbench and
design.

Program block syntax

program test(input clk, input [7:0] addr, output [7:0] wdata);


...
endprogram

or

program test (interface mem_intf);


...
endprogram
Program block,

 can be instantiated and ports can be connected the same as a


module
 can contain one or more initial blocks
 cannot contain always blocks, modules, interfaces, or other
programs
 In the program block, variables can only be assigned using
blocking assignments. Using non-blocking assignments within the
program shall be an error
difference between module and program

In the examples below, find the difference between writing testbench


with module block and program block.
In example-1 writing testbench with module block, because of race
condition testbench gets the dut signal addr value as 0.

In example-2 writing testbench with program block, testbench gets the


dut signal addr value as 1.

Therefore writing testbench with program block provides race-free


interaction between the design and the testbench.

example-1 with module block

//-------------------------------------------------------------------------
//design code
//-------------------------------------------------------------------------
module design_ex(output bit [7:0] addr);
initial begin
addr <= 10;
end
endmodule

//-------------------------------------------------------------------------
//testbench
//-------------------------------------------------------------------------
module testbench(input bit [7:0] addr);
initial begin
$display("\t Addr = %0d",addr);
end
endmodule

//-------------------------------------------------------------------------
//testbench top
//-------------------------------------------------------------------------
module tbench_top;
wire [7:0] addr;

//design instance
design_ex dut(addr);
//testbench instance
testbench test(addr);
endmodule

example-2 with a program block

//-------------------------------------------------------------------------
//design code
//-------------------------------------------------------------------------
module design_ex(output bit [7:0] addr);
initial begin
addr <= 10;
end
endmodule

//-------------------------------------------------------------------------
//testbench
//-------------------------------------------------------------------------

program testbench(input bit [7:0] addr);


initial begin
$display("\t Addr = %0d",addr);
end
endprogram

//-------------------------------------------------------------------------
//testbench top
//-------------------------------------------------------------------------
module tbench_top;
wire [7:0] addr;

//design instance
design_ex dut(addr);
//testbench instance
testbench test(addr);
endmodule

SystemVerilog Interface
In Verilog, the communication between blocks is specified using module
ports. SystemVerilog adds the interface construct which encapsulates
the communication between blocks. An interface is a bundle of signals
or nets through which a testbench communicates with a design. A virtual
interface is a variable that represents an interface instance. this section
describes the interface, interface over traditional method and virtual
interface.

Interface Construct

The interface construct is used to connect the design and testbench.

without Interface

Above diagram shows connecting design and


testbench without interface.

SystemVerilog
Interface

Above diagram shows connecting design and


testbench with the interface.

 An interface is a named bundle of wires, the interfaces aim is to


encapsulate communication.
 Also specifies the,
 directional information, i.e modports
 timing information, i.e clocking blocks
 An interface can have parameters, constants, variables, functions,
and tasks.

modports and clocking blocks are explained in later chapters.

 A simple interface declaration is,

interface interface_name;
...
interface_items
...
endinterface

 An interface can be instantiated hierarchically like a module, with


or without ports.

interface_name inst_name;

 An interface can have parameters, constants, variables, functions,


and tasks.

Advantages of the interface over the traditional connection,

 allows the number of signals to be grouped together and


represented as a single port, the single port handle is passed instead
of multiple signal/ports.
 interface declaration is made once and the handle is passed across
the modules/components.
 addition and deletion of signals are easy.

Connecting testbench and design without interface construct,

In the example below,

 Testbench has a design instance and testcase instance, both are


connected through wires.
 Inside Testcase, the environment is created and signals are passed
to below hierarchy (To drive/sample the stimulus).
 env again passes signals down the hierarchy, and it goes on.
 For any addition or deletion of signal/signals, it is required to
add/delete in many places. This problem can be overcome by
using an interface construct.

//-------------------------------------------------------------------------
// Verilog Design
//-------------------------------------------------------------------------
module memory ( clk, addr, wr_rd, wdata, rdata);
input clk;
input [7:0] addr;
input wr_rd;
input [7:0] wdata;
output [7:0] rdata;
……
endmodule

//-------------------------------------------------------------------------
// TestCase
//-------------------------------------------------------------------------
program testcase ( input bit clk, bit [7:0] addr,bit wr_rd,bit [7:0] wdata,
output bit [7:0] rdata);
environment env; //declaring environment
initial begin
env = new(clk, addr, wr_rd, wdata, rdata);
end
……
endprogram

//-------------------------------------------------------------------------
// TestBench Top
//-------------------------------------------------------------------------
module tbench_top;
//wire declaration
wire clk;
wire [7:0] w_addr;
wire w_wr_rd;
wire [7:0] w_wdata;
wire [7:0] w_rdata;
//DUT(Design Under Test) instance
memory dut(
.clk(clk),
.addr(w_addr),
.wr_rd(w_wr_rd),
.wdata(w_wdata),
.rdata(r_data)
);

//TestCase Instance
testcase test(
.clk(clk),
.addr(w_addr),
.wr_rd(w_wr_rd),
.wdata(w_wdata),
.rdata(r_data)
);
……
endmodule
Connecting testbench and design with interface construct,
In the below example,

 The interface will group the signals, the handle of an interface is


passed across the hierarchy
 Changes made to interface will reflect everywhere

//-------------------------------------------------------------------------
// Verilog Design
//-------------------------------------------------------------------------
module memory ( clk, addr, wr_rd, wdata, rdata);
input clk;
input [7:0] addr;
input wr_rd;
input [7:0] wdata;
output [7:0] rdata;
endmodule

//-------------------------------------------------------------------------
// Interface
//-------------------------------------------------------------------------
interface mem_intf;
logic clk;
logic [7:0] addr;
logic wr_rd;
logic [7:0] wdata;
logic [7:0] rdata;
endinterface

//-------------------------------------------------------------------------
// TestCase
//-------------------------------------------------------------------------
program testcase (interface intf);
environment env;
initial begin
env = new(intf);
end
……
endprogram

//-------------------------------------------------------------------------
// TestBench Top
//-------------------------------------------------------------------------
module tbench_top;`
//Interface instance
mem_intf intf();

//DUT(Design Under Test) instance


memory dut(
.clk(intf.clk),
.addr(intf.addr),
.wr_rd(intf.wr_rd),
.wdata(intf.wdata),
.rdata(intf.rdata)
);

//TestCase Instance
testcase test(intf);
……
endmodule
Interface example

The below example shows the writing interface, declaration of an


interface, connecting the interface with design, and accessing interface
signals.

Writing interface

interface intf;

//declaring the signals


logic [3:0] a;
logic [3:0] b;
logic [6:0] c;

endinterface
Interface declaration

//creatinng instance of interface


intf i_intf();
Connecting interface with design

//DUT instance
adder DUT (
.a(i_intf.a),
.b(i_intf.b),
.c(i_intf.c)
);
Accessing interface signal

i_intf.a = 6;
i_intf.b = 4;

$display("Value of a = %0d, b
= %0d",i_intf.a,i_intf.b);
#5;
$display("Sum of a and b, c = %0d",i_intf.c);

Virtual Interface
A virtual interface is a variable that represents an interface instance.

 The virtual interface must be initialized before using it. i.e, Virtual
interface must be connected/pointed to the actual interface.
 accessing the uninitialized virtual interface result in a run-time
fatal error.
 Virtual interfaces can be declared as class properties, which can
be initialized procedural or by an argument to new().
 Virtual interface variables can be passed as arguments to the tasks,
functions, or methods.
 All the interface variables/Methods can be accessed via a virtual
interface handle. i.e virtual_interface.variable

Syntax

virtual interface_name instance_name;


Virtual interface example

virtual mem_intf intf;

A virtual interface is a variable that represents an interface instance.

 The virtual interface must be initialized before using it. i.e, Virtual
interface must be connected/pointed to the actual interface
 Accessing the uninitialized virtual interface result in a run-time
fatal error
 Virtual interfaces can be declared as class properties, which can
be initialized procedural or by an argument to new()
 The virtual interfaces can be passed as arguments to the tasks,
functions, or methods
 All the interface variables/Methods can be accessed via a virtual
interface handle. i.e virtual_interface.variable
 A single virtual interface variable can thus represent different
interface instances at different times throughout the simulation

Only the following operations are directly allowed on virtual interface


variables,

 Assignment ( = ) to and Equality ( == ), inequality ( != ) with,

 another virtual interface of the same type


 an interface instance of the same type
 the special constant null

What is the need for a virtual interface in SystemVerilog?

SystemVerilog interface is static in nature, whereas classes are dynamic


in nature. because of this reason, it is not allowed to declare the
interface within classes, but it is allowed to refer to or point to the
interface. A virtual interface is a variable of an interface type that is used
in classes to provide access to the interface signals.

Syntax

virtual interface_name instance_name;


Virtual Interface example

The below example shows the declaration of a virtual interface,


connecting the virtual interface to an interface, and accessing interface
signals with a virtual interface handle.

Virtual Interface declaration

//virtual interface
virtual intf vif;
Connecting virtual interface with interface

//constructor
function new(virtual intf vif);
//get the interface from test
this.vif = vif;
endfunction
Accessing interface signal using a virtual interface handle

vif.a = 6;
vif.b = 4;

$display("Value of a = %0d, b = %0d",vif.a,vif.b);


#5;
$display("Sum of a and b, c = %0d",vif.c);
Complete env code
class environment;

//virtual interface
virtual intf vif;

//constructor
function new(virtual intf vif);
//get the interface from test
this.vif = vif;
endfunction

//run task
task run;
vif.a = 6;
vif.b = 4;

$display("Value of a = %0d, b = %0d",vif.a,vif.b);


#5;
$display("Sum of a and b, c = %0d",vif.c);
$finish;
endtask
endclass

TestCase Code

Testcase receives the interface handle from the testcase and passes it to
env.
program test(intf i_intf);

//declaring environment instance


environment env;

initial begin
//creating environment
env = new(i_intf);

//calling run of env


env.run();
end
endprogram
tbench_top Code

tbench_to is the top file, in which design instance, interface instance,


and the test case is instantiated.

module tbench_top;

//creatinng instance of interface


intf i_intf();

//Testcase instance
test t1(i_intf);

//DUT instance, interface signals are connected to


the DUT ports
adder DUT (
.a(i_intf.a),
.b(i_intf.b),
.c(i_intf.c)
);

endmodule
SystemVerilog Assertions
Assertions are primarily used to validate the behavior of a design. An
assertion is a check embedded in design Warnings or errors are
generated on the failure of a specific condition or sequence of events.

Assertions are used to,

 Check the occurrence of a specific condition or sequence of events.


 Provide functional coverage.

There are two kinds of assertions:

 Immediate Assertions
 Concurrent Assertions

Immediate Assertions:

Immediate assertions check for a condition at the current simulation


time.

An immediate assertion is the same as an if..else statement with


assertion control. Immediate assertions have to be placed in a
procedural block definition.

Syntax

label: assert(expression) action_block;

 The optional statement label (identifier and colon) creates a


named block around the assertion statement
 The action block is executed immediately after the evaluation of
the assert expression
 The action_block specifies what actions are taken upon success or
failure of the assertion

action_block;

pass_statement; else fail_statement;

 The pass statement is executed if the expression evaluates to true


 The statement associated with else is called a fail statement and
is executed if the expression evaluates to false
 Both pass and fail statements are optional

 Since the assertion is a statement that something must be true,


the failure of an assertion shall have a severity associated with it.
By default, the severity of an assertion failure is an error.
 Other severity levels can be specified by including one of the
following severity system tasks in the fail statement:

 $fatal is a run-time fatal.


 $error is a run-time error.
 $warning is a run-time warning, which can be suppressed in
a tool-specific manner.
 $info indicates that the assertion failure carries no specific
severity.
 If an assertion fails and no else clause is specified, the tool shall,
by default call $error.

Below are the different forms of immediate assertion syntax with and
without optional items
//With Pass and Fail statement; Fail verbosity info;
assert(expression) $display(“expression evaluates to
true”); else $display(“expression evaluates to false”);

//Only With Pass statement;


assert(expression) $display(“expression evaluates to true”);

//With Pass and Fail statement; Fail verbosity fatal;


assert(expression) $display(“expression evaluates to
true”); else $fatal(“expression evaluates to false”);

//Only With Fail statement; Multiple statements in Faile condition


and Fail verbosity fatal;
assert(expression)
else begin
…….
…….
$fatal(“expression evaluates to false”);
end

//Only With Fail statement; Fail verbosity warning;


assert(expression) else $warning(“expression evaluates to false”);

//With Label and Fail statement; Fail verbosity warning;


label: assert(expression) else $warning(“expression evaluates to
false”);
Immediate assertion example
Below is the simple immediate assertion,
always @(posedge clk) assert (a && b);

Below is the wave diagram for the above assertion.


Condition (a && b) will be checked at every posedge of the clock, failure
in the condition leads to an assertion failure.

SystemVerilog Assertions
module asertion_ex;
bit clk,a,b;

//clock generation
always #5 clk = ~clk;

//generating 'a'
initial begin
a=1;
b=1;
#15 b=0;
#10 b=1;
a=0;
#20 a=1;
#10;
$finish;
end

//Immediate assertion
always @(posedge clk) assert (a && b);

endmodule

Concurrent Assertions:

Concurrent assertions check the sequence of events spread over


multiple clock cycles.

 The concurrent assertion is evaluated only at the occurrence of a


clock tick
 The test expression is evaluated at clock edges based on the
sampled values of the variables involved
 It can be placed in a procedural block, a module, an interface or a
program definition

c_assert: assert property(@(posedge clk) not(a && b));

The Keyword differentiates the immediate assertion from the


concurrent assertion is “property.”

Below diagram shows the steps involved in the creation of an SVA


checker,
SystemVerilog Assertions
Boolean expressions

The functionality is represented by the combination of multiple logical


events. These events could be simple Boolean expressions.

Sequence

Boolean expression events that evaluate over a period of time involving


single/multiple clock cycles. SVA provides a keyword to represent these
events called “sequence.”

Syntax

sequence name_of_sequence;
……
endsequence
Property
A number of sequences can be combined logically or sequentially to
create more complex sequences. SVA provides a keyword to represent
these complex sequential behaviors called “property”.

Syntax

property name_of_property;
test expression or
complex sequence expressions
endproperty
Assert

The property is the one that is verified during a simulation. It has to be


asserted to take effect during a simulation. SVA provides a keyword
called “assert” to check the property.

Syntax

assertion_ name: assert_property( property_name );

Boolean expression events that evaluate over a period of time involving


single/multiple clock cycles. SVA provides a keyword to represent these
events called “sequence”.

SVA Sequence example

In the below example the sequence seq_1 checks that the signal “a” is
high on every positive edge of the clock. If the signal “a” is not high on
any positive clock edge, the assertion will fail.

sequence seq_1;
@(posedge clk) a==1;
endsequence
A sequence with a logical relationship

Below sequence, seq_2 checks that on every positive edge of the clock,
either signal “a” or signal “b” is high. If both the signals are low, the
assertion will fail.

sequence seq_2;
@(posedge clk) a || b;
endsequence

Sequence Expressions

By defining arguments in a sequence definition, the same sequence can


be re-used for similar behavior.

For example, we can define a sequence as below.

sequence seq_lib (a, b)


a || b ;
endsequence
this seq can be used as,
sequence s_lib_inst
seq_lib(req1,req2);
endsequence
Sequences with timing relationship

In SVA, clock cycle delays are represented by a “##” sign. For example,
##2 means 2 clock cycles.

Below sequence checks for the signal “a” being high on a given positive
edge of the clock. If the signal “a” is not high, then the sequence fails. If
signal “a” is high on any given positive edge of the clock, the signal “b”
should be high 2 clock cycles after that. If signal “b” is not asserted after
2 clock cycles, the assertion fails.

sequence seq;
@(posedge clk) a ##2 b;
endsequence

Note:: sequence begins when signal “a” is high on a positive edge of the
clock.

Clock usage in SVA

A clock can be specified in a sequence, in a property or even in an assert


statement.

Clock defined in the sequence definition

The previous example shows the usage of a clock inside the sequence
definition.

sequence seq;
@(posedge clk) a ##2 b;
endsequence
Clock defined in the property definition
sequence seq;
a ##2 b;
endsequence

property p;
@(posedge clk) seq;
endproperty
a_1 : assert property(p);

In general, it is a good idea to define the clocks in property definitions


and keep the sequences independent of the clocks. This will help
increase the re-use of the basic sequence definitions.

Forbidding a property

A separate property definition is not needed to assert a sequence. the


expression to be checked can be called from the assert statement
directly as shown below.

In all the examples shown so far, the property is checking for a true
condition. we expect the property to be false always. If the property is
true, the assertion fails.
sequence seq;
a ##2 b;
endsequence
a_2: assert property(@(posedge clk) seq);

Calling a property with a clock definition from within the assert


statement is not allowed.

a_3: assert property(@(posedge clk) p) ; //Not allowed

Below sequence checks that if signal “a” is high on a given positive edge
of the clock, then after 2 clock cycles, signal “b” shall not be high. The
keyword “not” is used to specify that the property should never be true.

sequence seq;
@(posedge clk) a ##2 b;
endsequence
property p;
not seq;
endproperty
a_1: assert property(p);

Implication Operator

sequence seq;
@(posedge clk) a ##2 b;
endsequence

In the above sequence, we can observe that sequence starts on every


positive edge of the clock and it looks for “a” to be high on every positive
clock edge. If the signal “a” is not high on any given positive clock edge,
an error is issued by the checker.

If we want the sequence to be checked only after “a” is high, this can be
achieved by using the implication operator.

The implication is equivalent to an if-then structure. The left-hand side


of the implication is called the “antecedent” and the right-hand side is
called the “consequent.” The antecedent is the gating condition. If the
antecedent succeeds, then the consequent is evaluated.

The implication construct can be used only with property definitions. It


cannot be used in sequences.

There are 2 types of implication:

 Overlapped implication
 Non-overlapped implication

Overlapped implication

The overlapped implication is denoted by the symbol |->.

If there is a match on the antecedent, then the consequent expression is


evaluated in the same clock cycle.

Below property checks that, if signal “a” is high on a given positive clock
edge, then signal “b” should also be high on the same clock edge.
property p;
@(posedge clk) a |-> b;
endproperty
a: assert property(p);

Non-overlapped implication

The non-overlapped implication is denoted by the symbol |=>.

If there is a match on the antecedent, then the consequent expression is


evaluated in the next clock cycle.

Below property checks that, if signal “a” is high on a given positive clock
edge, then signal “b” should be high on the next clock edge.

property p;
@(posedge clk) a |=> b;
endproperty
a: assert property(p);

The implication with a fixed delay on the consequent

Below property checks that, if signal “a” is high on a given positive clock
edge, then signal “b” should be high after 2 clock cycles.

property p;
@(posedge clk) a |-> ##2 b;
endproperty
a: assert property(p);

The implication with a sequence as an antecedent

Below property checks that, if the sequence seq_1 is true on a given


positive edge of the clock, then start checking the seq_2 (“d” should be
low, 2 clock cycles after seq_1 is true).

sequence seq_1;
(a && b) ##1 c;
endsequence
sequence seq_2;
##2 !d;
endsequence

property p;
@(posedge clk) seq_1 |-> seq_2;
endpeoperty
a: assert property(p);

Timing windows in SVA Checkers

Below property checks that, if signal “a” is high on a given positive clock
edge, then within 1 to 4 clock cycles, the signal “b” should be high.

property p;
@(posedge clk) a |-> ##[1:4] b;
endproperty
a: assert property(p);

Overlapping timing window

Below property checks that, if signal “a” is high on a given positive clock
edge, then signal “b” should be high in the same clock cycle or within 4
clock cycles.

property p;
@(posedge clk) a |-> ##[0:4] b;
endproperty
a: assert property(p);

Indefinite timing window

The upper limit of the timing window specified in the right-hand side can
be defined with a “$” sign which implies that there is no upper bound for
timing. This is called the “eventuality” operator. The checker will keep
checking for a match until the end of the simulation.

Below property checks that, if signal “a” is high on a given positive clock
edge, then signal “b” will be high eventually starting from the next clock
cycle.
property p;
@(posedge clk) a |-> ##[1:$] b;
endproperty
a: assert property(p);

Repetition Operators

property p;
@(posedge clk) a |-> ##1 b ##1 b ##1 b;
endproperty
a: assert property(p);

The above property checks that, if the signal “a” is high on given
posedge of the clock, the signal “b” should be high for 3 consecutive
clock cycles.

The Consecutive repetition operator is used to specify that a signal or a


sequence will match continuously for the number of clocks specified.

Syntax

signal [*n] or sequence [*n]

"n" is the number of repetitions.

with repetition operator above sequence can be re-written as,

property p;
@(posedge clk) a |-> ##1 b[*3];
endproperty
a: assert property(p);
go to repetition

The go-to repetition operator is used to specify that a signal will match
the number of times specified not necessarily on continuous clock cycles.

signal [->n]
property p;
@(posedge clk) a |-> ##1 b[->3] ##1 c;
endproperty
a: assert property(p);
The above property checks that, if the signal “a” is high on given
posedge of the clock, the signal “b” should be high for 3 clock cycles
followed by “c” should be high after ”b” is high for the third time.
Nonconsecutive repetition

This is very similar to “go to” repetition except that it does not require
that the last match on the signal repetition happens in the clock cycle
before the end of the entire sequence matching.

signal [=n]

Only expressions are allowed to repeat in “go to” and “nonconsecutive”


repetitions. Sequences are not allowed.

SVA Methods
$rose

$rose(boolean expression or signal name)

returns true if the least significant bit of the expression changed to 1.


Otherwise, it returns false.

sequence seq_rose;
@(posedge clk) $rose(a);
endsequence

Sequence seq_rose checks that the signal “a” transitions to a value of 1


on every positive edge of the clock. If the transition does not occur, the
assertion will fail.

$fell

$fell(boolean expression or signal name)

returns true if the least significant bit of the expression changed to 0.


Otherwise, it returns false.
sequence seq_fell;
@(posedge clk) $fell(a);
endsequence

Sequence seq_fell checks that the signal “a” transitions to a value of 0


on every positive edge of the clock. If the transition does not occur, the
assertion will fail.

$stable

$stable(boolean expression or signal name)

returns true if the value of the expression did not change. Otherwise, it
returns false.

sequence seq_stable;
@(posedge clk) $stable(a);
endsequence

Sequence seq_stable checks that the signal “a” is stable on every


positive edge of the clock. If there is any transition occurs, the assertion
will fail.

$past

$past(signal_name, number of clock cycles)

provides the value of the signal from the previous clock cycle.

Below Property checks that, in the given positive clock edge, if the “b” is
high, then 2 cycles before that, a was high.
property p;
@(posedge clk) b |-> ($past(a,2) == 1);
endproperty
a: assert property(p);
$past construct with clock gating

The $past construct can be used with a gating signal. on a given clock
edge, the gating signal has to be true even before checking for the
consequent condition.
$past (signal_name, number of clock cycles, gating signal)

Below Property checks that, in the given positive clock edge, if the “b” is
high, then 2 cycles before that, a was high only if the gating signal “c’ is
valid on any given positive edge of the clock.

Property p;
@(posedge clk) b |-> ($past(a,2,c) == 1);
endproperty
a: assert property(p);
Built-in system functions

 $onehot(expression)
 checks that only one bit of the expression can be high on
any given clock edge.
 $onehot0(expression)

 checks only one bit of the expression can be high or none of


the bits can be high on any given clock edge.
 $isunknown(expression)

 checks if any bit of the expression is X or Z.


 $.9j countones(expression)

 counts the number of bits that are high in a vector.

a_1: assert property( @(posedge clk) $onehot(state) );


a_2: assert property( @(posedge clk) $onehot0(state) );
a_3: assert property( @(posedge clk) $isunknown(bus) ) ;
a_4: assert property( @(posedge clk) $countones(bus)> 1 );

Assert statement a_1 checks that the bit vector “state” is one-hot.
Assert statement a_2 checks that the bit vector “state” is zero one-hot.
Assert statement a_3 checks if any bit of the vector “bus” is X or Z.
Assert statement a_4 checks that the number of ones in the vector “bus”
is greater than one.
Coverage

Coverage is used to measure tested and untested portions of the design.


Coverage is defined as the percentage of verification objectives that
have been met.

There are two types of coverage metrics,

 Code Coverage
 Functional Coverage

Code Coverage

 Code coverage measures how much of the “design Code” is


exercised.
 This includes the execution of design blocks, Number of Lines,
Conditions, FSM, Toggle and Path.
 The simulator tool will automatically extract the code coverage
from the design code.

Functional Coverage

Functional coverage is a user-defined metric that measures how much of


the design specification has been exercised in verification.

There are two types of functional coverage,

 Data-oriented Coverage – Checks combinations of data values


have occurred. We can get Data-oriented coverage by writing
Coverage groups, coverage points and also by cross coverage
 Control-oriented Coverage – Checks whether sequences of
behaviors have occurred. We can get assertion coverage by writing
SystemVerilog Assertions
Functional coverage is a user-defined metric that measures how much of
the design specification has been exercised in verification.

Defining the coverage model

The coverage model is defined using Covergroup construct.

The covergroup construct is a user-defined type. The type definition is


written once, and multiple instances of that type can be created in
different contexts.

Similar to a class, once defined, a covergroup instance can be created via


the new()operator. A covergroup can be defined in a module, program,
interface, or class.

Each covergroup specification can include,

 A clocking event that synchronizes the sampling of coverage


points
 A set of coverage points
 Cross coverage between coverage points
 Optional formal arguments
 Coverage options

Functional Coverage example


EXAMPLE-1

covergroup cov_grp @(posedge clk);


cov_p1: coverpoint a;
endgroup

cov_grp cov_inst = new();


EXAMPLE-2

covergroup cov_grp;
cov_p1: coverpoint a;
endgroup

cov_grp cov_inst = new();


@(abc) cov_inst.sample();
In the example-1 clocking, event specifies the event at which coverage
points are sampled.
In the example-2 coverage, sampling is triggered by calling a built-in
sample() method.

Defining coverage points

A covergroup can contain one or more coverage points. A coverage


point can be an integral variable or an integral expression. Each
coverage point is associated with “bin”.On each sample clock simulator
will increment the associated bin value.

The bins will automatically be created or can be explicitly defined.


Automatic Bins or Implicit Bins

An automatically single bin will be created for each value of the


coverpoint variable range. These are called automatic, or implicit, bins.

For an “n” bit integral coverpoint variable, a 2^n number of automatic


bins will get created.

module cov;
logic clk;
logic [7:0] addr;
logic wr_rd;

covergroup cg @(posedge clk);


c1: coverpoint addr;
c2: coverpoint wr_rd;
endgroup : cg
cg cover_inst = new();
...
endmodule

Below are the bins, will get created automatically,


for addr: c1.auto[0] c1.auto[1] c1.auto[2] … c1.auto[255]
for wr_rd: c2.auto[0]

Explicit bins

“bins” keyword is used to declare the bins explicitly to a variable.


A separate bin is created for each value in the given range of variable or
a single/multiple bins for the rage of values.
Bins are explicitly declared within curly braces { } along with the bins
keyword followed by bin name and variable value/range, immediately
after the coverpoint identifier.

Example on Bins Declaration,

module cov;
logic clk;
logic [7:0] addr;
logic wr_rd;

covergroup cg @(posedge clk);


c1: coverpoint addr { bins b1 = {0,2,7};
bins b2[3] = {11:20};
bins b3 = {[30:40],[50:60],77};
bins b4[] = {[79:99],[110:130],140};
bins b5[] = {160,170,180};
bins b6 = {200:$};
bins b7 = default;}
c2: coverpoint wr_rd {bins wrrd};
endgroup : cg

cg cover_inst = new();
...
endmodule
bins b1 = {0,2,7 }; //bin “b1” increments for addr = 0,2
or 7
bins b2[3] = {11:20}; //creates three bins b2[0],b2[1] and
b2[3].and The 11 possible values are
//distributed as follows:
(11,12,13),(14,15,16) and (17,18,19,20) respectively.
bins b3 = {[30:40],[50:60],77}; //bin “b3” increments for addr =
30-40 or 50-60 or 77
bins b4[] = {[79:99],[110:130],140};//creates three bins
b4[0],b4[1] and b4[3] with values 79-99,50-60 and 77 respectively
bins b5[] = {160,170,180}; //creates three bins b5[0],b5[1]
and b5[3] with values 160,170 and 180 respectively
bins b6 = {200:$}; //bin “b6” increments for addr = 200
to max value i.e, 255.
default bin; // catches the values of the coverage
point that do not lie within any of the defined bins.
bins for transitions

The transition of coverage point can be covered by specifying the


sequence,

value1 => value2

It represents transition of coverage point value from value1 to value2.

sequence can be single value or range,

value1 => value2 => value3 ….

range_list_1 => range_list_2

Example,

covergroup cg @(posedge clk);


c1: coverpoint addr{ bins b1 = (10=>20=>30);
bins b2[] = (40=>50),(80=>90=>100=>120);
bins b3 = (1,5 => 6, 7);}
c2: coverpoint wr_rd;
endgroup : cg

bins b1 = (10=>20=>30); // transition from 10->20->30


bins b2[] = (40=>50),(80=>90=>100=>120); // b2[0] = 40->50 and
b2[1] = 80->90->100->120
bins b3 = (1,5 => 6, 7);} // b3 = 1=>6 or 1=>7 or 5=>6 or
5=>7
ignore_bins

A set of values or transitions associated with a coverage-point can be


explicitly excluded from coverage by specifying them as ignore_bins.

covergroup cg @(posedge clk);


c1: coverpoint addr{ ignore_bins b1 = {6,60,66};
ignore_bins b2 = (30=>20=>10); }
endgroup : cg
illegal_bins

A set of values or transitions associated with a coverage-point can be


marked as illegal by specifying them as illegal_bins.

covergroup cg @(posedge clk);


c1: coverpoint addr{ illegal_bins b1 = {7,70,77};
ignore_bins b2 = (7=>70=>77);}
endgroup : cg

Cross Coverage

Cross Coverage is specified between the cover points or variables. Cross


coverage is specified using the cross construct.

Expressions cannot be used directly in a cross; a coverage point must be


explicitly defined first.

Cross coverage by cover_point name

bit [3:0] a, b;
covergroup cg @(posedge clk);
c1: coverpoint a;
c2: coverpoint b;
c1Xc2: cross c1,c2;
endgroup : cg
Cross coverage by the variable name

bit [3:0] a, b;
covergroup cov @(posedge clk);
aXb : cross a, b;
endgroup

In the above example, each coverage point has 16 bins, namely


auto[0]…auto[15]. The cross of a and b (labeled aXb), therefore, has 256
cross products, and each cross product is a bin of aXb.

Cross coverage between variable and expression

bit [3:0] a, b, c;
covergroup cov @(posedge clk);
BC : coverpoint b+c;
aXb : cross a, BC;
endgroup

The coverage group cov has the same number of cross products as the
previous example, but in this case, one of the coverage points is the
expression b+c, which is labeled BC.

Coverage options

Coverage options control the behavior of the covergroup, coverpoint,


and cross.

at_least

A minimum number of hits for each bin. A bin with a hit count that is less
than the number is not considered covered. the default value is ‘1’.

auto_bin_max

A maximum number of automatically created bins when no bins are


explicitly defined for a coverpoint. the default value is ‘64’.

cross_auto_bin_max

A maximum number of automatically created cross product bins for a


cross. there is no default value, it is unbounded.

Coverage options can be used as below,

covergroup cg @(posedge clk);


c1: coverpoint addr { option.auto_bin_max = 128;}
c2: coverpoint wr_rd { option.atleast = 2;}
c1Xc2: cross c1, c2 { option.cross_auto_bin_max = 128;}
endgroup : cg

SystemVerilog Parameters

There are two ways to define constants:

 parameter
 `define

Parameter

Parameters must be defined within module boundaries using the


keyword parameter.

A parameter is a constant that is local to a module that can optionally be


redefined on an instance. Parameters are typically used to specify the
width of variables and time delays.

Parameter example

module mem_model #(
parameter ADDR_WIDTH=8;
parameter DATA_WIDTH=32;)
(clk, addr, data);

input clk;
input [ADDR_WIDTH-1:0] addr;
output [DATA_WIDTH-1:0] data;
.....
.....
endmodule
Parameter redefinition

Parameter values are not allowed to modify at runtime but can be


modified using the defparam statement and #delay specification with
module instantiation.

//Creates mem_1 instance with default addr and data widths.


mem_model mem_1 (.clk(clk),
.addr(addr_1),
.data(data_1));

//Creates mem_2 instance with addr width = 4 and data width =


8.
mem_model #(4,8) mem_2 (.clk(clk),
.addr(addr_2),
.data(data_2));
//Creates mem_3 instance with addr width = 32 and data width =
64.
mem_model #(32,64) mem_3 (.clk(clk),
.addr(addr_3),
.data(data_3));

//Creates mem_4 instance with default addr width and data


width = 64.
mem_model #(DATA_WIDTH=64) mem_4 (.clk(clk),
.addr(addr_3),
.data(data_3));

//ADDR_WIDTH value of mem_1 instance can be changed by


using defparam as shown below,
defparam hierarchical_path.mem_1.ADDR_WIDTH = 32;
`define Macro

The `define compiler directive is used to perform global macro


substitution and remain active for all files read/compiled after the macro
definition.

It will available until another macro definition changes the value or until
the macro is undefined using the `undef compiler directive.

`define WIDTH 8
to avoid redefincation `ifdef can be used,

`ifdef WIDTH
// do nothing (better to use `ifndef)
`else
`define WIDTH 8
`endif

`ifndef WIDTH
`define WIDTH 8
`endif

`ifdef can be used as if..else

`ifdef TYPE_1
`define WIDTH 8
`else
`define WIDTH 32
`endif

//`ifdef can also be used to avoid redefining/recompiling the


module/class,
//In the below example,
//definition of MODULE_1 is checked, if it is not defined then
MODULE_1 will be defined and compiles the module/class inside
the `ifndef - `endif.
//suppose if the same file is compiled twice then at the first time
MODULE_1 will get defined, so second time `ifndef condition will
not be satisfied.
`ifndef MODULE_1
`define MODULE_1
module mem;
....
....
endmodule
`endif

SystemVerilog DPI C++

SystemVerilog DPI (Direct Programming Interface) is an interface which


can be used to interface SystemVerilog with foreign languages. These
Foreign languages can be C, C++, SystemC as well as others.

DPI allows the user to easily call functions of other language from
SystemVerilog and to export SystemVerilog functions, so that they can
be called in other languages.

Advantage of DPI is, it allows to make use of the code exists in other
languages.

DPI Import and export methods


Import Method

The methods (functions/tasks) implemented in Foreign language can be


called from SystemVerilog and such methods are called Import methods.
Export methods

The methods implemented in SystemVerilog can be called from Foreign


language such methods are called Export methods.

It is allowed to transfer the data between two languages through


arguments passing and return.

DPI Declaration
Import Declaration

import “DPI-C” function int calc_parity (input int a);


Export Declaration

export “DPI-C” my_cfunction = function myfunction;


SystemVerilog DPI Example
Calling C++ method from SystemVerilog file

//----------------------------------------------
// SystemVerilog File
//----------------------------------------------
module dpi_tb;

import "DPI-C" function void c_method();

initial
begin
$display("Before calling C Method");
c_method();
$display("After calling C Method");
end

endmodule

//----------------------------------------------
// C++ file
//----------------------------------------------
#include stdio.h
#include stdlib.h
extern "C" void c_method() {

printf(" Hello World...!\n");

Calling SystemVerilog method from C++ file

//----------------------------------------------
// SystemVerilog File
//----------------------------------------------
module dpi_tb;

export "DPI-C" function sv_method;


import "DPI-C" context function void c_method();

initial
begin
$display("Before calling C Method");
c_method();
$display("After calling C Method");
end

function void sv_method();


$display(" [SV-Prog] Hello World...!");
endfunction
endmodule

//----------------------------------------------
// C++ file
//----------------------------------------------
#include stdio.h
#include iostream
#include svdpi.h

using namespace std;

extern "C" void sv_method();


extern "C" void c_method() {
printf(" [C-Prog] Hello World...!\n");
sv_method();
}

SystemVerilog Struct

The SystemVerilog struct groups the data types of multiple types. The
entire group can be referenced as a whole, or the individual data type
can be referenced by name.

Struct Syntax

Struct is defined with the Struct keyword followed by variables of


multiple data type with in the curly braces.

typedef struct packed {


type_1 var_1;
type_2 var_2;
type_3 var_3;
} struct_name;
SystemVerilog Packed Struct

Struct defined with the keyword Packed is referred as packed struct


Only packed data types and integer data types are allowed with in the
packed struct

PACKED STRUCT EXAMPLE

In the below example, variable of bit type are grouped in the struct.

module struct_tb;

typedef struct packed {


bit [7:0] addr;
bit valid;
bit [31:0] data;
} mem_pkt;

mem_pkt pkt;
initial begin

// Initializing Struct
pkt = '{8'h6, 1'b1, 32'hC001_0FAB};
$display ("pkt = %p", pkt);

// Change the struct field value


pkt.addr = 8'h8;
$display ("pkt = %p", pkt);

// Change the struct field value


pkt.data = 32'hFFF0_0FFF;
$display ("pkt = %p", pkt);
end
endmodule

SystemVerilog UnPacked Struct

By default struct is of Unpacked type, i.e struct without Packed keyword.

PACKED STRUCT EXAMPLE


In the below example, byte, enum and bit types are grouped in the
struct.
module struct_tb;

typedef enum logic {INVALID_PKT,VALID_PKT} pkt_type;

typedef struct packed {


byte addr;
pkt_type valid;
bit [31:0] data;
} mem_pkt;

mem_pkt pkt;

initial begin

// Initializing Struct
pkt = '{8'h6, VALID_PKT, 32'hC001_0FAB};
$display ("pkt = %p", pkt);

// Change the struct field value


pkt.addr = 8'h8;
$display ("pkt = %p", pkt);

// Change the struct field value


pkt.valid = INVALID_PKT;
$display ("pkt = %p", pkt);
end
endmodule

SystemVerilog Callback
What is Callback?

As per the English dictionary, Callback definition is ‘a return call‘

What is SystemVerilog Callback?

SystemVerilog callback specifies the rules to define the


methods and placing method calls to achieve ‘a return call to methods’.

In simple words,
Callbacks are empty methods with a call to them.

or

A method of the class is implemented with calls to dummy methods. On


a need basis, the user can extend the class and implement the dummy
methods.

Where,
Dummy methods are callback methods
Calls to dummy methods are callback hooks

Note:
There won’t be any impact for not implementing the Callback methods.
How callback works?

SystemVerilog Callback
In the above diagram,

 temp() is the method call to invoke a method temp


 method temp has few statements (logic) also calls to callback_1
and callback_2
 callback_1 and callback_2 are dummy methods without any
logic in it.
 On calling callback_1() and callback_2() there won’t be any impact
to regular logic
 On a need basis, the user can extend the class and add required
logic to callback_1 and callback_2 without modifying the method
temp

“randomize” is one of the SystemVerilog builtin methods which has the


callback features implemented in it.

randomize method is implemented with the callback features to enable


the execution of pre_randomize() and post_randomize() methods before
and after the randomize() method execution respectively.

This is done by placing the callback hooks


to pre_randomize() and post_randomize() methods in it.

Methods will get executed in the order mentioned below,


 pre_randomize();
 randomize();
 pre_randomize();

If required user can implement


the pre_randomize() and post_randomize() methods.

How to implement callback?

Below is one of the ways to implement the SystemVerilog callback.

 Write a method which has calls (callback hooks) to dummy


methods
 Write dummy callback methods

How to use callback?

 Extend the class and implement the callback method


 Override the existing class by extended class

Callback example

Let’s consider an example of a Slave Driver (Slave Driver: drives response


to the master).

For simplicity, one callback hook is implemented and only response


generation logic is captured in the code.

Components of slave_driver example are,

 slave_driver – Normal driver to drive response

 response types are OKAY, EXOKAY, SLVERR, DECERR


 slave_driver is constrained to send OKAY response always
to see to the callback usage difference
 slave_env – Environment which has a slave driver created in it
 basic_test – Which sends the normal response
 error_test – Testcase with callback method used to generate an
error response
 err_inject – extended driver class to implement the callback
method
SystemVerilog Callback TestBench Example

By using a callback, will implement the testcase to drive SLVERR


response.

Will see though the below steps,

 Adding Callback Support


 Implementing Callback Method
 Using Callback

Adding Callback Support

In this step, will write the driver with a dummy method and callback
hooks to it.

 Writing slave_driver class with enum variable resp

typedef enum {OKAY, EXOKAY, SLVERR, DECERR} resp_type;

class slave_driver;
resp_type resp;
endclass

 Adding response driving task send_response with response


randomize logic
typedef enum {OKAY, EXOKAY, SLVERR, DECERR} resp_type;

class slave_driver;
resp_type resp;

//send response task


task send_response;
std::randomize(resp) with { resp == OKAY;};
endtask
endclass

 Defining the callback method by declaring the virtual task


update_resp

typedef enum {OKAY, EXOKAY, SLVERR, DECERR} resp_type;

class slave_driver;
resp_type resp;

//callback hook
virtual task update_resp; endtask

//send response task


task send_response;
std::randomize(resp) with { resp == OKAY;};
endtask
endclass

 Placing hook for callback. In this example, as it is required to


change the response once it is generated, so it is good to place
callback hook after calling the randomize method

typedef enum {OKAY, EXOKAY, SLVERR, DECERR} resp_type;

class slave_driver;
resp_type resp;

//callback hook
virtual task update_resp; endtask
//send response task
task send_response;
std::randomize(resp) with { resp == OKAY;};
update_resp();
endtask
endclass
Implementing Callback Method

For error response generation scenario, it is required to corrupt the


response generated.
This can be achieved by adding the logic in update_resp() callback to
overwrite the response generated.

Which involves below steps.

 extend the slave_driver and write new driver err_inject

class err_inject extends slave_driver;

endclass

 Implement the callback method update_resp

class err_inject extends slave_driver;


virtual task update_resp;
$display("Injecting SLVERR");
resp = SLVERR;
endtask
endclass
Using Callback

A callback can be used by,

 Writing new error response testcase

program error_test;
slave_env env;

initial begin
//Create env
env = new();
//Calling run of env
env.run();
end
endprogram

 Declare and create an err_inject driver

program error_test;
slave_env env;
err_inject err_driver;

initial begin
//Create env
env = new();
err_driver = new();

//Calling run of env


env.run();
end
endprogram

 Override the slave_driver by the new err_inject driver

program error_test;
slave_env env;
err_inject err_driver;

initial begin
//Create env
env = new();
err_driver = new();
//Overriding slave_driver by error_driver
env.slv_driver = err_driver;

//Calling run of env


env.run();
end
endprogram

Benefits of callback?

By looking into the above slave_driver examples we could say that


without modifying the existing environment it is possible to achieve
error injection.

 Easy to add additional functionality to existing logic


 Makes component reusable

Callback example

Let’s consider an example of a Slave Driver (Slave Driver: drives response


to the master).

For simplicity, one callback hook is implemented and only response


generation logic is captured in the code.

Components of slave_driver example are,

 slave_driver – Normal driver to drive response


 response types are OKAY, EXOKAY, SLVERR, DECERR
 slave_driver is constrained to send OKAY response always
to see to the callback usage difference
 slave_env – Environment which has a slave driver created in it
 basic_test – Which sends the normal response
 error_test – Testcase with callback method used to generate an
error response
 err_inject – extended driver class to implement the callback
method

By using a callback, will implement the testcase to drive the SLVERR


response.

Will see through the below steps,

 Adding Callback Support


 Implementing Callback Method
 Using Callback
Adding Callback Support

In this step, will write the driver with a dummy method and callback
hooks to it.

 Writing slave_driver class with enum variable resp

typedef enum {OKAY, EXOKAY, SLVERR, DECERR} resp_type;

class slave_driver;
resp_type resp;
endclass

 Adding response driving task send_response with response


randomize logic

typedef enum {OKAY, EXOKAY, SLVERR, DECERR} resp_type;

class slave_driver;
resp_type resp;

//send response task


task send_response;
std::randomize(resp) with { resp == OKAY;};
endtask
endclass

 Defining the callback method by declaring the virtual task


update_resp

typedef enum {OKAY, EXOKAY, SLVERR, DECERR} resp_type;

class slave_driver;
resp_type resp;

//callback hook
virtual task update_resp; endtask

//send response task


task send_response;
std::randomize(resp) with { resp == OKAY;};
endtask
endclass

 Placing hook for a callback. In this example, as it is required to


change the response once it is generated, so it is good to place
callback hook after calling the randomize method

typedef enum {OKAY, EXOKAY, SLVERR, DECERR} resp_type;

class slave_driver;
resp_type resp;

//callback hook
virtual task update_resp; endtask

//send response task


task send_response;
std::randomize(resp) with { resp == OKAY;};
update_resp();
endtask
endclass
Implementing Callback Method
For the error response generation scenario, it is required to corrupt the
response generated.
This can be achieved by adding the logic in update_resp() callback to
overwrite the response generated.

Which involves below steps.

 extend the slave_driver and write new driver err_inject

class err_inject extends slave_driver;

endclass

 Implement the callback method update_resp

class err_inject extends slave_driver;


virtual task update_resp;
$display("Injecting SLVERR");
resp = SLVERR;
endtask
endclass
Using Callback

A callback can be used by,

 Writing new error response testcase

program error_test;
slave_env env;

initial begin
//Create env
env = new();
//Calling run of env
env.run();
end
endprogram

 Declare and create an err_inject driver

program error_test;
slave_env env;
err_inject err_driver;

initial begin
//Create env
env = new();
err_driver = new();

//Calling run of env


env.run();
end
endprogram

 Override the slave_driver by the new err_inject driver


program error_test;
slave_env env;
err_inject err_driver;

initial begin
//Create env
env = new();
err_driver = new();
//Overriding slave_driver by error_driver
env.slv_driver = err_driver;

//Calling run of env


env.run();
end
endprogram

SystemVerilog TestBench Example — Adder

Let’s Write the SystemVerilog TestBench for the simple design “ADDER”.
Before writing the SystemVerilog TestBench, we will look into the design
specification.

ADDER:
Below is the block diagram of ADDER.

“Adder” Design block diagram

Adder is,

 fed with the inputs clock, reset, a, b and valid.


 has output is c.
The valid signal indicates the valid value on the a and b, On valid signal
adder will add the a and b, drives the result in the next clock on c.
Adder add/Sum the 4bit values ‘a’ and ‘b’, and drives the result on c in
the next clock.

waveform diagram:

“Adder” Waveform

For the simplicity and ease of understanding, let’s write the two
TestBecnh’s,

 Simple SystemVerilog TestBench without Monitor and Scoreboard


 SystemVerilog TestBench with Monitor and Scoreboard

‘ADDER’ TestBench Without Monitor, Agent and Scoreboard


TestBench Architecture

SystemVerilog simple TestBench block diagram


Transaction Class

 Fields required to generate the stimulus are declared in the


transaction class.
 Transaction class can also be used as a placeholder for the activity
monitored by the monitor on DUT signals.
 So, the first step is to declare the ‘Fields‘ in the transaction class.
 Below are the steps to write the transaction class.

1. Declaring the fields.

class transaction;

//declaring the transaction items


bit [3:0] a;
bit [3:0] b;
bit [6:0] c;

endclass

2. To generate the random stimulus, declare the fields as ‘rand‘.

class transaction;

//declaring the transaction items


rand bit [3:0] a;
rand bit [3:0] b;
bit [7:0] c;

endclass

3. Adding display() method to display Transaction properties.

class transaction;

//declaring the transaction items


rand bit [3:0] a;
rand bit [3:0] b;
bit [6:0] c;
function void display(string name);
$display("-------------------------");
$display("- %s ",name);
$display("-------------------------");
$display("- a = %0d, b = %0d",a,b);
$display("- c = %0d",c);
$display("-------------------------");
endfunction
endclass
Generator Class

Generator class is responsible for,

 Generating the stimulus by randomizing the transaction class


 Sending the randomized class to driver

class generator;

------

endclass

1. Declare the transaction class handle,

class generator;

//declaring transaction class


rand transaction trans;

endclass

2. ‘Randomize’ the transaction class,

class generator;

//declaring transaction class


rand transaction trans;

//main task, generates(create and randomizes) the packets and


puts into mailbox
task main();
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization
failed");
gen2driv.put(trans);
endtask

endclass

3. Adding Mailbox and event,


Mailbox is used to send the randomized transaction to Driver.
Event to indicate the end of packet generation.

This involves,

 Declaring the Mailbox and Event


 Getting the Mailbox handle from the env class ( because the same
mailbox will be shared across generator and driver).

class generator;

//declaring transaction class


rand transaction trans;

//declaring mailbox
mailbox gen2driv;

//event, to indicate the end of transaction generation


event ended;

//constructor
function new(mailbox gen2driv);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
endfunction

//main task, generates(create and randomizes) the packets and


puts into mailbox
task main();
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization
failed");
gen2driv.put(trans);
-> ended; //triggering indicatesthe end of generation
endtask

endclass

4. Adding a variable to control the number of packets to be created,

class generator;

//declaring transaction class


rand transaction trans;

//declaring mailbox
mailbox gen2driv;

//event, to indicate the end of transaction generation


event ended;

//repeat count, to specify number of items to generate


int repeat_count;

//constructor
function new(mailbox gen2driv);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
endfunction

//main task, generates(create and randomizes) the


repeat_count number of transaction packets and puts into
mailbox
task main();
repeat(repeat_count) begin
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization
failed");
gen2driv.put(trans);
end
-> ended; //triggering indicatesthe end of generation
endtask

endclass

5. Adding an event to indicate the completion of the generation process,


the event will be triggered on the completion of the Generation process.

class generator;

//declaring transaction class


rand transaction trans;

//declaring mailbox
mailbox gen2driv;

//repeat count, to specify number of items to generate


int repeat_count;

//event, to indicate the end of transaction generation


event ended;

//constructor
function new(mailbox gen2driv);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
endfunction

//main task, generates(create and randomizes) the


repeat_count number of transaction packets and puts into
mailbox
task main();

repeat(repeat_count) begin
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization
failed");
gen2driv.put(trans);
end
-> ended; //triggering indicatesthe end of generation
endtask
endclass
Interface

Interface will group the signals.


This is a simple interface without modport and clocking block.

interface intf(input logic clk,reset);


----
endinterface

1. Complete Interface code,

interface intf(input logic clk,reset);

//declaring the signals


logic valid;
logic [3:0] a;
logic [3:0] b;
logic [6:0] c;

endinterface
Driver Class

Driver class is responsible for,

 receive the stimulus generated from the generator and drive to


DUT by assigning transaction class values to interface signals.

class driver;
----
endclass

1. Declare interface and mailbox, Get the interface and mailbox handle
through a constructor.

//creating virtual interface handle


virtual intf vif;
//creating mailbox handle
mailbox gen2driv;

//constructor
function new(virtual intf vif,mailbox gen2driv);
//getting the interface
this.vif = vif;
//getting the mailbox handle from environment
this.gen2driv = gen2driv;
endfunction

2. Adding a reset task, which initializes the Interface signals to default


values.

//Reset task, Reset the Interface signals to default/initial values


task reset;
wait(vif.reset);
$display("[ DRIVER ] ----- Reset Started -----");
vif.a <= 0;
vif.b <= 0;
vif.valid <= 0;
wait(!vif.reset);
$display("[ DRIVER ] ----- Reset Ended -----");
endtask

3. Adding a drive task to drive the transaction packet to the interface


signal.

//drive the transaction items to interface signals


task drive;
transaction trans;
gen2driv.get(trans);
@(posedge vif.clk);
vif.valid <= 1;
vif.a <= trans.a;
vif.b <= trans.b;
@(posedge vif.clk);
vif.valid <= 0;
trans.c <= vif.c;
@(posedge vif.clk);
trans.display("[ Driver ]");
no_transactions++;
end
endtask
4. Adding a local variable to track the number of packets driven, and
increment the variable in the drive task.
(This
will be useful to end the test-case/Simulation. i.e compare the
generated pkt’s and driven pkt’s if both are same then end the
simulation)
//used to count the number of transactions
int no_transactions;

//drive the transaction items to interface signals


task drive;
------
------
no_transactions++;
endtask

5. Complete driver code.

class driver;
//used to count the number of transactions
int no_transactions;

//creating virtual interface handle


virtual intf vif;

//creating mailbox handle


mailbox gen2driv;

//constructor
function new(virtual intf vif,mailbox gen2driv);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.gen2driv = gen2driv;
endfunction

//Reset task, Reset the Interface signals to default/initial values


task reset;
wait(vif.reset);
$display("[ DRIVER ] ----- Reset Started -----");
vif.a <= 0;
vif.b <= 0;
vif.valid <= 0;
wait(!vif.reset);
$display("[ DRIVER ] ----- Reset Ended -----");
endtask

//drivers the transaction items to interface signals


task main;
forever begin
transaction trans;
gen2driv.get(trans);
@(posedge vif.clk);
vif.valid <= 1;
vif.a <= trans.a;
vif.b <= trans.b;
@(posedge vif.clk);
vif.valid <= 0;
trans.c <= vif.c;
@(posedge vif.clk);
trans.display("[ Driver ]");
no_transactions++;
end
endtask

endclass

Environment

Environment is container class contains Mailbox, Generator and Driver.

Creates the mailbox, generator and driver shares the mailbox handle
across the Generator and Driver.
class environment;
---
endclass

1. Declare the handles,

//generator and driver instance


generator gen;
driver driv;

//mailbox handle's
mailbox gen2driv;

//virtual interface
virtual intf vif;

2. In Construct Method, Create

 Mailbox
 Generator
 Driver

and pass the interface handle through the new() method.

//constructor
function new(virtual intf vif);
//get the interface from test
this.vif = vif;

//creating the mailbox (Same handle will be shared across


generator and driver)
gen2driv = new();

//creating generator and driver


gen = new(gen2driv);
driv = new(vif,gen2driv);
endfunction
3. For better accessibility.
Generator and Driver activity can be divided and controlled in three
methods.
 pre_test() – Method to call Initialization. i.e, reset method.
 test() – Method to call Stimulus Generation and Stimulus Driving.
 post_test() – Method to wait the completion of generation and
driving.

task pre_test();
driv.reset();
endtask

task test();
fork
gen.main();
driv.main();
join_any
endtask

task post_test();
wait(gen.ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
endtask

4. Add a run task to call the above methods,


call $finish after post_test() to end the simulation.

task run;
pre_test();
test();
post_test();
$finish;
endtask

5. Complete environment class code.

`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"
class environment;

//generator and driver instance


generator gen;
driver driv;

//mailbox handle's
mailbox gen2driv;

//virtual interface
virtual intf vif;

//constructor
function new(virtual intf vif);
//get the interface from test
this.vif = vif;

//creating the mailbox (Same handle will be shared across


generator and driver)
gen2driv = new();

//creating generator and driver


gen = new(gen2driv);
driv = new(vif,gen2driv);
endfunction

//
task pre_test();
driv.reset();
endtask

task test();
fork
gen.main();
driv.main();
join_any
endtask

task post_test();
wait(gen.ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
endtask

//run task
task run;
pre_test();
test();
post_test();
$finish;
endtask

endclass
Test

Test code is written with the program block.

The test is responsible for,

 Creating the environment.


 Configuring the testbench i.e, setting the type and number of
transactions to be generated.
 Initiating the stimulus driving.

program test;
----
endprogram

1. Declare and Create an environment,

//declaring environment instance


environment env;

initial begin
//creating environment
env = new(intf);
end

2. Configure the number of transactions to be generated,

//setting the repeat count of generator as 10, means to generate


10 packets
env.gen.repeat_count = 10;

3. Initiating the stimulus driving,


//calling run of env, it interns calls generator and driver main
tasks.
env.run();

4. Complete Test Code,

`include "environment.sv"
program test(intf intf);

//declaring environment instance


environment env;

initial begin
//creating environment
env = new(intf);

//setting the repeat count of generator as 10, means to


generate 10 packets
env.gen.repeat_count = 10;

//calling run of env, it interns calls generator and driver main


tasks.
env.run();
end
endprogram
TestBench Top

 This is the topmost file, which connects the DUT and TestBench.
 TestBench top consists of DUT, Test and Interface instances.
 The interface connects the DUT and TestBench.

module tbench_top;
---
endmodule

1. Declare and Generate the clock and reset,

//clock and reset signal declaration


bit clk;
bit reset;

//clock generation
always #5 clk = ~clk;

//reset Generation
initial begin
reset = 1;
#5 reset =0;
end

2. Create Interface instance,

//creatinng instance of interface, in-order to connect DUT and


testcase
intf intf(clk,reset);

3. Create Design Instance and Connect Interface signals,

//DUT instance, interface signals are connected to the DUT ports


adder DUT (
.clk(i_intf.clk),
.reset(i_intf.reset),
.a(i_intf.a),
.b(i_intf.b),
.valid(i_intf.valid),
.c(i_intf.c)
);

4. Create a test instance and Pass the interface handle,

//Testcase instance, interface handle is passed to test as an


argument
test t1(intf);

5. Add logic to generate the dump,

initial begin
$dumpfile("dump.vcd"); $dumpvars;
end

6. Complete testbench top code,

`include "interface.sv"
`include "random_test.sv"

module tbench_top;

//clock and reset signal declaration


bit clk;
bit reset;

//clock generation
always #5 clk = ~clk;

//reset Generation
initial begin
reset = 1;
#5 reset =0;
end

//creatinng instance of interface, inorder to connect DUT and


testcase
intf i_intf(clk,reset);

//Testcase instance, interface handle is passed to test as an


argument
test t1(i_intf);

//DUT instance, interface signals are connected to the DUT ports


adder DUT (
.clk(i_intf.clk),
.reset(i_intf.reset),
.a(i_intf.a),
.b(i_intf.b),
.valid(i_intf.valid),
.c(i_intf.c)
);
//enabling the wave dump
initial begin
$dumpfile("dump.vcd"); $dumpvars;
end
endmodule

‘ADDER’ TestBench With Monitor and Scoreboard


TestBench Architecture

SystemVerilog TestBench

Only monitor and scoreboard are explained here, Refer to ‘ADDER’


TestBench Without Monitor, Agent, and Scoreboard for other
components.

Monitor

 Samples the interface signals and converts the signal level activity
to the transaction level.
 Send the sampled transaction to Scoreboard via Mailbox.
 Below are the steps to write a monitor.

1. Writing monitor class.

class monitor;
------
endclass

2. Declare interface and mailbox, Get the interface and mailbox handle
through the constructor.

//creating virtual interface handle


virtual intf vif;

//creating mailbox handle


mailbox mon2scb;

//constructor
function new(virtual intf vif,mailbox mon2scb);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction

3. Sampling logic and sending the sampled transaction to the scoreboard.

task main;
forever begin
transaction trans;
trans = new();
@(posedge vif.clk);
wait(vif.valid);
trans.a = vif.a;
trans.b = vif.b;
@(posedge vif.clk);
trans.c = vif.c;
@(posedge vif.clk);
mon2scb.put(trans);
trans.display("[ Monitor ]");
end
endtask

4. Complete monitor code.

class monitor;

//creating virtual interface handle


virtual intf vif;

//creating mailbox handle


mailbox mon2scb;

//constructor
function new(virtual intf vif,mailbox mon2scb);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction

//Samples the interface signal and send the sample packet to


scoreboard
task main;
forever begin
transaction trans;
trans = new();
@(posedge vif.clk);
wait(vif.valid);
trans.a = vif.a;
trans.b = vif.b;
@(posedge vif.clk);
trans.c = vif.c;
@(posedge vif.clk);
mon2scb.put(trans);
trans.display("[ Monitor ]");
end
endtask

endclass
Scoreboard

Scoreboard receives the sampled packet from the monitor and compare
with the expected result, an error will be reported if the comparison
results in a mismatch.

class scoreboard;

------

endclass

1. Declaring the mailbox and variable to keep count of


transactions, connecting handle through the constructor,

//creating mailbox handle


mailbox mon2scb;

//used to count the number of transactions


int no_transactions;

//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
2. logic to compare the received result with the expected result,

Note: As the DUT behavior is simple, a single line is added for generating
the expected output.
Complex designs may use the reference model to generate the expected
output.

(trans.a+trans.b) == trans.c

//Compares the Actual result with the expected result


task main;
transaction trans;
forever begin
mon2scb.get(trans);
if((trans.a+trans.b) == trans.c)
$display("Result is as Expected");
else
$error("Wrong Result.\n\tExpeced: %0d
Actual: %0d",(trans.a+trans.b),trans.c);
no_transactions++;
trans.display("[ Scoreboard ]");
end
endtask

3. Complete scoreboard code.

class scoreboard;

//creating mailbox handle


mailbox mon2scb;

//used to count the number of transactions


int no_transactions;

//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction

//Compares the Actual result with the expected result


task main;
transaction trans;
forever begin
mon2scb.get(trans);
if((trans.a+trans.b) == trans.c)
$display("Result is as Expected");
else
$error("Wrong Result.\n\tExpeced: %0d
Actual: %0d",(trans.a+trans.b),trans.c);
no_transactions++;
trans.display("[ Scoreboard ]");
end
endtask
endclass
Environment

Here only updates are mentioned. i.e adding monitor and scoreboard to
the previous example.

1. Declare the handles,

//generator and driver instance


generator gen;
driver driv;
monitor mon; //---NEW CODE---
scoreboard scb; //---NEW CODE---

//mailbox handle's
mailbox gen2driv;
mailbox mon2scb; //---NEW CODE---

//virtual interface
virtual intf vif;

2. In Construct Method, Create

 Mailbox (mon2scb)
 Monitor
 Scoreboard

and pass the interface handle through the new() method.

//constructor
function new(virtual intf vif);
//get the interface from test
this.vif = vif;

//creating the mailbox (Same handle will be shared across


generator and driver)
gen2driv = new();
mon2scb = new(); //---NEW CODE---
//creating generator and driver
gen = new(gen2driv);
driv = new(vif,gen2driv);
mon = new(vif,mon2scb); //---NEW CODE---
scb = new(mon2scb); //---NEW CODE---
endfunction

3. Calling monitor and scoreboard tasks,

task pre_test();
driv.reset();
endtask

task test();
fork
gen.main();
driv.main();
mon.main(); //---NEW CODE---
scb.main(); //---NEW CODE---
join_any
endtask

task post_test();
wait(gen.ended.triggered);
wait(gen.repeat_count == driv.no_transactions);
wait(gen.repeat_count == scb.no_transactions); //---NEW
CODE---
endtask

4. Complete environment class code.

`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"
`include "monitor.sv"
`include "scoreboard.sv"
class environment;

//generator and driver instance


generator gen;
driver driv;
monitor mon;
scoreboard scb;

//mailbox handle's
mailbox gen2driv;
mailbox mon2scb;

//virtual interface
virtual intf vif;

//constructor
function new(virtual intf vif);
//get the interface from test
this.vif = vif;

//creating the mailbox (Same handle will be shared across


generator and driver)
gen2driv = new();
mon2scb = new();

//creating generator and driver


gen = new(gen2driv);
driv = new(vif,gen2driv);
mon = new(vif,mon2scb);
scb = new(mon2scb);
endfunction

//
task pre_test();
driv.reset();
endtask

task test();
fork
gen.main();
driv.main();
mon.main();
scb.main();
join_any
endtask

task post_test();
wait(gen.ended.triggered);
wait(gen.repeat_count == driv.no_transactions); //Optional
wait(gen.repeat_count == scb.no_transactions);
endtask

//run task
task run;
pre_test();
test();
post_test();
$finish;
endtask

endclass

Coverage is used to measure tested and untested portions of the design.


Coverage is defined as the percentage of verification objectives that
have been met.

There are two types of coverage metrics,


 Code Coverage
 Functional Coverage

Code Coverage

 Code coverage measures how much of the “design Code” is


exercised.
 This includes the execution of design blocks, Number of Lines,
Conditions, FSM, Toggle and Path.
 The simulator tool will automatically extract the code coverage
from the design code.

Functional Coverage

Functional coverage is a user-defined metric that measures how much of


the design specification has been exercised in verification.

There are two types of functional coverage,

 Data-oriented Coverage – Checks combinations of data values


have occurred. We can get Data-oriented coverage by writing
Coverage groups, coverage points and also by cross coverage
 Control-oriented Coverage – Checks whether sequences of
behaviors have occurred. We can get assertion coverage by writing
SystemVerilog Assertions

Functional coverage is a user-defined metric that measures how much of


the design specification has been exercised in verification.

Defining the coverage model

The coverage model is defined using Covergroup construct.

The covergroup construct is a user-defined type. The type definition is


written once, and multiple instances of that type can be created in
different contexts.

Similar to a class, once defined, a covergroup instance can be created via


the new()operator. A covergroup can be defined in a module, program,
interface, or class.

Each covergroup specification can include,


 A clocking event that synchronizes the sampling of coverage
points
 A set of coverage points
 Cross coverage between coverage points
 Optional formal arguments
 Coverage options

Functional Coverage example


EXAMPLE-1

covergroup cov_grp @(posedge clk);


cov_p1: coverpoint a;
endgroup

cov_grp cov_inst = new();


EXAMPLE-2

covergroup cov_grp;
cov_p1: coverpoint a;
endgroup

cov_grp cov_inst = new();


@(abc) cov_inst.sample();

In the example-1 clocking, event specifies the event at which coverage


points are sampled.
In the example-2 coverage, sampling is triggered by calling a built-in
sample() method.

Defining coverage points

A covergroup can contain one or more coverage points. A coverage


point can be an integral variable or an integral expression. Each
coverage point is associated with “bin”.On each sample clock simulator
will increment the associated bin value.

The bins will automatically be created or can be explicitly defined.


Automatic Bins or Implicit Bins

An automatically single bin will be created for each value of the


coverpoint variable range. These are called automatic, or implicit, bins.
For an “n” bit integral coverpoint variable, a 2^n number of automatic
bins will get created.

module cov;
logic clk;
logic [7:0] addr;
logic wr_rd;

covergroup cg @(posedge clk);


c1: coverpoint addr;
c2: coverpoint wr_rd;
endgroup : cg
cg cover_inst = new();
...
endmodule

Below are the bins, will get created automatically,


for addr: c1.auto[0] c1.auto[1] c1.auto[2] … c1.auto[255]
for wr_rd: c2.auto[0]

Explicit bins

“bins” keyword is used to declare the bins explicitly to a variable.

A separate bin is created for each value in the given range of variable or
a single/multiple bins for the rage of values.
Bins are explicitly declared within curly braces { } along with the bins
keyword followed by bin name and variable value/range, immediately
after the coverpoint identifier.

Example on Bins Declaration,

module cov;
logic clk;
logic [7:0] addr;
logic wr_rd;

covergroup cg @(posedge clk);


c1: coverpoint addr { bins b1 = {0,2,7};
bins b2[3] = {11:20};
bins b3 = {[30:40],[50:60],77};
bins b4[] = {[79:99],[110:130],140};
bins b5[] = {160,170,180};
bins b6 = {200:$};
bins b7 = default;}
c2: coverpoint wr_rd {bins wrrd};
endgroup : cg

cg cover_inst = new();
...
endmodule
bins b1 = {0,2,7 }; //bin “b1” increments
for addr = 0,2 or 7
bins b2[3] = {11:20}; //creates three bins
b2[0],b2[1] and b2[3].and The 11 possible values are
//distributed as follows:
(11,12,13),(14,15,16) and (17,18,19,20) respectively.
bins b3 = {[30:40],[50:60],77}; //bin “b3”
increments for addr = 30-40 or 50-60 or 77
bins b4[] = {[79:99],[110:130],140};//creates three
bins b4[0],b4[1] and b4[3] with values 79-99,50-60
and 77 respectively
bins b5[] = {160,170,180}; //creates three bins
b5[0],b5[1] and b5[3] with values 160,170 and 180
respectively
bins b6 = {200:$}; //bin “b6” increments
for addr = 200 to max value i.e, 255.
default bin; // catches the values of
the coverage point that do not lie within any of the
defined bins.
bins for transitions

The transition of coverage point can be covered by specifying the


sequence,

value1 => value2

It represents transition of coverage point value from value1 to value2.

sequence can be single value or range,


value1 => value2 => value3 ….

range_list_1 => range_list_2

Example,

covergroup cg @(posedge clk);


c1: coverpoint addr{ bins b1 = (10=>20=>30);
bins b2[] = (40=>50),(80=>90=>100=>120);
bins b3 = (1,5 => 6, 7);}
c2: coverpoint wr_rd;
endgroup : cg

bins b1 = (10=>20=>30); // transition from


10->20->30
bins b2[] = (40=>50),(80=>90=>100=>120); // b2[0] =
40->50 and b2[1] = 80->90->100->120
bins b3 = (1,5 => 6, 7);} // b3 = 1=>6 or 1=>7 or
5=>6 or 5=>7
ignore_bins

A set of values or transitions associated with a coverage-point can be


explicitly excluded from coverage by specifying them as ignore_bins.

covergroup cg @(posedge clk);


c1: coverpoint addr{ ignore_bins b1 = {6,60,66};
ignore_bins b2 = (30=>20=>10); }
endgroup : cg
illegal_bins

A set of values or transitions associated with a coverage-point can be


marked as illegal by specifying them as illegal_bins.

covergroup cg @(posedge clk);


c1: coverpoint addr{ illegal_bins b1 = {7,70,77};
ignore_bins b2 = (7=>70=>77);}
endgroup : cg
Cross Coverage is specified between the cover points or variables. Cross
coverage is specified using the cross construct.

Expressions cannot be used directly in a cross; a coverage point must be


explicitly defined first.

Cross coverage by cover_point name

bit [3:0] a, b;
covergroup cg @(posedge clk);
c1: coverpoint a;
c2: coverpoint b;
c1Xc2: cross c1,c2;
endgroup : cg
Cross coverage by the variable name

bit [3:0] a, b;
covergroup cov @(posedge clk);
aXb : cross a, b;
endgroup

In the above example, each coverage point has 16 bins, namely


auto[0]…auto[15]. The cross of a and b (labeled aXb), therefore, has 256
cross products, and each cross product is a bin of aXb.

Cross coverage between variable and expression

bit [3:0] a, b, c;
covergroup cov @(posedge clk);
BC : coverpoint b+c;
aXb : cross a, BC;
endgroup

The coverage group cov has the same number of cross products as the
previous example, but in this case, one of the coverage points is the
expression b+c, which is labeled BC.
Coverage options

Coverage options control the behavior of the covergroup, coverpoint,


and cross.
at_least

A minimum number of hits for each bin. A bin with a hit count that is less
than the number is not considered covered. the default value is ‘1’.

auto_bin_max

A maximum number of automatically created bins when no bins are


explicitly defined for a coverpoint. the default value is ‘64’.

cross_auto_bin_max

A maximum number of automatically created cross product bins for a


cross. there is no default value, it is unbounded.

Coverage options can be used as below,

covergroup cg @(posedge clk);


c1: coverpoint addr { option.auto_bin_max = 128;}
c2: coverpoint wr_rd { option.atleast = 2;}
c1Xc2: cross c1, c2 { option.cross_auto_bin_max = 128;}
endgroup : cg

You might also like