Logo

UVM Sequence Item

24 Nov 2024
5 mins

We have till now seen the classes which makes the base of all other classes in UVM. From this article we will start learning about the UVM classes which we use everyday. We will start from sequence item and move to upper level objects/components slowly.

In the world of UVM-based testbenches, UVM sequence items are fundamental building blocks used to represent the transactions exchanged between testbench components and the DUT. This article explores what UVM sequence items are, how to define and use them, and the utilities they offer.

What is a UVM Sequence Item?

UVM sequence item can be considered as a container for the transaction-level data which other components of the test bench like, driver, monitor, checkers, etc. can process. These are also known as transactions and are vital part of transaction-level modelling. A UVM sequence item is a class derived from the uvm_sequence_item UVM base class.

Defining a UVM Sequence Item

Let's see how to define a sequence item.

1. Define the class

First, we need to define the class by extending uvm_sequence_item class.

class my_seq_item extends uvm_sequence_item;

2. Register with UVM factory

As discussed on earlier articles, we need to register our class with UVM factory. As sequence item is derived from uvm_object, we use uvm_object_utils macro to register the class with the UVM factory.

`uvm_object_utils(my_seq_item)

3. Constructor Definition

The constructor of uvm_sequence_item is same as that of UVM object. It takes one input name

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

4. Transaction fields

As sequence item is wrapper for all the signals, that needs to be used by other components, thus we need to add all the variables that represent the transaction data.

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

With this we are done with the basic definition of the UVM sequence item. But there is one more thing we need to take care of. UVM sequence item represents transaction and may encapsulate lot of signals as variable. In many areas of code, we might need to make a copy of sequence item object or print the values of all the variable inside sequence item objects or compare the fields in the scoreboard.

To tackle this, UVM provides in-built mechanism which helps perform these tasks easily. To utilize these mechanism, we need to expose the variables using UVM field macros.

5. UVM field macros

UVM field macros expose the transaction fields to built-in methods like copy, compare and print , etc. These macros simplify handling sequence items in a testbench.

`uvm_object_utils_begin(my_seq_item)
   `uvm_field_int(addr, UVM_ALL_ON)
   `uvm_field_int(data, UVM_ALL_ON)
