uvm_comparer is the class that sets the policy for comparing two UVM objects and decides how miscompares are counted and reported. Every uvm_object has a compare() method, and passing it a uvm_comparer object lets you control settings such as how many miscompares are printed and at what verbosity and severity. The comparer also provides comparison methods for integers, strings, real numbers and objects.

8 min read | Intermediate Level

What You'll Learn

  • How compare() uses a uvm_comparer policy object
  • The comparer's settings and its field comparison methods
  • Why the comparer's result count must be cleared between direct comparisons
  • Write a do_compare() method and check compare results in a scoreboard

How uvm_comparer Works

When you call obj1.compare(obj0), UVM walks through every field registered with the field macros, plus anything added in do_compare(), and compares it with the same field in obj0. The comparer object does the actual work:

  • Each field comparison method, such as compare_field(), returns 1 on a match and 0 on a mismatch
  • Each mismatch increments the comparer's internal counter, result
  • The first show_max mismatches are printed as MISCMP messages
  • compare() returns 1 only if no mismatches were found

If you do not pass a comparer, compare() uses the global default comparer. When you do pass one, its settings apply to that comparison, and result is cleared before the comparison starts.

Syntax


bit match;
uvm_comparer cmp = new ();

match = obj1.compare (obj0);          // Use the global default comparer
match = obj1.compare (obj0, cmp);     // Use your own policy; cmp.result is cleared first

// Direct field comparison (returns 1 on match)
match = cmp.compare_field ("NAME", LHS, RHS, SIZE);

Comparer Settings

SettingDefaultEffect
show_max1Maximum number of miscompares printed. Further mismatches are still counted
verbosityUVM_LOWVerbosity of the MISCMP messages
sevUVM_INFOSeverity of the MISCMP messages
policyUVM_DEFAULT_POLICYHow nested objects are compared: UVM_DEEP, UVM_SHALLOW or UVM_REFERENCE
result0Number of miscompares found so far

In the newer IEEE 1800.2 versions of UVM, these settings are changed through methods such as set_show_max(), and the count is read with get_result(). The behavior is the same.

Comparison Methods

MethodCompares
compare_field()Integral values of any size, as uvm_bitstream_t (up to 4096 bits by default)
compare_field_int()Integral values up to 64 bits. Faster for small fields
compare_field_real()Real numbers
compare_string()Strings
compare_object()Two objects, field by field, following the recursion policy

Clearing the result Count

The number of miscompares is stored in the comparer's result variable, which is incremented on every mismatch. If you call the comparison methods directly, as the example below does, result keeps growing across calls, so the miscompare count reported by a later comparison also includes earlier ones.

Clear it yourself with cmp.result = 0; before each new direct comparison. This is not needed when the comparer is passed to uvm_object::compare(), which clears result before it starts.

Quick Check

Q: A comparer with show_max = 1 compares two objects that differ in five fields. What is printed, and what does compare() return?

Show Answer

Only the first mismatch is printed in detail, followed by the summary of 5 miscompares, because all five are still counted in result. compare() returns 0.

Example

This example uses the data objects my_data, format and colors from How to use uvm_printer. The first part calls each comparer method directly with the same comparer uc0, to show what each one does. The last part shows the recommended usage: pass the comparer to compare().


