笔者最近在看 IC 验证部分,由于时间有限,就初略做了一点 backbone 的笔记,简单梳理了从第八章到十一章大概介绍的知识点
1.针对ctrl-reg的测试
1. regs_cr_if
提供整个系统的 clk 时钟和 rstn 复位信号
2. regs_ini_if
测试 cmd 以及 输出输入 data
3. regs_rsp_if
读取 slave 的相关信息
2. sv组件
1. stm_ini
连接 regs_ini_if 模块,
module regs_ini_if(
input clk,
input rstn,
output [ 1:0] cmd,
output [ 7:0] cmd_addr,
output [31:0] cmd_data_w,
input [31:0] cmd_data_r
);
.......
endmodule
module stm_ini;
/*
这里相当于直接引用了 regs_ini_if 的接口,比如我要用其中的clk,直接敲入 vif.clk即可
*/
virtual interface regs_ini_if vif;
typedef struct{
bit [ 1:0] cmd;
bit [ 7:0] cmd_addr;
bit [31:0] cmd_data_w;
bit [31:0] cmd_data_r;
} trans;
trans ts[];//声明动态数组
//定义基本的行为
task op_wr(trans t);
@(posedge vif.clk);
vif.cmd <= t.cmd;
vif.cmd_addr <= t.cmd_addr;
vif.cmd_data_w <= t.cmd_data_w;
endtask
task op_rd(trans t);
.......
task op_idle();
......
task op_parse(trans t);
case(t.cmd)
WR: op_wr(t);
RD: op_rd(t);
IDLE: op_idle();
default: $error("Invalid CMD!");
endcase
endtask
initial begin: stmgen
wait(vif != null);
@(posedge vif.rstn);
foreach(ts[i]) begin
op_parse(ts[i]);
end
end
endmodule
module tests;
/*
详细阐述测试向量
*/
int fin;//控制仿真的结束
task test_wr;
tb.ini.ts = new[2];
tb.ini.ts[0].cmd = WR;
tb.ini.ts[0].cmd_addr = 0;
tb.ini.ts[0].cmd_data_w = 32'h0000_FFFF;
tb.ini.ts[0].cmd = RD;
tb.ini.ts[0].cmd_addr = 0;
fin = 150;
endtask
task test_rd;
tb.ini.ts = new[2];
tb.ini.ts[0].cmd = RD;
tb.ini.ts[0].cmd_addr = 'h10;
tb.ini.ts[1].cmd = RD;
tb.ini.ts[1].cmd_addr = 'h14;
fin = 200;
endtask
endmodule
// 实现stimulator、test vector 、tb的分立
module tb;
.......
endmodule
2. stimulate package
类内部的方法必须是 task
或 function
,不能使用 always
或 initial
默认定义了构造函数 new()
成员覆盖
子类作用域中出现与父类相同的变量名或方法名,则以子类的作用域为准。这里要注意类的对象句柄的传递。
basic_test t;//static binding
test_wr wr;
initial begin
wr = new();
t = wr;
...
end
//执行t.test(),则只会搜寻 basic_test::test 方法,而不会执行test_wr::test
要调用正确的方法则需要动态绑定,指调用方法时,在运行时确定句柄指向对象的类型,再动态指向应该调用的方法。所以可以把父类定义为 **虚方法**
class basic_test;
...
virtual task test(stm_ini ini);
$display("basic_test::test");
endtask
...
endclass
Tips:
- 定义父类方法,如果该方法可能会被覆盖或者继承,应该声明为虚方法。
- 定义虚方法,应定义在底层父类中。
- 虚方法通过
virtual
声明,且只用声明一次,子类无需声明。 - 虚方法继承需要遵循相同的参数和返回类型。
编译器遇到对象赋值只做静态检查,而静态检查只允许子类句柄赋值给父类句柄
那要实现动态检查则需要 $cast( , )函数。
对象的复制不能通过 “ = ” (这只是句柄的赋值)
复制对象通过创建新的对象(开辟新的空间),将目标对象的成员变量值复制给新对象成员
实现对象复制需要注意:
- 分为成员复制函数 copy_data() 和新对象生成函数 copy() 俩个方法
- 复制方法声明为虚函数
- 实现 copy_data() 过程中应注意句柄的类型转换
3.激励发生器的随机化
对于 MCDF 激励发生器的激励作用主要体现在:
- 时钟和复位激励
- 寄存器配置激励
- 从端通道数据输入
- 整形器响应端的反馈
激励生成顺序:
- 时钟和复位信号
- 配置寄存器
- 预先设置整形器响应端
- 给从端通道写入数据
packed vs unpacked
到这里都是对 ctrl_reg
的外部接口测试
4.检测器采样
开始测试 slave_channel
主要介绍了 interface clocking 的特性 以及 monitor 搭建
5.组件间的通信
SV 自带的通信**“三宝”**
1.通知的需求
event 事件触发
event start;
wait(start.triggered);//判断是否触发
@start;//线程多次通知需求需要用 @
2.资源共享需求
semaphore(旗语) 来保管 钥匙
semaphore key;
key.get(); //锁资源
key.put();//释放资源
3.数据通信需求
mailbox --> 存取方法 : put() get()
queue --> 存取方法 :push_back() pop_front()
“三宝”:可以参考这篇
1.event:
最小信息量触发,单一的通知功能
2.semaphore
共享资源安全卫士
3.mailbox
精小的 SV 原生 FIFO
到这里基本完成 slave 、arbiter 、 registers 的模块验证
6.比较器和参考模型
开始验证 formatter
分三类检查
1.异常检查
这块主要查复位,各端口信号是否正常复位
2.常规检查
- 拆分检查
- 整体检查
3.时序检查
完成所有组件的检测包括 check 如何实现
7.测试环境的报告规范
3.SV系统集成
1.包的意义
package 和 library 的区别
这块讨论了 package 使用的若干细节
2.验证环境组装
提出了 agent 的概念,并讨论模块验证的复用性 比如 check(这块还是有点模糊)
3.测试场景生成
进一步的封装 test 类 ----> vector 类 (生成随机数据)
1. vector 类
test 类 -->|
2. env
停止线程的俩种方式
- disable thread_trigger
- disable fork
并进一步介绍了几种关于线程控制的函数
然后介绍了向量群落的并发控制(挺有用的)
4.灵活化的配置
active agent: 含有stimulator的对象
passive agent
设置变量控制组件行为
5.环境的复用性
4.UVM世界观
之前的实验都是在Mentor QuestaSim 实现
此次是用 Synopsys VCS
1.工厂机制
factory 三大核心要素:注册、创建和覆盖
实例或类型替代在 uvm 中称为覆盖( override )
验证环境主要有
1.环境层次 uvm_component类
2.环境属性和数据传输 uvm_object类 ( uvm_componet 继承于 uvm_object )
factory 运用步骤:
- 将类注册到工厂(`uvm_component_utils)
- 例化前设置覆盖对象和类型(optional)
- 对象创建
详细介绍了 factory 注册的过程
! factory 的覆盖机制只会影响通过 factory 注册并且创建的对象,比如:
c2 = comp1::type_id::create("c2", null);
2.核心基类
uvm_void (root class)
|
/ \
uvm_object uvm_port_base
uvm_object:提供与数据操作的相关服务
介绍
copy()
compare()
print()
提供了几种打印格式
pack() & unpack()
3.phase机制
保证验证环境的连接关系
阐述 UVM 开始仿真的过程 run_test()
以及如何结束 UVM 仿真
4.config机制
uvm_config_db
配置类
作用:
1.将 virtual interface 传递到环境
2.设置单一变量
3.传递配置对象到环境
除了上面 uvm_config_db 配置外还可以使用 uvm_resource_db 类实现
以及 set_config_ *()/get_config_ *()
5.消息管理
提供了4个消息函数
5.UVM结构
这章给出待测设计 MCDF 的 UVM 验证结构
介绍构成环境的常见组件类:
1.uvm_driver
从 uvm_sequencer 获取 transaction ,经过转化在接口中对 DUT 进行时序激励
class uvm_driver #(type REQ = uvm_sequence_item, type RSP = REQ) extends uvm_component;
2.uvm_monitor
监测接口数据
3.uvm_sequencer
像一个管道,从中传送连续的激励事物,最终通过 LTM 端口送至 driver 一侧。
4.uvm_agent
一个标准的验证环境 “单位” 。通常包括 一个 driver、一个 monitor、一个 sequencer
uvm_active_passive_enum is_active = UVM_ACTIVE;
//active mode : enable driver monitor sequencer
//passive mode : enable monitor
agent 需要在 build_phase()
和 connect_phase()
等函数中通过选择语句对 driver 和 sequencer 来进行有条件的例化和连接
5.uvm_scoreboard
功能同 checker 即对数据比对和报告。
6.uvm_env
uvm_env 包含多个 uvm_agent 和其他 component
是一个结构化的容器,可以容纳其他组件
7.uvm_test
是验证环境建立的唯一入口,只有通过这个才能正常运转 UVM 的 phase 机制