| //----------------------------------------------------------------------------- |
| // Copyright 2007-2018 Cadence Design Systems, Inc. |
| // Copyright 2007-2014 Mentor Graphics Corporation |
| // Copyright 2013-2018 NVIDIA Corporation |
| // Copyright 2017-2018 Cisco Systems, Inc. |
| // Copyright 2018 Qualcomm, Inc. |
| // Copyright 2014 Intel Corporation |
| // Copyright 2013-2018 Synopsys, Inc. |
| // All Rights Reserved Worldwide |
| // |
| // Licensed under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in |
| // compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in |
| // writing, software distributed under the License is |
| // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
| // CONDITIONS OF ANY KIND, either express or implied. See |
| // the License for the specific language governing |
| // permissions and limitations under the License. |
| //----------------------------------------------------------------------------- |
| |
| //------------------------------------------------------------------------------ |
| // |
| // CLASS -- NODOCS -- uvm_comparer |
| // |
| // The uvm_comparer class provides a policy object for doing comparisons. The |
| // policies determine how miscompares are treated and counted. Results of a |
| // comparison are stored in the comparer object. The <uvm_object::compare> |
| // and <uvm_object::do_compare> methods are passed a uvm_comparer policy |
| // object. |
| // |
| //------------------------------------------------------------------------------ |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.1 |
| class uvm_comparer extends uvm_policy; |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.2.3 |
| `uvm_object_utils(uvm_comparer) |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.2.2 |
| extern virtual function void flush(); |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.3.5 |
| extern virtual function uvm_policy::recursion_state_e object_compared( |
| uvm_object lhs, |
| uvm_object rhs, |
| uvm_recursion_policy_enum recursion, |
| output bit ret_val |
| ); |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.3.8 |
| extern virtual function string get_miscompares(); |
| |
| extern virtual function int unsigned get_result(); |
| |
| extern virtual function void set_result(int unsigned result) ; |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.4.1 |
| extern virtual function void set_recursion_policy( uvm_recursion_policy_enum policy); |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.4.1 |
| extern virtual function uvm_recursion_policy_enum get_recursion_policy(); |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.4.2 |
| extern virtual function void set_check_type( bit enabled ); |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.4.2 |
| extern virtual function bit get_check_type(); |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.5.1 |
| extern virtual function void set_show_max (int unsigned show_max); |
| |
| extern virtual function int unsigned get_show_max (); |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.5.2 |
| extern virtual function void set_verbosity (int unsigned verbosity); |
| |
| extern virtual function int unsigned get_verbosity (); |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.5.3 |
| extern virtual function void set_severity (uvm_severity severity); |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.5.3 |
| extern virtual function uvm_severity get_severity (); |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.6 |
| extern virtual function void set_threshold (int unsigned threshold); |
| |
| extern virtual function int unsigned get_threshold (); |
| |
| typedef struct { |
| recursion_state_e state; |
| bit ret_val; |
| } state_info_t ; |
| state_info_t m_recur_states[uvm_object /*LHS*/][uvm_object /*RHS*/][uvm_recursion_policy_enum /*recursion*/]; |
| |
| // Variable -- NODOCS -- policy |
| // |
| // Determines whether comparison is UVM_DEEP, UVM_REFERENCE, or UVM_SHALLOW. |
| |
| `ifdef UVM_ENABLE_DEPRECATED_API |
| uvm_recursion_policy_enum policy = UVM_DEFAULT_POLICY; |
| `else |
| local uvm_recursion_policy_enum policy = UVM_DEFAULT_POLICY; |
| `endif |
| |
| // Variable -- NODOCS -- show_max |
| // |
| // Sets the maximum number of messages to send to the printer for miscompares |
| // of an object. |
| |
| `ifdef UVM_ENABLE_DEPRECATED_API |
| int unsigned show_max = 1; |
| `else |
| local int unsigned show_max = 1; |
| `endif |
| |
| // Variable -- NODOCS -- verbosity |
| // |
| // Sets the verbosity for printed messages. |
| // |
| // The verbosity setting is used by the messaging mechanism to determine |
| // whether messages should be suppressed or shown. |
| |
| `ifdef UVM_ENABLE_DEPRECATED_API |
| int unsigned verbosity = UVM_LOW; |
| `else |
| local int unsigned verbosity = UVM_LOW; |
| `endif |
| |
| |
| // Variable -- NODOCS -- sev |
| // |
| // Sets the severity for printed messages. |
| // |
| // The severity setting is used by the messaging mechanism for printing and |
| // filtering messages. |
| |
| `ifdef UVM_ENABLE_DEPRECATED_API |
| uvm_severity sev = UVM_INFO; |
| `else |
| local uvm_severity sev = UVM_INFO; |
| `endif |
| |
| |
| // Variable -- NODOCS -- miscompares |
| // |
| // This string is reset to an empty string when a comparison is started. |
| // |
| // The string holds the last set of miscompares that occurred during a |
| // comparison. |
| |
| `ifdef UVM_ENABLE_DEPRECATED_API |
| string miscompares = ""; |
| `else |
| local string miscompares = ""; |
| `endif |
| |
| |
| `ifdef UVM_ENABLE_DEPRECATED_API |
| // Variable -- NODOCS -- physical |
| // |
| // This bit provides a filtering mechanism for fields. |
| // |
| // The abstract and physical settings allow an object to distinguish between |
| // two different classes of fields. |
| // |
| // It is up to you, in the <uvm_object::do_compare> method, to test the |
| // setting of this field if you want to use the physical trait as a filter. |
| |
| bit physical = 1; |
| `endif |
| |
| |
| `ifdef UVM_ENABLE_DEPRECATED_API |
| // Variable -- NODOCS -- abstract |
| // |
| // This bit provides a filtering mechanism for fields. |
| // |
| // The abstract and physical settings allow an object to distinguish between |
| // two different classes of fields. |
| // |
| // It is up to you, in the <uvm_object::do_compare> method, to test the |
| // setting of this field if you want to use the abstract trait as a filter. |
| |
| bit abstract = 1; |
| `endif |
| |
| // Variable -- NODOCS -- check_type |
| // |
| // This bit determines whether the type, given by <uvm_object::get_type_name>, |
| // is used to verify that the types of two objects are the same. |
| // |
| // This bit is used by the <compare_object> method. In some cases it is useful |
| // to set this to 0 when the two operands are related by inheritance but are |
| // different types. |
| |
| `ifdef UVM_ENABLE_DEPRECATED_API |
| bit check_type = 1; |
| `else |
| local bit check_type = 1; |
| `endif |
| |
| |
| // Variable -- NODOCS -- result |
| // |
| // This bit stores the number of miscompares for a given compare operation. |
| // You can use the result to determine the number of miscompares that |
| // were found. |
| |
| `ifdef UVM_ENABLE_DEPRECATED_API |
| int unsigned result = 0; |
| `else |
| local int unsigned result = 0; |
| `endif |
| |
| local int unsigned m_threshold; |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.2.1 |
| function new(string name=""); |
| super.new(name); |
| m_threshold = 1; |
| endfunction |
| |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.2.4 |
| static function void set_default (uvm_comparer comparer) ; |
| uvm_coreservice_t coreservice ; |
| coreservice = uvm_coreservice_t::get() ; |
| coreservice.set_default_comparer(comparer) ; |
| endfunction |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.2.5 |
| static function uvm_comparer get_default () ; |
| uvm_coreservice_t coreservice ; |
| coreservice = uvm_coreservice_t::get() ; |
| return coreservice.get_default_comparer() ; |
| endfunction |
| |
| |
| // Function -- NODOCS -- compare_field |
| // |
| // Compares two integral values. |
| // |
| // The ~name~ input is used for purposes of storing and printing a miscompare. |
| // |
| // The left-hand-side ~lhs~ and right-hand-side ~rhs~ objects are the two |
| // objects used for comparison. |
| // |
| // The size variable indicates the number of bits to compare; size must be |
| // less than or equal to 4096. |
| // |
| // The radix is used for reporting purposes, the default radix is hex. |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.3.1 |
| virtual function bit compare_field (string name, |
| uvm_bitstream_t lhs, |
| uvm_bitstream_t rhs, |
| int size, |
| uvm_radix_enum radix=UVM_NORADIX); |
| uvm_bitstream_t mask; |
| string msg; |
| |
| if(size <= 64) |
| return compare_field_int(name, lhs, rhs, size, radix); |
| |
| mask = -1; |
| mask >>= (UVM_STREAMBITS-size); |
| if((lhs & mask) !== (rhs & mask)) begin |
| case (radix) |
| UVM_BIN: begin |
| $swrite(msg, "%s: lhs = 'b%0b : rhs = 'b%0b", |
| name, lhs&mask, rhs&mask); |
| end |
| UVM_OCT: begin |
| $swrite(msg, "%s: lhs = 'o%0o : rhs = 'o%0o", |
| name, lhs&mask, rhs&mask); |
| end |
| UVM_DEC: begin |
| $swrite(msg, "%s: lhs = %0d : rhs = %0d", |
| name, lhs&mask, rhs&mask); |
| end |
| UVM_TIME: begin |
| $swrite(msg, "%s: lhs = %0t : rhs = %0t", |
| name, lhs&mask, rhs&mask); |
| end |
| UVM_STRING: begin |
| $swrite(msg, "%s: lhs = %0s : rhs = %0s", |
| name, lhs&mask, rhs&mask); |
| end |
| UVM_ENUM: begin |
| //Printed as decimal, user should cuse compare string for enum val |
| $swrite(msg, "%s: lhs = %0d : rhs = %0d", |
| name, lhs&mask, rhs&mask); |
| end |
| default: begin |
| $swrite(msg, "%s: lhs = 'h%0x : rhs = 'h%0x", |
| name, lhs&mask, rhs&mask); |
| end |
| endcase |
| print_msg(msg); |
| return 0; |
| end |
| return 1; |
| endfunction |
| |
| |
| |
| // Function -- NODOCS -- compare_field_int |
| // |
| // This method is the same as <compare_field> except that the arguments are |
| // small integers, less than or equal to 64 bits. It is automatically called |
| // by <compare_field> if the operand size is less than or equal to 64. |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.3.2 |
| virtual function bit compare_field_int (string name, |
| uvm_integral_t lhs, |
| uvm_integral_t rhs, |
| int size, |
| uvm_radix_enum radix=UVM_NORADIX); |
| logic [63:0] mask; |
| string msg; |
| |
| mask = -1; |
| mask >>= (64-size); |
| if((lhs & mask) !== (rhs & mask)) begin |
| case (radix) |
| UVM_BIN: begin |
| $swrite(msg, "%s: lhs = 'b%0b : rhs = 'b%0b", |
| name, lhs&mask, rhs&mask); |
| end |
| UVM_OCT: begin |
| $swrite(msg, "%s: lhs = 'o%0o : rhs = 'o%0o", |
| name, lhs&mask, rhs&mask); |
| end |
| UVM_DEC: begin |
| $swrite(msg, "%s: lhs = %0d : rhs = %0d", |
| name, lhs&mask, rhs&mask); |
| end |
| UVM_TIME: begin |
| $swrite(msg, "%s: lhs = %0t : rhs = %0t", |
| name, lhs&mask, rhs&mask); |
| end |
| UVM_STRING: begin |
| $swrite(msg, "%s: lhs = %0s : rhs = %0s", |
| name, lhs&mask, rhs&mask); |
| end |
| UVM_ENUM: begin |
| //Printed as decimal, user should cuse compare string for enum val |
| $swrite(msg, "%s: lhs = %0d : rhs = %0d", |
| name, lhs&mask, rhs&mask); |
| end |
| default: begin |
| $swrite(msg, "%s: lhs = 'h%0x : rhs = 'h%0x", |
| name, lhs&mask, rhs&mask); |
| end |
| endcase |
| print_msg(msg); |
| return 0; |
| end |
| return 1; |
| endfunction |
| |
| |
| // Function -- NODOCS -- compare_field_real |
| // |
| // This method is the same as <compare_field> except that the arguments are |
| // real numbers. |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.3.3 |
| virtual function bit compare_field_real (string name, |
| real lhs, |
| real rhs); |
| string msg; |
| |
| if(lhs != rhs) begin |
| $swrite(msg, name, ": lhs = ", lhs, " : rhs = ", rhs); |
| print_msg(msg); |
| return 0; |
| end |
| return 1; |
| endfunction |
| |
| // Stores the passed-in names of the objects in the hierarchy |
| local string m_object_names[$]; |
| local function string m_current_context(string name=""); |
| if (m_object_names.size() == 0) |
| return name; //?? |
| else if ((m_object_names.size() == 1) && (name=="")) |
| return m_object_names[0]; |
| else begin |
| string full_name; |
| foreach(m_object_names[i]) begin |
| if (i == m_object_names.size() - 1) |
| full_name = {full_name, m_object_names[i]}; |
| else |
| full_name = {full_name, m_object_names[i], "."}; |
| end |
| if (name != "") |
| return {full_name, ".", name}; |
| else |
| return full_name; |
| end |
| endfunction : m_current_context |
| |
| // Function -- NODOCS -- compare_object |
| // |
| // Compares two class objects using the <policy> knob to determine whether the |
| // comparison should be deep, shallow, or reference. |
| // |
| // The name input is used for purposes of storing and printing a miscompare. |
| // |
| // The ~lhs~ and ~rhs~ objects are the two objects used for comparison. |
| // |
| // The ~check_type~ determines whether or not to verify the object |
| // types match (the return from ~lhs.get_type_name()~ matches |
| // ~rhs.get_type_name()~). |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.3.4 |
| virtual function bit compare_object (string name, |
| uvm_object lhs, |
| uvm_object rhs); |
| int old_result ; |
| uvm_field_op field_op ; |
| uvm_policy::recursion_state_e prev_state; |
| bit ret_val = 1; |
| |
| // Fast Pass |
| if (rhs == lhs) |
| return ret_val; |
| |
| // Push the name on the stack |
| m_object_names.push_back(name); |
| |
| // Reference Fail |
| if (policy == UVM_REFERENCE && lhs != rhs) begin |
| print_msg_object(lhs, rhs); |
| ret_val = 0; |
| end |
| |
| // Fast fail on null |
| if (ret_val && (rhs == null || lhs == null)) begin |
| print_msg_object(lhs, rhs); |
| // if ((get_active_object_depth() == 0) && (lhs != null)) begin |
| // uvm_report_info("MISCMP", |
| // $sformatf("%0d Miscompare(s) for object %s@%0d vs. null", |
| // result, |
| // lhs.get_name(), |
| // lhs.get_inst_id()), |
| // get_verbosity()); |
| // end |
| ret_val = 0; |
| end |
| |
| // Hierarchical comparison |
| if (ret_val) begin |
| // Warn on possible infinite loop |
| prev_state = object_compared(lhs,rhs,get_recursion_policy(),ret_val); |
| if (prev_state != uvm_policy::NEVER) // |
| `uvm_warning("UVM/COPIER/LOOP", {"Possible loop when comparing '", |
| lhs.get_full_name(), "' to '", rhs.get_full_name(), "'"}) |
| |
| |
| push_active_object(lhs); |
| m_recur_states[lhs][rhs][get_recursion_policy()] = '{uvm_policy::STARTED,0}; |
| old_result = get_result(); |
| |
| // Check typename |
| // Implemented as if Mantis 6602 was accepted |
| if (get_check_type() && (lhs.get_object_type() != rhs.get_object_type())) begin |
| if(lhs.get_type_name() != rhs.get_type_name()) begin |
| print_msg({"type: lhs = \"", lhs.get_type_name(), "\" : rhs = \"", rhs.get_type_name(), "\""}); |
| end |
| else begin |
| print_msg({"get_object_type() for ",lhs.get_name()," does not match get_object_type() for ",rhs.get_name()}); |
| end |
| end |
| |
| field_op = uvm_field_op::m_get_available_op(); |
| field_op.set(UVM_COMPARE,this,rhs); |
| lhs.do_execute_op(field_op); |
| if (field_op.user_hook_enabled()) begin |
| ret_val = lhs.do_compare(rhs,this); |
| end |
| field_op.m_recycle(); |
| |
| // If do_compare() returned 1, check for a change |
| // in the result count. |
| if (ret_val && (get_result() > old_result)) |
| ret_val = 0; |
| |
| // Save off the comparison result |
| m_recur_states[lhs][rhs][get_recursion_policy()] = '{uvm_policy::FINISHED,ret_val}; |
| void'(pop_active_object()); |
| end // if (ret_val) |
| |
| // Pop the name off the stack |
| void'(m_object_names.pop_back()); |
| |
| // Only emit a message on a miscompare, and only if |
| // we're at the top level |
| if (!ret_val && (get_active_object_depth() == 0)) begin |
| string msg ; |
| |
| // If there are stored results |
| if(get_result()) begin |
| // If there's a display limit that we've hit |
| if (get_show_max() && (get_show_max() < get_result())) |
| $swrite(msg, "%0d Miscompare(s) (%0d shown) for object ", |
| result, show_max); |
| // Else there's either no limit, or we didn't hit it |
| else |
| $swrite(msg, "%0d Miscompare(s) for object ", result); |
| end |
| |
| uvm_pkg::uvm_report(sev, "MISCMP", $sformatf("%s%s@%0d vs. %s@%0d", msg, |
| (lhs == null) ? "<null>" : lhs.get_name(), |
| (lhs == null) ? 0 : lhs.get_inst_id(), |
| (rhs == null) ? "<null>" : rhs.get_name(), |
| (rhs == null) ? 0 : rhs.get_inst_id()), |
| get_verbosity(), `uvm_file, `uvm_line); |
| |
| end // if (!ret_val && (get_active_object_depth() == 1)) |
| |
| return ret_val; |
| endfunction |
| |
| |
| // Function -- NODOCS -- compare_string |
| // |
| // Compares two string variables. |
| // |
| // The ~name~ input is used for purposes of storing and printing a miscompare. |
| // |
| // The ~lhs~ and ~rhs~ objects are the two objects used for comparison. |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.3.6 |
| virtual function bit compare_string (string name, |
| string lhs, |
| string rhs); |
| string msg; |
| if(lhs != rhs) begin |
| msg = { name, ": lhs = \"", lhs, "\" : rhs = \"", rhs, "\""}; |
| print_msg(msg); |
| return 0; |
| end |
| return 1; |
| endfunction |
| |
| |
| // Function -- NODOCS -- print_msg |
| // |
| // Causes the error count to be incremented and the message, ~msg~, to be |
| // appended to the <miscompares> string (a newline is used to separate |
| // messages). |
| // |
| // If the message count is less than the <show_max> setting, then the message |
| // is printed to standard-out using the current verbosity and severity |
| // settings. See the <verbosity> and <sev> variables for more information. |
| |
| // @uvm-ieee 1800.2-2017 auto 16.3.3.7 |
| function void print_msg (string msg); |
| |
| string tmp = m_current_context(msg); |
| result++; |
| if((get_show_max() == 0) || |
| (get_result() <= get_show_max())) begin |
| msg = {"Miscompare for ", tmp}; |
| uvm_pkg::uvm_report(sev, "MISCMP", msg, get_verbosity(), `uvm_file, `uvm_line); |
| end |
| miscompares = { miscompares, tmp, "\n" }; |
| endfunction |
| |
| |
| |
| // Internal methods - do not call directly |
| |
| // print_msg_object |
| // ---------------- |
| |
| function void print_msg_object(uvm_object lhs, uvm_object rhs); |
| string tmp = $sformatf("%s: lhs = @%0d : rhs = @%0d", |
| m_current_context(), |
| (lhs != null ? lhs.get_inst_id() : 0), |
| (rhs != null ? rhs.get_inst_id() : 0)); |
| result++; |
| |
| if((get_show_max() == 0) || |
| (get_result() <= get_show_max())) begin |
| uvm_pkg::uvm_report(sev, |
| "MISCMP", |
| {"Miscompare for ", tmp}, |
| get_verbosity(), |
| `uvm_file, |
| `uvm_line); |
| end |
| |
| miscompares = { miscompares, tmp, "\n" }; |
| endfunction |
| |
| int depth; //current depth of objects |
| bit compare_map[uvm_object][uvm_object]; |
| |
| endclass |
| |
| function void uvm_comparer::flush(); |
| miscompares = "" ; |
| check_type = 1 ; |
| result = 0 ; |
| m_recur_states.delete(); |
| endfunction |
| |
| function uvm_policy::recursion_state_e uvm_comparer::object_compared( |
| uvm_object lhs, |
| uvm_object rhs, |
| uvm_recursion_policy_enum recursion, |
| output bit ret_val |
| ); |
| if (!m_recur_states.exists(lhs)) return NEVER ; |
| else if (!m_recur_states[lhs].exists(rhs)) return NEVER ; |
| else if (!m_recur_states[lhs][rhs].exists(recursion)) return NEVER ; |
| else begin |
| if (m_recur_states[lhs][rhs][recursion].state == FINISHED) |
| ret_val = m_recur_states[lhs][rhs][recursion].ret_val; |
| return m_recur_states[lhs][rhs][recursion].state ; |
| end |
| endfunction |
| |
| function string uvm_comparer::get_miscompares(); |
| return miscompares ; |
| endfunction |
| |
| function int unsigned uvm_comparer::get_result(); |
| return result ; |
| endfunction |
| |
| function void uvm_comparer::set_result(int unsigned result); |
| this.result = result ; |
| endfunction |
| |
| function void uvm_comparer::set_recursion_policy( uvm_recursion_policy_enum policy); |
| this.policy = policy ; |
| endfunction |
| |
| function uvm_recursion_policy_enum uvm_comparer::get_recursion_policy(); |
| return policy ; |
| endfunction |
| |
| function void uvm_comparer::set_check_type( bit enabled ); |
| check_type = enabled ; |
| endfunction |
| |
| function bit uvm_comparer::get_check_type(); |
| return check_type ; |
| endfunction |
| |
| function void uvm_comparer::set_show_max (int unsigned show_max); |
| this.show_max = show_max ; |
| endfunction |
| |
| function int unsigned uvm_comparer::get_show_max(); |
| return show_max ; |
| endfunction |
| |
| function void uvm_comparer::set_verbosity (int unsigned verbosity); |
| this.verbosity = verbosity ; |
| endfunction |
| |
| function int unsigned uvm_comparer::get_verbosity(); |
| return verbosity ; |
| endfunction |
| |
| function void uvm_comparer::set_severity (uvm_severity severity); |
| sev = severity ; |
| endfunction |
| |
| function uvm_severity uvm_comparer::get_severity(); |
| return sev ; |
| endfunction |
| |
| function void uvm_comparer::set_threshold (int unsigned threshold); |
| m_threshold = threshold; |
| endfunction |
| |
| function int unsigned uvm_comparer::get_threshold(); |
| return m_threshold; |
| endfunction |
| |