SystemVerilog Notes
SystemVerilog Notes
A always_ff procedure adds a restriction that it can contain one and only one
event control and no blocking timing controls. Variables written on the left-hand
side of assignments within always_ff, including variables from contents of a
called function, cannot be written by other processes.1
1. DATA TYPES
1.1 String
No need of making reg [8*18-1:0] str1 = “Hello Verilog WOrld”;
--> string s1 = “Hello WOrld”;
--> string s2 = {“Hi“, “! ”, s1};
Format specified = %s
1.2 Logic
logic is the improved version of reg form Verilog to SystemVerilog, so it Can be
driven by continuous assignments, gates, and modules in addition to being a
variable.
It is declared just as reg and wire.
1.3 Void
The void data type represents non-existent data. This type can be specified as the
return type of functions, indicating no return value. Used to indicate no return type in
functions.
--> void’(function_call());
1.4 Event
An event is now a handle to a synchronization object that can be passed around to
routines. In Verilog, if the triggering thread executes before the blocking thread, the
trigger is missed. SystemVerilog introduces triggered function that lets you check
whether an event has been triggered.
--> event e1;
1.6 Class
If class is just kept as a separate construct in SV, like task/functions, we still would
have the
benefits of polymorphism, data abstraction, inheritance etc. But, by making it as a
data type
too, we get to pass objects inside functions and tasks.
--> class MyClass;
int data;
function new(int val);
data = val;
endfunction
endclass
module top;
MyClass obj;
initial begin
obj = new(42);
pass_object(obj);
end
2. ENUMERATIONS
An enumerated type defines a set of named values. The simplest enumerated type
declaration contains a list of constant names and one or more variables.
Here, the actual values are defaulted to integers starting at 0 and then increase. in the
above example by default variable will get the default value of 0,1,2,3,4,5 respectively
from red. The values can be set for the names and also values can be set for some of
the names and not set for other names. A name without a value is automatically
assigned an increment of the value of the previous name.
--> enum { red=0, green, blue=4, yellow, white=10, black } Colors;
In the following example value is set for red = 0, blue = 4, white = 10. green, yellow,
black automatically assigned to the increment-value of 1,5,11 respectively.
Method Description
first() returns the value of the first member of the enumeration
NOTE:
changed at run-time.
The space for a dynamic array doesn’t exist until the array is explicitly created at
run-time, space is allocated when new[number] is called. the number indicates the
number of space/elements to be allocated.
--> //declaration
int d_array2[ ];
//memory allocation
//array initialization
d_array1 = {0,1,2,3};
foreach(d_array2[j]) d_array2[j] = j;
//array resize
//Array Deletion
d_array1.delete;
If an already allocated dynamic array is done ‘new[n]’ again, then it allocates n new
indices in memory and erase the previous ones.
Associative arrays allocate the storage only when it is used. So, useful when we
have to allocate a large chunk of memory.
Unlike in the dynamic array where we need to allocate memory before using it
An associative array implements a lookup table of the elements of its declared type.
` ` The data type to be used as an index serves as the lookup key and imposes an
ordering.
Use: An associative array implements a lookup table of the elements of its declared `
` type. The data type to be used as an index serves as the lookup key and imposes
an` ` ordering
Declaration Examples:-
Method Description
3.3 Queues
QUEUE DECLARATION-
Data_type queue_name[$];
--> byte queue_3[$:255]; // queue of byte (bounded queue with 256 entries)
--> bit queue_1[$]; // queue of bits (unbound queue)
QUEUE INITIALISATION-
Method Description
The only difference between working of bounded and unbounded queue is that when all
the elements of bounded queue are filled, then when we push_front(), the last element
will be popped back, everything will be shifted and the front element will store the given
value. Similarly, when we do push_back() in bounded queue, then the first element will
be popped out.
Evaluates all conditions at the same time, parallely and gives warning for 2 situations:
(i) >1 Conditions are true
//variables declaration
int a,b,c;
initial begin
//initialization
a=10;
b=20;
c=40;
end
endmodule
OUTPUT:
a is less than b
//variables declaration
int a,b,c;
initial begin
//initialization
a=50;
b=20;
c=40;
end
endmodule
OUTPUT:
If the first example if done with ‘priority’ keyword rather than ‘unique’, then it will also
work parallely and on top of that, the statement with priority if statement will be priorities
and executed without ay warning, now, even though 2 conditions are true
simultaneously.
//variables declaration
int a,b,c;
initial begin
//initialization
a=10;
b=20;
c=40;
end
endmodule
OUTPUT:
a is less than b
5. FOREACH LOOP
int a[4];
initial begin
$display("-----------------------------------------------------------------");
foreach(a[i]) a[i] = i;
$display("-----------------------------------------------------------------");
end
Endmodule
int a[3][2];
initial begin
$display("-----------------------------------------------------------------");
$display("-----------------------------------------------------------------");
end
endmodule
Break statement ends the whole loop, while continue statement ends 1 iteration of the
loop.
bit clk;
bit rst
begin
end
initial begin
#20 $finish;
end
Endmodule
Here, the always block will be executed at every negedge of clk or posedge of rst.
bit clk;
bit reset;
begin :block-1
//always block will be executed at every posedge and negedge of clk signal
begin :block-2
end :block-2
initial begin
#40 $finish;
end
initial begin
reset = 1;
#7 reset = 0;
#8 reset = 1;
#5 reset = 0;
end
endmodule
8. FORK-JOIN
8.1 fork-join
STEP-1 STEP-2 STEP-3
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
OUTPUT:
-----------------------------------------------------------------
0 Process-1 Started
0 Process-2 Startedt
5 Process-1 Finished
20 Process-2 Finished
20 Outside Fork-Join
-----------------------------------------------------------------
8.2 fork-join_any
--> 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
OUTPUT:
--------------------------------------------------------
0 Thread-1 Started
0 Thread-2 Started
5 Thread-1 Finished
5 Outside Fork-Join
-----------------------------------------------------------------
20 Process-2 Finished
8.3 fork-join_none
--> 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 Started");
#20;
$display($time,"\tProcess-2 Finished");
End
join_none
$display($time,"\tOutside Fork-Join_none");
$display("-----------------------------------------------------------------");
end
endmodule
If there is $finish statement after fork-join-none/any is $finish, then the parallel blocks
will not ` ` be executed and the process will be terminated before their execution.
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
causes the process to kill/terminate all the active processes started from fork blocks.
initial begin
$display("-----------------------------------------------------------------");
Fork
//Process-1
Begin
#5;
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
9. CLASSES
● Every class has its own default new() constructor and is used, until you explicitly
define a new() function
● A base class handle can point a child class handle, but a child class handle can’t
point a base class handle which is pointing to base class object. If that base
class handle already points to a child class object, then we can assign it to a new
child class handle using dynamic casting.
● Static Vars
○ All properties allocate fresh set of memory for each object. If we wish to
allocate the memory only once and all the objects access the same mem.,
we use ‘static’ keyword.
○ We can initialise static var without new() function of object handle.
○ Static metaphors work the same and cannot access non-static vars, only
static vars.
//static members
//class properties
bit write;
string pkt_type;
byte pkt_id;
//constructor
this.addr = addr;
this.data = data;
this.write = write;
this.pkt_type = pkt_type;
this.pkt_id = pkts_created;
width = 8;
pkts_created++;
endfunction
$display("--------------------------------------");
$display("--------------------------------------");
endfunction
$display("---------------------------------------------------------");
$display("---------------------------------------------------------");
endfunction
endclass
module sv_constructor;
packet pkt[3];
initial begin
pkt[0].display_packets_created();
pkt[1] = new(32'h10,32'hFF,1,"BAD_PKT");
pkt[1].display_packets_created();
//CLASS ASSIGNMENT
pkt[2] = pkt[1]; // can also be cone with another handle, other than the array
foreach(pkt[i]) begin
pkt[i].display();
end
end
Endmodule
-->Shallow Copy
Its Limitations:
→ Deep Copy
● Classes can also have parameters. These parameters can also have data types:
T address;
T data ;
function new();
address = 10;
data = 20;
endfunction
endclass
9.1 Inheritance
● A class can either inherit the whole class using ‘extends’ keyword, or just make a
class handle of the class to be inherited inside the child class.
endclass
endclass
module inheritence;
initial begin
child_class c = new();
c.addr = 10;
c.data = 20;
end
endmodule
endclass
endfunction
endclass
endfunction
endclass
base_class b_c[3];
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();
OUTPUT:
● Normally, when I call the display function through the child class’s object, the
display function of the child class will be executed, even if the base class also
has a display function.
● To also get the base class’s display function executed, we use ‘super’ keyword
inside the display function of the child class. Because at that time, it is not bound
to any object, so no early binding. So, using super keyword, we specify that the
base class’s function is to be called.
function display();
$display("Addr = %0d",addr);
Endfunction
Endclass
function display();
super.display();
$display("Data = %0d",data);
endfunction
endclass
module inheritance;
initial begin
child_class c=new();
c.addr = 10;
c.data = 20;
c.display();
end
endmodule
● Casting
○ Def: SystemVerilog casting means the conversion of one data type to
another datatype.
○ Uses: During value or variable assignment to a variable, it is required to
assign value or variable of the same data type. Some situations need
assignment of different data type
○ Static Casting
■ Not applicable to OOP
■ Here, the data type converted to is fixed at compile-time only.
■ Uses a cast(‘) operator with the val./expression/var enclosed in
brackets - ()
■ Int_a = int’(2.1 * 3.2);
○ Dynamic Casting
■ Dynamic casting is used to, safely cast a super-class pointer
(reference) into a subclass pointer (reference) in a class hierarchy
■ Compatibility checked during runtime.
■ Done using ‘$cast(destination, source)’ syntax
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;wait I
c.data = 20;
$cast(c1,p); //with the use of $cast, type chek will occur during
runtime
c1.display();
end
endmodule
● Access Modifiers(AM)
○ Has only 2, cuz no AM means public.
○ The keywords, local and protected need to be given individually to every
member/method like this:
○ Protected members can be accessed by that own class and its derived
class. While, local members can only be accessed by that own class.
● ABstract CLass: An abstract class can be derived/inherited, but not instantiated.
endclass
Endfunction
endclass
module virtual_class;
initial begin
extended_packet p;
p = new();
p.addr = 10;
p.display();
end
Endmodule
● SImilarly, we can make virtual methods also. SO when a base class handle
pointing to a derived class object calls display function(present in both classes),
will execute the display function of derived class, if base class’s function is virtual.
● External Functions: A method part of class, but defined outside the class using
scope resolution operator, although declared inside the class only, using ‘extern’
keyword.
Endclass
Endfunction
module extern_method;
initial begin
packet p;
p = new();
p.addr = 10;
p.data = 20;
p.display();
end
Endmodule
//class-1
class c1;
endclass
//class-2
class c2;
c1 c;
Endclass
module typedef_class;
initial begin
c1 class1;
c2 class2;
$display("Inside typedef_class");
End
Endmodule
An event is a way to synchronize two or more different processes. One process waits
for the` ` ` event to happen while another process triggers the event.
The declaration of an event is done just like a datatype, outside the procedural
blocks.
--> @eventA;
--> wait(eventA.triggered);
Both are valid ways to wait until the event is triggered. This is done in another
procedural block
Example Code:
10.2 Semaphores
Due to the mutually exclusivity, Semaphores are best for basic synchronisation and
access control to shared resources.
A Semaphore is a built-in class, so is used just like another class, by making its object
and calling its functions.
Name Description
function new (int keyCount = 0); Specifies number of keys initially allocated to the semaphore bucket
function void put (int keyCount = 1); Specifies the number of keys being returned to the semaphore
task get (int keyCount = 1); Specifies the number of keys to obtain from the semaphore
function int try_get (int keyCount = 1); Specifies the required number of keys to obtain from the semaphore
join_none
end
key.get(1);
endtask
endtask
personA ();
getRoom (1);
endtask
// This person tries to get the room after 5 time units and
puts it back after // 10 time units
#5 getRoom (2);
endtask
endmodule
OUTPUT:
[0] Trying to get a room for id[1] ...
10.3 Mailbox
1. Mailbox also uses semaphores to synchronize the push & pop.
2. We can’t access a specific index within the mailbox queue.
A SystemVerilog mailbox is typically used when there are multiple threads running in
parallel… ` and want to share data for which a certain level of determinism is required.
module tb; // Create a new mailbox that can hold utmost 2 items
mailbox mbx = new(2);
initial begin
#1 mbx.put (i);
end
end
initial begin
forever begin
int idx;
#2 mbx.get (idx);
end
end
endmodule
Function Description
Returns a mailbox handle, bound > 0 represents size of
function new (int bound = 0);
mailbox queue
task get (ref singular Blocking method until it can retrieve one message from
message); the mailbox, if empty blocks the process
function int try_get (ref Non-blocking method which tries to get one message
singular message); from the mailbox, returns 0 if empty
task peek (ref singular Copies one message from the mailbox without
message); removing the message from the mailbox queue.
function int try_peek (ref Tries to copy one message from the mailbox without
singular message); removing the message from queue
By default, mailbox is typeless i.e it can send and receive objects of mixed datatypes.
This option came handy, but it led to mismatches during simulation time. So, mailbox
can also be parameterized with a data type name and that mailbox will only get and put
a specific fixed data type. This is called a parameterized mailbox. Before using it, its
handle is made globally:
And then, wherever needed, the handle name, s_mbox is instantiated instead of
mailbox(done previously).
11. INTERFACE
An interface is a set of signals or ports that can be used to connect between different
modules. Specifically, they are a set of signals, based on the ports of DUT. They directly
connect to DUT ports. And then their bi-directional ports are connected to other
modules, mainly testbench modules, so that they’re indirectly connected to the DUT.
Advantage: This indirect connection to DUT helps keep the testbench environment
immune to the change of pinsin DUT, as the change has to be done only in the
interface.
Syntax:-
logic enable;
endinterface
else
busIf.data <= 0;
module tb_top;
initial begin
busIf.enable <= 0;
#100
$finish;
end
endmodule
The pins are declared as ‘logic’. This data type lets you drive signals via assign
statements and store value i.e it acts as both wire & reg. This comes handy to connect
the reg of tb and wire of DUT.
In order to define the direction of ports, the signals’ direction is defined from the tb and
DUT’s POV.
modport TB (input data, clk, output enable);
Modports also help to select a specific set of interface signals that can be accessed by
a specifc module.
Here’s an example:
logic wr_en_vi;
logic rd_en_vi;
endinterface
//TESTBENCH CODE
//Interface connection
//DUT connection
.clk(i_intf.clk),
.clr(i_intf.clr),
.addr(i_intf.addr_vi),
.wr_en(i_intf.wr_en_vi),
.rd_en(i_intf.rd_en_vi),
.wdata(i_intf.wdata_vi),
.rdata(i_intf.rdata_vi)
);
In a level-1 testbench, where we had to give stimulus input and had to sychronize the
inputs manually and that was done just before the posedge or the event where the
value reflects at the output. Clocking block automates the process, by acknowledging
the triggering event and then, we just change value at that event only through some
syntax.
Now, our DUT is also a sequential circuit only. So, it also needs sequentially at
posedge. So, this clocking block that could have been used for a level-1 testbench, is
also specified inside the interface as a property. This clocking block is used by tb and
DUT to drive/sample values to interface. Then, the interface handles the values
sequentially.
Also, this approach also removes the problem of glitches in sending stimulus. And
without this, the asynchronous flow of data could lead to race conditions.
Interface:
Default input #1 output #2 //input sampled 1 ns before the posedge & output is
driven 2 ns ` after the posedge
output data_out;
endclocking
endinterface
Testbench:
logic clk;
initial begin
clk = 0;
end
initial begin
b.cb.enable <= 0;
b.cb.write <= 0;
b.cb.read <= 0;
b.cb.data_in <= 0;
b.cb.enable <= 1;
b.cb.write <= 1;
b.cb.data_in <= 1;
b.cb.write <= 0;
b.cb.read <= 1;
@(b.cb);
end
endmodule
A clocking block can be multiple in a module, but 1 clock can only have 1 clocking
block.
Here, the clocking block, cb is used through the instance made of interface as event
and values are changed accordingly.
DUT:
if (b.enable) begin
end
end
endmodule
DUT also accesses the clocking block like tb, receives and sends data to the
interface synchronously.
10. Randomisation
To randomise in a class, we first declare a var as ‘rand’ inside the packet class:
As some inputs might be invalid for the DUT, if the var is randomized from 0 to infinite,
So, to make the randomisation within a user-defined bracket, we call it constraints and
the process if constrained randomisation:
And then, the object of this packet class is randomised using a built-in function, inside a
generator class, or a module.
--> pkt.randomize();
Note that the whole object is randomized, but only those vars where we have written
‘rand’/’randc’ will be randomized.
Now, for selective randomisation i.e to randomize one var of the object, but not the
others, we use rand_mode() function.
--> pkt.var1.rand_mode(0);
pkt.randomize();
How to use rand_mode(1):- This activates a particular signal, but we still have to use
packet.randomize() function. Also, if I have 3 signals var1, var2, var3 and we only want
to randomize var1, we can’t directly activate var1 using rand_mode(1). Instead, we
deactivate var2 & var3 and then randomize. rand_mode(1) is just used to reactivate the
already deactivated signal. For eg. if we disable randomisation for var2 & var3 and then
do randomisation, then if again randomize the signal but this time for var1 & var2, so I
activate var2’s randomisation. Here’s the code for the example:
--> pkt.var2.rand_mode(0);
pkt.var3.rand_mode(0);
pkt.randomize();
$display(....);
pkt.var2.rand_mode(1);
pkt.randomize();
$display(....);
These methods will be called before & after the randomization respectively.
bit tmp_wr_rd;
//pre randomization function - disabling randomization of
addr,
if(tmp_wr_rd==1) addr.rand_mode(0);
else addr.rand_mode(1);
endfunction
tmp_wr_rd = wr_rd;
endfunction
endclass
module rand_methods;
initial begin
packet pkt;
pkt = new();
repeat(4) pkt.randomize();
end
endmodule
Constraints
Now, to perform constrained randomisation(the one done with randc vars) we define
constraint blocks, which can define the range of randomisation in several ways.
...
<condition/expression>; }
The constraint block is defined in the packet class only, after the rand vars
declaration.
class packet;
constraint addr_range;
endclass
endclass
module const_inhe;
initial begin
packet pkt1;
packet2 pkt2;
pkt1 = new();
pkt2 = new();
$display("------------------------------------");
repeat(5) begin
pkt1.randomize();
end
$display("------------------------------------");
repeat(5) begin
pkt2.randomize();
$display("\tpkt2:: addr = %0d",pkt2.addr);
end
$display("------------------------------------");
end
endmodule
After specifying a range, every element has an equal probability to occur. Weighted
distribution can also be added to specify the occurrence of a particular part of the range.
● := operator - assigns the specified weight to the item, or if the item is a range,
specified weight to every value in the range.
● /= 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.
Observational Note:- If I give dist{[0:3] := 1}; then every iteration will result in the
same set of values chosen/randomized. And if we do dist{[0:3] :/ 4}; in place of
that, then as the individual value’s weight assigned is the same, both will give the
same set of values as output.
class packet;
endclass
module constr_implication;
initial begin
packet pkt;
pkt = new();
$display("------------------------------------");
repeat(4) begin
pkt.randomize();
$display("\taddr_range = %s addr =
%0d",pkt.addr_range,pkt.addr);
end
$display("------------------------------------");
end
endmodule
Instead of having multiple constraints, we can also give if else conditions for the string
parameter in 1 constraint only.
string addr_range;
addr < 8;
addr > 8;
addr == 8;
else
addr != 8;
endclass
module constr_if_else;
initial begin
packet pkt;
pkt = new();
pkt.addr_range = "small";
$display("------------------------------------");
repeat(3) begin
pkt.randomize();
$display("\taddr_range = %s addr =
%0d",pkt.addr_range,pkt.addr);
end
$display("------------------------------------");
pkt.addr_range = "high";
$display("------------------------------------");
repeat(3) begin
pkt.randomize();
$display("\taddr_range = %s addr =
%0d",pkt.addr_range,pkt.addr);
end
$display("------------------------------------");
end
endmodule
If the rand signal is an unbound array, then we can also give a constraint for each index
using foreach loop inside the constraint block.
if(i == 0)
addr[i] inside{4,8,12,16};
else
addr[i] inside{5,10,15,20};}
endclass
module constr_iteration;
initial begin
packet pkt;
pkt = new();
$display("------------------------------------");
repeat(2) begin
pkt.randomize();
$display("------------------------------------");
end
end
endmodule
This is the only way to iteratively specify constraint for each index. Otherwise, if we only
want to specify a constraint for 1 index, then we can just specify the index in the
constraint without foreach loop.
As there can be multiple constraints in one class, we specify the constraint name we
have set the mode for. Just like in rand_mode().
Static constraint:- Just like other class properties, a constraint of class can also be
made static. And with that, each handle can access the same constraint. Due to its
rules, the features of static constraint is:-
1. Fixed nature: The conditions in static constraints are constant and do not
depend on runtime values or dynamic changes.
2. Deterministic: They always apply in the same way regardless of any external
factors or randomization seeds.
3. Simple and efficient: Since the constraints are static, they are computationally
simpler and lead to predictable behavior during randomization.
packet pkt2;
--> pkt1.randomize();
$display();
pkt2.randomize();
$display();
pkt2.addr = 146
Pkt2.addr = 5
Inline Constraints
Purpose: Whenever a function is called, the arguments passed are copied, pushed and
then popped from a stack, then operation’s return value is passed again. This sending
and sampling causes loss of performance. So, the inline functions perform its operation
at the place of calling only, hence no need to access vars from stack. This enhances
performance and is only effective when there are multiple arguments and a return value.
But, in case of inline constraints, we code it that way, literally at the line where we do
randomisation. The packet class can also have its constraints, we still can define an
inline constraint, where we randomize the packet class. In that case, both the
constraints will be considered.
endclass
module inline_constr;
initial begin
packet pkt;
pkt = new();
repeat(2) begin
end
end
endmodule
addr = 8 data = 5
CONCLUSION: Here, both the constraints can be followed simultaneously, so its done
without an error.
endclass
module inline_constr;
initial begin
packet pkt;
pkt = new();
repeat(2) begin
$display("\taddr = %0d",pkt.addr);
end
end
endmodule
testbench.sv, 15
Please check the inconsistent constraints being printed above and rewrite them.
addr = 0
FUNCTIONS IN CONSTRAINTS: The constraint can also equate the concerned var
with a function’s return value.
→ class packet;
start_addr = 0;
End
start_addr = e_addr - 4;
return start_addr;
Endfunction
endclass
module m;
packet pkt;
initial begin
pkt = new();
repeat(48) begin
End
end
Endmodule
Then in that case, there is no error, even at runtime and whenever end address is
randomized to, 4, the ‘csa’ constraint automatically adjusts to a value between 1 to 4,
instead of 0 to 4 and doesn’t give any error.
Soft Constraints: Now, during such corner cases, it is important to specify what
constraint to override and continue the randomisation. To specify a constraint block that
can be overridden, we use ‘soft’ keyword.
endclass
module soft_constr;
initial begin
packet pkt;
pkt = new();
repeat(2) begin
$display("\taddr = %0d",pkt.addr);
End
end
endmodule
output: addr = 1
addr = 3
So, the constraint in class, which is declared as soft, is overriden by the inline
constraint.
Unique Constraint:
constraint array_c {unique {array};} //all elements’ value will never be same
$display("var_1 = %d",var_1);
$display("var_2 = %d",var_2);
$display("var_3 = %d",var_3);
$display("array = %p",array); //%p for a complex data type like struct, array,
class etc.
endfunction
endclass
module unique_elements_randomization;
unique_elements pkt;
initial begin
pkt = new();
pkt.randomize();
pkt.display();
end
Endmodule
Note: A class’ handle(packet pkt) and class’ object(pkt = new()) both can be made,
either in or out of procedural blocks.
Eg.-
else b == 0; }
constraint a_value { a == b + c; }
endclass
module bidirectional_constr;
initial begin
packet pkt;
pkt = new();
repeat(5) begin
pkt.randomize();
end
end
endmodule
Here, value of a has to be b+c and b&c also has to have their sum = a, when
randomized at once. This is why they’re bidirectional.
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.
rand bit a;
endclass
module inline_constr;
initial begin
packet pkt;
pkt = new();
repeat(10) begin
pkt.randomize();
end
end
endmodule
Now here, a will be randomized, and based on that, b will be randomised. This could
also have been opposite. That we way ‘solve b before a’ in that case, b will be
randomized first, then if b = 0, a will be given 1 and if b != 0, then a = 1.
addr2 = $urandom(2);
12. Assertions
Assertions are primarily used to validate the behavior of a design. Warnings or errors
are generated on the failure of a specific condition or sequence of events.
Since the assertion is a statement that something must be true, the failure of an
assertion shall have a severity associated with it. By default, the severity of an
assertion failure is an error.
Severity levels can be specified by including one of the following severity system tasks
in the fail statement:
1. $fatal is a run-time fatal.Its a system error and causes program to crash/end right
there.
2. $error is a run-time error. It still allows the program to run.
3. $warning is a run-time warning, which can be suppressed in a tool-specific
manner.
4. $info indicates that the assertion failure carries no specific severity.
If an assertion fails and no else clause is specified, the tool shall, by default calls
$error.
--> Example
//Only With Fail statement; Multiple statements in Faile condition and Fail
verbosity fatal;
assert(expression)
else begin
…….
…….
End