class base_test extends uvm_test;
   `uvm_component_utils (base_test)

   my_data obj0, obj1;
   derivative dv0, dv1;

   uvm_comparer uc0;

   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");
      obj1 = my_data::type_id::create ("obj1");
      uc0 = new();
   endfunction

   virtual task run_phase (uvm_phase phase);

      cfg_comparer();
      // Do not use the same uvm_compare object to do multiple comparisons, like shown below 
      // The example is a demonstration of the different methods of uvm_compare
      // NOTE: There's an internal variable "result" that stores the number of miscompares.
      // This variable will continue to be incremented for every mismatch, until cleared manually 

      `uvm_info ("COMPARE", "Trying out compare_field", UVM_MEDIUM)
      uc0.compare_field ("compare_field1", 7, 7, 2);            // pass
      uc0.compare_field ("compare_field2", 8'd45, 8'd8, 2);     // fail

      `uvm_info ("COMPARE", "Trying out field_int", UVM_MEDIUM)
      uc0.compare_field_int ("field_int1", 64'hface_deaf_feed_cafe, 64'hfabe_deaf_feed_cafe, 64);  // fail 
      uc0.compare_field_int ("field_int2", 64'habcd_ef12_3456_7890, 64'habcd_ef12_3456_7890, 64);  // pass
      
      uc0.compare_field_int ("field_int3", 64'hface_deaf_feed_cafe, 64'h7ace_deaf_feed_cafe, 63);  // pass 
      uc0.compare_field_int ("field_int4", 64'hface_deaf_feed_cafe, 64'h8abe_deaf_feed_cafe, 64);  // fail 

      uc0.compare_field_int ("field_int5", 68'hbbbb_face_deaf_feed_cafe, 68'haaaa_8abe_deaf_feed_cafe, 68); //  won't work; nothing happens
      uc0.compare_field ("field", 68'hb_face_deaf_feed_cafe, 68'haa_8abe_deaf_feed_cafe, 68); // fail: will work with field, because size > 64

      `uvm_info ("COMPARE", "Trying out compare_object", UVM_MEDIUM)
      void'(obj0.randomize());
      void'(obj1.randomize());
      uc0.compare_object ("object1", obj0, obj1);  // fail
      obj1.copy (obj0);
      uc0.compare_object ("object2", obj0, obj1);  // pass

      `uvm_info ("COMPARE", "Trying out compare_string", UVM_MEDIUM)
      uc0.compare_string ("string1", "Hello", "World");   // fail
      uc0.compare_string ("string2", "Hello", "Hello");   // pass

      
      // Proper Usage: Set the configuration for uvm_comparer and pass it to compare()
      // The "result" variable is cleared before comparison starts within uvm_object::compare()
      cfg_comparer();
      void'(obj0.randomize());
      obj1.compare (obj0, uc0);

      // randomize and compare again
      void'(obj0.randomize());
      obj1.compare (obj0, uc0);
      

   endtask

   virtual function void cfg_comparer();
      uc0.show_max = 20;     // total number of miscompares to be printed
      uc0.verbosity = UVM_MEDIUM;
   endfunction

endclass
Output
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO ./tb/test_pkg.sv(43) @ 0: uvm_test_top [COMPARE] Trying out compare_field
UVM_INFO @ 0: reporter [MISCMP] Miscompare for compare_field2: lhs = 'h1 : rhs = 'h0
UVM_INFO ./tb/test_pkg.sv(47) @ 0: uvm_test_top [COMPARE] Trying out field_int
UVM_INFO @ 0: reporter [MISCMP] Miscompare for field_int1: lhs = 'hfacedeaffeedcafe : rhs = 'hfabedeaffeedcafe
UVM_INFO @ 0: reporter [MISCMP] Miscompare for field_int4: lhs = 'hfacedeaffeedcafe : rhs = 'h8abedeaffeedcafe
UVM_INFO @ 0: reporter [MISCMP] Miscompare for field: lhs = 'hbfacedeaffeedcafe : rhs = 'ha8abedeaffeedcafe
UVM_INFO ./tb/test_pkg.sv(57) @ 0: uvm_test_top [COMPARE] Trying out compare_object
UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.data: lhs = 'h5a : rhs = 'h47
UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.addr: lhs = 'h1 : rhs = 'h2
UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.m_format0.header: lhs = 'hb : rhs = 'h2
UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.m_format0.footer: lhs = 'h0 : rhs = 'h7
UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.m_format0.body: lhs = 'h1 : rhs = 'h2
UVM_INFO @ 0: reporter [MISCMP] Miscompare for object1.m_format0.m_color.color: lhs = 'h8 : rhs = 'h2
UVM_INFO @ 0: reporter [MISCMP] 10 Miscompare(s) for object obj1@2722 vs. obj0@2687
UVM_INFO @ 0: reporter [MISCMP] 10 Miscompare(s) for object obj1@2722 vs. obj0@2687
UVM_INFO ./tb/test_pkg.sv(64) @ 0: uvm_test_top [COMPARE] Trying out compare_string
UVM_INFO @ 0: reporter [MISCMP] Miscompare for string1: lhs = "Hello" : rhs = "World"
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.data: lhs = 'h5a : rhs = 'hee
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.addr: lhs = 'h1 : rhs = 'h2
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.header: lhs = 'hb : rhs = 'hf
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.body: lhs = 'h1 : rhs = 'h2
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.m_color.color: lhs = 'h8 : rhs = 'h7
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.m_color.enable: lhs = 'h0 : rhs = 'h1
UVM_INFO @ 0: reporter [MISCMP] 6 Miscompare(s) for object obj0@2687 vs. obj1@2722
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.data: lhs = 'h5a : rhs = 'h2d
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.addr: lhs = 'h1 : rhs = 'h3
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.header: lhs = 'hb : rhs = 'h9
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.footer: lhs = 'h0 : rhs = 'h6
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.enable: lhs = 'h1 : rhs = 'h0
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.m_color.color: lhs = 'h8 : rhs = 'h0
UVM_INFO @ 0: reporter [MISCMP] Miscompare for obj1.m_format0.m_color.enable: lhs = 'h0 : rhs = 'h1
UVM_INFO @ 0: reporter [MISCMP] 7 Miscompare(s) for object obj0@2687 vs. obj1@2722

--- UVM Report catcher Summary ---

Points to notice in the output:

  • compare_field2: only the lowest 2 bits are compared. 45 is 'b101101 and 8 is 'b1000, so the 2-bit values are 'h1 and 'h0.
  • field_int3 passes although the values differ, because the only difference is in bit 63 and the size is 63 bits.
  • field_int5 prints nothing. compare_field_int() handles at most 64 bits, so a 68-bit comparison is not performed. compare_field() handles the same 68-bit values correctly.
  • "10 Miscompare(s)" after object1 shows the result problem. Only 6 fields differ, but the count also includes the 4 earlier miscompares of uc0.
  • The last two comparisons report 6 and 7 miscompares, exactly the number of differing fields, because compare() cleared result first.

Where is uvm_comparer Used?

Scoreboards compare every transaction from the DUT with the expected transaction from a reference model, often millions of times per test. A dedicated comparer lets the scoreboard print a few miscompares per transaction, at a severity and verbosity that suit the project's log conventions.

Custom Comparison with do_compare()

For fields that are not registered with field macros, override do_compare() and use the comparer's methods. Use the bitwise & operator, not &&, so every field is still compared and reported after the first mismatch.


class pkt extends uvm_sequence_item;
  `uvm_object_utils (pkt)

  rand bit [7:0]  addr;
  rand bit [31:0] data;
  // Rest of the code (constructor)

  virtual function bit do_compare (uvm_object rhs, uvm_comparer comparer);
    pkt rhs_;
    if (!$cast (rhs_, rhs))
      return 0;                                                    // Different type: not equal
    return super.do_compare (rhs, comparer)
         & comparer.compare_field ("addr", addr, rhs_.addr, $bits(addr))
         & comparer.compare_field ("data", data, rhs_.data, $bits(data));
  endfunction
