UVM Macros, Messaging and UVM Reporting:
Macros: The UVM Library has built-in utility and field automation macros that allow objects to be created by the factory and have access to common functions such as copy( ) or clone( ). To understand how the macros work, let’s build a simple data packet for the DUT. If we were not using the UVM, a typical packet may have looked like the following:
class packet;
rand bit enable;
rand bit [14:0] data_in;
rand bit [14:0] data_out;
rand int delay;
constraint timing {delay inside {[0 : 10]};}
endclass : packet
Here is a standard SystemVerilog class with several random data members and one constraint block. Now, let’s build the UVM version of above mentioned packet class
class packet extends uvm_sequence_item;
rand bit enable;
rand bit [14:0] data_in;
rand bit [14:0] data_out;
rand int delay;
constraint timing {delay inside {[0 : 10]};}
`uvm_object_utils_begin(packet)
`uvm_field_int(enable, UVM_DEFAULT)
`uvm_field_int(data_in, UVM_DEFAULT)
`uvm_field_int(data_out, UVM_DEFAULT)
`uvm_field_int(delay, UVM_DEFAULT)
`uvm_object_utils_end
function new(string name = "packet");
super.new(name);
endfunction
endclass : packet
The class is now derived from uvm_sequence_item. Every data item that you create must be derived from this base class directly or indirectly. In addition to our data members, we added the utility and field automation macros.
Adding the macros looks like extra code, but it allows lots of other UVM library code to copy, clone, etc. this object. Now, if you were to cut and paste this code into your favorite editor and then try and compile with your favorite simulation tool, you would receive a whole host of errors. As is, the compiler has no idea of where to find uvm_sequence_item, or ‘uvm_object_utils, etc. To compile code with UVM objects
you must do two things: import and include. You must import the uvm_pkg and include the uvm_macros. Let’s review the updated compile-friendly code:
Import uvm_pkg::*;
`include “uvm_macros.svh”
Class packet extends uvm_sequence_item;
…
…
…
endclass
The utility macro `uvm_object_utils registers this class with the factory, which we will discuss later, and allows access to the create method which is needed for cloning. After the utility macro are the field automation macros in the form ‘uvm_field_*(data member, flag). They allow access to the functions copy, compare, pack, unpack, record, print, and sprint. Essentially, these macros work together to implement
clone( ), print(), and the other methods that would be tedious and repetitive to code manually. There are macros for the various data types such as integers, enumerations, queues, etc. I recommend reviewing the UVM Reference Guide for an exhaustive list of all of the macros. The flag indicates what type of automation to enable for that data member. Multiple flag values can be used by using a bit-wise OR. They
can also be added using the ‘+’ sign. Here is a table with the flags and their descriptions.
UVM_ALL_ON | Set all operations on (default). |
UVM_DEFAULT | Use the default flag settings. |
UVM_NOCOPY | Do not copy this field. |
UVM_NOCOMPARE | Do not compare this field. |
UVM_NOPRINT | Do not print this field. |
UVM_NODEFPRINT | Do not print the field if it is the same as its |
UVM_NOPACK | Do not pack or unpack this field. |
UVM_PHYSICAL | Treat as a physical field. Use physical setting in policy class for this field. |
UVM_ABSTRACT | Treat as an abstract field. Use the abstract setting in the policy class for this field. |
UVM_READONLY | Do not allow setting of this field from the set_*_local methods. |
UVM_BIN | Print / record the field in binary (base-2). |
UVM_DEC | Print / record the field in decimal (base-10). |
UVM_UNSIGNED | Print / record the field in unsigned decimal (base-10). |
UVM_OCT | Print / record the field in octal (base-8). |
UVM_HEX | Print / record the field in hexadecimal (base-16). |
UVM_STRING | Print / record the field in string format. |
UVM_TIME | Print / record the field in time format. |
If you did not need to use the field macros in your class, then your utility macro would simply be ‘uvm_object_utils (or ‘uvm_component_utils for extensions of UVM components) without the _begin and _end.
Messaging and UVM_Reporting
Messaging and UVM Reporting has the concepts of Severity, Verbosity and Simulation Handing Behavior. Each of them can be independently specified and controlled. Now lets see what each of these indicates:
- Severity
- Severity indicates importance
- Examples are Fatal, Error, Warning & Info
- Verbosity
- Verbosity indicates filter level
- Examples are None, Low, Medium, High, Full & Debug
- Simulation Handling Behavior
- Simulation handling behavior controls simulator behavior
- Examples are Exit, Count, Display, Log, Call Hook & No Action
Simulation Handling Behavior in-fact is the Action taken by the Simulator which is dependent on Severity being produced by the Verification Environment.
The testbench needs the ability to report informative messages and debug information. The following macros provide that capability.
`uvm_info(ID, MSG, VERBOSITY)
`uvm_warning(ID, MSG)
`uvm_error(ID, MSG)
`uvm_fatal(ID, MSG)
With `uvm_info, you not only provide a string ID and string MSG, but you also provide an integer verbosity level. The ID field is simply a tag that you can easily identify in the log. The MSG field is the message you want printed and the verbosity indicates when you want that message printed. If the message’s verbosity level is lower or equal to the current verbosity level of that simulation run, then the message is displayed. The verbosity levels, in order of smallest to largest are, UVM_NONE, UVM_LOW, UVM_MEDIUM, UVM_HIGH, UVM_FULL, and UVM_DEBUG.
As an example, let’s add a task to the packet that displays the current values of the data members.
virtual function void displayAll();
`uvm_info("DP", $sformatf("enable = %0h data_in = %0h data_out = %0h delay = %0d", enable, data_in, data_out, delay), UVM_LOW)
endfunction : displayAll
This message will always print when this task is called because it has the lowest verbosity level, provided the user did not set verbosity to UVM_NONE. If there are messages you only want to print for debug, you would give them a higher verbosity such as UVM_HIGH or UVM_DEBUG.
You can change that from the command line with +UVM_VERBOSITY=UVM_<VERBOSITY_LEVEL>. It is important to note that macro invocations do not end with a semicolon.
Modify Simulator Default Action:
As mentioned above, UVM allows to bind a Reporting Severity with a particular valid Simulator Action. Usually it’s done inside the start_of_simulation() phase.
Actions can be assigned using set_report_*_action() functions. These can be done for one or all in the priority order from lowest to highest.
- set_report_severity_action(Severity, Action);[Lowest Priority]
- set_report_id_action(ID, Action);
- set_report_severity_id_action(Severity, ID, Action);[Highest Priority]
Example:
virtual function void start_of_simulation_phase (uvm_phase phase);
set_report_severity_action(UVM_FATAL | UVM_LOG, UVM_DISPLAY);
set_report_id_action("CFG_ERROR", UVM_NO_ACTION);
set_report_severity_id_action(UVM_ERROR, "CFG_ERROR", UVM_EXIT);
endfunction: start_of_simulation_phase
Controlling Messages Verbosity:
Fundamentally the Verbosity level describes how verbose a Testbench can be. The default Verbosity is UVM_MEDIUM. There are different Verbosity level being supported by UVM. These are UVM_NONE, UVM_LOW, UVM_MEDIUM (Default), UVM_HIGH, UVM_FULL, UVM_DEBUG. In case of default Verbosity level i.e. UVM_MEDIUM, any messages with UVM_HIGH or above are filtered out.
Different Verbosity Levels
Important Notes:
- A message with the Verbosity level UVM_NONE can not be disabled.
- `uvm_fatal, `uvm_error & `uvm_warning can not be filtered out via Verbosity level.
We can set the Verbosity level of a uvm_component individually or hierarchically using the following commands:
drv.set_report_verbosity_level(UVM_HIGH);
env.set_report_verbosity_level_hier(UVM_FULL);