In a random verification environment where data objects are being continuously generated and operated upon by different components, debug would become easier if contents of an object can be displayed.

Old-school style

Traditionally this is done by printing out values to either a logfile or screen via $display statements and custom print functions.

  
  
class Packet;
	bit [31:0]  addr;
	int         count_id;
	bit [3:0]   length;
	...
	function display ();
		$display ("addr=0x%0h count_id=0x%0h length=0x%0h", p1.addr, p1.count_id, p1.length);
	endfunction
endclass

module tb;
	initial begin
		Packet p1 = new();
		p1.display();
	end
endmodule

  

There was no built-in print mechanisms for a class which made testbench developers to write customized versions of print functions for every class like the display() method in the snippet above.

How its done in UVM

UVM avoids the need for customized print functions by incorporating its own uvm_printer class that takes care of all the required configurations to print class properties. It makes sense to include print features in uvm_object so that all child classes will automatically gain access to those features.

Every class item derived from uvm_object will have a printer instance within it. So, a data class derived from uvm_sequence_item or uvm_component will have access to the print() function as well. The major advantage UVM provides are the `uvm_field_* macros that automates the process of writing special print methods.

  
  
virtual class uvm_object extends uvm_void;
	...
	function void print (uvm_printer printer=null);
		if (printer==null)
    		printer = uvm_default_printer;
    	...
	endfunction
	...
endclass

  


Styles

There are three main printers in UVM and have the following display formats.

  • Table printer

  •   ---------------------------------------------------
      Name        Type            Size        Value
      ---------------------------------------------------
      c1          container       -           @1013
      d1          mydata          -           @1022
      v1          integral        32          'hcb8f1c97
      e1          enum            32          THREE
      str         string          2           hi
      value       integral        12          'h2d
      ---------------------------------------------------
    
  • Tree printer

      c1: ([email protected]) {
        d1: ([email protected]) {
             v1: 'hcb8f1c97
             e1: THREE
             str: hi
        }  
        value: 'h2d
      }
    
  • Line printer

      c1: ([email protected]) { d1: ([email protected]) { v1: 'hcb8f1c97 e1: THREE str: hi } value: 'h2d }
    


Usage

By default, UVM assigns table printer to handle every print() function, and hence is the uvm_default_printer. For convenience, global instances of each printer type are available for direct reference in your testbenches.

uvm_printer hierarchy
  
  
uvm_table_printer   uvm_default_table_printer = new();
uvm_tree_printer    uvm_default_tree_printer = new();
uvm_line_printer    uvm_default_line_printer = new();

uvm_printer uvm_default_printer = uvm_default_table_printer;

  

You can pass any of the three printer instance to the print() function to make it print in that particular style. Remember that the print() function receives a printer object.

  
  
class my_data extends uvm_sequence_item;
	bit [2:0] mode;
endclass

my_data obj0;
obj0.print ();                               // Calls table printer by default
obj0.print (uvm_default_line_printer);       // Calls line printer

  


Conditions

Calling print() is only possible if you do either of the two things.

  • Add any member that needs to be printed, within `uvm_object_utils_begin and `uvm_object_utils_end

  •   
      
    class my_data extends uvm_sequence_item;
       rand bit [7:0]   data;
       rand bit [7:0]   addr;
    
       constraint c_addr { addr > 0; addr < 8;}
    
       `uvm_object_utils_begin (my_data)
          `uvm_field_int (data, UVM_ALL_ON)
          `uvm_field_int (addr, UVM_ALL_ON)
       `uvm_object_utils_end
    
       function new (string name = "my_data");
          super.new (name);
       endfunction
    endclass  
    
      
    

    By applying variables to the macros, UVM automates these members to be included in the common utilties defined in uvm_object class namely, print(), copy(), compare(), etc

  • Define a do_print() function for the class

  •   
      
    class derivative extends my_data;
       `uvm_object_utils (derivative)
    
       rand bit [2:0] mode;
    
       function new (string name="derivative");
          super.new (name);
       endfunction
    
       virtual function void do_print (uvm_printer printer);
          printer.print_int ("mode", mode, $bits(mode));
          `uvm_info ("DVR", "do_print called", UVM_MEDIUM)
       endfunction
    endclass 
    
      
    

    If you did not use the utils_begin/end macros, you should define the do_print() method. Remember to use functions within the uvm_printer class to print.

Note that you can also do both the approaches together. The do_print() method will simply append whatever it is provided with to the macro.

  
  
class derivative extends my_data;

   rand bit [2:0] mode;
   rand bit [1:0] format;
   
   `uvm_object_utils_begin (derivative)
   		`uvm_field_int (format, UVM_ALL_ON)
   `uvm_object_utils_end

   function new (string name="derivative");
      super.new (name);
   endfunction

   virtual function void do_print (uvm_printer printer);
      printer.print_int ("mode", mode, $bits(mode));
      `uvm_info ("DVR", "do_print called", UVM_MEDIUM)
   endfunction
endclass 

  


Knobs

There is an instance of a class called uvm_printer_knobs within every printer. This can be used to change some of the settings and parameters used by the print() function.

  
  
uvm_default_printer.knobs.size = 0;                   // Will not display the size column
uvm_default_printer.knobs.indent = 4;                 // Indents to the right by 4 spaces instead of the default 2
uvm_default_printer.hex_radix = "0x";                 // Replaces the hex radix of 'h with 0x

  
Refer Class Definitions for full usage.



Example

example nested classes

First, we'll define a few data item objects. colors is nested inside format, which is inturn nested in the class my_data to create a nested hierarchy.

  
  
//---------------------- colors --------------------------
class colors extends uvm_sequence_item;
   rand bit [3:0] color;
   rand bit       enable;

   `uvm_object_utils_begin (colors)
      `uvm_field_int (color, UVM_ALL_ON)
      `uvm_field_int (enable, UVM_ALL_ON)
   `uvm_object_utils_end

endclass

//---------------------- format --------------------------
class format extends uvm_sequence_item;

   rand bit [3:0]    header;
   rand bit [2:0]    footer;
   rand bit          enable;
   rand bit [1:0]    body;
   rand colors       m_color;

   `uvm_object_utils_begin (format)
      `uvm_field_int (header, UVM_ALL_ON)
      `uvm_field_int (footer, UVM_ALL_ON)
      `uvm_field_int (enable, UVM_ALL_ON)
      `uvm_field_int (body, UVM_ALL_ON)
   `uvm_object_utils_end

   function new (string name = "format");
      super.new (name);
      m_color = colors::type_id::create ("m_color");
   endfunction

   virtual function void do_print (uvm_printer printer);
      printer.print_object ("m_color", m_color);
   endfunction
endclass


//---------------------- my_data --------------------------
class my_data extends uvm_sequence_item;
   rand format      m_format0;
   rand bit [7:0]   data;
   rand bit [7:0]   addr;

   constraint c_addr { addr > 0; addr < 8;}

   `uvm_object_utils_begin (my_data)
      `uvm_field_int (data, UVM_ALL_ON)
      `uvm_field_int (addr, UVM_ALL_ON)
      `uvm_field_object (m_format0, UVM_ALL_ON)
   `uvm_object_utils_end

   function new (string name = "my_data");
      super.new (name);
      m_format0 = format::type_id::create ("m_format0");
   endfunction
