一. 讲解 uvm‑component 的 new(name,parent) 参数
1、先看普通uvm_object 和 uvm_component 的构造区别
- uvm_object(事务、寄存器、sequence、transaction)
systemverilog
function new(string name = "obj");
只需要名字,没有parent参数。
它属于数据对象,没有层级结构,可以随时创建、销毁、拷贝。
- uvm_component(driver/monitor/agent/env/test)
systemverilog
function new(string name = "clk_rst_driver",uvm_component parent=null);
拥有两个入参:name、parent
2、parent 参数本质作用:搭建UVM树层级
UVM‑TB 是一棵组件树:
uvm_test_top
└── tb_env
└── clk_rst_agent
└── clk_rst_driver
- 创建driver的时候传入
parent = clk_rst_agent - parent就是当前组件的父节点句柄
父节点带来的核心能力
-
层级路径自动生成
driver 的完整路径:
uvm_test_top.tb_env.clk_rst_agent.clk_rst_driverUVM依靠每一层parent向上遍历拼接路径;打印log、uvm‑info打印层级名称全部依赖它。
-
phase机制可以逐层递归执行
build_phase / connect_phase / run_phase
UVM遍历整颗组件树,从父组件挨个向下调用所有子组件的phase函数。没有parent就找不到孩子。
-
config_db配置数据库层级查找
uvm_config_db#()::get(this,"","xxx",val)组件会顺着parent向上一层层父组件空间搜寻配置参数。
-
消息回调、report机制、dump波形、资源管理
全部依托组件树。
3、super.new(name,parent); 干什么
systemverilog
super.new(name, parent);
调用父类 uvm_component 的构造函数;
底层源码内部执行:
- 保存成员变量
m_name = name - 保存成员变量
m_parent = parent - 如果parent不为空,就把自己注册添加进父组件的子组件列表
至此挂载到UVM组件树上。
4、什么时候 parent = null
只有最顶层组件 uvm_test_top 的父节点为 null;
其余所有 driver、agent、monitor、scoreboard 在build阶段实例化的时候,都必须传入this当作parent。
示例标准写法:
systemverilog
clk_rst_driver drv;
drv = clk_rst_driver::type_id::create("clk_rst_driver",this);
// this 就是当前agent,作为driver的父组件
type_id::create(name,parent)本质就是封装new函数,第二个参数就是parent。
5、大白话总结
- uvm_object:普通数据包,不需要家族关系,只要名字;
- uvm_component:房子里面各个房间,需要记录自己的上层父房间(parent),构成完整家谱树;
- parent就是用来搭建UVM树形结构,支撑phase、路径名、config‑db、日志层级。
6、容易踩坑点
- 创建component忘记传入parent(第二个参数),parent默认等于null;
那么该组件脱离组件树,phase不会自动执行,config_db读取失败,log路径异常。 - 数据类(transaction/reg)不要继承uvm‑component,不需要parent。
二. UVM‑Component 完整层级搭建标准模板
下面整套是验证通用规范,从 top→env→agent→driver/monitor/sequencer,附带注释、规范写法、避坑要点。
1、整体树状层级结构(标准架构)
uvm_test_top //顶层test,parent=null
└── tb_env //环境层
└── clk_rst_agent //功能agent
├─clk_rst_driver
├─clk_rst_monitor
└─clk_rst_sequencer
所有子组件实例化第二个入参必须传
this,绑定父节点parent,挂载UVM组件树。
模板1:driver 标准模板
c
class clk_rst_driver extends uvm_driver#(clk_rst_trans);
`uvm_component_utils(clk_rst_driver)
//构造函数
function new(string name = "clk_rst_driver",uvm_component parent=null);
super.new(name,parent);
endfunction
virtual clk_rst_if vif; //虚拟接口
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
//从config‑db获取接口
if(!uvm_config_db#(virtual clk_rst_if)::get(this,"","clk_rst_vif",vif)) begin
`uvm_fatal("NO_VIF","无法获取clk_rst虚拟接口!")
end
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
`uvm_info("DRV_RUN","时钟复位驱动启动",UVM_MEDIUM)
forever begin
seq_item_port.get_next_item(req);
drive_signal(req);
seq_item_port.item_done();
end
endtask
virtual task drive_signal(clk_rst_trans req);
//引脚驱动逻辑
endtask
endclass
模板2:monitor 标准模板
c
class clk_rst_monitor extends uvm_monitor;
`uvm_component_utils(clk_rst_monitor)
function new(string name="clk_rst_monitor",uvm_component parent=null);
super.new(name,parent);
endfunction
virtual clk_rst_if vif;
uvm_analysis_port#(clk_rst_trans) ap;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
ap = new("ap",this);
if(!uvm_config_db#(virtual clk_rst_if)::get(this,"","clk_rst_vif",vif)) begin
`uvm_fatal("NO_VIF","monitor获取接口失败")
end
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
sample_signal();
end
endtask
virtual task sample_signal();
//采样引脚,发包到analysis_port
endtask
endclass
模板3:agent 标准模板(build_phase 创建子组件重点)
agent 负责实例化 driver、monitor、sequencer,
type_id::create(名字,this)this 就是父组件句柄,传给子组件的 parent 参数
c
class clk_rst_agent extends uvm_agent;
`uvm_component_utils(clk_rst_agent)
clk_rst_driver drv;
clk_rst_monitor mon;
clk_rst_sequencer seqr;
function new(string name="clk_rst_agent",uvm_component parent=null);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// 创建子组件,第二个参数 this → parent = 当前agent
drv = clk_rst_driver::type_id::create("drv",this);
mon = clk_rst_monitor::type_id::create("mon",this);
seqr = clk_rst_sequencer::type_id::create("seqr",this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
//driver 和 sequencer 端口绑定
drv.seq_item_port.connect(seqr.seq_item_export);
endfunction
endclass
模板4:顶层env环境层
c
class tb_env extends uvm_env;
`uvm_component_utils(tb_env)
clk_rst_agent clk_rst_agt;
function new(string name="tb_env",uvm_component parent=null);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
//实例化agent,parent = env(this)
clk_rst_agt = clk_rst_agent::type_id::create("clk_rst_agt",this);
endfunction
endclass
模板5:最顶层test(顶层组件parent自动为null)
c
class base_test extends uvm_test;
`uvm_component_utils(base_test)
tb_env env;
function new(string name="base_test",uvm_component parent=null);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = tb_env::type_id::create("env",this);
//顶层test的父节点由UVM框架默认赋值为null
endfunction
endclass
2、核心硬性规范
- uvm_component 子类 new 必须带上两个形参 name + parent
c
function new(string name="xxx",uvm_component parent=null);
super.new(name,parent);
endfunction
- 创建组件一律使用
type_id::create("组件名",this)
不要手动调用new();create内部会自动执行new,并且开启factory工厂注册。 - build_phase 只负责实例化子组件、获取config‑db、创建端口;
connect_phase 专门用来做端口之间的connect连接;
run_phase 放动态仿真任务task。 - 所有下层组件parent依靠
this向上挂载,形成UVM树;
一旦忘记第二个参数,parent=null,组件脱离组件树:phase不会自动执行、config_db读取失败、打印路径异常。
3、最简记忆口诀
子类构造传name和parent;
super向上调用父类new;
build里面create子部件,第二个参数固定填this。