UVM入门和进阶实验2 详细学习资料
基于《UVM学习-3》、《芯片验证漫游指南-刘斌著》文档整理,涵盖TLM通信、Sequence机制、寄存器模型、AMBA接口及验证环境搭建等核心内容。
目录
- [Day 1:UVM入门实验2与Phase机制](#Day 1:UVM入门实验2与Phase机制)
- [Day 2:TLM通信机制](#Day 2:TLM通信机制)
- [Day 3:同步通信元件](#Day 3:同步通信元件)
- [Day 4:Sequence与Item机制](#Day 4:Sequence与Item机制)
- [Day 5:Sequencer与Driver交互](#Day 5:Sequencer与Driver交互)
- [Day 6:Sequence层次化与Virtual Sequence](#Day 6:Sequence层次化与Virtual Sequence)
- [Day 7:寄存器模型(RGM)](#Day 7:寄存器模型(RGM))
- [Day 8:MCDF设计更新与AMBA接口](#Day 8:MCDF设计更新与AMBA接口)
- [Day 9:UVM验证环境更新策略](#Day 9:UVM验证环境更新策略)
- 自测题目
- 艾宾浩斯复习计划表
Day 1:UVM入门实验2与Phase机制
1.1 实验2概述
实验2的代码几乎是从SV最后的实验5移植过来的,核心包括:
- SV验证环境结构
- SV组件之间的通信管道
- SV的激励产生和发送模式
- SV的数据检查和报告
- SV的测试开始与结束方式
- SV的配置方式
1.2 Phase机制要点
核心概念: UVM通过uvm_root自动安排执行父模块调用子模块的phase,因此不需要手动调用 driver.run() 或 monitor.run()。
代码示例:
systemverilog
// 不需要在test中手动调用以下代码:
// driver.run(); // 注释掉
// monitor.run(); // 注释掉
// uvm_root会自动按层次调用各组件的run_phase
链接关系放置位置: 组件之间的连接关系应放在 connect_phase 中完成。
systemverilog
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
// 端口连接放在这里
agent.driver.seq_item_port.connect(sequencer.seq_item_export);
endfunction
★ 记忆要点:
- ★
uvm_root是UVM的隐形顶层,自动调度phase执行。 - ★ 连接关系放在
connect_phase,创建关系放在build_phase。 - ★ 不要在test中手动调用组件的run方法。
Day 2:TLM通信机制
2.1 TLM通信概述
TLM(Transaction Level Modeling)是UVM组件间通信的核心机制。建立TLM通信的常规步骤:
- 定义数据类型:分别定义request类和response类。
- 声明和创建端口:在各个层次的component中声明和创建TLM端口对象。
- 端口连接 :通过
connect()函数完成端口之间的连接。 - 实现方法:在imp端口类中实现需要提供给initiator的可调用方法。
2.2 单向通信(Put/Get)
核心概念: 单向通信分为 put(发起方发送数据)和 get(发起方获取数据)两种模式。
关键规则:
import不能连接importport无法连接多个import(一对一连接)
代码示例(非阻塞put imp):
systemverilog
class comp2 extends uvm_component;
uvm_nonblocking_put_imp #(request, comp2) nbp_imp;
// 必须实现以下方法
virtual function bit try_put(request t);
// 实现逻辑
endfunction
virtual function bit can_put();
// 实现逻辑
endfunction
endclass
代码示例(阻塞get imp):
systemverilog
class comp4 extends uvm_component;
uvm_blocking_get_imp #(request, comp4) bg_imp;
virtual task get(output request t);
// 实现逻辑
endtask
endclass
try_get 默认返回值: try_get 默认返回值为0,表示未成功获取数据。
2.3 双向通信(Transport)
双向通信通过 transport 实现request和response的数据交换。
systemverilog
class env1 extends uvm_env;
comp1 c1;
comp2 c2;
`uvm_component_utils(env1)
function void build_phase(uvm_phase phase);
super.build_phase(phase);
c1 = comp1::type_id::create("c1", this);
c2 = comp2::type_id::create("c2", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
c1.bt_port.connect(c2.bt_imp); // 双向端口连接
endfunction
endclass
★ 记忆要点:
- ★
imp端口必须实现对应方法,否则即使连接也无法传输数据。 - ★
port不能连接多个import,只能一对一。 - ★
try_get默认返回0。
2.4 通信管道(Channel)
Request & Response通信管道:
通过 uvm_tlm_req_rsp_channel 进行request与response的数据交换:
systemverilog
// 连接方式
initiator.put_port.connect(req_rsp_channel.put_request_export);
target.get_peek_port.connect(req_rsp_channel.get_peek_request_export);
target.put_port.connect(req_rsp_channel.put_response_export);
initiator.get_peek_port.connect(req_rsp_channel.get_peek_response_export);
特性:
write函数不耗时- Target不需要实现put、get和peek(通过channel中转)
2.5 TLM2通信
TLM2使用socket进行通信:
systemverilog
class env1 extends uvm_env;
comp1 c1;
comp2 c2;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
c1 = comp1::type_id::create("c1", this);
c2 = comp2::type_id::create("c2", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
c1.b_ini_skt.connect(c2.b_tgt_skt); // socket连接
endfunction
endclass
★ 记忆要点:
- ★
uvm_tlm_req_rsp_channel是initiator与target之间的数据交换管道。 - ★ TLM2使用socket(ini_skt / tgt_skt)进行连接。
Day 3:同步通信元件
3.1 uvm_event(事件同步)
核心概念: UVM提供 uvm_event 和 uvm_event_pool 用于跨组件的事件同步。
触发方式区别:
wait_ptrigger():电平触发,如果事件已经触发过,不会等待。wait_trigger():边沿触发,等待下一次事件触发。
代码示例:
systemverilog
uvm_event e1;
e1 = new("e1");
// 触发事件
e1.trigger();
// 等待事件(电平触发)
e1.wait_ptrigger();
// 等待事件(边沿触发)
e1.wait_trigger();
★ 记忆要点:
- ★ 如果pool中没有e1,调用时会自动创建出一个e1。
- ★
wait_ptrigger()是电平触发,wait_trigger()是边沿触发。
3.2 uvm_barrier(屏障同步)
核心概念: uvm_barrier 用于对多个组件进行同步协调,uvm_barrier_pool 全局管理这些barrier对象。
背景:
- SV中的semaphore、mailbox、fork-join受到作用域局限。
- UVM的
uvm_barrier解决了跨组件独立运作的封闭性问题。
继承关系:
uvm_void -> uvm_object -> uvm_pool<KEY,T> -> uvm_object_string_pool<T>
uvm_barrier_pool与uvm_event_pool一样,基于uvm_object_string_pool定义。
3.3 uvm_callback(回调机制)
核心概念: uvm_callback 使得钩子属性变得更加容易控制和继承。
关键步骤:
- 继承callback类:
systemverilog
class cb2 extends cb1; // cb2继承cb1,实现更多定制
- 注册callback匹配关系:
systemverilog
`uvm_register_cb(T, CB) // 在组件T中声明T与CB的匹配
- 执行callback(层次性):
systemverilog
`uvm_do_callbacks(T, CB, METHOD) // 循环执行已结对的方法
`uvm_do_callbacks_exit_on(T, CB, METHOD, VAL) // 控制退出条件
- 结对子(添加callback对象):
systemverilog
uvm_callbacks #(T, CB)::add(obj, cb); // 将callback对象添加到组件
★ 记忆要点:
- ★
uvm_register_cb(T, CB)用于注册组件类型T与callback类型CB的匹配关系。 - ★
uvm_do_callbacks宏会循环执行已与该对象结对的callback方法。 - ★ callback执行具有层次性,子类继承父类的绑定后可直接插入使用。
- ★
uvm_do_callbacks_exit_on可在返回值与VAL相同时退出。
Day 4:Sequence与Item机制
4.1 Sequence与Item的关系
核心概念:
- 一个sequence可以包含一些有序组织起来的item实例。
- item在创建后需要被随机化。
- sequence在声明时需要预留可供外部随机化的变量。
Sequence分类:
| 类型 | 说明 |
|---|---|
| 扁平类(flat sequence) | 只用来组织更细小的粒度,即item实例构成的组织 |
| 层次类(hierarchical sequence) | 由更高层的sequence组织底层sequence,顺序或并行挂载到同一个sequencer |
| 虚拟类(virtual sequence) | 最终控制整个测试场景,协调不同sequencer上的sequence,不固定挂载于某一种sequencer |
4.2 Item的生命周期
流程:创建 -> 随机化 -> 发送
systemverilog
class my_sequence extends uvm_sequence #(my_item);
`uvm_object_utils(my_sequence)
virtual task body();
my_item req;
// 创建
req = my_item::type_id::create("req");
// 随机化
assert(req.randomize());
// 发送
start_item(req);
finish_item(req);
endtask
endclass
返回response的注意事项:
- 注意item生命周期,用clone方式返回resp,避免引用同一对象导致数据被覆盖。
systemverilog
// 正确做法:clone返回resp
RSP rsp = RSP::type_id::create("rsp");
rsp.set_id_info(req);
// 填充rsp数据
seq_item_port.item_done(rsp);
4.3 Sequence宏与方法
核心概念: 正确区别 start() 方法和 uvm_do 宏,是掌握sequence发送和嵌套的关键。
最终形态: body可以自动执行,无需手动调用。
systemverilog
// 在test中启动sequence
my_sequence seq;
seq = my_sequence::type_id::create("seq");
seq.start(sequencer); // 挂载到指定sequencer
★ 记忆要点:
- ★ 最小颗粒度还是item。
- ★
item_done()无参数时表示不返回response。 - ★ 只有组件可以例化端口,sequence不是组件,不能例化端口。
- ★ Object类型(如sequence)通过
new()创建,不是create()(因为不是uvm_component)。
Day 5:Sequencer与Driver交互
5.1 端口与方法
核心概念: Sequencer与Driver之间通过TLM端口进行通信。
端口连接:
- 通常RSP类型与REQ类型保持一致,便于统一处理。
- 只需一组port连接即可,另外analysis一组可以不用链接。
systemverilog
// driver中典型的run_phase
class driver extends uvm_driver #(bus_trans);
`uvm_component_utils(driver)
task run_phase(uvm_phase phase);
REQ tmp;
bus_trans req;
forever begin
seq_item_port.get_next_item(tmp); // 获取item
void'($cast(req, tmp));
`uvm_info("DRV", $sformatf("got a item %0d from parent sequence %s",
req.data, req.get_parent_sequence().get_name()), UVM_LOW)
// 驱动到接口
seq_item_port.item_done(); // 告知完成(无rsp)
end
endtask
endclass
5.2 返回Response
方式一:通过item_done返回
systemverilog
seq_item_port.item_done(rsp); // 在item_done参数中填入rsp
方式二:通过put_response单独发送
systemverilog
seq_item_port.put_response(rsp);
// 或通过driver的rsp_port
uvm_driver::rsp_port.write(RSP);
5.3 Task功能对比
task get / peek / put 与 task get_next_item 功能类似,都是driver从sequencer获取item的方法。
★ 记忆要点:
- ★ driver消化完request后,通过
item_done()告知sequence传输结束。 - ★ driver可以通过
put_response()或put()方法单独发送response。 - ★
get_next_item与get/peek功能类似,但get_next_item需要配对item_done。
Day 6:Sequence层次化与Virtual Sequence
6.1 Sequencer的仲裁
核心概念: 多个sequence可以挂载到同一个sequencer上,sequencer通过仲裁机制决定执行顺序。
Locks优先级: locks优先级可以修改(如改成400),那么跑出来的顺序可能是不一样的。
systemverilog
// sequence中设置优先级
seq.start(sequencer, null, 400); // 优先级参数
6.2 Sequence的层次化
挂载到同一个reg sqr: 多个子sequence可以挂载到同一个sequencer上执行。
使用fork-join_none: 因为 seq.start() 一直执行不会退出,所以需要用 fork-join_none 并行启动多个sequence。
systemverilog
virtual task body();
base_seq seq1, seq2;
seq1 = base_seq::type_id::create("seq1");
seq2 = base_seq::type_id::create("seq2");
fork
seq1.start(p_sequencer);
seq2.start(p_sequencer);
join_none
endtask
6.3 Virtual Sequence
核心概念: Virtual Sequence是最终控制整个测试场景的方式,协调不同sequencer上的sequence执行。
典型应用场景(MCDF):
virt_seq内部通过uvm_do(reg_seq)、uvm_do(fmt_seq)、uvm_do(chnl_seq)分别控制寄存器、formatter、channel的sequence。- Virtual sequence挂载到
virtual_sequencer上,而virtual sequencer内部持有各子sequencer的句柄。
systemverilog
class virtual_sequencer extends uvm_sequencer;
reg_sequencer reg_sqr;
fmt_sequencer fmt_sqr;
chnl_sequencer chnl_sqr;
endclass
6.4 Layering Sequence示例
systemverilog
class test1 extends uvm_test;
layering_sequence layer_sqr;
phy_master_agent phy_agt;
function void build_phase(uvm_phase phase);
layer_sqr = layering_sequence::type_id::create("layer_sqr", this);
phy_agt = phy_master_agent::type_id::create("phy_agt", this);
endfunction
function void connect_phase(uvm_phase phase);
phy_agt.sqr.up_sqr = layer_sqr;
endfunction
task run_phase(uvm_phase phase);
top_seq seq;
adapter_seq adapter;
phase.raise_objection(phase);
seq = new();
adapter = new();
fork
adapter.start(phy_agt.sqr);
join_none
seq.start(layer_sqr);
phase.drop_objection(phase);
endtask
endclass
★ 记忆要点:
- ★ Virtual sequence不固定挂载于某一种sequencer类型。
- ★ 使用
fork-join_none并行启动sequence,因为start()会阻塞直到完成。 - ★ 层次化sequence可以将高层transaction转换为低层transaction。
Day 7:寄存器模型(RGM)
7.1 寄存器模型概览
核心组件:
uvm_reg:寄存器uvm_reg_field:寄存器域uvm_reg_block:寄存器块uvm_reg_map:寄存器映射
关键区别:
- 与
build_phase没有关系,寄存器模型没有phase(只有component才有phase)。 - Object无自动化,需要手动层次调用build。
systemverilog
class ctrl_reg extends uvm_reg;
rand uvm_reg_field reserved;
rand uvm_reg_field pkt_len;
// ...
virtual function void build();
reserved = uvm_reg_field::type_id::create("reserved");
pkt_len = uvm_reg_field::type_id::create("pkt_len");
// 配置field:name, size, lsb_pos, access, volatile, reset, has_reset, is_rand, individually_accessible
reserved.configure(this, 24, 8, "RW", 0, 24'h0, 1, 1, 0);
pkt_len.configure(this, 4, 4, "RW", 0, 4'h0, 1, 1, 0);
endfunction
endclass
Rand句柄: 在randomize时会导致rand寄存器的reg_field进行rand。
7.2 寄存器模型集成
寄存器模型构建示例:
systemverilog
class mcdf_rgm extends uvm_reg_block;
rand ctrl_reg chnl0_ctrl_reg;
rand ctrl_reg chnl1_ctrl_reg;
rand ctrl_reg chnl2_ctrl_reg;
rand stat_reg chnl0_stat_reg;
rand stat_reg chnl1_stat_reg;
rand stat_reg chnl2_stat_reg;
virtual function void build();
chnl0_ctrl_reg = ctrl_reg::type_id::create("chnl0_ctrl_reg");
chnl0_ctrl_reg.configure(this);
chnl0_ctrl_reg.build();
// ... 重复创建其他寄存器
// 创建map:name, offset, number of bytes, endianness
map = create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);
map.add_reg(chnl0_ctrl_reg, 32'h00000000, "RW");
map.add_reg(chnl1_ctrl_reg, 32'h00000004, "RW");
map.add_reg(chnl2_ctrl_reg, 32'h00000008, "RW");
map.add_reg(chnl0_stat_reg, 32'h00000010, "RO");
map.add_reg(chnl1_stat_reg, 32'h00000014, "RO");
map.add_reg(chnl2_stat_reg, 32'h00000018, "RO");
lock_model(); // 锁定模型,防止后续修改
endfunction
endclass
7.3 寄存器模型的常规方法
常用方法:
read()/write():读写寄存器peek()/poke():后门访问mirror():镜像值更新reset():复位模型set()/get():期望值设置与获取
7.4 寄存器模型应用场景------覆盖率自动收集
覆盖率的例化与采样:
- 判断是否需要例化覆盖率:
systemverilog
if (has_coverage(UVM_CVR_ALL)) begin
// 例化covergroup
end
- 采样方法:
sample():可以理解为read()/write()方法的回调函数,在读写时自动采样。sample_values():供用户外部调用的方法,在特定事件触发时(如中断、复位)调用。
- 判断是否允许采样:
has_coverage():是否具备对应的covergroup。get_coverage():是否允许使用对应的covergroup进行采样。
★ 记忆要点:
- ★
lock_model()锁定后不可再修改寄存器模型结构。 - ★
has_coverage()判断是否具备covergroup;get_coverage()判断是否允许采样。 - ★ Object类型(如reg_field)需要手动调用build,没有component的自动化build_phase。
Day 8:MCDF设计更新与AMBA接口
8.1 MCDF设计更新
MCDF(Multi-Channel Data Formatter)多通道数据整形器,包含以下模块:
- Slave1/2/3:三个数据通道从机
- FIFO1/2/3:数据缓存
- Arbiter:仲裁器
- Formater:数据整形器
- Registers:寄存器配置模块
8.2 AMBA标准接口(APB)
APB写时序要点:
pready拉高的一拍为数据传输的有效拍。- 写时序与读时序画在一起,并非表示同时刻能同时读写,只是示意图。
- 为了省电,
paddr与pwrite信号线在idle的时钟周期不会出现变化。
APB信号:
| 信号 | 说明 |
|---|---|
| pclk | 时钟 |
| presetn | 复位 |
| paddr | 地址 |
| psel | 选通 |
| penable | 使能 |
| pwrite | 读写方向 |
| pwdata | 写数据 |
| prdata | 读数据 |
| pready | 从机就绪 |
★ 记忆要点:
- ★ APB是低速、低功耗的总线接口,适合寄存器访问。
- ★
pready拉高时表示当前传输完成。 - ★ idle状态下地址和控制信号保持不变以节省功耗。
Day 9:UVM验证环境更新策略
9.1 环境更新要点
- 与MCDF连接的各个接口信号需要重新定义。
- 在顶层testbench,对于各个接口信号的连接也需要更新。
9.2 验证IP模板(VIP)
主要VIP提供商:
- Cadence
- Synopsys
- Mentor
- Avery Design Systems
- eInfochips
- HDL Design House
- SmartDV Technologies
- TVS (Test and Verification Solutions)
- Truechip Solutions
- Arasan
VIP的作用:
- 提供标准的总线协议验证组件(如APB、AHB、AXI等)。
- 包含master/slave agent、scoreboard、sequence library等。
- 可直接集成到UVM验证环境中,加速验证平台搭建。
★ 记忆要点:
- ★ VIP通常包含active和passive两种模式,active模式产生激励,passive模式只监测。
- ★ 重用VIP可以大幅减少验证平台开发时间。
自测题目
一、选择题
-
UVM中,
uvm_root对phase的调度方式是?- A. 需要手动调用每个组件的run方法
- B. 自动按层次调用各组件的phase
- C. 只调用顶层组件的phase
- D. 随机调用
-
TLM通信中,
imp端口的作用是?- A. 发起通信请求
- B. 实现具体的方法供initiator调用
- C. 转发数据到analysis组件
- D. 只用于阻塞传输
-
wait_ptrigger()与wait_trigger()的区别是?- A. 没有区别
- B. 前者是电平触发,后者是边沿触发
- C. 前者是边沿触发,后者是电平触发
- D. 前者阻塞,后者非阻塞
-
关于Sequence与Item的关系,以下说法正确的是?
- A. Sequence可以直接驱动接口
- B. Item是最小颗粒度的激励单位
- C. Sequence可以例化TLM端口
- D. Item需要通过component发送
-
driver中
get_next_item()必须配对使用的方法是?- A.
put_response() - B.
item_done() - C.
try_next_item() - D.
peek()
- A.
二、填空题
-
TLM单向通信中,port不能连接多个______,只能一对一连接。
-
建立TLM通信的四个步骤是:定义数据类型、______、端口连接、实现方法。
-
uvm_barrier_pool与uvm_event_pool一样,都是基于______定义的。 -
Virtual sequence不同于hierarchical sequence的最大一点是:virtual sequence本身______于某一种sequencer类型。
-
寄存器模型中,
has_coverage()判断是否具备covergroup,get_coverage()判断是否______采样。
三、简答题
-
简述建立TLM通信的常规步骤。
-
解释为什么sequence返回response时建议使用clone方式。
-
说明Virtual Sequence在MCDF验证环境中的作用。
-
寄存器模型集成到UVM环境时,为什么需要手动调用build()?
-
描述APB总线中
pready信号的作用及有效时机。
参考答案
- B --- uvm_root自动按层次调用各组件的phase,无需手动调用run。
- B --- imp端口需要实现具体的方法(如put/get/try_put等)。
- B --- wait_ptrigger是电平触发,wait_trigger是边沿触发。
- B --- Item是最小颗粒度的激励单位,sequence负责组织item。
- B --- get_next_item必须配对item_done使用。
- import
- 声明和创建端口
- uvm_object_string_pool
- 不固定挂载
- 允许
- 定义数据类型(request/response);声明和创建TLM端口;通过connect()连接端口;在imp端实现对应方法。
- 避免返回同一对象的引用,防止数据在后续操作中被覆盖,保证每次返回的都是独立的resp对象。
- Virtual sequence协调不同sequencer上的sequence执行(如reg_seq、fmt_seq、chnl_seq),控制整个测试场景,而不固定挂载于某一种sequencer。
- 因为uvm_reg等寄存器模型类继承自uvm_object而非uvm_component,没有component的自动化phase机制(如build_phase),因此需要手动层次调用build()来创建子对象。
- pready是从机就绪信号,当pready拉高时表示从机已完成数据传输,当前拍为有效传输拍。
艾宾浩斯复习计划表
根据艾宾浩斯遗忘曲线,在以下时间节点进行复习,记忆效果最佳。
| 学习内容 | 第1次学习 | 第1天复习 | 第2天复习 | 第4天复习 | 第7天复习 | 第15天复习 |
|---|---|---|---|---|---|---|
| Day 1:Phase机制 | 2026-07-11 | 2026-07-12 | 2026-07-13 | 2026-07-15 | 2026-07-18 | 2026-07-26 |
| Day 2:TLM通信 | 2026-07-12 | 2026-07-13 | 2026-07-14 | 2026-07-16 | 2026-07-19 | 2026-07-27 |
| Day 3:同步通信元件 | 2026-07-13 | 2026-07-14 | 2026-07-15 | 2026-07-17 | 2026-07-20 | 2026-07-28 |
| Day 4:Sequence与Item | 2026-07-14 | 2026-07-15 | 2026-07-16 | 2026-07-18 | 2026-07-21 | 2026-07-29 |
| Day 5:Sequencer与Driver | 2026-07-15 | 2026-07-16 | 2026-07-17 | 2026-07-19 | 2026-07-22 | 2026-07-30 |
| Day 6:Sequence层次化 | 2026-07-16 | 2026-07-17 | 2026-07-18 | 2026-07-20 | 2026-07-23 | 2026-07-31 |
| Day 7:寄存器模型 | 2026-07-17 | 2026-07-18 | 2026-07-19 | 2026-07-21 | 2026-07-24 | 2026-08-01 |
| Day 8:MCDF与AMBA | 2026-07-18 | 2026-07-19 | 2026-07-20 | 2026-07-22 | 2026-07-25 | 2026-08-02 |
| Day 9:验证环境更新 | 2026-07-19 | 2026-07-20 | 2026-07-21 | 2026-07-23 | 2026-07-26 | 2026-08-03 |
复习方法建议
- 第1天复习(学习后24小时内): 快速浏览核心概念和★标记的记忆要点,回顾当日代码示例。
- 第2天复习: 遮住代码,尝试自行写出关键通信连接和sequence启动代码。
- 第4天复习: 完成对应章节的自测题目,标记错题。
- 第7天复习: 做整套自测题,重点复习错题对应的知识点。
- 第15天复习: 快速通读全文,在脑海中构建完整的UVM验证环境架构图。
本文档由学习笔记自动生成,建议结合源代码和实验平台动手实践以加深理解。