endclass 

//---------------------- derivative --------------------------
class derivative extends my_data;
   `uvm_object_utils (derivative)

   rand bit [2:0] mode;

   function new (string name="derivative");
      super.new (name);
   endfunction

   virtual function void do_print (uvm_printer printer);
      printer.knobs.depth=0;
      printer.print_int ("mode", mode, $bits(mode));
      `uvm_info ("DVR", "do_print called", UVM_MEDIUM)
   endfunction
endclass 


  

Note the following for the code shown above:

  • All the members of the class colors are added to the `uvm_object_utils_begin/end macro
  • All the class objects have to be instantiated either using new() or type_id::create()
  • In class format, all members except m_color is used in the macro, while m_color is used inside do_print() method
  • Class derivative adds the mode via do_print() method.
  
  
class base_test extends uvm_test;
   `uvm_component_utils (base_test)

   my_data obj0;
   derivative dv0;
   uvm_table_printer tprinter;

   function new (string name = "base_test", uvm_component parent);
      super.new (name, parent);
   endfunction

   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      obj0 = my_data::type_id::create ("obj0");
      dv0 = derivative::type_id::create ("dv0");
      tprinter = new();
      cfg_printer();
   endfunction

   virtual task run_phase (uvm_phase phase);
      void'(obj0.randomize());
      `uvm_info ("TST", "print() called with no arguments", UVM_MEDIUM)
      obj0.print ();     // uses uvm_default_printer

      `uvm_info ("TST", "Calling print (uvm_default_line_printer)", UVM_MEDIUM)
      obj0.print (uvm_default_line_printer);

      `uvm_info ("TST", "Calling print (uvm_default_tree_printer)", UVM_MEDIUM)
      obj0.print (uvm_default_tree_printer);

      `uvm_info ("TST", "Calling print (uvm_default_table_printer)", UVM_MEDIUM)
      obj0.print (uvm_default_table_printer);
      
      `uvm_info ("TST", "Calling print (tprinter)", UVM_MEDIUM)
      obj0.print (tprinter);
      
      `uvm_info ("TST", "Print derivative (tprinter)", UVM_MEDIUM)
      dv0.print (uvm_default_table_printer);
   endtask

   // Configuration to change display settings for the printer
   virtual function void cfg_printer ();
      tprinter.knobs.full_name = 1;               // Show full name of the variable relative to class instance
      tprinter.knobs.size = 0;                    // Do not show size column
      tprinter.knobs.depth = 1;                   // Only show upto 1 level of the nested hierarchy
      tprinter.knobs.reference = 2;               // Do not print object ID handles
      tprinter.knobs.type_name = 0;               // Do not show Type column
      tprinter.knobs.indent = 4;                  // Indent from the left by 4
      tprinter.knobs.hex_radix = "0x";            // Replace 'h radix by 0x - observe Value field to see this
   endfunction
