System Verilog Questions
System Verilog Questions
Design Verification
Engineer
DATA TYPE
TWO-STATE INTEGER TYPES
==========================
.==============================================================. logic is a better name than reg, so is preferred.
| TYPE | Description | Example | As we shall see, you can use logic where in the past,
'==============================================================' you have may have used reg or where you may have used wire
| bit | user-defined size | bit [3:0] a_nibble; |
| byte | 8 bits, signed | byte a, b; | ===============
//WIRE AND REG
| shortint | 16 bits, signed | shortint c, d; | ===============
| int | 32 bits, signed | int i,j; |
| longint | 64 bits, signed | longint lword; | 1. Structural data types called nets, which model hardware connections between circuit
components.
'--------------------------------------------------------------'
2. The wire nets act like real wires in circuits.
3. The reg type holds their values until another value is put on them, just like a register
========================== hardware component.
//FOUR-STATE INTEGER TYPES 4. The declarations for wire and reg signals are inside a module but outside any initial or
always block.
==========================
//Default Value
.==============================================================. reg is x unknown
| TYPE | Description | Example | wire is z.
'=============================================================='
| reg | user-defined size | reg [7:0] a_byte; | What is the difference between logic[7:0] and byte variable in SystemVerilog?
| logic | identical to reg | logic [7:0] a_byte; |
byte is a signed variable which means it can only be used to count values till 127. A
| | in every way | |
logic[7:0] variable can be used for an unsigned 8 bit variable that can count up to 255.
| integer | 32 bits, signed | integer i, j, k; |
'--------------------------------------------------------------'
===========================
//PACKED & UNPACKED ARRAY
===========================
MEMORIES
==================
//DYNAMIC ARRAY //ASSOCIATIVE ARRAY
================== ====================
1. When size of a collection is unknown or the data space is sparse, an associative array is
1. A dynamic array is declared with empty word subscripts [] or square a better option.
bracket. 2. Associative arrays do not have any storage allocated until it is used.
2. You .do .not .specify the array size at compile time; instead, you give it at 3. Index expression is not restricted to integral expressions, but can be of any type.
run-time.
3. The array is initially empty, and so you must call the new[] constructor to SYNTAX:
--------
allocate space, passing
data_type array_identifier [ index_type ];
in the number of entries in the square brackets
module dynamic; int array1 [int]; // An integer array with integer index
.-------------------------------------------. int array2 [string]; // An integer array with string index
o
string array3 [string]; // A string array with string index
| int dyn[], d2[]; | // Empty dynamic arrays
| | // EXAMPLE:
| initial | ----------
| begin | module tb;
| dyn = new[5]; | // Allocate 5 elements
int array1 [int];
| foreach (dyn[j]) |
int array2 [string];
| dyn[j] = j; | // Initialize the elements string array3 [string];
| d2 = dyn; | // Copy a dynamic array
| d2[0] = 5; | // Modify the copy initial
| | begin
// Initialize each dynamic array with some values
| $display(dyn[0],d2[0]); | // See both values (0 &
array1 = '{ 1 : 22,6 : 34 };
5) array2 = '{ "Ross" : 100,"Joey" : 60 };
| dyn = new[20](dyn); | // Expand and copy array3 = '{ "Apples" : "Oranges","Pears" : "44" };
| dyn = new[100]; | // Allocate 100 new
integers ,Old values are lost // Print each array
| dyn.size | // Size of dyn is 100 $display ("array1 = %p", array1);
$display ("array2 = %p", array2);
| dyn.delete; | // Delete all elements
$display ("array3 = %p", array3);
| end | end
'-------------------------------------------' endmodule
endmodule
INTERFACE
1. Verilog connects between different modules through its module ports. For large designs, this method of
connection can become more time consuming and repetitious SO SV introduces Interfaces.
2. A SystemVerilog interface allows us to group a number of signals together and represent them as a single
port.
3. Interface blocks are defined and described within interface and endinterface keywords. It can be
instantiated like a module.
4. All these signals can be declared and maintained at a single place and be easily maintained.
5. Signals within an interface are accessed by the interface instance handle.
/EXAMPLE
==========
APB bus protocol signals are put together in the given .interface. Note that signals are declared .within interface .and endinterface
// NON-Parameterized
-----------------
interface apb_if (input pclk);
logic [31:0] paddr;
logic [31:0] pwdata;
logic [31:0] prdata;
logic penable;
logic pwrite;
logic psel;
endinterface
/How to define port directions? SystemVerilog allows a module to accept an interface as the port list instead of
===================================== individual signals.
*/
/* MODPORT
/* WITHOUT USING INTERFACE */ || /* USING INTERFACE HANDLE */
=========== ------------------------- || ------------------------
1. modport is used to define signal directions. module dut (output data,input enable,clk); || module dut (myBus busIf);
2. Different modport definitions can be passed to different components, allowing always @ (posedge clk) || always @ (posedge busIf.clk)
us to define different input-output directions for each component. if (enable) || if (busIf.enable)
data <= data+1; || busIf.data <= busIf.data+1;
else || else
//EXAMPLE data <= 0; || busIf.data <= 0;
========== endmodule || endmodule
interface myBus (input clk);
// Filename : tb_top.sv
========================
logic [7:0] data;
logic enable; module tb_top;
bit clk;
// From TestBench perspective, 'data' is input and 'enable' is output always #10 clk = ~clk; // Create a clock
modport TB (input data, clk, output enable);
myBus busIf (clk); // Create an interface object
// From DUT perspective, 'data' is output and 'enable' is input dut dut0 (busIf.DUT); // Instantiate the DUT; pass modport
modport DUT (output data, input enable, clk); DUT of busIf
initial
endinterface
begin
busIf.enable <= 0;
//How to connect an interface with DUT ? #10 busIf.enable <= 1;
========================================== #40 busIf.enable <= 0;
#20 busIf.enable <= 1;
#100 $finish;
/*
end
An interface object should be created in the top testbench module where DUT is endmodule
instantiated and passed to DUT. It is essential to ensure that the correct modport
is assigned to DUT.
//Clocking Blocks
//WHAT ARE CLOCKING BLOCKS ???? ------------------
================================ 1. Module ports and interfaces by default do not specify any timing requirements or synchronization
1. Signals that are specified inside a clocking block will be sampled/driven with schemes between signals.
respect to that clock.
A clocking block defined between clocking and endcocking does exactly that. It is a collection of signals
2. There can be mulitple clocking blocks in an interface. Note that this is for
synchronous with a
testbench related signals.
particular clock and helps to specify the timing requirements between the clock and the signals.
3. We want to control when the TB drives and samples signals from DUT.
4. Solves some part of the race condition, but not entirely. 2. Clocking blocks allow inputs to be sampled and outputs to be driven at a specified
5. We can also parameterize the skew values clock event.
interface my_int (input bit clk); 3. input skew ==> input signals will be sampled at skew time units before the clock event
clocking dut_clk @(posedge clk); //WHAT ARE INPUT AND OUTPUT SKEWS ???
======================================
default input #3ns output #2ns;
input enable; clocking cb @(clk);
output data; input #1ps req;
endclocking output #2 gnt;
input #1 output #3 sig;
endclocking
// From DUT perspective, 'data' is output and 'enable' is
input
1. Signal req is specified to have a skew of 1ps and will be sampled 1 ps before the clock edge clk.
modport DUT (output data, input enable, clk); 2. The output signal gnt has an output skew of 2 time units hence will be driven 2 time units after
the clock edge.
clocking tb_clk @(negedge clk); 3. The last signal sig is of inout type and will be sampled 1 ns before the clock edge and driven 3 ns
default input #3ns output #2ns; after the clock edge.
output enable;
clocking cb @(posedge clk);
input data;
input #1step req;
endclocking endclocking
// From TestBench perspective, 'data' is input and 'enable' 1. An input skew of 1step indicates that the signal should be sampled at the .end of the previous
is output time step, or in other words, immediately before the positive clock edge
modport TB (input data, clk, output enable);
BASIC OOP
//What are classes ?
//What is a class handle ?
----------------------
--------------------------
/*class is a user-defined datatype, an OOP construct, that can be used to
/*
encapsulate
1. A class variable such as pkt below is only a name by which that object is known.
data (property) and tasks/functions (methods) which operate on the
2. It can hold the handle(POINTER) to an object of class Packet.
data.
3. Until assigned with something ,it is always null. At this point, the class object does
*/ not exist yet.
*/
//EXAMPLE: //Class Handle Example
----------- -----------------------
class myPacket;
bit [2:0] header; class Packet;
bit encode; int count;
bit [2:0] mode; endclass
bit [7:0] data;
bit stop; module tb;
function new (bit [2:0] header = 3'h1, bit [2:0] mode Packet pkt; // Note: This "handle" now points to NULL
= 5);
this.header = header; initial
begin
this.encode = 0;
if (pkt == null)
this.mode = mode;
$display ("Packet handle 'pkt' is null");
this.stop = 1;
endfunction
$display ("count = %0d", pkt.count); // Display the class
member using the "handle"
function display (); // Expect a run-time error because pkt is not an
$display ("Header = 0x%0h, Encode = %0b, Mode = 0x%0h, object
Stop = %0b", // yet, and is still pointing to NULL. So pkt is not
this.header, this.encode, this.mode, this.stop); // aware that it should hold a member
endfunction end
endclass endmodule
//CLASS CONSTRUCTOR //COPYING AN OBJECT
-------------------- --------------------
Class Object are created with the new method(function) //If we assign pkt to a new variable called pkt2, the new
variable will also point to the contents in pkt.
1.Every Class has a default constructor new method
class Packet;
Constructor new method-
bit [31:0] addr;
1.Allocates Memory
bit [31:0] data;
2.Return the address of the memory to handle
endclass
3.Initialize all the properties
module tb;
//EXPLICITY DEFINED: Packet pkt,pkt2;
==================== initial
class Packet; begin
bit [31:0] addr; pkt = new;
bit [31:0] data; pkt.addr=10;
function new (bit [31:0] data=15); pkt.data=20;
addr = 20; $display ("addr=%d,data=%d",
endfunction pkt.addr,pkt.data);
endclass pkt2=new pkt;
$display ("addr=%d,data=%d",
module tb; pkt2.addr,pkt2.data);
pkt2.addr=30;
Packet pkt; pkt2.data=40;
initial $display ("addr=%d,data=%d",
begin pkt2.addr,pkt2.data);
$display ("addr=%d,data=%d",
pkt = new;
pkt.addr,pkt.data);
$display ("addr=%d,data=%d", pkt.addr,pkt.data);
end
end
endmodule
endmodule
//COPYING OBJECTS-SHALLOW COPY /COPYING OBJECTS-DEEP COPY
-----------------------------
------------------------------- 1.Deep Copy - properties of main .class as well as of subclass will get copied
1.Shallow Copy - only properties of main .class are copied not
the object of subclass
class sub;
int addr;
M1 = new; initial
M1.data=4; begin
M1 = new;
M1.sub_h.addr=5; M1.data=4;
M2 = new M1; M1.sub_h.addr=5;
M2 = M1.copy;
M2.sub_h.addr=10;
end end
endmodule
endmodule
//SYSTEMVERILOG 'THIS' KEYWORD
-------------------------------
/*1.The this keyword is used to refer to class properties, parameters and methods of the current instance.
2.It can only be used within non-static methods, constraints and covergroups.
3.This is basically a pre-defined object handle that refers to the object that was used to invoke the method in which this is
used.
*/
//EXAMPLE-
------------
class Packet;
int addr;
int data;
data = b;
// addr = addr; // Which addr should get assigned ?
this.addr = addr; // addr variable in Packet class should be
// assigned with local variable addr in new()
endfunction
endclass
ADVANCED OOP
INHERITANCE
=============================
1. Inheritance is a concept in OOP that allows us to extend a class to create another class and have access to all the properties and methods of the original parent class from the handle of child class object.
2. A derived class may add new properties and methods, or modify the inherited properties and methods. Inheritance allows re-usability. i.e. derived class by default includes the properties and methods, which is
ready to use
3. If the class is derived from a derived class, then it is referred to as Multilevel inheritance
PARENT CLASS:
==============
1. It’s an existing class;
2. The class whose features are inherited
3. The parent class is also known as a base class, superclass
CHILD CLASS:
=============
1. It’s an extended class;
2. The class that inherits the other class is known as subclass
3. The child class is also known as an extended class, derived class, subclass
//Example
----------
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
//EXAMPLE:
-----------
POLYMORPHISM // base class
class base_class;
============================== virtual function void display();
$display("Inside base class");
1.Polymorphism means many forms. Polymorphism in SystemVerilog endfunction
endclass
provides an ability to an object to take on many forms.
// child class 1
2. Polymorphism allows the use of a variable of the parent class type to class child_class_1 extends base_class;
function void display();
hold child class objects and to $display("Inside child class 1");
endfunction
reference the methods of those child classes directly from the parent endclass
endclass
module inheritence;
initial
begin
child_class c=new();
c.addr = 10;
c.data = 20;
c.display();
end
endmodule
RANDOMIZATION
//RAND KEYWORD
//CONSTRAINT BLOCKS
=============== =========================
1. Variable declared with the rand keyword are standard Basically, we use constaints to generate meaningfull data, if there is no constraint
random variable. then the simulator will try
2. There values are uniformly distributed over their range to generate pure random and it might not be usefull for verification
3. Random values can be repeated
1. Constraint blocks are_class members like tasks, functions, and variables
2. Constraint blocks will have a unique name within a .class
rand bit[1:0] var1;
3. Constraint blocks consist of conditions or expressions to limit or control the
values for a random variable
0,1,2,3 4. Constraint blocks are enclosed within curly braces { }
0,3,2,3 5. Constraint blocks can be defined inside the_class or outside the_class like .extern
1,2,1,3 methods,
6. constraint block defined outside the_class is called as .extern constraint block
//RANDC KEYWORD
================ CONSTRAINT BLOCK SYNTAX
--------------------------
constraint <constraint_block_name> { <condition/expression>;
1. For variable declared with randc keyword
...
2. On randomization variable value does not repeat untill
<condition/expression>;}
every possible value has been assigned CONSTRAINT BLOCK EXAMPLE
1. std::randomize(), enables users to randomize data in the current scope without the need to define a_class or instantiate
a_class object.
2. Variable which are passed as arguments are randomized
3. There are no limit on the no of arguments
4. If a property does not of rand or randc type then it can be randomize by giving that property as arguments to randomize()
method
6. Most important is that the std::randomize_function behaves exactly the same as a_class randomize method,
except that it operates on the variables of the current scope instead of_class member variables.
5. if the value needs to be outside the range, then it can be specified as inverse (!) of inside
module static_constr;
CONSTRAINT_MODE() METHOD
initial
begin
packet pkt;
1. constraint_mode(1) ----> means constraint block is enabled
pkt = new();
2. constraint_mode(0) ----> means constraint block is disabled
$display("Before Constraint disable");
repeat(2)
default value of constraint_mode is 1, i.e enabled begin
pkt.randomize();
$display("\taddr = %0d",pkt.addr);
5. once the constraint block is disabled, it is required to make end
4. The foreach constraint will be applicable to an array with one or more than one element
5. So it’s required to_specify or constrain the size of the dynamic array
Syntax
-------
Semaphore is a SystemVerilog built-in_class, used for access control to shared resources, and for basic synchronization.
A semaphore is like a bucket with a number of keys. processes using semaphores must first procure a key from the bucket before they can continue to execute, All
other processes must wait until a sufficient number of keys are returned to the bucket.
Imagine a situation where two processes try to access a shared memory area. where one process tries to write and the other process is trying to read the same
memory location. this leads to an unexpected result. A semaphore can be used to overcome this situation.
//SEMAPHORE SYNTAX
====================
semaphore semaphore_name;
//SEMAPHORE METHODS
======================
Semaphore is a built-in_class that provides the following methods,
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();
======== EXAMPLE WITH 4 KEYS
The semaphore put() method is used to return key/keys to a semaphore.
=================
.-----------------------------------------------------------------. Semaphore with 4 keys
| semaphore_name.put(number_of_keys); or semaphore_name.put(); | In the example below,
'-----------------------------------------------------------------'
Creating semaphore with ‘4’ keys
When the semaphore_name.put() method is called,
module semaphore_ex;
1. the specified number of keys are returned to the semaphore.
semaphore sema; //declaring semaphore sema
2. The default number of keys returned is 1.
initial
//get(); begin
========= sema=new(4); //creating sema with '4' keys
The semaphore get() method is used to get key/keys from a semaphore.
fork
.-----------------------------------------------------------------. display(); //process-1
| semaphore_name.get(number_of_keys); or semaphore_name.get(); | display(); //process-2
'-----------------------------------------------------------------'
join
When the semaphore_name.get() method is called, end
//display method
1. If the specified number of keys are available, then the method returns and execution continues
task automatic display();
2. If the specified number of keys are not available, then the process blocks until the keys become available
3. The default number of keys requested is 1 sema.get(4); //getting '4' keys from sema
$display($time,"\tCurent Simulation Time");
//try_get(); #30;
=============
The semaphore try_get() method is used to procure a specified number of keys from a semaphore, but without blocking.
sema.put(4); //putting '4' keys to sema
endtask
.-------------------------------------------------------------------------. endmodule
| semaphore_name.try_get(number_of_keys); or semaphore_name.try_get(); |
'-------------------------------------------------------------------------'
Simulator Output
When the semaphore_name.try_get() method is called,
0 Current Simulation Time
1. If the specified number of keys are available, the method returns 1 and execution continues
2. If the specified number of keys are not available, the method returns 0 and execution continues
30 Current Simulation Time
3. The default number of keys requested is 1
MAILBOX
================================
A mailbox is a communication mechanism that allows messages to be exchanged between
processes. Data can be sent to a mailbox by one process and retrieved by another.
mailbox mbxRcv;
mbxRcv = new();
To place a message in a mailbox, two methods are supported put() (blocking) and peek()
(nonblocking). To retrieve a message from mailbox, two methods are supported get()
(blocking) and try_get() (nonblocking). To retrieve the number of messages in the
mailbox, we can use num().
COVERAGE
COVERAGE is:
1. Defined as the percentage of verification objective that have been met
2. Used as a metric for evaluating the progress of a verification project
3. Used to reduce the number of cycles spent in verifying a design
4. Allow the user to tell how well a design has been tested
Code Coverage:
1. It checks the quality of testbench/inputs
2. Either code is wrong or testbench is wrong
3. Measure how much of the code has been executed
4. Code, Path, Expression, FSM Coverage are type of code coverage
Functional Coverage:
1. User-specified to tie the verification environment to the design intent or functionality
2. Functional coverage goals are derived from the functional specification
3. Functional coverage is not automatically inferred from the design
4. It is user defined model
============== ===============
//COVERGROUP //COVERPOINTS
============== ===============
-> It is built-in .class
-> The_covergroup construct encapsulate the specification of a -> Coverpoints are the variables you are interested in tracking
coverage model and may contain: -> from these variablers you may be interested in tracking
1. A clocking event that synchronizes sampling of points specific values or range of values
2. A set of coverage points -> During simulation the values for variables defined as
3. Cross coverage between coverage points coverpoints are tracked and stored in coverage
4. Optional formal arguments database
2) fork .. join_any: Processes that are created using “fork … join_any” run as separate threads but the parent process that spawned those stalls only until any one of the threads complete. Further, the
remaining threads and the parent process can run parallely. If we look at the example below: there are three processes - task1, task2 and task3 that will run parallely. When one of task1/task2/task3
completes,
the join_any will complete and cause the $display() to execute while other tasks might still be running.
initial begin
fork
task1; // Process 1
task2; // Process 2
task3; // Process 3
join_any
$display(“Any one of task1/2/3 finished”);
3) fork .. join_none: Processes that are created using “fork … join_none” run as separate threads but the parent process that spawned them doesn’t stall and also proceed parallely. Refer to the following
example and there are three processes - task1, task2 and task3 that will run parallely with the parent process.
initial begin
fork
task1; // Process 1
task2; // Process 2
task3; // Process 3
join_none
$display(“All tasks launched and running”);
end
INTERVIEW QUESTIONS
class array_abc;
rand unsigned int myarray[];
endclass
constraint c_abc_val {
myarray.size inside { [10:16] };
foreach (myarray[i])
if (i>0) myarray[i] < myarray[i-1];
}
How can we use constraints to generate a dynamic array with random but unique values ? Refer the
code below:
class TestClass;
rand bit[3:0] my_array[]; //dynamic array of bit[3:0]
endclass
There are two ways in which this can be done - one using the SV unique constraint and one without
using it as shown in 2) below.
1) Add a unique constraint to the class as below
constraint c_rand_array_uniq {
my_array.size == 6; //or any size constraint
unique {my_array}; //unique array values
}
2) Without using unique constraint, you can still generate incremental values and then do an array
shuffle() in post_randomize() ;
constraint c_rand_array_inc {
my_array.size == 6 ;// or any size constraint
foreach (my_array[i])
if(i >0) my_array[i] > my_array[i-1];
}
function post_randomize();
my_array.shuffle();
endfunction
Is it possible to override a constraint defined in the base class in a derived class and if so how?
Yes, a constraint defined in the base class can be overridden in a derived class by changing the
definition using the same constraint name. For Example: Refer the constraint c_a_b_const in
following code. In the base class, it is defined to always have a value of a < b , but in a derived
class, it has been overridden to have always a > b .
class Base;
rand int a ;
rand int b;
constraint c_a_b_const {
a < b;
}
endclass
class Derived extends Base;
constraint c_a_b_const {
a > b;
}
endclass
What is a virtual interface and where is it used?
A virtual interface is a variable that points to an actual interface. It is used in classes to provide
a connection point to access the signals in an interface through the virtual interface pointer.
The following example shows an actual interface bus_if that groups a set of bus signals. A
BusTransactor class then defines a virtual interface of this type that is used to access all signals
from this bus_if for driving a request or waiting for a grant signal. The top level test module
which instantiates the physical interface will pass the handle of same to the BusTransactor
class through constructor which gets assigned to the virtual interface pointer.