乐于分享
好东西不私藏

UVM 源码精读 Day3:核心对象体系 uvm_object

UVM 源码精读 Day3:核心对象体系 uvm_object

— UVM 源码精读系列 - Day 3 —

uvm_object 核心架构:中心类与七大操作体系

UVM 源码精读 Day3:核心对象体系 uvm_object

`uvm_object` 是 UVM 中所有可见对象的根基,包括 `uvm_component`、`uvm_sequence_item`、`uvm_sequence` 乃至寄存器模型中的 `uvm_reg` 都直接或间接继承自它。理解 `uvm_object` 的设计,等于掌握了 UVM 整个对象体系的 DNA。

一、类层次与核心属性

virtual class uvm_object extends uvm_void;

`uvm_object` 直接继承自 `uvm_void`(一个空基类,作为整个 UVM 类树的根)。这种设计使得 UVM 可以在最顶层统一处理所有对象的公共行为。

核心属性

属性类型作用
`m_leaf_name``string`对象实例名,可通过 `get_name()`/`set_name()` 访问
`m_inst_id``int`实例唯一 ID,通过静态计数器递增分配
`m_inst_count``static int`全局实例计数器
`use_uvm_seeding``static bit`是否使用 UVM 全局种子机制(默认 1)

设计意图每个对象都自带名称和唯一 ID,这是调试、打印、比较的基础。`m_inst_id` 保证了在大量对象实例化时,日志中总能精确定位到具体对象。

二、工厂模式创建 create()

static function uvm_object create (string name="");

`create()` 是 UVM 工厂模式的核心入口。与直接调用 `new()` 不同,`create()` 通过 `uvm_factory` 在运行时查找对应的 `uvm_object_wrapper`,由工厂负责实例化正确的类型。

function uvm_object uvm_object::create (string name="");
  uvm_coreservice_t cs = uvm_coreservice_t::get();
  uvm_factory factory = cs.get_factory();
  return factory.create_object_by_type(get_type(), get_full_name(), name);
endfunction

关键设计思想

  • 运行时多态`create()` 返回的是 `uvm_object` 基类句柄,但实际类型由工厂根据注册信息决定。这使得在测试平台中可以通过配置覆盖(override)将 `my_driver` 替换为 `my_err_driver`,而无需修改源代码。
  • 与 `new()` 的区别`new()` 是编译时绑定,而 `create()` 是运行时绑定。UVM 推荐所有可复用组件都通过 `create()` 创建。

三、数据复制体系 copy / clone

clone 完整复制

function uvm_object uvm_object::clone();
  uvm_object tmp;
  tmp = this.create(get_name());
  if(tmp == null) begin
    uvm_report_warning("CRFLD", ...);
  end
  else begin
    tmp.copy(this);
  end
  return(tmp);
endfunction

`clone()` 的精髓是"先创建,再复制"利用 `create()` 确保工厂覆盖生效,然后调用 `copy()` 完成数据深拷贝。

copy 与 do_copy 钩子

function void uvm_object::copy (uvm_object rhs, uvm_copier copier=null);
  uvm_coreservice_t coreservice = uvm_coreservice_t::get();
  uvm_copier m_copier;
  if(copier == null) begin
    m_copier = coreservice.get_default_copier();
  end else begin
    m_copier = copier;
  end
  if(m_copier.get_active_object_depth() == 0) begin
    m_copier.flush();
  end
  m_copier.copy_object(this, rhs);
endfunction

钩子模式`copy()` 是公共接口,负责初始化 `uvm_copier` 并委托给 `m_copier.copy_object()`。最终底层会回调 `do_copy()`,子类通过覆盖 `do_copy()` 实现自定义字段复制。

function void uvm_object::do_copy (uvm_object rhs);
  return;
endfunction

设计意图将"框架流程"与"业务逻辑"分离。框架负责递归遍历、深度控制、循环检测,用户只需在 `do_copy()` 中处理自己新增的字段。

四、数据比较体系 compare

function bit uvm_object::compare (uvm_object rhs, uvm_comparer comparer=null);
  if(comparer == null) begin
    comparer = uvm_comparer::get_default();
  end
  if(comparer.get_active_object_depth() == 0) begin
    comparer.flush();
  end
  compare = comparer.compare_object(get_name(), this, rhs);
endfunction

与 `copy` 完全对称的设计:初始化 `uvm_comparer` -> 委托给 `comparer.compare_object()` -> 底层回调 `do_compare()`。

function bit uvm_object::do_compare (uvm_object rhs, uvm_comparer comparer);
  return 1;
endfunction

默认返回 `1`(相等),子类覆盖后添加自定义比较逻辑。这种设计让比较操作可以递归进行——`uvm_comparer` 会自动处理嵌套对象的深度比较。

五、打印体系 print / sprint / convert2string

三种打印接口

方法返回值用途
`print(printer)``void`直接输出到文件/屏幕
`sprint(printer)``string`返回格式化字符串,用于拼接日志
`convert2string()``string`自定义简短描述,子类覆盖
function void uvm_object::print(uvm_printer printer=null);
  if(printer==null) begin
    printer = uvm_printer::get_default();
  end
  $fwrite(printer.get_file(), sprint(printer));
endfunction

function string uvm_object::sprint(uvm_printer printer=null);
  if(printer==null) begin
    printer = uvm_printer::get_default();
  end
  if(printer.get_active_object_depth() == 0) begin
    printer.flush();
    name = printer.get_root_enabled() ? get_full_name() : get_name();
  end else begin
    name = get_name();
  end
  printer.print_object(name, this);
  return printer.emit();
endfunction

