Thought Leadership

UVM Transaction Coding Style

How to write a UVM transaction class?

There has been a split in UVM – how to create a sequence item class? Having worked for the big 3 EDA companies and supported many customers, I’ve seen both sides. Let’s explore the two leading flavors, chocolate and vanilla, and have a look a new one. This post assumes you know the basics of UVM.

Three flavors

UVM defines a set of transaction methods to copy, compare, and print these objects (plus about a dozen more). With these methods, your driver, monitor, and scoreboard components can clone, compare, and print the objects. How do you implement these to operate on the properties in your transaction? Download the code for this post with:

% git clone https://github.com/chrisspear/uvm_xact

You want it now!

With the UVM field macros you can build the code for these methods with about one line per property. Here is a trivial example with a destination address and a command field, and the field macros that generate all the support code for the UVM transaction methods. The full example is in fm.svh

// fm.sv: field macro flavor
class tx_item extends uvm_sequence_item;
  function new(...);                 // Constructor not shown
  rand bit [31:0] dst;               // Transaction property
  rand command_t cmd;                // Enumerated type
  `uvm_object_utils_begin(tx_item)  
     `uvm_field_int(dst, UVM_ALL_ON) // One line per property
     `uvm_field_enum(command_t, cmd, UVM_ALL_ON)
  `uvm_object_utils_end
endclass

You want it perfect, no matter what!

The downfall of the UVM field macros is lack of control. What if you don’t want to compare the destination address if the command is NOOP? Then you should turn to the do methods. At a minimum you need to write do_copy(), do_compare(), and do_print(). Here is a subset of the code in do.svh.

// do.svh do method flavor
class tx_item extends uvm_sequence_item;
  `uvm_object_utils(tx_item)
  function new(...); // Constructor not shown
  function bit do_compare(uvm_object rhs, uvm_comparer comparer);
    tx_item rhs_;
    if (!$cast(rhs_, rhs)) `uvm_fatal(...)
    if (cmd == NOOP) // For NOOP command
      return 1;      // always return success
    return (super.do_compare(rhs, comparer) &&
            (dst === rhs_.dst) &&
            (cmd === rhs_.cmd));
  endfunction
  function void do_copy(...);  // TBD
  function void do_print(...); // Not shown      
endclass

You want it just right!

A big thanks to Michael Pirov who showed me a hybrid style where you start with the field macros, and only add code for special cases, and to improve the reusability. I am always up for learning something new!

In the following snippet of hybrid.svh, I used the field macros, and specified that I would do my own compare. The do_compare method calls comparer.compare_field() to do a verbose comparison. As Michael explained, this prints more details about the miscompares to the engineers using his code, which reduces his support work. That sounds like a great tradeoff.

// fm.sv: field macro flavor
class tx_item extends uvm_sequence_item;
  function new(...);                 // Constructor not shown
  rand bit [31:0] dst;               // Transaction property
  rand command_t cmd;                // Enumerated type
  `uvm_object_utils_begin(tx_item)  
     `uvm_field_int(dst, UVM_ALL_ON | UVM_NOCOMPARE)
     `uvm_field_enum(command_t, cmd, UVM_ALL_ON | UVM_NOCOMPARE)
  `uvm_object_utils_end

  // Conditional compare, plus verbose compare
  function bit do_compare(uvm_object rhs, uvm_comparer comparer);
    tx_item rhs_;
    if (!$cast(rhs_, rhs)) $fatal(0, "$cast failed");
    if (cmd == NOOP) 
      do_compare = 1; 
    else begin // Call the verbose compare methods 
      do_compare = (super.do_compare(rhs, comparer) && 
        comparer.compare_field("dst", dst, rhs_.dst, 32) && 
        comparer.compare_field("cmd", cmd, rhs_.cmd,2,UVM_ENUM));
    end
  endfunction
endclass

Summary

When you write a transaction class, consider mixing together the compactness of the field macro style with the preciseness of the do methods. In the past I have recommended against mixing the two, because of possible side effects. So far the worst problem happened when I tried to combine print(), do_print(), and convert2string(). Let me know what you find.

Enjoy your verification journey!
Chris Spear

View my recent webinars including an overview of UVM Transactions.

Chris Spear

Chris brings over forty years of EDA expertise to Siemens customers. Holding a degree in electrical engineering from Cornell University, Chris has developed deep roots in the EDA industry, including as a Principal Application Consultant. Chris is also an industry author, writing the 2012 best-selling “SystemVerilog for Verification” and developing the IEEE standard for random seeding and File I/O PLI package that is part of SystemVerilog. Having taught thousands of engineers around the world, Chris is driven by a passion for learning new techniques and then helping others learn best practices for hardware verification. Outside of work, you may see Chris bicycling over 12,000-foot mountain passes.

More from this author

Comments

2 thoughts about “UVM Transaction Coding Style

Leave a Reply

This article first appeared on the Siemens Digital Industries Software blog at https://blogs.sw.siemens.com/verificationhorizons/2020/10/26/uvm-transaction-coding-style/