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 统一申请 + 运行时查表替换 实现:组件解耦、动态替换、环境高度复用、测试用例极简扩展

相关推荐
啄缘之间17 天前
8.【学习】工业级详细接口约束&覆盖率
开发语言·笔记·学习·uvm·sv
Ether IC Verifier18 天前
SystemVerilog 数据类型详解
php·systemverilog·uvm·ic验证
liuluyang5301 个月前
UVM 工厂机制 完整可编译运行 Demo
uvm·uvm工厂机制
liuluyang5301 个月前
UVM工厂机制
uvm·工厂机制
liuluyang5302 个月前
SystemVerilog常用关键词与函数
uvm·systermverilog
liuluyang5302 个月前
SV主要关键词详解
fpga开发·uvm·sv
liuluyang5302 个月前
clk_mux_seq sv改进
fpga开发·uvm
谷公子的藏经阁2 个月前
DVCon 2025 论文精华导读及下载链接
ai·论文·systemverilog·uvm·dvcon
蓝天下的守望者5 个月前
SystemVerilog中 `timescale的使用问题
systemverilog·uvm·vcs