跳转至

输入查询内容

    本页内容

    自定义比较:检查类型设置和不匹配消息数量

    影响范围

    依赖 comparer 配置持久化、跨类型比较、继承注册或手写 get_type_name、旧 init/scope 接口或消息数量时,逐项核对。

    具体差异

    以下变化按实际使用的 comparer 入口分别核对:

    使用点 VCS UVM-1.2 VCS 2.0
    check_type=1 的类型判据 比较 get_type_name() 字符串 比较 get_object_type() wrapper;同名但 wrapper 不同会新增不匹配,不同名共用 wrapper 时可能漏检
    顶层 uvm_object::compare() 的配置 保留 check_type 设置 默认和显式 comparer 都先 flush(),并将 check_type 重置为 1
    show_max=0 不显示不匹配消息 不限制消息数量
    compare_field_intsize>64 静默截断 新增 UVM/COMPARER/INT/BAD_SIZE 并返回 0
    消息与旧接口 原消息格式,提供 init()print_rollup()scope 字段名内嵌进消息,删除上述接口;具体替代见下文

    wrapper 相同时,若字段与 do_compare 均通过,目标可返回相等;类型名比较仅决定 wrapper 不同分支中的消息内容。不能将类型判据变化概括为单向加严。

    修改方案

    uvm_comparer::init() 改用 uvm_comparer::get_default() 获取默认实例;需要清理状态时另调 flush()不能用较大的 show_max 恢复静默,它只放宽输出数量;可在 severity 为 UVM_INFO 时将 comparer verbosity 设为高于报告阈值,或在报告配置中针对 MISCMP 设置 action,并保留 compare 返回值与 result 检查。顶层 obj.compare(rhs, comparer) 对显式传入和默认 comparer 都会先 flush(),本实现将 check_type 重置为 1;确需跨类型比较时,可先 flush()、再 set_check_type(0),然后直接调用 comparer.compare_object(name, lhs, rhs),由调用方管理独立比较之间的清理。

    业务若要求类型名不同即比较失败,不能只依赖 check_type=1。例如两个派生类继承同一个已注册基类的 wrapper,却分别覆盖 get_type_name():旧版会报告类型不匹配,目标版在字段相等时可能通过。对两个非空顶层对象,可在原调用处增加类型名限制:

    1
    2
    3
    4
    5
    // 修改前:依赖库的类型判据
    same = lhs.compare(rhs, cmp);
    
    // 修改后:业务要求类型名相同才继续比较
    same = (lhs.get_type_name() == rhs.get_type_name()) && lhs.compare(rhs, cmp);
    

    此检查只约束该调用处的顶层类型;嵌套对象仍需按业务契约比较。同名不保证属于同一类型,具体派生类的 factory 注册和 wrapper 也应与实际类型一致。检查 same,不要用上次保留的 comparer result 代替短路返回值。comparer_fact_probe 在 check_type 始终为 1 时实测原调用返回 0 / 1,增加上述限制后两版均返回 0;目标 comparer 与 Accellera 2.0 源码相同。

    仅当业务确实允许比较不同派生类型时,旧代码可能先关闭类型检查:

    cmp.check_type = 0;
    same = lhs.compare(rhs, cmp);
    

    目标版的对象 compare 入口会重置该设置。应通过 comparer 入口完成此类比较:

    1
    2
    3
    cmp.flush();
    cmp.set_check_type(0);
    same = cmp.compare_object("pair", lhs, rhs);
    

    每次独立比较前先清理旧状态再配置。普通同类型事务比较无需改用此写法。另注意 show_max=0 在目标表示不限制消息数,不能再用它静音;比较是否成功应检查 same,不能按日志是否为空判断。

    其他 comparer 调用按下面处理:

    旧依赖 修改方法
    uvm_comparer::init() uvm_comparer::get_default() 获取默认实例;独立比较需要清理时另调 flush()
    show_max=0 静音 INFO 比较消息可将 comparer verbosity 配为高于报告阈值,或按 MISCMP 的 severity/ID 设置 action;仍检查比较返回值
    compare_field_int 比较超过 64 位的数据 改用 compare_field(name, lhs, rhs, size, radix),同时核对 size 不超过实际 bitstream 类型容量
    直接操作旧 scope、调用 print_rollup() 改由比较入口管理路径,通过返回值及 get_result() 获取结果,自定义摘要由用户报告逻辑生成;旧 scope 和摘要调用没有统一改名

    保留自己的字段比较逻辑;只替换其依赖的接口和消息策略,不要因删除 scope 而跳过字段比较。

    兼容措施与范围:主库保留 policy/show_max/verbosity/sev/miscompares/result 等公开字段,可继续直写;这不恢复类型名判据或 show_max=0 的旧含义,也不阻止顶层 compare 重置 check_type。deprecated / legacy field 开关及 compat 包均不恢复旧类型判据。相关实现:uvm_comparer.svh:107-204

    迁移后验证

    注入字段不匹配并检查返回值及消息数;分别核对类型名与 wrapper,尤其是不同类型名共用 wrapper 的对象;重复比较核对 flush 后 check_type;超过 64 位字段改用适合宽度的接口。

    记录本条结果M02-005 · 状态与证据

    已有实测与复现

    补充查阅

    扫描定位与记录结果

    有扫描规则,覆盖部分可识别写法。脚本检查名称、参数和部分 FLAG 表达式,并对少数直接声明关联使用点;未做完整类型解析或预处理,行为结果仍需验证。

    uv run tools/uvm_migration_scan.py <SoC代码目录> --rules S8,C28
    

    核对命中对象及生效分支后,在条目清单记录修改与验证证据;扫描规则记录用于整理命中位置。

    扫描器下载、输入范围与报告说明

    实现细节

    类型判据实现注释引用 Mantis 6602;内部 compare_map 改为 bit[lhs][rhs]。顶层 uvm_object::compare() 调用 flush() 的路径见目标 uvm_object.svh:987-990。公开接口的影响及处理见上方对照。

    源码与标准依据

    核查方式:源码与实测核对。实测仅覆盖本条所列场景。

    1. 类型判据从类型名改为 wrapper

    VCS 内置 UVM-1.2check_type 启用时比较 get_type_name()uvm_object.svh:1137–1141

    VCS 内置 uvm-ieee-2020-2.0:外层条件比较 get_object_type();内层类型名比较只选择消息内容:uvm_comparer.svh:432–439。不同类型名共用 wrapper 时,不进入这个类型不匹配分支。

    2. 顶层 compare 会重新启用 check_type

    VCS 内置 UVM-1.2:顶层入口清理结果与比较映射:uvm_object.svh:1104–1113;原 check_type 设置保留的效果另见本条实测。

    VCS 内置 uvm-ieee-2020-2.0:顶层 compare 调用 flush:uvm_object.svh:987–994;flush 明确将 check_type 设为 1:uvm_comparer.svh:568–573。两个函数需连起来看。

    3. show_max=0 从抑制消息变为不限制数量

    VCS 内置 UVM-1.2result 先递增,只有 result <= show_max 才输出:uvm_comparer.svh:342–354

    VCS 内置 uvm-ieee-2020-2.0:输出条件增加 get_show_max() == 0uvm_comparer.svh:524–534。0 因而直接满足数量条件,仍受报告 verbosity/action 控制。

    4. compare_field_int 增加 size>64 的错误分支

    VCS 内置 UVM-1.2:旧入口直接构造 64 位 mask:uvm_comparer.svh:202–211;超宽截断的具体观察见本条实测。

    VCS 内置 uvm-ieee-2020-2.0:新入口在构造 mask 前检查 size,报 BAD_SIZE 并返回 0:uvm_comparer.svh:274–284。旧 init/print_rollup 的声明位置保留在完整引用中。

    补充定位

    VCS 内置 UVM-1.2uvm_comparer.svh:34(class)、uvm_comparer.svh:411static function uvm_comparer init())、uvm_comparer.svh:364(print_rollup);uvm_object.svh:1137(按 get_type_name 判断类型不匹配)

    VCS 内置 uvm-ieee-2020-2.0uvm_comparer.svh:40(class extends uvm_policy)、uvm_comparer.svh:432(get_object_type 检查)、uvm_comparer.svh:282(BAD_SIZE 报错)、uvm_comparer.svh:528(show_max 逻辑)

    标准依据:IEEE 1800.2-2020 §16.3.3.9(get_result)、§16.3.5.1(show_max 默认 1)、§16.3.6(threshold)

    标为“源码标注”或未注明原文核对的条款仅作定位;具体库行为以源码和实测为准。

    引用代码(高亮为引用行);上方行号链接可直接定位。

    src/vcs-uvm-1.2/base/uvm_comparer.svh
    //
    //------------------------------------------------------------------------------
    
    class uvm_comparer;
    
      // Variable: policy
      //
      // Determines whether comparison is UVM_DEEP, UVM_REFERENCE, or UVM_SHALLOW.
    
      uvm_recursion_policy_enum policy = UVM_DEFAULT_POLICY;
    
    
      // Variable: show_max
      //
      // Sets the maximum number of messages to send to the printer for miscompares
      // of an object. 
    
      int unsigned show_max = 1;
    

    src/vcs-uvm-1.2/base/uvm_comparer.svh
      // 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.
    
      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
          uvm_object::__m_uvm_status_container.scope.set_arg(name);
          case (radix)
            UVM_BIN: begin
                  $swrite(msg, "lhs = 'b%0b : rhs = 'b%0b", 
                           lhs&mask, rhs&mask);
    

    src/vcs-uvm-1.2/base/uvm_comparer.svh
      // is printed to standard-out using the current verbosity and severity
      // settings. See the <verbosity> and <sev> variables for more information.
    
      function void print_msg (string msg);
        uvm_root root;
        uvm_coreservice_t cs;
        cs = uvm_coreservice_t::get();
        root = cs.get_root();
    
        result++;
        if(result <= show_max) begin
           msg = {"Miscompare for ", uvm_object::__m_uvm_status_container.scope.get(), ": ", msg};
           root.uvm_report(sev, "MISCMP", msg, verbosity, `uvm_file, `uvm_line);
        end
        miscompares = { miscompares, uvm_object::__m_uvm_status_container.scope.get(), ": ", msg, "\n" };
      endfunction
    

    src/vcs-uvm-1.2/base/uvm_comparer.svh
      // ------------
    
      //Need this function because sformat doesn't support objects
      function void print_rollup(uvm_object rhs, uvm_object lhs);
        uvm_root root;
        uvm_coreservice_t cs;
    
        string msg;
        cs = uvm_coreservice_t::get();
        root = cs.get_root();
        if(uvm_object::__m_uvm_status_container.scope.depth() == 0) begin
          if(result && (show_max || (uvm_severity'(sev) != UVM_INFO))) begin
            if(show_max < result) 
               $swrite(msg, "%0d Miscompare(s) (%0d shown) for object ",
                 result, show_max);
            else begin
               $swrite(msg, "%0d Miscompare(s) for object ", result);
            end
    

    src/vcs-uvm-1.2/base/uvm_comparer.svh
      // init ??
    
      static function uvm_comparer init();
        if(uvm_default_comparer==null) uvm_default_comparer=new;
        return uvm_default_comparer;
      endfunction
    
    
      int depth;                      //current depth of objects
      uvm_object compare_map[uvm_object];
      uvm_scope_stack scope    = new;
    
    endclass
    

    完整源码 · SHA256:170399db43a721fb81dafca9418152b2acadc946f8d8c657853c84d81440d1ba

    src/vcs-uvm-1.2/base/uvm_object.svh
        __m_uvm_status_container.comparer = uvm_default_comparer;
      comparer = __m_uvm_status_container.comparer;
    
      if(!__m_uvm_status_container.scope.depth()) begin
        comparer.compare_map.delete();
        comparer.result = 0;
        comparer.miscompares = "";
        comparer.scope = __m_uvm_status_container.scope;
        if(get_name() == "")
          __m_uvm_status_container.scope.down("<object>");
        else
          __m_uvm_status_container.scope.down(this.get_name());
      end
      if(!done && (rhs == null)) begin
        if(__m_uvm_status_container.scope.depth()) begin
          comparer.print_msg_object(this, rhs);
        end
        else begin
          comparer.print_msg_object(this, rhs);
    

    src/vcs-uvm-1.2/base/uvm_object.svh
        done = 1;  //don't do any more work after this case, but do cleanup
      end
    
      if(!done && comparer.check_type && (rhs != null) && (get_type_name() != rhs.get_type_name())) begin
        __m_uvm_status_container.stringv = { "lhs type = \"", get_type_name(), 
                         "\" : rhs type = \"", rhs.get_type_name(), "\""};
        comparer.print_msg(__m_uvm_status_container.stringv);
      end
    
      if(!done) begin
        comparer.compare_map[rhs]= this;
        __m_uvm_field_automation(rhs, UVM_COMPARE, "");
        dc = do_compare(rhs, comparer);
      end
    
      if(__m_uvm_status_container.scope.depth()==1)  begin
        __m_uvm_status_container.scope.up();
      end
    

    完整源码 · SHA256:5bacc747c3d5a16a0206864bd84eedf1aeff153a7152dcb0d0e40af208fbe4ec

    src/vcs-uvm-ieee-2020-2.0/base/uvm_comparer.svh
    //------------------------------------------------------------------------------
    
    // @uvm-ieee 1800.2-2020 auto 16.3.1
    class uvm_comparer extends uvm_policy;
    
       // @uvm-ieee 1800.2-2020 auto 16.3.2.3
       `uvm_object_utils(uvm_comparer)
    
      // @uvm-ieee 1800.2-2020 auto 16.3.2.2
      extern virtual function void flush();
    
      // @uvm-ieee 1800.2-2020 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
      );
    

    src/vcs-uvm-ieee-2020-2.0/base/uvm_comparer.svh
      // by <compare_field> if the operand size is less than or equal to 64.
    
      // @uvm-ieee 1800.2-2020 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;
        if(size > 64) begin
            `uvm_error("UVM/COMPARER/INT/BAD_SIZE",$sformatf("compare_field_int cannot be called with operand size of more than 64 bits. Input argument size=%0d",size))
            return 0;
        end
    
        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
    

    src/vcs-uvm-ieee-2020-2.0/base/uvm_comparer.svh
          // 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();
    

    src/vcs-uvm-ieee-2020-2.0/base/uvm_comparer.svh
      // settings. See the <verbosity> and <sev> variables for more information.
    
      // @uvm-ieee 1800.2-2020 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);
    

    src/vcs-uvm-ieee-2020-2.0/base/uvm_comparer.svh
    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 ;
    

    完整源码 · SHA256:da5c3a1045bfe5a1e4c44375ca886115c903ca45a2081cd08161dc3af163cede

    src/vcs-uvm-ieee-2020-2.0/base/uvm_object.svh
    // compare
    // -------
    
    function bit  uvm_object::compare (uvm_object rhs,
                                       uvm_comparer comparer=null);
      if (comparer == null) comparer = uvm_comparer::get_default();
      if (comparer.get_active_object_depth() == 0) 
        comparer.flush() ;
      compare = comparer.compare_object(get_name(),this,rhs);
    
    endfunction
    
    
    // do_compare
    // ----------
    
    function bit  uvm_object::do_compare (uvm_object rhs,
                                          uvm_comparer comparer);
      return 1;
    

    完整源码 · SHA256:53c99f38f1ce6cb3c6a215858b84938d18eb858f9396d3dba281a32addb3c9c9

    术语解释 · 兼容配置

    返回Comparer / Printer / Packer / Recorder · 迁移清单

    版本适用范围