endclass

  
Simulation Log

----------------------------------------------------------------
CDNS-UVM-1.1d (14.22-s009)
(C) 2007-2013 Mentor Graphics Corporation
(C) 2007-2013 Cadence Design Systems, Inc.
(C) 2006-2013 Synopsys, Inc.
(C) 2011-2013 Cypress Semiconductor Corp.
----------------------------------------------------------------
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO ./tb/test_pkg.sv(34) @ 0: uvm_test_top [TST] print() called with no arguments
-----------------------------------
Name          Type      Size  Value
-----------------------------------
obj0          my_data   -     @2687
  data        integral  8     'h5a
  addr        integral  8     'h1
  m_format0   format    -     @2700
    header    integral  4     'hb
    footer    integral  3     'h0
    enable    integral  1     'h1
    body      integral  2     'h1
    m_color   colors    -     @2711
      color   integral  4     'h8
      enable  integral  1     'h0
-----------------------------------
UVM_INFO ./tb/test_pkg.sv(37) @ 0: uvm_test_top [TST] Calling print (uvm_default_line_printer)
obj0: ([email protected]) { data: 'h5a  addr: 'h1  m_format0: ([email protected]) { header: 'hb  footer: 'h0  enable: 'h1  body: 'h1  m_color: ([email protected]) { color: 'h8  enable: 'h0  } } }
UVM_INFO ./tb/test_pkg.sv(40) @ 0: uvm_test_top [TST] Calling print (uvm_default_tree_printer)
obj0: ([email protected]) {
  data: 'h5a
  addr: 'h1
  m_format0: ([email protected]) {
    header: 'hb
    footer: 'h0
    enable: 'h1
    body: 'h1
    m_color: ([email protected]) {
      color: 'h8
      enable: 'h0
    }
  }
}
UVM_INFO ./tb/test_pkg.sv(43) @ 0: uvm_test_top [TST] Calling print (uvm_default_table_printer)
-----------------------------------
Name          Type      Size  Value
-----------------------------------
obj0          my_data   -     @2687
  data        integral  8     'h5a
  addr        integral  8     'h1
  m_format0   format    -     @2700
    header    integral  4     'hb
    footer    integral  3     'h0
    enable    integral  1     'h1
    body      integral  2     'h1
    m_color   colors    -     @2711
      color   integral  4     'h8
      enable  integral  1     'h0
-----------------------------------
UVM_INFO ./tb/test_pkg.sv(46) @ 0: uvm_test_top [TST] Calling print (tprinter)
-------------------------
Name                Value
-------------------------
obj0                -
    obj0.data       0x5a
    obj0.addr       0x1
    obj0.m_format0  -
-------------------------
UVM_INFO ./tb/test_pkg.sv(49) @ 0: uvm_test_top [TST] Print derivative (tprinter)
UVM_INFO ./tb/my_pkg.sv(94) @ 0: reporter@@dv0 [DVR] do_print called
-------------------------------------
Name          Type        Size  Value
-------------------------------------
dv0           derivative  -     @2722
  data        integral    8     'h0
  addr        integral    8     'h0
  m_format0   format      -     @2732
    header    integral    4     'h0
    footer    integral    3     'h0
    enable    integral    1     'h0
    body      integral    2     'h0
    m_color   colors      -     @2741
      color   integral    4     'h0
      enable  integral    1     'h0
  mode        integral    3     'h0
-------------------------------------

--- UVM Report catcher Summary ---