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 auvm_comparerpolicy object - The comparer's settings and its field comparison methods
- Why the comparer's
resultcount 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_maxmismatches are printed asMISCMPmessages 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
| Setting | Default | Effect |
|---|---|---|
show_max | 1 | Maximum number of miscompares printed. Further mismatches are still counted |
verbosity | UVM_LOW | Verbosity of the MISCMP messages |
sev | UVM_INFO | Severity of the MISCMP messages |
policy | UVM_DEFAULT_POLICY | How nested objects are compared: UVM_DEEP, UVM_SHALLOW or UVM_REFERENCE |
result | 0 | Number 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
| Method | Compares |
|---|---|
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
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
'b101101and 8 is'b1000, so the 2-bit values are'h1and'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
resultproblem. 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()clearedresultfirst.
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_comparersets the policy foruvm_object::compare()and counts miscompares inresultshow_max,verbosity,sevandpolicycontrol what is printed and how nested objects are comparedcompare_field()handles any width, whilecompare_field_int()is limited to 64 bitsresultmust be cleared manually between direct method calls, butcompare()clears it automaticallydo_compare()adds custom fields, combined with&so every field is checked- Miscompares are
UVM_INFOby default, so always check the return value ofcompare()