关键细节

  • `get_active_object_depth() == 0` 判断当前是否为顶层对象,顶层才刷新 printer 缓冲区,避免中间对象干扰格式化。
  • `get_root_enabled()` 控制是否打印完整层次名(`env.agent.driver`)还是仅打印局部名(`driver`)。
  • 最终通过 `printer.print_object()` 递归打印所有字段,底层回调 `do_print()`。

六、序列化体系 pack / unpack

pack 族方法

function int uvm_object::pack (ref bit bitstream[], input uvm_packer packer=null);
  m_pack(packer);
  packer.get_packed_bits(bitstream);
  return packer.get_packed_size();
endfunction
方法输出格式
`pack(bitstream[])``bit` 数组
`pack_bytes(bytestream[])``byte unsigned` 数组
`pack_ints(intstream[])``int unsigned` 数组

三种接口共享同一个 `m_pack()` 核心,只是最终获取二进制数据的格式不同。

function void uvm_object::m_pack (inout uvm_packer packer);
  if(packer == null) begin
    packer = uvm_packer::get_default();
  end
  if(packer.get_active_object_depth() == 0) begin
    packer.flush();
  end
  packer.pack_object(this);
endfunction

设计意图序列化体系是 UVM 跨进程通信和事务记录的基础。`uvm_packer` 负责将对象字段按位/字节/int 打包成连续的二进制流,支持大小端转换和深度控制。

unpack 反向恢复

与 pack 完全对称,通过 `uvm_unpacker` 将二进制流还原为对象字段。同样是"框架处理递归,用户覆盖 `do_unpack()` 处理自定义字段"的模式。

七、记录体系 record

function void uvm_object::record (uvm_recorder recorder=null);
  if(recorder == null) begin
    recorder = uvm_recorder::get_default();
  end
  if(recorder.get_active_object_depth() == 0) begin
    recorder.flush();
  end
  recorder.record_object(get_name(), this);
endfunction

`record()` 用于将对象状态持久化到波形数据库(如 FSDB、TRN 等)。与 print/pack 相同的设计范式,底层回调 `do_record()`。

八、field automation 标志位

`uvm_object_globals.svh` 中定义了 `uvm_field_flag_t` 类型和一系列标志位,用于在 `uvm_field_*` 宏中声明字段需要支持哪些操作。

typedef bit [31:0] uvm_field_flag_t;

parameter uvm_field_flag_t UVM_COPY       = 32'b00000000000000000000000000000001;
parameter uvm_field_flag_t UVM_COMPARE    = 32'b00000000000000000000000000000010;
parameter uvm_field_flag_t UVM_PRINT      = 32'b00000000000000000000000000000100;
parameter uvm_field_flag_t UVM_RECORD     = 32'b00000000000000000000000000001000;
parameter uvm_field_flag_t UVM_PACK       = 32'b00000000000000000000000000010000;
parameter uvm_field_flag_t UVM_UNPACK     = 32'b00000000000000000000000000100000;

使用方式

`uvm_object_utils_begin(my_item)
  `uvm_field_int(data, UVM_ALL_ON | UVM_DEC)
  `uvm_field_string(name, UVM_DEFAULT)
`uvm_object_utils_end

`UVM_ALL_ON` 等价于 `UVM_COPY | UVM_COMPARE | UVM_PRINT | UVM_RECORD | UVM_PACK | UVM_UNPACK`,表示该字段参与所有自动化操作。也可以通过按位或组合精确控制,例如 `UVM_COPY | UVM_COMPARE` 表示只参与复制和比较,不参与打印和序列化。

设计意图用位掩码实现编译时/运行时的行为控制,既灵活又高效。这种设计使得一个字段可以在不同场景下有不同的处理方式——例如密码字段可以设置 `UVM_NOCOMPARE` 避免在比较时暴露。

九、随机化事件回调

function void uvm_object::pre_randomize();
  m_field_automation(null, UVM_PRE_RANDOMIZE, "");
endfunction

function void uvm_object::post_randomize();
  m_field_automation(null, UVM_POST_RANDOMIZE, "");
endfunction

`pre_randomize()` 和 `post_randomize()` 是 SystemVerilog 内建随机化机制的标准回调。UVM 在这里增加了 `m_field_automation()` 调用,使得 `uvm_field_*` 宏注册的字段可以在随机化前后自动执行额外的约束处理。

十、今日总结

体系公共接口钩子方法辅助类核心设计模式
创建`create()``uvm_factory`工厂模式
复制`copy()` / `clone()``do_copy()``uvm_copier`模板方法模式
比较`compare()``do_compare()``uvm_comparer`模板方法模式
打印`print()` / `sprint()``do_print()``uvm_printer`模板方法模式
序列化`pack()` / `unpack()``do_pack()` / `do_unpack()``uvm_packer` / `uvm_unpacker`模板方法模式
记录`record()``do_record()``uvm_recorder`模板方法模式
自动化`m_field_automation()`位掩码标志控制

`uvm_object` 的设计堪称 SystemVerilog 中模板方法模式(Template Method Pattern)的教科书级实现:公共接口定义算法骨架,钩子方法(`do_*`)留待子类扩展。这种设计带来的好处是

1. 一致性所有数据操作方法(copy/compare/print/pack/record)遵循相同的"初始化工具类 -> 委托给工具类 -> 回调钩子"流程。 2. 可扩展性子类只需覆盖对应的 `do_*` 方法,无需关心递归深度、循环引用检测等复杂逻辑。 3. 可组合性通过 `uvm_field_*` 宏和标志位,可以在不修改类定义的情况下精细控制字段行为。