UVM工厂机制(二)

一、先回顾核心三要素

  1. 注册uvm_component_utils / uvm_object_utils 把类注册进全局工厂
  2. 创建 :必须用 xxx::type_id::create(名字, 父节点),禁止直接 new()
  3. 覆盖 :分类型覆盖实例覆盖,实现不改原有代码替换组件 / 对象

二、两大注册宏详解 & 必须遵守的写法

1. 组件注册(uvm_component 子类)

用于:driver/monitor/agent/env/test 等有层级、有 phase 的组件

复制代码
class my_driver extends uvm_driver#(my_trans);
  // 固定写法:类名传进去
  `uvm_component_utils(my_driver)

  function new(string name="my_driver", uvm_component parent=null);
    super.new(name, parent);
  endfunction
endclass

2. 对象注册(uvm_object 子类)

用于:transaction/config/sequence_item 无层级、无 phase 的数据类

复制代码
class my_trans extends uvm_sequence_item;
  `uvm_object_utils(my_trans)

  function new(string name="my_trans");
    super.new(name);
  endfunction
endclass

3. 带参数类 / 派生类注册(易错点)

带参数的类要用 uvm_component_param_utils

复制代码
class my_driver#(type T=uvm_sequence_item) extends uvm_driver#(T);
  `uvm_component_param_utils(my_driver#(T))
  // ...
endclass

硬性规则:

  1. 基类、派生类都必须注册才能被工厂覆盖
  2. 构造函数 new 形参必须和父类一致
  3. 组件必须带 parent,object 不用 parent

三、工厂标准创建语法(固定模板)

1. 组件创建

复制代码
// 在build_phase里创建
my_driver drv;
drv = my_driver::type_id::create("drv", this);

2. 事务 / 配置对象创建

复制代码
my_trans tr;
tr = my_trans::type_id::create("tr");

关键区别:

  • component create 传 name + parent
  • object create 只传 name

四、两种覆盖方式 完整可运行模板

前提:继承关系

复制代码
// 基类
class base_driver extends uvm_driver#(my_trans);
  `uvm_component_utils(base_driver)
  ...
endclass

// 派生替换类
class ext_driver extends base_driver;
  `uvm_component_utils(ext_driver)
  ...
endclass

方式 1:类型覆盖(全局所有实例都替换)

整个环境中所有 base_driver 都变成 ext_driver

写法 1:通过类的 type_id 调用(常用)
复制代码
// 在test的build_phase中设置
virtual function void build_phase(uvm_phase phase);
  super.build_phase(phase);
  // 基类.set_type_override(派生类类型)
  base_driver::type_id::set_type_override(ext_driver::get_type());
endfunction
写法 2:直接调用全局工厂
复制代码
uvm_factory factory = uvm_factory::get();
factory.set_type_override_by_type(
  base_driver::get_type(),
  ext_driver::get_type()
);

方式 2:实例覆盖(只替换某一个指定路径)

只替换 env.agent.drv 这一个实例,其他不变

复制代码
virtual function void build_phase(uvm_phase phase);
  super.build_phase(phase);
  uvm_factory factory = uvm_factory::get();
  
  factory.set_inst_override_by_type(
    base_driver::get_type(),    // 原始类型
    ext_driver::get_type(),     // 替换类型
    "uvm_test_top.env.agent.drv"// 完整实例路径
  );
endfunction
路径通配符支持
复制代码
// 匹配所有 agent 下的 drv
factory.set_inst_override_by_type(..., "*.agent.drv");

五、覆盖优先级(必考重点)

实例覆盖 > 类型覆盖

  1. 先查有没有实例路径匹配的覆盖 → 有就用实例覆盖
  2. 没有再查全局类型覆盖
  3. 都没有 → 创建原始类本身

六、工厂宏底层做了什么(原理深挖)

uvm_component_utils(my_driver) 宏展开后主要干 3 件事:

  1. 定义静态单例 type_iduvm_object_registry#(my_driver)
  2. 实现 get_type()get_type_name() 静态函数
  3. 把当前类信息注册到全局 uvm_factory 注册表

所以:

  • xxx::type_id 是工厂的代理入口
  • create() 不是直接 new,而是委托工厂去查表再创建

七、实战完整工程结构(标准复用架构)

复制代码
test_base
  └── env
       └── agent
            ├── driver(base_driver)
            ├── monitor
            └── sequencer

test_case1:不覆盖,用原生base_driver
test_case2:类型覆盖 → 全部换成ext_driver
test_case3:实例覆盖 → 只替换某一个driver

好处:env/agent/driver 代码完全不用改,只用在 test 里改工厂覆盖


八、工厂常见坑 & 避坑指南

  1. 只派生类注册、基类没注册 → 覆盖失效
  2. 覆盖写在 build_phase 之后(connect/run)→ 已经创建完了,覆盖无效
  3. 用 new () 代替 type_id::create () → 完全绕开工厂,覆盖不生效
  4. 替换类没有继承基类 → 多态不成立,编译 / 运行报错
  5. 实例路径写错 → 路径不匹配,覆盖不生效,建议用 print_topology() 打印路径核对
  6. 带参数类用普通 utils 宏 → 注册异常,必须用 _param_utils

九、常用工厂调试命令(仿真器控制台)

运行时打印工厂所有注册、覆盖信息:

复制代码
// 在test或env中调用
uvm_factory::get().print();
// 打印环境拓扑,看实例完整路径
uvm_root::get().print_topology();

十、一句话总结

UVM 工厂 = 宏注册入库 + create 统一申请 + 运行时查表替换 实现:组件解耦、动态替换、环境高度复用、测试用例极简扩展

相关推荐
liuluyang5306 天前
SystemVerilog常用关键词与函数
uvm·systermverilog
liuluyang5306 天前
SV主要关键词详解
fpga开发·uvm·sv
liuluyang53021 天前
clk_mux_seq sv改进
fpga开发·uvm
谷公子的藏经阁1 个月前
DVCon 2025 论文精华导读及下载链接
ai·论文·systemverilog·uvm·dvcon
蓝天下的守望者3 个月前
SystemVerilog中 `timescale的使用问题
systemverilog·uvm·vcs
蓝天下的守望者4 个月前
uvm_field_automation机制学习
uvm
Piri_LogicBldr4 个月前
【验证技能树】UVM 源码解读11 -- TLM2 —— Blocking vs Non-blocking 背后的建模取舍
uvm·芯片验证·验证技能
蓝天下的守望者4 个月前
uvm_config_db机制学习
uvm
Piri_LogicBldr4 个月前
【验证技能树】UVM 源码解读10 --TLM 是通信机制,还是架构边界?
uvm·芯片验证·验证技能