SV Notes
SV Notes
Introduction:
HILO-2
Occam parallel-processing 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.
In 2009, the standard was merged with the base Verilog (IEEE 1364-2005)
standard, creating IEEE Standard 1800-2009.
SystemVerilog TestBench
About TestBench
TestBench Components
integer Syntax:
integer integer_variable_name;
Syntax
real real_variable_name;
Examples:
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
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 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,
In the following example value is set for red = 0, blue = 4, white = 10.
syntax error.
A type name can be given so that the same type can be used in many
places.
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;
module enum_datatype;
//declaration
enum { red=0, green, blue=4, yellow, white=10, black } Colors;
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);
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
Multidimensional array
int arr[2][3];
THREE-DIMENSIONAL ARRAY
int arr[2][2][2];
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}};
$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]);
UnPacked array
data_type array_name [ ];
Dynamic array methods
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
//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;
endmodule
Associative array
When the size of the collection is unknown or the data space is sparse,
an associative array is a better option.
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
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
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
Queue in SystemVerilog
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.
Queue Declaration
data_type queue_name[$];
where:
data_type – data type of the queue elements.
queue_name – name of the queue.
queue_1 = {0,1,0,1};
queue_4 = {“Red”,"Blue”,"Green”};
Unbounded Queue
SystemVerilog queue
Bounded Queue
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
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
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
initial begin
//Queue Initialization:
queue = {7,3,1};
queue.push_front(10);
endmodule
Blocking Assignment
module blocking_assignment;
//variables declaration
int a,b;
initial begin
$display("-----------------------------------------------------------------");
//initializing a and b
a = 10;
b = 15;
a = b;
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
module nonblocking_assignment;
//variables declaration
int a,b;
a <= b;
b <= 20;
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.
Unique if example’s
value of a=10, b=20 and c=40. conditions a<b and a<c are true,
module unique_if;
//variables declaration
int a,b,c;
initial begin
//initialization
a=10;
b=20;
c=40;
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,
initial begin
//initialization
a=50;
b=20;
c=40;
endmodule
Unique if example 3
module unique_if;
//variables declaration
int a,b,c;
initial begin
//initialization
a=50;
b=20;
c=40;
endmodule
priority if
Priority if evaluates all the conditions in sequential order.
priority if examples
module priority_if;
//variables declaration
int a,b,c;
initial begin
//initialization
a=50;
b=20;
c=40;
module priority_if;
//variables declaration
int a,b,c;
initial begin
//initialization
a=10;
b=20;
c=40;
endmodule
do while loop
do begin
// statement -1
...
// statement -n
end
while(condition);
In do-while,
module do_while;
int a;
initial begin
$display("-----------------------------------------------------------------");
do
begin
$display("\tValue of a=%0d",a);
a++;
end
while(a<5);
$display("-----------------------------------------------------------------");
end
endmodule
initial begin
$display("-----------------------------------------------------------------");
do
begin
$display("\tValue of a=%0d",a);
a++;
end
while(a>5);
$display("-----------------------------------------------------------------");
end
endmodule
while(condition) begin
// statement -1
...
// statement -n
end
In a while,
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
foreach(<variable>[<iterator>]]) begin
//statement - 1
...
//statement - n
end
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
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
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.
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
module for_loop;
initial begin
$display("-----------------------------------------------------------------");
$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("-----------------------------------------------------------------");
$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(<variable>) begin
//statement - 1
...
//statement - n
end
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 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
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
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
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("-----------------------------------------------------------------");
$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
syntax
continue;
Continue example
In below example,
module continue_in_loop;
initial begin
$display("-----------------------------------------------------------------");
$display("-----------------------------------------------------------------");
end
endmodule
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.
module event_ctrl;
bit [2:0] a,b;
initial begin
#2 a=5;
#3 b=2;
#2 a=1;
#1 b=7;
#2;
$finish;
end
endmodule
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
In below example,
always block will be executed at every negedge of the clk signal.
module event_ctrl;
bit clk;
always #2 clk = ~clk;
initial begin
#20 $finish;
end
endmodule
module event_ctrl;
bit clk;
bit reset;
always #2 clk = ~clk;
//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
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
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.
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.
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
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.
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
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.
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,
task examples
task arguments in parentheses
module sv_task;
int x;
initial begin
sum(10,5,x);
$display("\tValue of x = %0d",x);
end
endmodule
functions
static
automatic
Static Function
Static functions share the same storage space for all function calls.
Automatic Function
SystemVerilog allows,
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;
module sv_function;
int x;
initial begin
x=sum(10,5);
$display("\tValue of x = %0d",x);
end
endmodule
Void function
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
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
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
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.
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
if any value is passed to an argument with a default value, then the new
value will be considered.
module argument_passing;
int q;
initial begin
q = sum( , ,10);
$display("-----------------------------------------------------------------");
$display("\tValue of z = %0d",q);
$display("-----------------------------------------------------------------");
end
endmodule
module argument_passing;
int x,y,z;
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
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.
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
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
endclass
module sv_constructor;
packet pkt;
initial begin
pkt = new(32'h10,32'hFF,1,"GOOD_PKT");
pkt.display();
end
endmodule
Class Constructors
On calling the new method it allocates the memory and returns the
address to the class handle.
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
module sv_constructor;
packet pkt;
initial begin
pkt = new();
pkt.display();
end
endmodule
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
Note:
Static class properties and methods can be used without creating an
object of that type.
Syntax
class packet;
//class properties
byte packet_id;
//constructor
function new();
//incrementing pkt count on creating an object
no_of_pkts_created++;
packet_id = no_of_pkts_created;
endfunction
module static_properties;
packet pkt[3];
initial begin
foreach(pkt[i]) begin
pkt[i] = new();
pkt[i].display();
end
end
endmodule
//class properties
byte packet_id;
//constructor
function new();
//incrementing pkt count on creating an object
no_of_pkts_created++;
packet_id = no_of_pkts_created;
endfunction
module static_properties;
packet pkt[3];
initial begin
foreach(pkt[i]) begin
pkt[i] = new();
pkt[i].display();
end
end
endmodule
class packet;
byte packet_id;
//constructor
function new();
//incrementing pkt count on creating an object
no_of_pkts_created++;
endfunction
initial begin
foreach(pkt[i]) begin
pkt[i] = new();
end
pkt[0].display_packets_created();
end
endmodule
class packet;
//constructor
function new();
//incrementing pkt count on creating an object
no_of_pkts_created++;
endfunction
module static_properties;
packet pkt[3];
packet p;
initial begin
foreach(pkt[i]) begin
pkt[i] = new();
end
Class Assignment
packet pkt_1;
pkt_1 = new();
packet pkt_2;
pkt_2 = pkt_1;
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
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";
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.
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
//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();
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.
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
//constructor
function new();
addr = 32'h10;
data = 32'hFF;
ar = new(); //creating object
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
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.
function new();
address = 10;
data = 20;
endfunction
endclass
SystemVerilog Inheritance
Inheritance Terminology
Parent Class
Inheritance Example
Parent class properties are accessed using child class handle, i.e child
class will have (inherit) parent class properties and methods.
class parent_class;
bit [31:0] addr;
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.
Polymorphism example
class base_class;
virtual function void display();
$display("Inside base class");
endfunction
endclass
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;
b_c[0].display();
b_c[1].display();
b_c[2].display();
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
endmodule
In below example,
class parent_class;
bit [31:0] addr;
function display();
$display("Addr = %0d",addr);
endfunction
endclass
module inheritence;
initial begin
child_class c=new();
c.addr = 10;
c.data = 20;
c.display();
end
endmodule
Super keyword
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.
In below example,
class parent_class;
bit [31:0] addr;
function display();
$display("Addr = %0d",addr);
endfunction
endclass
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
Static casting
Dynamic casting
Static casting
module casting;
real r_a;
int i_a;
initial begin
Dynamic casting
$cast(child_class,parent_class);
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.
class parent_class;
bit [31:0] addr;
function display();
$display("Addr = %0d",addr);
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
class parent_class;
bit [31:0] addr;
function display();
$display("Addr = %0d",addr);
endfunction
endclass
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
class parent_class;
bit [31:0] addr;
function display();
$display("Addr = %0d",addr);
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
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
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;
c1.display();
end
endmodule
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
SYNTAX:
local integer x;
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 display();
$display("tmp_addr = %0d",tmp_addr);
endfunction
endclass
// module
module encapsulation;
initial begin
parent_class p_c = new(5);
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 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
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 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
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 display();
$display("tmp_addr = %0d",tmp_addr);
endfunction
endclass
// module
module encapsulation;
initial begin
child_class c_c = new(10);
Abstract Class
//abstract class
virtual class packet;
bit [31:0] addr;
endclass
module virtual_class;
initial begin
packet p;
p = new();
end
endmodule
//abstract class
virtual class packet;
bit [31:0] addr;
endclass
module virtual_class;
initial begin
extended_packet p;
p = new();
p.addr = 10;
p.display();
end
endmodule
Virtual Methods,
Virtual Functions
Virtual Tasks
Virtual Functions
Virtual Task
Task declared with a virtual keyword before the task keyword is referred
to as virtual task
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.
base_class b_c;
extended_class e_c;
b_c = e_c;
On calling b_c.display()
//Function definition
endfunction
Virtual task syntax
virtual task task_name;
//task definition
endtask
Virtual Method Examples
Method without virtual keyword
class base_class;
endclass
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 ::
//class
class packet;
bit [31:0] addr;
static bit [31:0] id;
module sro_class;
int id=10;
initial begin
packet p;
p = new();
packet::id = 20;
p.display(packet::id,id);
end
endmodule
If the definition of the method written outside the body of the class then
the method is called an external method.
Note:
module extern_method;
initial begin
packet p;
p = new();
p.addr = 10;
p.data = 20;
p.display();
end
endmodule
module extern_method;
initial begin
packet p;
p = new();
p.addr = 10;
p.data = 20;
p.display();
end
endmodule
typedef class
typedef syntax
//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
//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
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.
rand keyword
Variables declared with the rand keyword are standard random variables.
Their values are uniformly distributed over their range.
randc keyword
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.
//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
rand_mode syntax
<object_hanlde>.<variable_name>.rand_mode(enable);
//enable = 1, randomization enable
//enable = 0, randomization disable
randomization disable examples
without randomization disable
class packet;
rand byte addr;
rand byte data;
endclass
module rand_methods;
initial begin
packet pkt;
pkt = new();
//calling randomize method
pkt.randomize();
class packet;
rand byte addr;
rand byte data;
endclass
module rand_methods;
initial begin
packet pkt;
pkt = new();
class packet;
rand byte addr;
rand byte data;
endclass
module rand_methods;
initial begin
packet pkt;
pkt = new();
Randomization Methods
pre_randomize
post_randomize
pre_randomize
post_randomize
For example, Users can override the randomized values or can print the
randomized values of variables.
class packet;
rand bit [7:0] addr;
randc bit [7:0] data;
//pre randomization function
function void pre_randomize();
$display("Inside pre_randomize");
endfunction
module rand_methods;
initial begin
packet pkt;
pkt = new();
pkt.randomize();
end
endmodule
//class
class packet;
rand bit [7:0] addr;
randc bit wr_rd;
bit tmp_wr_rd;
module rand_methods;
initial begin
packet pkt;
pkt = new();
repeat(4) pkt.randomize();
end
endmodule
Constrained randomization
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
class packet;
rand bit [3:0] addr;
module constr_blocks;
initial begin
packet pkt;
pkt = new();
repeat(10) begin
pkt.randomize();
$display("\taddr = %0d",pkt.addr);
end
end
endmodule
class packet;
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
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
class packet;
rand bit [3:0] addr;
rand bit [3:0] start_addr;
rand bit [3:0] end_addr;
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
class packet;
rand bit [3:0] addr;
rand bit [3:0] start_addr;
rand bit [3:0] end_addr;
weighted distribution
syntax
value := weight or
value :/ weight
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 };
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.
class packet;
rand bit [3:0] addr;
module constr_dist;
initial begin
packet pkt;
pkt = new();
$display("------------------------------------");
repeat(10) begin
pkt.randomize();
$display("\taddr = %0d",pkt.addr);
end
$display("------------------------------------");
end
endmodule
class packet;
rand bit [3:0] addr_1;
rand bit [3:0] addr_2;
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
If the expression on the LHS of implication operator (->) is true, then the
only constraint on the RHS will be considered.
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
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
class packet;
rand byte addr [];
rand byte data [];
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
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
class packet;
rand bit [3:0] addr;
module static_constr;
initial begin
packet pkt;
pkt = new();
pkt.randomize();
$display("\taddr = %0d",pkt.addr);
end
endmodule
class packet;
rand bit [3:0] addr;
module static_constr;
initial begin
packet pkt;
pkt = new();
//disabling constraint
pkt.addr_range.constraint_mode(0);
class packet;
rand bit [3:0] addr;
module static_constr;
initial begin
packet pkt;
pkt = new();
//disabling constraint
pkt.addr_range.constraint_mode(0);
end
endmodule
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
class packet;
rand bit [7:0] addr;
module static_constr;
initial begin
packet pkt1;
packet pkt2;
pkt1 = new();
pkt2 = new();
pkt2.addr_range.constraint_mode(0);
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;
module static_constr;
initial begin
packet pkt1;
packet pkt2;
pkt1 = new();
pkt2 = new();
pkt2.addr_range.constraint_mode(0);
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
class packet;
rand bit [3:0] addr;
rand bit [3:0] data;
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
class packet;
rand bit [3:0] addr;
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 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
class packet;
rand bit [3:0] start_addr;
rand bit [3:0] end_addr;
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
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
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
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};}
program unique_elements_randomization;
unique_elements pkt;
initial begin
pkt = new();
pkt.randomize();
pkt.display();
end
endprogram
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];
program unique_elements_randomization;
unique_elements pkt;
initial begin
pkt = new();
pkt.randomize();
pkt.display();
end
endprogram
Bidirectional Constraints
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
In below example,
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;)
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 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.
class pakcet;
rand bit a;
rand bit [3:0] b;
Writing below constraint will direct solver to solve ‘a’ first, so more
frequently a will take value of 1.
class packet;
rand bit a;
rand bit [3:0] b;
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
$urandom( )
variable = $urandom(seed);
$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;
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 syntax
semaphore semaphore_name;
Semaphore methods
new( );
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( );
semaphore_name.put(number_of_keys); or semaphore_name.put();
get( );
The semaphore get() method is used to get key/keys from a semaphore.
semaphore_name.get(number_of_keys); or semaphore_name.get();
try_get();
semaphore_name.try_get(number_of_keys); or semaphore_name.try_g
et();
Semaphore examples
two processes accessing the same resource
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
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
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
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
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
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
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.
Mailbox types
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)
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( );
//------
-------------------------------------------------------------------
// Packet
//-------------------------------------------------------------------------
class packet;
rand bit [7:0] addr;
rand bit [7:0] data;
//-------------------------------------------------------------------------
//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;
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
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.
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.
@(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.
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;
Merging events
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
module events_ex;
initial begin
fork
//process-1, triggers the event
begin
#40;
$display($time,"\tTriggering The Event");
->ev_1;
end
events examples
trigger and wait for an event at the same time
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
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
wait_order example
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
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
Program Block
or
//-------------------------------------------------------------------------
//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
//-------------------------------------------------------------------------
//design code
//-------------------------------------------------------------------------
module design_ex(output bit [7:0] addr);
initial begin
addr <= 10;
end
endmodule
//-------------------------------------------------------------------------
//testbench
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//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
without Interface
SystemVerilog
Interface
interface interface_name;
...
interface_items
...
endinterface
interface_name inst_name;
//-------------------------------------------------------------------------
// 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,
//-------------------------------------------------------------------------
// 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();
//TestCase Instance
testcase test(intf);
……
endmodule
Interface example
Writing interface
interface intf;
endinterface
Interface declaration
//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
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
Syntax
//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;
//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;
TestCase Code
Testcase receives the interface handle from the testcase and passes it to
env.
program test(intf i_intf);
initial begin
//creating environment
env = new(i_intf);
module tbench_top;
//Testcase instance
test t1(i_intf);
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.
Immediate Assertions
Concurrent Assertions
Immediate Assertions:
Syntax
action_block;
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”);
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:
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
Syntax
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
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.
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);
Forbidding a property
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);
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
If we want the sequence to be checked only after “a” is high, this can be
achieved by using the implication operator.
Overlapped implication
Non-overlapped implication
Overlapped implication
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
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);
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);
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);
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);
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);
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.
Syntax
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]
SVA Methods
$rose
sequence seq_rose;
@(posedge clk) $rose(a);
endsequence
$fell
$stable
returns true if the value of the expression did not change. Otherwise, it
returns false.
sequence seq_stable;
@(posedge clk) $stable(a);
endsequence
$past
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)
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
Code Coverage
Functional Coverage
Code Coverage
Functional Coverage
covergroup cov_grp;
cov_p1: coverpoint a;
endgroup
module cov;
logic clk;
logic [7:0] addr;
logic wr_rd;
Explicit bins
module cov;
logic clk;
logic [7:0] addr;
logic wr_rd;
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
Example,
Cross Coverage
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
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
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
cross_auto_bin_max
SystemVerilog Parameters
parameter
`define
Parameter
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
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 TYPE_1
`define WIDTH 8
`else
`define WIDTH 32
`endif
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 Declaration
Import Declaration
//----------------------------------------------
// SystemVerilog File
//----------------------------------------------
module dpi_tb;
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() {
//----------------------------------------------
// SystemVerilog File
//----------------------------------------------
module dpi_tb;
initial
begin
$display("Before calling C Method");
c_method();
$display("After calling C Method");
end
//----------------------------------------------
// C++ file
//----------------------------------------------
#include stdio.h
#include iostream
#include svdpi.h
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
In the below example, variable of bit type are grouped in the struct.
module struct_tb;
mem_pkt pkt;
initial begin
// Initializing Struct
pkt = '{8'h6, 1'b1, 32'hC001_0FAB};
$display ("pkt = %p", pkt);
mem_pkt pkt;
initial begin
// Initializing Struct
pkt = '{8'h6, VALID_PKT, 32'hC001_0FAB};
$display ("pkt = %p", pkt);
SystemVerilog Callback
What is Callback?
In simple words,
Callbacks are empty methods with a call to them.
or
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,
Callback example
In this step, will write the driver with a dummy method and callback
hooks to it.
class slave_driver;
resp_type resp;
endclass
class slave_driver;
resp_type resp;
class slave_driver;
resp_type resp;
//callback hook
virtual task update_resp; endtask
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
endclass
program error_test;
slave_env env;
initial begin
//Create env
env = new();
//Calling run of env
env.run();
end
endprogram
program error_test;
slave_env env;
err_inject err_driver;
initial begin
//Create env
env = new();
err_driver = new();
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;
Benefits of callback?
Callback example
In this step, will write the driver with a dummy method and callback
hooks to it.
class slave_driver;
resp_type resp;
endclass
class slave_driver;
resp_type resp;
class slave_driver;
resp_type resp;
//callback hook
virtual task update_resp; endtask
class slave_driver;
resp_type resp;
//callback hook
virtual task update_resp; endtask
endclass
program error_test;
slave_env env;
initial begin
//Create env
env = new();
//Calling run of env
env.run();
end
endprogram
program error_test;
slave_env env;
err_inject err_driver;
initial begin
//Create env
env = new();
err_driver = new();
initial begin
//Create env
env = new();
err_driver = new();
//Overriding slave_driver by error_driver
env.slv_driver = err_driver;
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 is,
waveform diagram:
“Adder” Waveform
For the simplicity and ease of understanding, let’s write the two
TestBecnh’s,
class transaction;
endclass
class transaction;
endclass
class transaction;
class generator;
------
endclass
class generator;
endclass
class generator;
endclass
This involves,
class generator;
//declaring mailbox
mailbox gen2driv;
//constructor
function new(mailbox gen2driv);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
endfunction
endclass
class generator;
//declaring mailbox
mailbox gen2driv;
//constructor
function new(mailbox gen2driv);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
endfunction
endclass
class generator;
//declaring mailbox
mailbox gen2driv;
//constructor
function new(mailbox gen2driv);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
endfunction
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
endinterface
Driver Class
class driver;
----
endclass
1. Declare interface and mailbox, Get the interface and mailbox handle
through a constructor.
//constructor
function new(virtual intf vif,mailbox gen2driv);
//getting the interface
this.vif = vif;
//getting the mailbox handle from environment
this.gen2driv = gen2driv;
endfunction
class driver;
//used to count the number of transactions
int no_transactions;
//constructor
function new(virtual intf vif,mailbox gen2driv);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.gen2driv = gen2driv;
endfunction
endclass
Environment
Creates the mailbox, generator and driver shares the mailbox handle
across the Generator and Driver.
class environment;
---
endclass
//mailbox handle's
mailbox gen2driv;
//virtual interface
virtual intf vif;
Mailbox
Generator
Driver
//constructor
function new(virtual intf vif);
//get the interface from test
this.vif = vif;
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
task run;
pre_test();
test();
post_test();
$finish;
endtask
`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"
class environment;
//mailbox handle's
mailbox gen2driv;
//virtual interface
virtual intf vif;
//constructor
function new(virtual intf vif);
//get the interface from test
this.vif = vif;
//
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
program test;
----
endprogram
initial begin
//creating environment
env = new(intf);
end
`include "environment.sv"
program test(intf intf);
initial begin
//creating environment
env = new(intf);
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
//clock generation
always #5 clk = ~clk;
//reset Generation
initial begin
reset = 1;
#5 reset =0;
end
initial begin
$dumpfile("dump.vcd"); $dumpvars;
end
`include "interface.sv"
`include "random_test.sv"
module tbench_top;
//clock generation
always #5 clk = ~clk;
//reset Generation
initial begin
reset = 1;
#5 reset =0;
end
SystemVerilog TestBench
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.
class monitor;
------
endclass
2. Declare interface and mailbox, Get the interface and mailbox handle
through the constructor.
//constructor
function new(virtual intf vif,mailbox mon2scb);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
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
class monitor;
//constructor
function new(virtual intf vif,mailbox mon2scb);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
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
//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
class scoreboard;
//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
Here only updates are mentioned. i.e adding monitor and scoreboard to
the previous example.
//mailbox handle's
mailbox gen2driv;
mailbox mon2scb; //---NEW CODE---
//virtual interface
virtual intf vif;
Mailbox (mon2scb)
Monitor
Scoreboard
//constructor
function new(virtual intf vif);
//get the interface from test
this.vif = vif;
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
`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"
`include "monitor.sv"
`include "scoreboard.sv"
class environment;
//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;
//
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
Code Coverage
Functional Coverage
covergroup cov_grp;
cov_p1: coverpoint a;
endgroup
module cov;
logic clk;
logic [7:0] addr;
logic wr_rd;
Explicit bins
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.
module cov;
logic clk;
logic [7:0] addr;
logic wr_rd;
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
Example,
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
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
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
cross_auto_bin_max