-
Notifications
You must be signed in to change notification settings - Fork 14
02.Basics
The UVM library defines a set of base classes and utilities that facilitate the design of modular, scalable, reusable verification environments.
Fig-1: Base classes in UVM
This doesn't have any purpose, but servers as base class for all UVM classes.
All components and transactions derive from uvm_object, which defines an interface of core class-based operations: create, copy, compare, print, sprint, record, etc. It also defines interfaces for instance identification (name, type name, unique id).
Code Snippet
`include "uvm_macros.svh"
import uvm_pkg::*;
typedef enum{RED, GREEN, BLUE} color_type;
class my_object extends uvm_object;
color_type c;
function new(string name="my_object");
super.new(name);
`uvm_info("NEW", "creating my_object", UVM_LOW)
endfunction
function void display();
for(int i = 0;i<3;i++)begin
`uvm_info("my_object created", $sformatf("colour: %0s", c.name()), UVM_LOW)
`uvm_info("ID", $sformatf("id: %0d",c), UVM_LOW)
c=c.next();
end
endfunction:display
endclass:my_object
my_object mb;
module tb();
initial begin
mb = new();
mb.display();
end
endmodule
Output
Fig-2: Output of UVM_object
GitHub Code Link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/obj_class/obj_ut/obj_ut.sv
GitHub log file link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/obj_class/obj_ut/obj_ut.sv
The uvm_report_object provides an interface to the UVM reporting facility. Through this interface, components issue the various messages that occur during simulation. Users can configure what actions are taken and what file(s) are output for individual messages from a particular component or for all messages from all components in the environment. Defaults are applied where there is no explicit configuration.
Code Snippet
`include "uvm_macros.svh"
import uvm_pkg::*;
class my_object extends uvm_object;
function new(string name = "my_object");
super.new(name);
`uvm_info("NEW", "creating my_object class", UVM_LOW)
endfunction
function void display();
`uvm_info("1", "hi_high", UVM_HIGH)
`uvm_info("2", "hello_none", UVM_NONE)
`uvm_info("3", "ciao_deb", UVM_DEBUG)
`uvm_info("4", "hey_med", UVM_MEDIUM)
`uvm_info("5", "hola_low", UVM_LOW)
`uvm_info("6", "bonjour_full", UVM_FULL)
endfunction:display
endclass:my_object
module tb();
my_object mb;
initial begin
mb = new;
mb.display();
end
endmodule
Output
Fig-3: Output of UVM_report_object
GitHub Code Link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/obj_class/obj_rep/obj_rep.sv
GitHub log file link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/obj_class/obj_rep/obj_rep_op.log
The uvm_component class is the root base class for all UVM components. Components are quasi-static objects that exist throughout simulation. This allows them to establish structural hierarchy much like modules and program blocks.
Hierarchy | Description |
---|---|
Phasing | defines a phased test flow that all components follow, with a group of standard phase methods and an API for custom phases and multiple independent phasing domains to mirror DUT behavior |
Configuration | provides methods for configuring component topology and other parameters ahead of and during component construction. |
Reporting | provides a convenience interface to the uvm_report_handler. All messages, warnings, and errors are processed through this interface. |
Transaction recording | provides methods for recording the transactions produced or consumed by the component to a transaction database (vendor specific). |
Factory | provides a convenience interface to the uvm_factory. The factory is used to create new components and other objects based on type-wide and instance-specific configuration. |
The uvm_root class is special uvm_component that serves as the top-level component for all UVM components, provides phasing control for all UVM components, and other global services.
Code Snippet
`include "uvm_macros.svh"
import uvm_pkg::*;
class my_comp extends uvm_component;
bit [3:0]a;
function new(string name="my_comp", uvm_component parent);
super.new(name, null);
`uvm_info("NEW", "creating my_comp", UVM_LOW)
endfunction
function void display();
for(int i = 0;i<3;i++)
begin
`uvm_info("Randamaizing", $sformatf("a: %0d", a), UVM_LOW)
`uvm_info("STAT", $sformatf("Randamaize successful"), UVM_LOW)
a = $urandom();
end
endfunction:display
endclass:my_comp
module tb();
my_comp cp;
initial begin
cp= new("cp",null);
cp.display();
end
endmodule
Output
Fig-4: Output of UVM_root
GitHub Code Link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/root_class/root.sv
GitHub log file link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/root_class/root_op.log
The uvm_transaction is the root base class for UVM transactions, which, unlike uvm_components, are transient in nature. It extends uvm_object to include a timing and recording interface.
Code Snippet
`include "uvm_macros.svh"
import uvm_pkg::*;
class my_tran extends uvm_transaction;
randc bit [3:0] a;
constraint c {a>9;}
function new(string name = "my_tran");
super.new(name);
endfunction
task display();
#1 `uvm_info("Randomize", $sformatf("[%0t]a = %0d",$time,a),UVM_LOW)
endtask
endclass
module tb;
my_tran t;
initial begin
t=new();
repeat(3)
begin
void'(t.randomize());
t.display();
end
end
endmodule
Output
Fig-5: Output of UVM_transaction
GitHub Code Link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/transcation/tran.sv
GitHub log file link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/transcation/tran_op.log
The base class for user-defined sequence items and also the base class for the uvm_sequence class. The uvm_sequence_item class provides the basic functionality for objects, both sequence items and sequences, to operate in the sequence mechanism.
Code Snippet
`include "uvm_macros.svh"
import uvm_pkg::*;
class my_seq_item extends uvm_sequence_item;
rand bit [3:0] a;
constraint c {a>9;}
function new(string name = "my_seq_item");
super.new(name);
endfunction
function void display();
`uvm_info("Randomize", $sformatf("a = %0d",a),UVM_LOW)
endfunction
endclass
module tb;
my_seq_item s;
initial begin
s=new();
repeat(3)
begin
void'(s.randomize());
s.display();
end
end
endmodule
Output
Fig-6: Output of UVM_sequence_item
GitHub Code Link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/seq_item/seq.sv
GitHub log file link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/seq_item/seq_op.log
UVM sequence is a container that holds data items (uvm_sequence_items) which are sent to the driver via the sequencer.
Code Snippet
`include "uvm_macros.svh"
import uvm_pkg::*;
class my_seq_item extends uvm_sequence_item;
rand bit [3:0] a;
constraint c {a>9;}
function new(string name = "my_seq_item");
super.new(name);
endfunction
function void display();
`uvm_info("Randomize", $sformatf("a = %0d",a),UVM_LOW)
endfunction
endclass
module tb;
my_seq_item s;
initial begin
s=new();
repeat(3)
begin
void'(s.randomize());
s.display();
end
end
endmodule
Output
Fig-7: Output of UVM_sequence
GitHub Code Link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/seq/seq.sv GitHub log file link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/base_classes/seq/seq_op.log
The term ‘macro’ refers to the substitution of a line or a few lines of text code. The directive “define” creates a macro for substitution code. Once the macro is defined, it can be used anywhere in a compilation unit scope, wherever required. It can be called by the (`) character followed by the macro name. A macro can be defined with arguments. Arguments are useful to customize the macro to use widely, like a function task. Such macro arguments can be defined with default values so that if a DV engineer won’t pass any specific value, the macro substitutes the default value.
Example for the macro in SV
`define calculate(val1,val2,result,expression) \
result = valu1 expression val2; \
$display("result is %d",result);
// the new line is preceded by a backslash
module macro;
int a=15,b=7;
int c;
initial begin
calculate(a,b,c,+) //it will expand to calculate and display result
end
endmodule
In UVM, To reduce coding overhead, the UVM library provides a set of macro declarations. These macros can be used to combine the definition of multiple things into one step (e.g., declare a field and a task, when both are required to exist at the same time).
UVM uses the concept of a factory, where all objects are registered with it so that it can return an object of the requested type when required. The utility macros help to register each object with the factory. It is required to be used inside every user-defined class that is derived from uvm_object which includes all types of sequence items and components.
Object Utility
All classes derived directly from uvm_object or uvm_transaction require them to be registered using `uvm_object_utils macro. Note that it is mandatory for the new function to be explicitly defined for every class, and takes the name of the class instance as an argument.
class ABC extends uvm_object;
// Register this user defined class with the factory
`uvm_object_utils(ABC)
function new(string="ABC");
super.new(name);
endfunction
endclass
Component Utility
All classes derived directly or indirectly from uvm_component require them to be registered with the factory using `uvm_component_utils macro. Note that it is mandatory for the new function to be explicitly defined for every class derived directly or indirectly from uvm_component and takes the name of the class instance and a handle to the parent class where this object is instantiated.
class DEF extends uvm_component;
// Class derived from uvm_component, register with factory
`uvm_component_utils(DEF)
function new(string="DEF",uvm_component parent=null);
super.new(name,parent);
endfunction
endclass
Creation of class object
It is recommended that all class objects are created by calling the type_id::create() method which is already defined using the macro `m_uvm_object_create_func. This makes any child class object to be created and returned using factory mechanism and promotes testbench flexibility and reusability.
class ABC extends uvm_object;
`uvm_object_utils(ABC)
function new(string name ="ABC");
super.new(name);
endfunction
endclass
class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name ="base_test",uvm_component parent=null);
super.new(name,parent);
endfunction
virtual function voidbuild_phase(uvm_phase phase);
// An object of class "ABC" is instantiated in UVM by calling// its "create()"
function which has been defined using a macroas shown above
ABC abc=ABC::type_id::create("abc_inst");
endfunction
endclass
`uvm_field_* macros that were used between *_begin and *_end utility macros are basically called field macros since they operate on class properties and provide automatic implementations of core methods like copy, compare and print. This helps the user save some time from implementing custom do_copy, do_compare and do_print functions for each and every class. However, since these macros are expanded into general code, it may impact simulation performance and are generally not recommended.
Usage Example
class ABC extends uvm_object;
randbit[15:0] m_addr;
randbit[15:0] m_data;
`uvm_object_utils_begin(ABC)
`uvm_field_int(m_addr,UVM_DEAFULT)
`uvm_field_int(m_data,UVM_DEAFULT)
`uvm_object_utils_end
function new(string name="ABC");
super.new(name);
endfunction
endclass
The uvm_field_*
corresponding to the data type of each variable should be used. For example, variables of type int, bit, byte should use `uvm_field_int, while variables of type string should use uvm_field_string and so on. The macro accepts atleast two arguments: ARG and FLAG.
Argument | Description |
---|---|
ARG | Name of the variable, whose type should be appropriate for the macro that is used |
FLAG | When set to something other than UVM_DEFAULT or UVM_ALL_ON, it specifies which data method implementations will not be included. For example, if FLAG is set to NO_COPY, everything else will be implemented for the variable except copy. |
The field FLAG can take the following values and multiple values can be OR'ed together. A combination of these flags determine the kind of operations that are allowed to be done on the given variable.
Flag | Description |
---|---|
UVM_ALL_ON | All operations are turned on |
UVM_DEFAULT | Enables all operations and equivalent to UVM_ALL_ON |
UVM_NOCOPY | Do not copy the given variable |
UVM_NOCOMPARE | Do not compare the given variable |
UVM_NOPRINT | Do not print the given variable |
UVM_NOPACK | Do not pack or unpack the given variable |
UVM_REFERENCE | Operate only on handles, i.e. for object types, do not do deep copy, etc |
Along with control on operations, `uvm_field_* macros also provide some control on the radix of the given variable which can take the following values. This value can be used with the operational flags listed in the table above. UVM_HEX is the default radix if none is specified.
Radix | Description |
---|---|
UVM_BIN | Print/record the given variable in binary format |
UVM_DEC | Print/record the given variable in decimal format |
UVM_HEX | Print/record the given variable in hexadecimal format |
UVM_OCT | Print/record the given variable in octal format |
UVM_STRING | Print/record the given variable in string format |
UVM_TIME | Print/record the given variable in time format |
UVM Object is basically a class in which functions to print, copy and compare objects of the same class are defined.
So, it is important to import uvm_object class and also extend from this class to use these methods in our test bench.
Fig-8: UVM_object
This function prints the content i.e., name, size, datatype and value of the variable in table format by default.
Here is a basic example to know how print function of uvm_object works.
Code Snippet
class example extends uvm_object;
int a[3];
string b;
bit [2:0]c;
`uvm_object_utils_begin(example)
`uvm_field_sarray_int(a, UVM_DEFAULT)
`uvm_field_string(b, UVM_DEFAULT)
`uvm_field_int(c, UVM_DEFAULT)
`uvm_object_utils_end
function new(string name="example");
super.new(name);
this.a={1,2,3};
this.b="HELLO";
this.c=4;
endfunction
endclass
Fig-9: Creating a class which extends uvm_object
TB Code:
class test extends uvm_test;
`uvm_component_utils(test)
function new(string name="test", uvm_component parent=null);
super.new(name,parent);
endfunction
function void build;
example Ex = example::type_id::create("Ex");
Ex.print();
endfunction
endclass
module uvm_object_print_tb;
test t;
initial begin
t=test::type_id::create("t",null);
t.build();
end
endmodule
Fig-10: Creating a test class to print contents of above class
Output
Fig-11: Output of UVM_object_print
GitHub Code Link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/obj_print/uvm_object_print.sv
GitHub Test Bench code link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/obj_print/uvm_object_print_tb.sv
Instead of using macros in the print() function to register the class properties into factory we can explicitly write a do_print() function in which we use the object of uvm_printer to print the required contents of the class.
Here, a new function do_print() is added to the code.
Code Snippet
class example extends uvm_object;
string b;
bit [2:0]c;
`uvm_object_utils(example)
function new(string name="example");
super.new(name);
this.b="HELLO";
this.c=4;
endfunction
function void do_print(uvm_printer printer);
super.do_print(printer);
printer.print_string("b",b);
printer.print_int("c",c,$bits(c));
endfunction
endclass
TB code:-
class test extends uvm_test;
`uvm_component_utils(test)
function new(string name="test", uvm_component parent=null);
super.new(name,parent);
endfunction
function void build;
example Ex = example::type_id::create("Ex");
Ex.print();
endfunction
endclass
module uvm_object_print_do_tb;
test t;
initial begin
t=test::type_id::create("t",null);
t.build();
end
endmodule
Fig-12: do_print()
do_print() function is by default called by print() function.
Output
Fig-13: Output of do_print()
GitHub Code Link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/obj_print/uvm_object_do_print.sv
GitHub Test Bench code link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/obj_print/uvm_object_print_do_tb.sv
It returns the formatted content in string format which can further be stored in a variable of string datatype.
Code Snippet
TB Code:
class test extends uvm_test;
`uvm_component_utils(test)
function new(string name="test", uvm_component parent=null);
super.new(name,parent);
endfunction
function void build;
example Ex = example::type_id::create("Ex");
string s=Ex.sprint();
$display(s);
endfunction
endclass
module uvm_object_sprint_tb;
test t;
initial begin
t=test::type_id::create("t",null);
t.build();
end
endmodule
Output
Fig-14: Output of sprint()
GitHub Code Link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/obj_print/uvm_object_print.sv
GitHub Test Bench code link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/obj_print/uvm_object_sprint_tb.sv
NOTE: Here in the above 3 functions- print() and do_print() are of void type so they will not return any thing and sprint() is of string type. Also do_print() is called by using print() and print() function called by sprint() internally for printing the contents.
This function is written explicitly which returns a string instead of printing in a predefined format. And we can print the contents in a single line if wanted.
Code Snippet
class example extends uvm_object;
string b;
bit [2:0]c;
`uvm_object_utils(example)
function new(string name="example");
super.new(name);
this.b="HELLO";
this.c=4;
endfunction
function string convert2string();
string s=" ";
$sformat(s,"%s b = %s",s,b);
$sformat(s,"%s c = %0h",s,c);
return s;
endfunction
endclass
TB code:-
import uvm_pkg::*;
`include "uvm_object_conv2str.sv"
`include "uvm_macros.svh"
class test extends uvm_test;
`uvm_component_utils(test)
function new(string name="test", uvm_component parent=null);
super.new(name,parent);
endfunction
function void build;
example Ex = example::type_id::create("Ex");
`uvm_info(get_type_name(),$sformatf("Contents:%s",Ex.convert2string()),UVM_LOW)
endfunction
endclass
module uvm_object_con2str_tb;
test t;
initial begin
t=test::type_id::create("t",null);
t.build();
end
endmodule
Output
Fig-15: Output of convert2string()
GitHub Code Link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/obj_print/uvm_object_con2str.sv
GitHub Test Bench code link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/obj_print/uvm_object_con2str_tb.sv
UVM object has copy method to copy the content of one object to another object. This copy method is same as deep copy in systemverilog.
syntax:
Destination.copy(source);
code snippet:
class mem_seq_item extends uvm_sequence_item;
rand bit [3:0]a;
rand bit b;
rand bit c;
//Utility and Field macros,
`uvm_object_utils_begin(mem_seq_item)
`uvm_field_int(a,UVM_ALL_ON)
`uvm_field_int(b,UVM_ALL_ON)
`uvm_field_int(c,UVM_ALL_ON)
`uvm_object_utils_end
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass
//-------------------------------------------------------------------------
//Simple TestBench to access sequence item
//-------------------------------------------------------------------------
module seq_item_tb;
//instance
mem_seq_item seq_item_0;
mem_seq_item seq_item_1;
initial begin
//create method
seq_item_0 = mem_seq_item::type_id::create("seq_item_0");
seq_item_1 = mem_seq_item::type_id::create("seq_item_1");
seq_item_0.randomize(); //randomizing the seq_item
seq_item_0.print(); //printing the seq_item_0
//copy method
seq_item_1.copy(seq_item_0); //copy seq_item_0 to seq_item_1
seq_item_1.print(); //printing the seq_item_1
end
endmodule
Output:
Fig-16: Output of Object_copy
By using the method "seq_item_1.copy(seq_item_0); " the contents of seq_item_0 are copied in to seq_item_1.
object clone method is same as object copy but the only difference is that clone method create an object and returns the copy of the object so, that clone method saves the time of creating another object before copying. simply we can say that clone method is create method followed by copy method .In clone method $cast is used because we are assigning a parent class handle to a derived class handle.
syntax
$cast(destination,source.clone());
code snippet:
class mem_seq_item extends uvm_sequence_item;
rand bit [3:0]a;
rand bit b;
rand bit c;
//Utility and Field macros,
`uvm_object_utils_begin(mem_seq_item)
`uvm_field_int(a,UVM_ALL_ON)
`uvm_field_int(b,UVM_ALL_ON)
`uvm_field_int(c,UVM_ALL_ON)
`uvm_object_utils_end
//Constructor
function new(string name = "mem_seq_item");
super.new(name);
endfunction
endclass
//-------------------------------------------------------------------------
//Simple TestBench to access sequence item
//-------------------------------------------------------------------------
module seq_item_tb;
//instance
mem_seq_item seq_item_0;
mem_seq_item seq_item_1;
initial begin
//creating seq_item_0
seq_item_0 = mem_seq_item::type_id::create("seq_item_0");
seq_item_0.randomize(); //randomizing the seq_item
seq_item_0.print(); //printing the seq_item_0
//clone method
$cast(seq_item_1,seq_item_0.clone()); cloning seq_item_0 to seq_item_1
seq_item_1.print(); //printing the seq_item_1
end
endmodule
In the code we created object seq_item_0 but object seq_item_1 is not created ,even though the clone method creates the object seq_item_1 and copies the content of seq_item_0 to seq_item_1
** Output:**
Fig-17: Output of UVM_Object_clone
In the output we an see that by using "$cast(seq_item_1,seq_item_0.clone());" the clone method creates the object seq_item_1 and copies the content of seq_item_0 to seq_item_1.
uvm_object
is a base_class which have many common functions like print,copy and compare in it. These functions are available to all the child classes of uvm_object base_class.
Deep compares members of this data object with those of the object provided in the rhs (right-hand side) argument, returning 1 on a match, 0 othewise.
The compare method is not virtual and should not be overloaded in derived classes.
This is the function general syntax for a compare() method in base_class uvm_object
function bit compare(uvm_object rhs, uvm_comparer comparer=null);
The optional comparer
argument specifies the comparison policy. It allows you to control some aspects of the comparison operation. It also stores the results of the comparison, such as field-by-field miscompare information and the total number of miscompares.
If a compare policy is not provided, then the global uvm_default_comparer
policy is used.
Syntax:
class_name object_1,object_2;
object_1.compare(object_2);
code snippet:
`include "uvm_macros.svh"
import uvm_pkg::*;
class object extends uvm_object;
rand bit [3:0]a;
rand bit [2:0]b;
`uvm_object_utils_begin(object)
`uvm_field_int(a,UVM_DEFAULT)
`uvm_field_int(b,UVM_DEFAULT)
`uvm_object_utils_end
function new(string name = "object");
super.new(name);
endfunction:new
endclass:object
class sample;
object obj_1,obj_2;
function void creating();
obj_1 = object::type_id::create("obj_1");
obj_2 = object::type_id::create("obj_2");
endfunction:creating
function void randomization();
void'(obj_1.randomize() with {a inside {1,2,3};});
void'(obj_2.randomize() with {a inside {4,5,6};});
endfunction:randomization
function void comparing();
if(obj_1.compare(obj_2))
begin
`uvm_info("v","obj_1 matching with obj_2",UVM_LOW)
obj_1.print();
obj_2.print();
end
else
begin
`uvm_error("v","obj_1 is not matching with obj_2")
obj_1.print();
obj_2.print();
end
endfunction:comparing
function void copying();
obj_2.copy(obj_1);
endfunction:copying
endclass:sample
module top;
sample s = new;
initial begin:BEGIN_I
s.creating();
repeat(3)
begin:BEGIN_LOOP
s.randomization();
s.comparing();
end:BEGIN_LOOP
s.copying();
s.comparing();
end:BEGIN_I
endmodule:top
output:
In the below Fig-18 , we are doing comparision between two objects of same class i.e., obj_1 and obj_2. There are two rand variables a and b after randomization we are comparing all the variables in both the objects if any one of the variable was not matched then the simulator gives an info_message as Mismatched.
Next we are doing copy of all the variables from obj_1 to obj_2 Then again we compare both the objects.
Fig-18: Output for uvm_object compare method.
Github lab link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/obj_compare/uvm_object_compare.sv
Github log_file link: https://github.com/mbits-mirafra/UVMCourse/blob/b7_Team_BJT/basics/obj_compare/compare.log
The do_compare
method is the virtual user-definable hook called by the compare
method. A derived class should override this method to include its fields in a compare operation. It should return 1 if the comparison succeeds, 0 otherwise.
To compare the fields of a derived class, that class should override the do_compare
method.
Syntax:
virtual function bit do_compare (uvm_object rhs,uvm_comparer comparer);
Pack and unpack are a part of UVM automation macros that are used to pack class variables to convert into bitstream.
Method | Description |
---|---|
pack | Performs a bit-wise concatenation of object's properties into an array of type bit. |
pack_bytes | Performs a bit-wise concatenation of object's properties into an array of type byte. |
pack_ints | Performs a bit-wise concatenation of the object's properties into an array of type int. |
do_pack | The do_pack method is the user-definable hook called by the pack methods. |
unpack | Extracts property values from an array of type bit and stores into correct class variable. |
unpack_bytes | Extracts property values from an array of type byte and stores into correct class variable. |
unpack_ints | Extracts property values from an array of type int and stores into correct class variable. |
do_unpack | The do_unpack method is the user-definable hook called by the pack methods. |
code snippet:
class test extends uvm_test;
`uvm_component_utils(test)
function new(string name= "test",uvm_component parent=null);
super.new(name,parent);
endfunction
my_object obj;
bit bits[];
byte unsigned bytes[];
int unsigned ints[];
my_object unp_obj;
virtual function void build_phase(uvm_phase phase);
obj=my_object::type_id::create("my_object",this);
obj.randomize();
obj.print();
$display("before pack values of byte are %p and stream bytes is %p",obj.data,bytes);
obj.pack(bits);
obj.pack_bytes(bytes);
$display("after pack values of byte are %p and stream bytes is %p",obj.data,bytes);
obj.pack_ints(ints);
`uvm_info(get_type_name(),$sformatf("pack bitdata=%p",bits),UVM_LOW)
`uvm_info(get_type_name(),$sformatf("pack bytedata=%p",bytes),UVM_LOW)
`uvm_info(get_type_name(),$sformatf("pack intdata=%p",ints),UVM_LOW)
$display("----------------------------------------------------------");
unp_obj=my_object::type_id::create("my_object",this);
unp_obj.unpack(bits);
unp_obj.unpack_bytes(bytes);
unp_obj.unpack_ints(ints);
unp_obj.print();
`uvm_info(get_type_name(),$sformatf("pack bitdata=%p",bits),UVM_LOW)
`uvm_info(get_type_name(),$sformatf("pack bytedata=%p",bytes),UVM_LOW)
`uvm_info(get_type_name(),$sformatf("pack intdata=%p",ints),UVM_LOW)
endfunction
endclass
output:
Fig-19: Output of the code
Output explanation
In the above code obj
is the handle of the my_object class that extends uvm_object.All the properties in the class are packed into bitstream in the bits
array and all the class properties are packed into bytestream in bytes
array and into intstream by ints
array. The main objective of the pack
is to convert all the object properties into the required stream,for example in the code the array bytes
stores all the values of the object properties in the form of byte datatype,you can see in the output that the byte type class property is only data
and the size is 5 bytes but after packing the bytestream bytes
has 10 values of byte type,this is how the pack works.And unpack helps us to distribute the values to the respective class variables.
Note
1.do_pack and do_unpack are functions that implement all the packing and unpacking in single function call.
2.unpack actions are done by the receiver side like instance of same one class can unpack the values packed usong the instance o another class.
Why pack and unpack?
In bus protocol,you will be having a large amount of data on particular bus transaction and hence it becomes very tough to extract specific field value from that bus transactions. So we use classes on higher level,so that by using different fields,it becomes easy for you to control the value of those fields.
But in real hardware,everything is in bits,so you need to convert those class fields into valid bit patterns and transmit them along the bus.for that we use pack at transmitter and unpack at receiver.
Github Lab link:- https://github.com/vineethkumarv/uvm-repos/blob/master/pack/goex.sv