SystemVerilog Interview Questions PART-1
SystemVerilog Interview Questions PART-1
VERILOG
INTERVIEW
QUESTIONS
PART
1. When import is used and when `include is used?
In SystemVerilog, both import and include are used for including external files, but they serve
different purposes.
1. `include directive:
The include directive is used to insert the contents of an external file directly into the
current file at the location where the directive is placed.
It is mainly used for including common definitions, declarations, or macros that are
required across multiple files.
The included file is treated as if its content was directly written in place of the include
directive.
The syntax for include directive is: include "filename"
2. `import statement:
The import statement is used to bring in names (such as modules, packages, or individual
identifiers) from an external file or a namespace into the current scope.
It is primarily used for modularizing designs and organizing code hierarchically.
The imported items can be used directly in the current file without any prefix.
The syntax for import statement depends on what you are importing. For example, to
import a module, the syntax is: import module_name::*;
endfunction : static_func
endclass : myClass
module mod_def();
initial begin
end
endmodule : mod_def
To answer the question about the output of the code, it's important to analyze the code and
understand its structure and behavior. Let's break it down:
The code defines a SystemVerilog class called myClass with a type parameter T defaulting to
int.
Within the class, there is a static variable a that is assigned the result of calling a
static_function().
The class also contains a static function static_func() that returns an integer value of 10 and
displays the message "In class myClass#()" using $display.
The code then defines a module called mod_def without any specific functionality inside its
initial block.
Now, since the code does not include any instantiation of the myClass or any usage of the mod_def
module, there is no direct output produced by the code itself. Therefore, the output would be empty.
3. What will be the output if the following line is added to the above code?
typedef cls_def#(int) cl_int;
In the provided code, the line typedef cls_def#(int) cl_int; is attempting to create a typedef of a
class called cls_def instantiated with the int type parameter, and the typedef name cl_int. However,
there is no definition or instantiation of the cls_def class in the code snippet you provided.
Therefore, if you add this line to the code, it will result in a compilation error.
Without the definition of the cls_def class, it is not possible to determine the output of the code.
4. In the below example, "a1 = b1" is not allowed. Why is it not allowed, and what is the
solution?
class MyBase #(type T = int);
virtual function void func1();
$display("Inside MyBase");
endfunction : func1
endclass
endfunction : func1
endclass
module top;
MyBase a1;
MyDerived b1 = new();
initial begin
endmodule
The assignment 'a1 = b1' is not allowed because SystemVerilog requires explicit casting when
assigning objects of derived classes to objects of base classes. To resolve this, we can use explicit
casting by adding the line '$cast(a1, b1); // Correct explicit casting syntax. $cast(a1, b1) casts the
object b1 of type MyDerived to type MyBase and assigns it to a1. This casting ensures type safety
and allows 'a1' to refer to the 'MyDerived' object.
5. If a virtual method is defined in a class using extended class handle/object, how can the virtual
method defined in the base class be called?
To call the virtual method defined in the base class when using an extended class handle/object,
we can use the super keyword. By declaring an object or handle of the base class type and assigning
an instance of the derived class to it, we can then use super.myVirtualMethod() inside the derived
class to explicitly call the virtual method defined in the base class. This way, we ensure that the
base class implementation of the virtual method is executed.
endfunction
endclass
endclass
module top;
initial begin
end
endmodule
SystemVerilog Function:
SystemVerilog introduces a more advanced and enhanced version of functions.
SystemVerilog functions can be declared inside modules, classes, or interfaces.
SystemVerilog functions can operate on complex data types, including arrays, structures,
and user-defined types.
SystemVerilog functions support hierarchical references to module instances and can be
called from other modules or classes.
SystemVerilog functions can have input/output ports, allowing them to be instantiated and used
like modules.
SystemVerilog functions can have additional features like automatic tasks, which allow you to
include procedural statements along with the function.
initial begin
fork
fork
#20 $display("time = %0d at T20", $time);
join_any
disable fork;
end
initial
#100 $finish;
endprogram
time = 10 at T10
time = 20 at T20
time = 25 at T25
Explanation:
At time 5, the $display statement inside the second fork block is executed and displays "time = 5
at T10".
At time 10, the $display statement inside the second fork block is executed again and displays
"time = 10 at T10".
At time 20, the $display statement inside the second fork block is executed once more and displays
"time = 20 at T20".
At time 25, the $display statement outside the fork blocks is executed and displays "time = 25 at
T25".
After the execution reaches time 100, the program finishes and terminates.
Virtual functions, on the other hand, are used to enable polymorphism, which allows objects of
different derived classes to be treated as objects of their common base class. Virtual functions are
invoked based on the actual runtime type of the object rather than the declared type of the handle
or reference to the object.
module Testbench;
Color myColor;
initial begin
myColor = RED;
end
endmodule
In this example, the enumerated variable myColor is defined with three possible values: RED,
GREEN, and BLUE. Inside the initial block, the value of myColor is set to RED, and then it is
displayed using $display statement with the %s format specifier, which is used for displaying string
values. The output will show the string representation of the enumerated value RED.
module myModule();
string myString[7];
int i, j, k, file;
initial begin
string s;
myString[i] = s;
i++;
end
$fclose(file);
$finish;
end
endmodule
================
aa
bb
cc
================
The expected output is:
Index j = 0, string = aa
Index j = 1, string = bb
Index j = 2, string = cc
Index j = 3, string = cc
Here's an example:
testclass obj; // Declare a handle obj for testclass
obj = new; // Construct a testclass object and store the address in obj. "new" allocates space for
testclass
obj = null; // Deallocate the object. This frees up the memory space for the object.
For example, let's consider a scenario where you want to inject new functionality into a driver
without modifying its code. You can achieve this by adding the desired functionality in a pre-
callback task or a post-callback task.
Here's an example:
task Driver::run;
forever begin
...
<pre_callback> // Calls the pre_callback function.
transmit(tr);
end
endtask
task pre_callback;
// Code for the pre-callback functionality goes here.
endtask
By utilizing the pre_callback task as a callback, you can add the desired functionality without
directly modifying the Driver task. This allows for flexibility and customization within the
testbench.
mailbox mbx;
tr = new;
task run;
repeat (10)
begin
assert(tr.randomize);
mbx.put(tr);
end
endtask
endclass
Bug: Here the object "tr" is constructed once outside the loop. Then "tr" is randomized and put
into the mailbox "mbx". But the mailbox "mbx" holds only handles, not objects. Therefore, the
mailbox contains multiple handles pointing to a single object. Here, the code gets the last set of
random values.
task run;
repeat (10)
begin
endtask
Bug: The run task constructs a transaction object and immediately randomizes it. This means the
transaction "tr" uses whatever constraints are turned on by default.
Solution: Separate the construction of "tr" from its randomization by using a method called the
"Factory Pattern".
Factory Pattern:
class Generator;
mailbox mbx;
Transaction tr;
repeat (10)
begin
assert (blueprint.randomize); // 2. Randomizing the Blueprint
endtask
endclass
14. Explain the difference between data types logic, reg, and wire.
WIRE:
1) Wire is just an interconnection between two elements which does not have any driving strength.
2) It is used for modeling combinational circuits as it cannot store a value.
3) Wire has a default value of "z" and gets values continuously from the outputs of devices it is
connected to.
Example:
wire A;
assign A = b & c;
Note: Wire A is evaluated for every simulation delta time, so there is no need to store the value.
REG:
1) Reg is a 4-state unsigned variable that can hold a value and retains it until a new value is
assigned.
2) Register data type can be used for modeling both combinational and sequential logic.
3) The default value for a register is "x" and it can be driven from initial and always blocks. Values
of the register can be changed anytime in the simulation by assigning a new value to it.
Example:
reg A;
always @*
begin
A = b & c;
end
Note: A is declared as a reg which can be evaluated only when there is a change in any of the
signals in the sensitivity list. Reg needs to store the value until there is a change in the sensitivity
list.
LOGIC:
Example:
module sample1;
logic crc, d, q_out;
initial
begin
clk = 1'b0; // procedural assignment
endmodule
Example:
clocking cb @(posedge clk);
default input #1ns output #2ns; // Input skew and output skew
input grant; // Input from testbench to DUT
output request; // Output from DUT to testbench
endclocking
Note: Input signals (grant) are sampled at 1ns before the clock event, and output signals (request)
are driven 2ns after the corresponding clock event. If skew is not specified, the default input skew
is 1 step and the output skew is 0.
16. What are the ways to avoid race conditions between Testbench and RTL using
SystemVerilog?
The clock given to the DUT and the Testbench should have a phase difference.
The DUT should work on the posedge of the clock while the Testbench should work on the
negedge of the clock.
Testbench output and DUT output pins should always be driven using non-blocking statements.
Clocking blocks.
Program blocks.
20. How to have a #delay statement that is independent of timescale? In Verilog, the #delay
is dependent on the timescale.
To have a #delay statement that is independent of timescale in Verilog, you can use scaled time
values. Here's an example:
Let's say you want a delay of 1 microsecond (1us) regardless of the timescale.
1) Determine the timescale in your Verilog code. For example, let's assume the timescale is set to
1ns/1ns.
2) Calculate the scaling factor by dividing the desired delay (1us) by the timescale (1ns):
Scaling Factor = Desired Delay / Timescale
= 1us / 1ns
= 1000
3) Use the scaled time value in the #delay statement:
#1000; // Delay of 1 microsecond (1us) independent of the timescale
By using the scaled time value, you can achieve a consistent delay of 1 microsecond regardless of
the timescale setting.