endclass

Where is do_compare() Used?

Many teams skip field macros in large transaction classes for speed and write do_compare() by hand. This also lets them skip fields that should not be checked, such as timestamps or IDs assigned by the testbench.

Common Mistakes

Mistake #1: Reusing a comparer for direct calls without clearing result

Wrong: calling uc0.compare_object() several times in a row and reading the miscompare count each time
Right: set uc0.result = 0; before each new direct comparison, or use obj.compare(other, uc0)

Why? result only grows. As the log shows, a comparison with 6 real differences can report 10 miscompares.

Mistake #2: Using compare_field_int() for fields wider than 64 bits

Wrong: uc0.compare_field_int ("key", lhs, rhs, 128);
Right: uc0.compare_field ("key", lhs, rhs, 128);

Why? compare_field_int() takes 64-bit arguments. For larger sizes, it does not report the mismatch, so a real difference can pass silently.

Mistake #3: Ignoring the return value of compare()

Wrong: exp.compare (act); as a statement in a scoreboard
Right: if (!exp.compare (act)) `uvm_error ("SCB", "Mismatch")

Why? By default, MISCMP messages are UVM_INFO. A mismatch prints a message but does not fail the test, unless you check the return value or set sev to UVM_ERROR.

Practice Exercise

Challenge: A Scoreboard Check Function

Write a scoreboard function check_pkt(pkt exp, pkt act) that uses its own comparer. It should print at most 3 miscompares per transaction at UVM_LOW, and report a `uvm_error that includes both transactions when they differ.

Hint

Create the comparer once, for example in build_phase, set show_max and verbosity, and pass it to compare(). Use sprint() to put both objects in the error message.

Solution

class my_scoreboard extends uvm_scoreboard;
  `uvm_component_utils (my_scoreboard)
  // Rest of the code (constructor, analysis imps, etc.)

  uvm_comparer m_cmp;

  virtual function void build_phase (uvm_phase phase);
    super.build_phase (phase);
    m_cmp           = new ();
    m_cmp.show_max  = 3;            // Print up to 3 miscompares per transaction
    m_cmp.verbosity = UVM_LOW;
  endfunction

  function void check_pkt (pkt exp, pkt act);
    // compare() clears m_cmp.result before it starts
    if (!act.compare (exp, m_cmp))
      `uvm_error ("SCB", $sformatf ("%0d miscompare(s)\nExpected:\n%s\nActual:\n%s",
                                     m_cmp.result, exp.sprint (), act.sprint ()))
  endfunction
endclass

Because compare() clears result on each call, the count in each error message covers only that transaction.

Summary

In this tutorial, you learned:

  • uvm_comparer sets the policy for uvm_object::compare() and counts miscompares in result
  • show_max, verbosity, sev and policy control what is printed and how nested objects are compared
  • compare_field() handles any width, while compare_field_int() is limited to 64 bits
  • result must be cleared manually between direct method calls, but compare() clears it automatically
  • do_compare() adds custom fields, combined with & so every field is checked
  • Miscompares are UVM_INFO by default, so always check the return value of compare()
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5