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