一、先回顾核心三要素
- 注册 :
uvm_component_utils / uvm_object_utils把类注册进全局工厂 - 创建 :必须用
xxx::type_id::create(名字, 父节点),禁止直接new() - 覆盖 :分类型覆盖 、实例覆盖,实现不改原有代码替换组件 / 对象
二、两大注册宏详解 & 必须遵守的写法
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
硬性规则:
- 基类、派生类都必须注册才能被工厂覆盖
- 构造函数
new形参必须和父类一致- 组件必须带
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");
五、覆盖优先级(必考重点)
实例覆盖 > 类型覆盖
- 先查有没有实例路径匹配的覆盖 → 有就用实例覆盖
- 没有再查全局类型覆盖
- 都没有 → 创建原始类本身
六、工厂宏底层做了什么(原理深挖)
uvm_component_utils(my_driver) 宏展开后主要干 3 件事:
- 定义静态单例 type_id :
uvm_object_registry#(my_driver) - 实现
get_type()、get_type_name()静态函数 - 把当前类信息注册到全局
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 里改工厂覆盖
八、工厂常见坑 & 避坑指南
- 只派生类注册、基类没注册 → 覆盖失效
- 覆盖写在 build_phase 之后(connect/run)→ 已经创建完了,覆盖无效
- 用 new () 代替 type_id::create () → 完全绕开工厂,覆盖不生效
- 替换类没有继承基类 → 多态不成立,编译 / 运行报错
- 实例路径写错 → 路径不匹配,覆盖不生效,建议用
print_topology()打印路径核对 - 带参数类用普通 utils 宏 → 注册异常,必须用
_param_utils
九、常用工厂调试命令(仿真器控制台)
运行时打印工厂所有注册、覆盖信息:
// 在test或env中调用
uvm_factory::get().print();
// 打印环境拓扑,看实例完整路径
uvm_root::get().print_topology();
十、一句话总结
UVM 工厂 = 宏注册入库 + create 统一申请 + 运行时查表替换 实现:组件解耦、动态替换、环境高度复用、测试用例极简扩展