UVM Object

The uvm_object is the cornerstone of the Universal Verification Methodology (UVM). It’s the base class for almost all other classes in UVM, providing fundamental functionalities that are essential for building robust and reusable verification environments.

UVM Object Diagram

Here’s a simplified diagram to illustrate the relationship between various UVM objects:

+----------------------+
|       uvm_object     |
|                      |
|  +----------------+  |
|  |  uvm_component |  |
|  +----------------+  |
+----------------------+
          |
          v
+----------------------+
|    uvm_sequence_item |
+----------------------+
          |
          v
+----------------------+
|      uvm_sequence    |
+----------------------+
          |
          v
+----------------------+
|    uvm_transaction   |
+----------------------+

Key Concepts of UVM Objects:

  1. Class-based Verification: UVM objects are class-based, which allows for encapsulation, inheritance, and polymorphism—essential features of object-oriented programming.
  2. Transactions and Sequence Items: These are the data objects that represent the communication between different components in the verification environment.
  3. Sequences: These are collections of transactions that describe complex stimulus generation.
  4. Configuration Objects: These objects hold configuration information and are used to configure other components in the testbench.

Why is uvm_object Important?

  • Foundation for Polymorphism: It enables polymorphism, allowing you to treat objects of derived classes as objects of the base class. This is crucial for creating flexible and extensible testbenches.
  • Common Functionality: It provides common methods for object creation, copying, printing, comparison, and recording, which are used throughout UVM.  
  • Transaction Recording: It provides the base for transaction recording mechanisms.
  • Reporting: It provides the base for UVM reporting.

Key Features and Methods of uvm_object:

  1. Object Creation (create()):

    • The create() method is the preferred way to create UVM objects. It uses the UVM factory, which allows you to override the default object creation with derived types.
    • Syntax: static function uvm_object create(string name="");
    my_transaction trans = my_transaction::type_id::create("my_trans");
    
  2. Copying (copy()):

    • The copy() method creates a deep copy of an object, including all its members.
    • Syntax: virtual function void copy(uvm_object rhs);
    my_transaction trans_copy = new();
    trans_copy.copy(trans);
    
  3. Printing (print()):

    • The print() method prints the contents of an object to the UVM message stream.
    • Syntax: virtual function void print(uvm_printer printer=null);
    trans.print();
    
  4. Comparison (compare()):

    • The compare() method compares two objects for equality.
    • Syntax: virtual function bit compare(uvm_object rhs, uvm_comparer comparer=null);
    if (trans.compare(other_trans)) begin
      // Objects are equal
    end
    
  5. Cloning (clone()):

    • The clone() method creates a new object that is an exact copy of the original object. It returns a uvm_object handle.
    • Syntax: virtual function uvm_object clone();
    uvm_object cloned_trans = trans.clone();
    my_transaction actual_clone;
    $cast(actual_clone, cloned_trans);
    
  6. Recording (record()):

    • The record() method records the object’s data for later analysis.
    • Syntax: virtual function void record(uvm_recorder recorder=null);
  7. get_type() and get_type_name():

    • get_type() returns the type handle of the object.
    • get_type_name() returns the string name of the object’s type.
    uvm_object_wrapper type = trans.get_type();
    string type_name = trans.get_type_name();
    

Diagram 1: uvm_object in the UVM Hierarchy

uvm_void
    └── uvm_object
        ├── uvm_component
        │   ├── uvm_env
        │   │   └── ...
        │   ├── uvm_agent
        │   │   ├── uvm_driver
        │   │   ├── uvm_sequencer
        │   │   └── uvm_monitor
        │   └── ...
        └── uvm_sequence_item (and thus, user-defined transactions)

Diagram 2: Key uvm_object Methods

+-----------------+
| uvm_object      |
+-----------------+
| create()        |
| copy()          |
| print()         |
| compare()       |
| clone()         |
| record()        |
| get_type()      |
| get_type_name() |
+-----------------+

 

Example Usage in a Transaction Class:

class my_transaction extends uvm_sequence_item;
  rand bit [7:0] data;

  `uvm_object_utils(my_transaction) // Crucial for factory and other UVM features

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

  function void do_print(uvm_printer printer);
    super.do_print(printer);
    printer.print_field("data", data, 8, UVM_DEC);
  endfunction
endclass

Defining UVM Objects

1. Transaction (uvm_sequence_item)

Transactions are the basic units of communication in UVM testbenches. They are derived from uvm_sequence_item.

class transaction extends uvm_sequence_item;
  `uvm_object_utils(transaction)

  rand bit [31:0] addr;
  rand bit [31:0] data;

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

  function void do_print(uvm_printer printer);
    super.do_print(printer);
    printer.print_field_int("addr", addr, $bits(addr));
    printer.print_field_int("data", data, $bits(data));
  endfunction
endclass

2. Sequence (uvm_sequence)

Sequences are collections of transactions used to generate stimulus.

class my_sequence extends uvm_sequence#(transaction);
  `uvm_object_utils(my_sequence)

  task body();
    transaction t;
    t = transaction::type_id::create("t");
    start_item(t);
    if (!t.randomize())
      `uvm_fatal("RAND_ERR", "Randomization failed")
    finish_item(t);
  endtask
endclass

3. Configuration Object

Configuration objects hold configuration parameters used by various components in the testbench.

class config extends uvm_object;
  `uvm_object_utils(config)

  rand bit [31:0] addr_width;
  rand bit [31:0] data_width;

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

  function void do_print(uvm_printer printer);
    super.do_print(printer);
    printer.print_field_int("addr_width", addr_width, $bits(addr_width));
    printer.print_field_int("data_width", data_width, $bits(data_width));
  endfunction
endclass

Usage of UVM Objects in a Testbench

In a UVM testbench, these objects are instantiated and used within components like drivers, monitors, and agents.

Example: Using Transactions and Sequences

class my_driver extends uvm_driver#(transaction);
  `uvm_component_utils(my_driver)

  task main_phase(uvm_phase phase);
    transaction t;
    forever begin
      seq_item_port.get_next_item(t);
      // Drive transaction to DUT
      seq_item_port.item_done();
    end
  endtask
endclass

Key Takeaways:

  • uvm_object is the root of most UVM classes.
  • It provides essential methods for object management.
  • The \uvm_object_utils` macro is crucial for enabling UVM features like the factory and reporting.
  • Understanding uvm_object is fundamental for working with UVM.