UVM学习-2 详细学习资料
基于《芯片验证漫游指南-刘斌著》整理
目录
- [Day12 - 功能覆盖策略](#Day12 - 功能覆盖策略)
- [Day13 - 面向对象高级特性](#Day13 - 面向对象高级特性)
- [Day14 - 参数化类与工厂机制](#Day14 - 参数化类与工厂机制)
- [Day15 - UVM核心基础](#Day15 - UVM核心基础)
- [Day16 - Config配置机制](#Day16 - Config配置机制)
- [Day17 - 消息管理与入门实验](#Day17 - 消息管理与入门实验)
- [Day18 - UVM组件家族与MCDF验证方案](#Day18 - UVM组件家族与MCDF验证方案)
- [Day19 - 构建验证环境的内经](#Day19 - 构建验证环境的内经)
- 艾宾浩斯复习计划表
Day12 - 功能覆盖策略
1. 主要内容概览
- 覆盖率类型
- 功能覆盖策略
- 覆盖组(covergroup)
- 数据采样
- 覆盖选项
- 数据分析
2. 功能覆盖策略核心思想
收集信息而非数据
- 对于MCDF,关心的是合法的寄存器地址和非法的寄存器地址,可写的寄存器域和非法的寄存器域,而不是具体的寄存器地址数值
- 一旦关注的地方着眼于感兴趣的状态,而不是具体数值,那么对如何定义功能覆盖率会减轻很大负担
- 设计信号如果数量范围太大 ,应该拆分为多个小范围再加上边界情况
只测量需要的内容
- Verifier需要懂得:使能覆盖率收集时,这一特性会降低很大的仿真性能
- 由于收集功能覆盖率数据的开销很大,所以应该只测量你会用来分析并且改进测试的那部分数据
- 需要设定合理的覆盖率采样的事件 ,一方面提升采样效率,一方面降低收集覆盖率的开销
验证的完备性
- 完备的覆盖率测量结果和漏洞增长曲线,可以帮助确认设计是否被完整地验证过
- 代码覆盖率低但功能覆盖率高:说明验证计划不完整,测试没有执行设计的所有代码
- 代码覆盖率高但功能覆盖率低:说明即使测试平台很好地执行了设计的所有代码,但是测试还是没有把设计定位到所有感兴趣的状态上
- 目标:同时驱动高的代码覆盖率和功能覆盖率
| 功能覆盖率 \ 代码覆盖率 | Low | High |
|---|---|---|
| High | Need more FC points, including corner cases | Good coverage: check bug rate |
| Low | Start of project | Is design complete? Perhaps try formal tools |
3. 覆盖组(covergroup)
在类里定义covergroup
systemverilog
class Transactor;
Transaction tr;
mailbox mbx_in;
covergroup CovPort; // 声明covergroup
coverpoint tr.port; // 采样点
endgroup
function new(mailbox mbx_in);
CovPort = new(); // 例化covergroup
this.mbx_in = mbx_in;
endfunction
endclass
- covergroup在类中声明,在new()函数中例化
- 可以例化多次:
CovPort cg1 = new(); CovPort cg2 = new();
数据采样概述
- 在coverpoint指定采样一个变量或表达式时,SV会创建很多的"仓(bin)"来记录每个数值被捕捉到的次数
- 这些bin是衡量功能覆盖率的基本单位
- covergroup中可以定义多个coverpoint,coverpoint中可以自定义多个cover bin或者SV帮助自动定义多个cover bin
- 每次covergroup采样,SV都会在一个或者多个cover bin中留下标记,用来记录采样时变量的数值和匹配的cover bin
- 在仿真之后,可以使用分析工具读取这些数据库来生成覆盖率报告,包含了各部分和总体的覆盖率
条件覆盖率(iff)
systemverilog
covergroup CoverPort;
coverpoint port iff (!bus_if.reset); // 复位期间不采样
endgroup
// 也可以手动控制start/stop
initial begin
CovPort ck = new();
#1ns;
ck.stop(); // 暂停采样
bus_if.reset = 1;
#100ns bus_if.reset = 0;
ck.start(); // 恢复采样
ck.sample(); // 手动触发采样
end
- 可以使用关键字
iff给coverpoint添加条件 - 常用于在复位期间关闭覆盖以忽略不合理的条件触发
- 也可以使用
start()和stop()函数来控制covergroup各个独立实例
忽略的bin(ignore_bins)
systemverilog
bit [2:0] low_ports_0_5; // 只使用数值0-5
covergroup CoverPort;
coverpoint low_ports_0_5 {
ignore_bins hi = {[6,7]}; // 忽略数值6-7
}
endgroup
- 在某些coverpoint可能始终无法得到全部的域值
- 对于那些不计算功能的域值可以使用
ignore_bins来排除,最终它们并不会计入coverpoint的覆盖率
交叉覆盖率(cross)
systemverilog
class Transaction;
rand bit a, b;
endclass
covergroup CrossBinNames;
a: coverpoint tr.a {
bins a0 = {0};
bins a1 = {1};
option.weight=0; // 不计算覆盖率
}
b: coverpoint tr.b {
bins b0 = {0};
bins b1 = {1};
option.weight=0; // 不计算覆盖率
}
ab: cross a, b {
bins a0b0 = binsof(a.a0) && binsof(b.b0);
bins a1b0 = binsof(a.a1) && binsof(b.b0);
bins b1 = binsof(b.b1);
}
endgroup
- 权重是0,则不搜集或不关心此覆盖率
- 同时cross不会生成任何的bin,则可自己声明关心的组合情况
- coverpoint定义的时候有可能没有把所有的bin列出来,但cross会全算上
- 带中括号则要遍历所有枚举才能记录一条覆盖率
4. 覆盖选项
单个实例的覆盖率
systemverilog
covergroup CoverLength;
coverpoint tr.length;
option.per_instance = 1; // 单独列出每个covergroup实例的覆盖率
endgroup
- 如果对一个covergroup例化多次,默认情况下SV会将所有实例的覆盖率合并到一起
- 如果需要单独列出每个covergroup实例的覆盖率,需要设置
option.per_instance = 1
5. 数据分析
systemverilog
$get_coverage(); // 得到总体的覆盖率
covergroup_inst.get_inst_coverage(); // 获取单个covergroup实例的覆盖率
- 这些函数最实际的用处是在一个测试当中监测覆盖率的变化
- 如果覆盖率水平在一段时间之后没有提高 ,那么这个测试就应该停止
- 重启新的随机种子 或者测试可能有望提高覆盖率
- 如果测试可以基于功能覆盖率采取一些深入的行动,例如重新限定随机的约束,那将是非常好的事情,但是这种测试很难编写
Day13 - 面向对象高级特性
1. 类型转换
子类句柄赋值于父类句柄(隐式转换)
systemverilog
class Transaction;
rand bit [31:0] src;
function void display(input string prefix="");
$display("%sTransaction: src=%0d", prefix, src);
endfunction
endclass
class BadTr extends Transaction;
bit bad_crc;
function void display(input string prefix="");
$display("%sBadTr: bad_crc=%b", prefix, bad_crc);
super.display(prefix); // 调用父类display
endfunction
endclass
Transaction tr;
BadTr bad, bad2;
- 子类句柄可以直接赋值给父类句柄(向上转型,隐式)
父类句柄转换为子类句柄(动态转换)
systemverilog
bad = new(); // 创建BadTr子对象
tr = bad; // 父类句柄指向子类对象
// 动态类型转换,检查tr的源对象是否是bad2类型或者其子类
// 如果转换失败,将报告错误信息
if (!$cast(bad2, tr))
$display("cannot assign tr to bad2");
$display(bad2.bad_crc); // bad2指向的对象包含bad_crc成员
bad2.display();
- 使用
$cast()进行动态类型转换 - 转换失败会返回0,成功返回1
2. 虚方法(virtual method)
核心概念
- 无虚成员,只有虚方法
- 方法名相同、参数相同、返回类型相同才叫继承/多态
systemverilog
class basic_test;
virtual task test(stm_ini ini); // 声明为虚方法
...
endtask
endclass
class test_wr extends basic_test;
task test(stm_ini ini); // 重写虚方法
...
endtask
endclass
basic_test t;
test_wr wr;
wr = new();
t = wr; // 父类句柄指向子类对象
t.test(ini); // 调用的是test_wr::test,而非basic_test::test
虚方法调用原理
- 由于声明了
basic_test::test为虚方法,系统在执行t.test时,会检查t所指向对象的类型为test_wr类,进而调用test_wr::test - 于是,输出结果与调用
wr.test一致 - 通过虚方法的使用来实现类成员方法调用时的动态查找
- 用户无需担心使用的是父类句柄还是子类句柄,因为最终都会实现动态方法查找,执行正确的方法
3. 对象拷贝
4个隐藏剧情
- 句柄拷贝 vs 对象拷贝(深拷贝 vs 浅拷贝)
- 拷贝构造函数的使用
- copy/clone方法的重写
- 父类成员的处理
4. 回调函数(Callback)
systemverilog
class Driver;
Driver_cbs cbs[$]; // 回调队列
task run();
bit drop;
Transaction tr;
forever begin
drop = 0;
agt2drv.get(tr);
foreach (cbs[i]) cbs[i].pre_tx(tr, drop); // 预发送回调
if (drop) continue; // 如果drop为1,则不发送
transmit(tr);
foreach (cbs[i]) cbs[i].post_tx(tr); // 发送后回调
end
endtask
endclass
- 回调函数用于在不修改原有类的情况下扩展功能
- 通过注册回调对象到主类的回调队列中实现
- 常用场景:错误注入、覆盖率收集、调试信息等
Day14 - 参数化类与工厂机制
1. 参数化的类
实现一个简化的mailbox
systemverilog
class mailbox #(type T=int); // 默认类型为int
local T queue[$];
task put(input T i);
queue.push_back(i);
endtask
task get(ref T o);
wait(queue.size()>0);
o=queue.pop_front();
endtask
task peek(ref T o);
wait(queue.size()>0);
o=queue[0];
endtask
endclass
- 在类定义时添加参数
#(type T=int),表示后期类在声明变量时如果不指定参数类型,则默认采用int类型 - 将原代码int用参数T来代替
- 参数化的类将可以在后期例化时使用不同的参数,以此来存储不同的数据类型
- 注意:变量类型不一样,存储空间不一样,无法直接转换
2. 验证方法学概述
我们所处的验证时代
- UVM融合的积极意义在于,打通了各个EDA公司和IC设计公司的验证技能通道,便于验证技术交流和人才流动,也方便了IC设计公司的技术及工具选择
- 用户不再受限于使用何种仿真器、使用哪一家的验证IP,而只需要将主要精力着眼于设计的功能验证,由此也提升了验证效率
- SV的核心特性包括面向对象、随机约束、线程通信、功能覆盖率收集等,这些特性也为建立一个验证环境提供了足够多的便利
- UVM方法学通过吸取eRM(Specman/e验证方法学)、AVM、OVM、UVM等之前不同方法学的优点,可谓集众家之所长
3. 类库地图
UVM类库继承关系(从底向上):
uvm_void→uvm_object→uvm_report_object→uvm_component- 各组件继承自
uvm_component - 数据对象继承自
uvm_object
4. 工厂机制(Factory)
工厂提供的便利------创建(create)
systemverilog
// SV传统创建方式
comp1 c1, c2;
obj1 o1, o2;
initial begin
c1 = new("c1"); // SV创建
o1 = new("o1"); // SV创建
// UVM工厂创建方式
c2 = comp1::type_id::create("c2", null);
o2 = obj1::type_id::create("o2");
end
注册后的对象创建
- 创建对象时,需要结合工厂的注册和覆盖机制来决定,应该使用哪一个类型来创建
- 工厂维护一张表:要求类型 → 覆盖类型
- 如果没有覆盖,则使用基础类型创建;如果有覆盖,则使用覆盖类型创建
覆盖方法(override)
systemverilog
// 条件:Comp2继承comp1;要有virtual虚方法
// Test比env优先级高,最终test的override生效
- 工厂机制的核心是:在运行时动态替换对象的类型
- Test比env优先级高,最终test的override生效
- 条件:继承关系 + 虚方法
Day15 - UVM核心基础
1. 核心基类 uvm_object
核心方法(数据操作服务)
- Copy:对象拷贝
- Clone:克隆对象
- Compare:对象比较
- Print:打印对象
- Pack/Unpack:打包和解包
比较(compare)
systemverilog
function bit compare (uvm_object rhs, uvm_comparer comparer=null);
- 默认情况下,如果不对比较的情况作出额外配置,用户可以在调用compare()方法时,省略第二项参数,即采用默认的比较配置
- 比较方法经常会在两个数据类中进行。例如从generator产生的一个transaction(数据类),和在设计输出上捕捉的transaction(数据类)
- 如果它们为同一种类型,除了可以自定义数据比较之外,也可以直接使用
uvm_object::compare()函数来实现数据比较和消息打印
打包和解包(pack & unpack)
systemverilog
class box extends uvm_object;
int volume = 120;
int height = 20;
color_t color = WHITE;
`uvm_object_utils_begin(box)
...
`uvm_object_utils_end
endclass
// 使用
box b1, b2;
bit packed_bits[];
initial begin
b1 = new("box1");
b2 = new("box2");
b1.volume = 100;
b1.height = 40;
b1.color = RED;
b1.print();
b1.pack(packed_bits); // 打包为bit流
$display("packed bits stream size is %d", packed_bits.size());
b2.unpack(packed_bits); // 从bit流解包
b2.print();
end
- 打包将对象序列化为bit数组,便于传输(如通过PC/USB/JTAG/UART在Simulation和Scoreboard之间传输)
uvm_object其他特性
UVM_NOCOPY:禁止拷贝UVM_PRINT:已经例化好,不需要再例化- 值从10变为20等枚举概念
2. phase机制
UVM世界的"诞生"
systemverilog
task run_test(string test_name="");
uvm_root top;
uvm_coreservice_t cs;
cs = uvm_coreservice_t::get();
top = cs.get_root();
top.run_test(test_name);
endtask
- UVM顶层类
uvm_root,继承于uvm_component,是UVM环境结构中的顶层结构类 - 它提供了
run_test()方法,来充当UVM世界中的核心角色 - 在uvm_pkg中,有且只有一个顶层类uvm_root所例化的对象,即
uvm_top
objection防止仿真退出
systemverilog
class test1 extends uvm_test;
task run_phase(uvm_phase phase);
phase.raise_objection(this); // 提起objection
`uvm_info("run_phase", "entered ..", UVM_LOW)
#1us;
`uvm_info("run_phase", "exited ..", UVM_LOW)
phase.drop_objection(this); // 撤销objection
endtask
endclass
- 如果没有raise_objection,则1us和uvm_info无法执行就会退出
#1ps的延迟太短,下面3句话就来不及执行了- 9个phase执行完后,自动结束仿真
- 不建议使用12个分支phase(如pre_reset_phase、reset_phase等),它们与run_phase并行执行,容易混淆
Day16 - Config配置机制
1. 变量设置
systemverilog
class test1 extends uvm_test;
`uvm_component_utils(test1)
comp1 c1;
function void build_phase(uvm_phase phase);
// 先set
uvm_config_db#(int)::set(this, "c1", "val1", 100);
uvm_config_db#(string)::set(this, "c1", "str1", "comp1");
// 再create(create的build_phase中会有get)
c1 = comp1::type_id::create("c1", this);
endfunction
endclass
输出结果:
UVM_INFO @ 0: uvm_test_top.c1 [SETVAL] val1 is 1 before get
UVM_INFO @ 0: uvm_test_top.c1 [SETVAL] str1 is null before get
UVM_INFO @ 0: uvm_test_top.c1 [SETVAL] val1 is 100 after get
UVM_INFO @ 0: uvm_test_top.c1 [SETVAL] str1 is comp1 after get
核心要点
- 先set再create,因为create的build_phase的时候有get函数
uvm_config_db#(类型)::set(上下文, 路径, 字段名, 值)uvm_config_db#(类型)::get(上下文, 路径, 字段名, 变量)- config_db用于在不同组件之间传递配置信息
Day17 - 消息管理与入门实验
1. 消息管理
消息方法
systemverilog
function void uvm_report_info(string id, string message, int verbosity = UVM_MEDIUM,
string filename = "", int line = 0);
function void uvm_report_warning(string id, string message, int verbosity = UVM_MEDIUM,
string filename = "", int line = 0);
function void uvm_report_error(string id, string message, int verbosity = UVM_LOW,
string filename = "", int line = 0);
function void uvm_report_fatal(string id, string message, int verbosity = UVM_NONE,
string filename = "", int line = 0);
- 在UVM环境中或者环境外,只要有引入uvm_pkg,均可以通过这些方法来按照消息的严重级别和冗余度来打印消息
消息冗余等级
UVM_NONE:最重要,一定会打印UVM_LOWUVM_MEDIUM(默认)UVM_HIGHUVM_FULL:没有LOW重要- 宏冗余等级默认low
set_max_quit_count可以设置为10,控制遇到多少个error后退出仿真
UVM入门实验0讲解
- 懂得如何编译UVM代码
- 理解SV和UVM之间的关系
- 了解UVM验证顶层盒子与SV验证顶层盒子之间的联系
- 掌握启动UVM验证的必要步骤
- 9个phase执行完后,自动结束仿真
Day18 - UVM组件家族与MCDF验证方案
1. 组件家族概览
| 组件 | 功能 |
|---|---|
| uvm_driver | 发送激励到DUT |
| uvm_monitor | 监测DUT信号 |
| uvm_sequencer | 产生连续激励事务,通过TLM端口送至driver |
| uvm_agent | 封装sequencer、driver、monitor |
| uvm_scoreboard | 数据比对和验证 |
| uvm_env | 验证环境顶层容器 |
| uvm_test | 测试用例顶层 |
2. uvm_driver / uvm_monitor
- driver负责将transaction转换为DUT的接口信号
- monitor负责监测DUT接口信号并转换为transaction
3. uvm_sequencer
- 从名uvm_sequencer就如同一个管道,从这个管道中会产生连续的激励事务,并最终通过TLM端口送至driver一侧
- 如果需要的话,uvm_sequencer也可以从uvm_driver那里获取随后的RSP对象来得知数据通信是否正常
- uvm_sequencer是这些组件中技能最超凡的一个成员了,单从它的继承层级就可见一斑
- uvm_sequencer类同uvm_driver一样是个参数类,需要在定义sequencer时声明REQ的类型
继承层级:
uvm_void → uvm_object → uvm_report_object → uvm_component → uvm_sequencer_base
→ uvm_sequencer_param_base #(REQ, RSP) → uvm_sequencer #(REQ, RSP)
4. uvm_agent
- 具有路由功能
- 通过
is_active变量判断是否需要例化uvm_sequencer和uvm_driver - active模式:包含sequencer + driver + monitor
- passive模式:只包含monitor
5. uvm_scoreboard
- 正是由于uvm_scoreboard通用的比较数据特性,UVM自带的其它两个用来做数据比较的类其实很少被使用到
uvm_in_order_comparator #(type T):顺序比较器uvm_algorithm_comparator #(type BEFORE, type AFTER, type TRANSFORMER):算法比较器
uvm_in_order_comparator
- 是一个参数类,并且它有两个端口before_export和after_export
- 分别从DUT的输入端monitor和输出端monitor获取观测到的数据事务
- 这些数据事务是将多个时钟周期内的数据整合为更高抽象级的数据对象
- 要求前后端监测到数据事务类型应该相同
uvm_algorithm_comparator
- 也是一个参数类,参数数目更多
- 贴合更多实际场景:DUT的输入端监测数据格式不同于输出端数据格式
- type BEFORE与type AFTER两个事务类可以不相同
- 用户应提供将BEFORE类转换为AFTER类的转换类type TRANSFORMER
创建scoreboard注意
systemverilog
// 用另一种方法创建的时候,注意不要漏掉 #(bus_xact)
uvm_scoreboard #(bus_xact) sb;
6. uvm_env / uvm_test
- uvm_env:验证环境的容器,包含agent、scoreboard等
- uvm_test:测试用例的顶层,通过config_db配置env,并通过factory进行override
7. MCDF顶层验证方案
reg_env(寄存器模块验证环境)
reg_master_agent:提供寄存器接口驱动信号reg_slave_agent:提供寄存器接口反馈信号scoreboard:分别从reg_master_agent内的monitor和reg_slave_agent内的monitor获取监测数据,并进行数据比对
环境集成方案二
- 方案一最大的额外投入在于需要新建一个scoreboard用来检查MCDF的整体功能
- 如果顶层设计没那么复杂,重新实现一个顶层scoreboard其复杂度还是可控的;但是如果将来的顶层环境更加复杂,那么复用底层的scoreboard就变得省时省力了
- 方案二的目的在于复用底层模块环境的scoreboard,减少顶层环境的额外成本
- 顶层环境的组件都直接复用了各个模块验证环境
- 顶层环境在集成模块验证环境时,需要将各个子模块中的agent配置为不同模式(active或者passive),以此适应顶层场景
- 不再需要实现新的scoreboard,而是可以复用原有模块验证环境的scoreboard
Day19 - 构建验证环境的内经
1. 回归创建
核心思想
- 环境的框架建立主要就靠这一技能了
- 通过这种方式,上一级的组件在例化自身(执行new()函数)之后,会执行各个phase阶段
- 通过build_phase可以进一步创建子组件,而这些子组件也通过一样的过程去创建下一级组件
- 回归创建之所以可以实现,这要依赖于自顶向下执行顺序的build_phase
- 通过build_phase这种结构化执行顺序可以保证父组件必先于子组件创建
创建过程步骤
- 在定义成员变量时赋予默认值,或者在new()函数中赋予初始值
- 结构配置变量用来决定组件的条件生成,例如uvm_agent依靠
is_active变量来判断是否需要例化uvm_sequencer和uvm_driver - 模式配置变量用来决定各个子组件的工作模式
- 子组件按照自顶向下、从前到后的顺序依次生成
2. 顶层配置
配置层次
reg_test
├── rgm (寄存器模型)
├── topcfg (顶层配置)
│ └── is_active
├── virtual sequence
│ ├── mst_t
│ └── slv_t
└── reg_env
├── rgm
├── topcfg
├── reg_master_agent
│ ├── is_active
│ ├── sequencer
│ └── driver
├── monitor
├── reg_slave_agent
│ ├── is_active
│ ├── sequencer
│ └── driver
├── monitor
└── scoreboard
- 顶层test通过config_db设置env和agent的配置
is_active控制agent是active模式还是passive模式- 配置信息自顶向下传递
艾宾浩斯复习计划表
根据艾宾浩斯遗忘曲线,最佳复习节点为:第1天、第2天、第4天、第7天、第15天
每日复习安排(每天上午10:00)
| 学习日 | 第1天复习 | 第2天复习 | 第4天复习 | 第7天复习 | 第15天复习 |
|---|---|---|---|---|---|
| Day12 | Day12 | Day12+13 | Day12~14 | Day12~15 | Day12~16 |
| Day13 | Day13 | Day13+14 | Day13~15 | Day13~16 | Day13~17 |
| Day14 | Day14 | Day14+15 | Day14~16 | Day14~17 | Day14~18 |
| Day15 | Day15 | Day15+16 | Day15~17 | Day15~18 | Day15~19 |
| Day16 | Day16 | Day16+17 | Day16~18 | Day16~19 | Day16~19 |
| Day17 | Day17 | Day17+18 | Day17~19 | Day17~19 | Day17~19 |
| Day18 | Day18 | Day18+19 | Day18~19 | Day18~19 | Day18~19 |
| Day19 | Day19 | Day19 | Day19 | Day19 | Day19 |
每节复习核心记忆点
Day12 必记
- ★ 功能覆盖率关注"状态"而非"具体数值"
- ★ 代码覆盖率低+功能覆盖率高 = 验证计划不完整
- ★ covergroup在类中声明,在new()中例化
- ★ ignore_bins排除不关心的域值
- ★ cross交叉覆盖率,weight=0不计算
- ★ iff条件采样,start/stop手动控制
Day13 必记
- ★ 子类→父类:隐式转换;父类→子类:$cast动态转换
- ★ 无虚成员,只有虚方法
- ★ virtual实现动态方法查找(多态)
- ★ 回调函数:pre_tx/post_tx扩展功能
Day14 必记
- ★ 参数化类:
class mailbox #(type T=int) - ★ UVM工厂创建:
type_id::create() - ★ 工厂override条件:继承 + 虚方法
- ★ Test优先级 > env优先级
Day15 必记
- ★ uvm_object核心方法:copy/clone/compare/print/pack/unpack
- ★ uvm_root是UVM世界顶层,唯一实例uvm_top
- ★ run_test()启动UVM世界
- ★ phase机制:build_phase自顶向下,run_phase需要raise_objection
- ★ 没有raise_objection,仿真直接退出
Day16 必记
- ★ config_db:先set,再create
- ★
uvm_config_db#(类型)::set/get - ★ 配置信息在build_phase中传递
Day17 必记
- ★ 消息级别:NONE < LOW < MEDIUM < HIGH < FULL
- ★ 宏冗余等级默认low
- ★ uvm_report_info/warning/error/fatal
- ★ set_max_quit_count控制error退出阈值
Day18 必记
- ★ 组件家族:driver/monitor/sequencer/agent/scoreboard/env/test
- ★ sequencer是参数类,需声明REQ类型
- ★ agent通过is_active控制active/passive模式
- ★ scoreboard复用策略(方案二)
- ★ MCDF顶层验证:reg_env、master/slave agent
Day19 必记
- ★ 回归创建:父组件先于子组件创建
- ★ build_phase自顶向下执行
- ★ is_active决定agent内部组件生成
- ★ 顶层配置通过config_db层层传递
自测题库
Day12
- 代码覆盖率高但功能覆盖率低说明什么问题?
- covergroup中的bin是什么?
- 如何在复位期间暂停覆盖率采样?
- cross coverage中option.weight=0的作用是什么?
Day13
- $cast函数的作用是什么?转换失败返回什么?
- 虚方法(virtual)的核心作用是什么?
- 回调函数的典型应用场景有哪些?
Day14
- UVM工厂机制相比new()创建有什么优势?
- 工厂override需要什么条件?
- 参数化类的默认参数如何设置?
Day15
- uvm_object提供了哪些核心数据操作方法?
- 为什么run_phase中需要raise_objection?
- UVM世界的顶层类是什么?唯一实例是什么?
Day16
- config_db的set和get应该在什么顺序执行?
- config_db通常在哪个phase中使用?
Day17
- UVM消息冗余等级的默认值是什么?
- uvm_report_fatal的冗余度默认值是什么?
Day18
- uvm_sequencer的继承层级是怎样的?
- agent的is_active变量有什么作用?
- MCDF顶层验证方案二中,scoreboard如何处理?
Day19
- UVM验证环境的创建顺序是怎样的?
- build_phase的执行顺序是什么?