`uvm_object_utils_end
Do note that UVM field macros saves time and effort by exposing field to various utility functions but at the same time it can cause performance issues. This is because the field macros gets replaces with lot of codes and if there lots of fields, then the compiled code is very inflated.

The entire definition of uvm_sequence_item looks like below.

class my_seq_item extends uvm_sequence_item;
   `uvm_object_utils_begin(my_seq_item)
      `uvm_field_int(addr, UVM_ALL_ON)
      `uvm_field_int(data, UVM_ALL_ON)
   `uvm_object_utils_end

   // Transaction fields
   bit [7:0] addr;
   bit [31:0] data;

   // Constructor
   function new(string name = "my_seq_item");
      super.new(name);
   endfunction
endclass

Built-in Methods and callback methods

UVM sequence items has lot of built-in methods to help manage transactions easily for various activities. Only the variables exposed through field macros are available inside the in-built functions. When using field macros we can expose the variables either to all in-built method or few methods or just one method.

This done using various field macros flag provided by UVM.

FlagDescription
UVM_DEFAULTAll field operations turned on
UVM_COPYField will participate in copy
UVM_COMPAREField will participate in compare
UVM_PRINTField will participate in print
UVM_PACKField will participate in pack/unpack
UVM_NOCOPYField will not participate in copy
UVM_NOCOMPAREField will not participate in compare
UVM_NOPRINTField will not participate in print
UVM_NORECORDField will not participate in record
UVM_NOPACKField will not participate in pack/unpack
UVM_DEEPObject field will be deep copied
UVM_SHALLOWObject field will be shallow copied
UVM_REFERENCEObject field will copied by reference
UVM_READONLYObject field will NOT be automatically configured
Above FLAGS can be bitwise ORed to use multiple flags at a time.

Callback methods

UVM provides various callback methods methods which are called automatically when in-built functions are executed. These methods can be used to override the functionality of the in-built functions.

In-built functionCallback method
copy()do_copy(uvm_object rhs)
compare()do_compare(uvm_object rhs,uvm_comparer comparer)
print()do_print(uvm_printer printer)
pack()do_pack (uvm_packer packer)
unpack()do_unpack (uvm_packer packer)
record()do_record(uvm_recorder recorder)
convert2string()NA
convert2string() is a special in-built function which has no implementation. To use this, we need to implement this method in our code. This method is used to convert a transaction object into string and helpful to print the object details in a single line.

Example

We will learn various UVM components by creating a actual and simple test bench for a 8-bit ALU (arithmetic logic unit). Slowly, with each article, we will develop different components of the test bench.

The ALU has following port signals:

SignalWidthI/ODescription
OPA8-bitIFirst Operand for operation
OPB8-bitISecond Operand for operation
cin1-bitICarry In bit
mode1-bitIMode of operation
0 - Arithmetic
1 - Logical
cmd4-bitIThe operation that needs to be performed by ALU
For mode 0, valid cmd is 0 to 9
For mode 1, valid cmd is 0 to 13
res9-bitOresult of the operation
cout1-bitOcarry out bit
err1-bitOIndicates that error has happened
oflow1-bitOIndicates overflow in subtraction operation
e1-bitOIs 1 when both operands are equal
g1-bitOis 1 when OPA > OPB
l1-bitOis 1 when OPA < OPB

Let us start with creating the uvm_sequence_item class with name transaction.

import uvm_pkg::*;

class transaction extends uvm_sequence_item;
    `uvm_object_utils(transaction)
     // Inputs to DUT
    rand logic [7:0] OPA, OPB;
    rand logic cin, mode, ce;
    rand logic [3:0] cmd;

    constraint cmd_val {
        if (mode == 1)
            cmd inside { [0:9] };
        else
            cmd inside { [0:13] };
    };
    constraint ce_dist { ce dist { 1 := 9, 0 := 1 }; }

    // Ouput from DUT
    logic [2:0] egl;
    logic [8:0] res;
    logic cout, err, oflow;

    static bit [7:0] trans_ID;    // Transaction ID
    static bit isRandom;            // Control bit

    function new(string name = "trans");
        super.new(name);
        init_val();
    endfunction //new()

    function void pre_randomize();
        trans_ID++;
    endfunction

    // Helper functions
    function void init_val();
        OPA = 'z;
        OPB = 'z;
        cin = 'z;
        cmd = 'z;
        ce = 'z;
        //r_bit = 'z;
        mode = 'z;
        egl = 'z;
        res = 'z;
        cout = 'z;
        err = 'z;
        oflow = 'z;
    endfunction

    function void do_print(uvm_printer printer);
        printer.print_field("Transaction ID: %0d", trans_ID);
        printer.print_field("OPA", OPA, 8, UVM_UNSIGNED);
        printer.print_field("OPB", OPB, 8, UVM_UNSIGNED);
        printer.print_field("Mode", mode, 1, UVM_BIN);
        printer.print_field("CMD", cmd, 4, UVM_BIN);
        printer.print_field("CE", ce, 1, UVM_BIN);
        printer.print_field("Result", res, 9, UVM_UNSIGNED);
        printer.print_field("cout", cout, 1, UVM_BIN);
        printer.print_field("err", err, 1, UVM_BIN);
        printer.print_field("oflow", oflow, 1, UVM_BIN);
        printer.print_field("EGL", egl, 3, UVM_BIN);
    endfunction

    function string convert2string();
        string s;
        s = $sformatf("Packet ID: %0d", trans_ID);
        s = $sformatf("%s\nInput: OPA = %b, OPB = %b, cin = %b, cmd = %b, mode = %b, ce = %b, ",
                s, OPA, OPB, cin, cmd, mode, ce);
        s = $sformatf("%s\nOuput: result = %b, cout = %b, err = %b, oflow = %b, EGL = %b",
                s, res, cout, err, oflow, egl);
        return s;
    endfunction

    function bit do_compare(uvm_object rhs, uvm_comparer comparer);
        transaction _rhs;
        bit cmp;
        if(!$cast(_rhs, rhs))
            `uvm_fatal(get_type_name(), "object passed is not compatible with Transaction");

        cmp = this.egl === _rhs.egl & this.res === _rhs.res & cout === _rhs.cout
            & err === _rhs.err & oflow === _rhs.oflow;
        return cmp;
    endfunction

    function void do_copy(uvm_object rhs);
        transaction _rhs;
        if(!$cast(_rhs, rhs))
            `uvm_fatal(get_type_name(), "rhs is not compatible with this class");

        OPA = _rhs.OPA;
        OPB = _rhs.OPB;
        cin = _rhs.cin;
        cmd = _rhs.cmd;
        ce = _rhs.ce;
        mode = _rhs.mode;
        egl = _rhs.egl;
        res = _rhs.res;
        cout = _rhs.cout;
        err = _rhs.err;
        oflow = _rhs.oflow;
    endfunction
endclass //transaction extends uvm_sequence_item

Observations

  • In above code, we have used callback methods to use the utility functions. Using the field macros uvm_field_int will help perform same functionality without implementing any callback methods.
  • We call a custom method init_val in the object constructor to initialize variables.
  • Implemented convert2string() method to covert the object into a string.
  • All the variables which are input to the design have been declared as rand so that they can be randomized in each object of transaction class.
  • Used pre_randomize() method to increment trans_ID. We are not using constructor to increment this variable as we can create new objects which are copy of other objects and does not account for a new trans_ID. This is a general randomization concept from SV.
  • trans_id is a static variable, therefore we can increment the value of this variable across any object.

Conclusion

This article outlines the essentials of uvm_sequence_item including its definition, usage and declaration of variables inside sequence item. Sequence items form the backbone of transaction-based verification in UVM.
In the next, article we will about UVM sequences and how they are used to interact with sequence items.