1.验证的主要任务
1.DUT的行为表现是否与特性列表中要求的一致;
2.DUT是否实现了所有特性列表中列出的特性;
3.DUT对于异常状况的反应是否与特性列表和设计规格说明书中的一致,如中断是否置起;
4.DUT是否足够稳健,能够从异常状态中恢复到正常的工作模式;
2.验证方法学
1.VMM(Synopsys)
VMM中集成了寄存器解决方案RAL
2.OVM(Cadence和Mentor)
OVM引进了factory机制,功能非常强大,但是它里面没有寄存器解决方案
3.UVM
UVM几乎完全继承了 OVM ,同时又采纳了 Synopsys 在 VMM 中的寄存器解决方案 RAL 。同时, 还吸收了 VMM 中的 一些优秀的实现方式三大EDA厂商都完美支持UVM,越来越多的公司也开始使用UVM,UVM2011年推出,还很年轻且具有活力。
3.典型的UVM验证平台框图![](https://img-blog.csdnimg.cn/013c54881cf3464ea4e88b53ec2816e2.png)
4.component
每一个派生自uvm_component或其派生类的类在其new函数中要指明两个参数:
name和parent
function new(string name = "", uvm_component parent = );
5.phase
UVM由phase来管理验证平台的运行,这些phase统一以xxxx_phase来命名,且都有一个类型为uvm_phase、名字为phase的参数;
6.`uvm_info
`uvm_info的功能与 display 语句类似,但是它 更加强大。它有三个参数,第一个参数是字符串,用于把打印的信息归类;第二个参数也是字符串,是具体需要打印的信息;第三个参数则是冗余级别。`uvm_info(ID, MSG, VERBOSITY)
UVM_NONE 完全不过滤(全部打印)
7.get_full_name
路径索引函数,可用如下代码得知当前节点的路径索引
$display("the full name of current component is: %s", get_full_name());
8.
`include "uvm_macros.svh"
import uvm_pkg::*;
1.uvm_macros.svh文件通过include语句包含进来。这是UVM中的一个文件,里面包含了众多的宏定义,只需要包含一次
2.import语句将整个uvm_pkg导入验证平台中。只有导入了这个库,编译器在编译my_driver.sv文件时才会识别其中的 uvm_driver等类名
9.run_test()
1.一个run_test语句会创建一个名为uvm_test_top的实例;
2.可以通过在命令行添加 +UVM_TESTNAME=TEST_NAME 调动仿真;
命令行语言会覆盖run_test();更推荐使用命令行语句,只需要编译一次而无需修改代码;
10.uvm_component_utils
1.所有派生自uvm_component及其派生类的类都应该使用uvm_component_utils宏注册;
2.在UVM验证平台中,只要一个类使用uvm_component_utils注册且被实例化,那么这个类的main_phase就会自动被调用;
`uvm_component_utils_begin(TYPE) `uvm_field_* macro invocations here `uvm_component_utils_end
11.objection
1.uvm通过objection机制来控制验证平台的关闭;(默认有超时退出)
2.raise_objection和drop_objection总是成对出现;
3.raise_objection语句必须在main_phase中第一个消耗仿真时间的语句之前
4.在每个phase中,UVM会检查是否有objection被提起;
如果有,那么等待这个objection被撤销后停止仿真;如果没有,则马上结束当前phase。
(一般只在main_phase,因为其他phase不消耗时间)
12.virtual interface
1.避免绝对路径方法是使用宏或使用interface;
绝对路径的使用大大减弱了验证平台的可移植性--一个信号的变更需要大量修改代码
2.在软件域中没法使用interface,只能使用virtual interface;
13.config_db
1.config_db机制分为两步,set和get;
uvm_config_db#(type)::set(,this, "*", "A"); uvm_config_db#(type)::get(,this, "*", "A");
前两个参数为相对路径,第三个参数第四个参数
2.通常在new函数之后的build_phase中执行;
3.双冒号是因为这两个函数都是静态函数;
14.transaction
1.component组件之间信息的传递都是基于transaction;
2.所有的transaction都要从uvm_sequence_item派生,只有从uvm_sequence_item派生的transaction才能使用sequence机制;
3.transaction有生命周期,需要使用uvm_object_utils注册;
15. type_name::type_id::create
1.只有使用factory机制注册过的类才能使用这种方式实例化;
2.只有使用这种方式实例化的实例,才能使用的factory机制中最为强大的重载功能;
16.uvm_monitor
1.所有的monitor类应该派生自uvm_monitor;
2.由于monitor需要时刻收集数据,在main_phase中使用while(1)循环来实现这一目的;
driver可以直接传给reference model,这里还是推荐使用monitor,原因是:
1.在一个大型的项目中, driver 根据某一协议发送数据,而monitor根据这种协议收集数据,如果 driver 和 monitor 由不同人员实现,那么可以大大减少其中任何一方对协议理解的错误;2.在实现代码重用时,使用monitor 是非常有必要的
17.uvm_agent
1.所有的agent都要派生自uvm_agent类,本身是一个component,使uvm_component_utils宏来实现factory注册。
2.is_active
在build_phase中根据该变量的值决定driver、sequencer是否创建实例。
这个枚举变量仅有两个值:UVM_PASSIVE和UVM_ACTIVE
3.不同的agent代表不同的协议
dri和monitor代码高度相似,所以被封装在一起
sqr与dri联系密切,所以被封装在一起
4.实例化只能在build_phase中执行吗?
当然不是,但强烈建议。
在new函数中也可以进行实例化的动作,例如在new函数中实例化dri和mon,但是这样会导致无法通过直接赋值的方式传递is_active的值,可以通过在agt实例化之前传递。
但是无论是代码规范性还是uvm的设计哲学,强烈建议仅在build_phase中完成实例化
18.field_automation机制
`uvm_object_utils_begin(my_transaction) `uvm_field_int(dmac, UVM_ALL_ON) `uvm_field_int(smac, UVM_ALL_ON) `uvm_field_int(ether_type, UVM_ALL_ON) `uvm_field_array_int(pload, UVM_ALL_ON) `uvm_field_int(crc, UVM_ALL_ON) `uvm_object_utils_end
1.使用uvm_object_utils_begin和uvm_object_utils_end来实现my_transaction的factory注册,在这两个宏中间,使用uvm_field宏 注册所有字段;
2.当使用上述宏注册之后,可以直接调用copy、compare、print等函数,而无需自己定义。这极大地简化了验证平台的搭建,提高了效率;
3.在把所有的字段变成byte流放入data_q中时,字段按照uvm_field系列宏书写的顺序排列;
19.sequence机制
1.sequence
sequence是一个 uvm_object,每一个 sequence都有一个body任务,当一个sequence启动之后,会自动执行body中的代码
2.sequencer
派生自uvm_sequencer,并且使用uvm_component_utils宏来注册到factory中。uvm_sequencer是一个参数化的类,其参数是此sequencer产生的transaction的类型
3.sequence就像是一个弹夹,里面的子弹是transaction,而sequencer是一把枪。弹夹只有放入枪中才有意义,枪只有在放入弹夹后才能发挥威力。
20.`uvm_do(通常用在seq的body任务)
1.创建一个my_transaction的实例m_trans;
2.将其随机化;
3.最终将其送给 sequencer;
21.dri与sqr的握手
sequencer要做的事
第一,检测仲裁队列里是否有某个sequence发送transaction的请求;
第二,检测driver是否申请 transaction。
如果仲裁队列中有发送请求,同时driver 也在向 sequencer 申请新的 transaction ,那么将会同意发送请求,sequence产生 transaction并交给 sequencer ,最终 driver 获得 transaction;如果二者缺少其中之一,则会等待;driver要做的事
第一,通过get_next_item任务向sequencer申请新的transaction,并且驱动它,
第二,驱动完成后调用item_done通知sequencer
当driver使用get_next_item得到一个transaction时, sequencer自己也保留一份刚刚发送出的transaction。当出现 sequencer发出了transaction,而driver并没有得到的情况时, sequencer会把保留的这份transaction再发送出去;
在下次调用get_next_item前, item_done被调用,那么sequencer就认为driver已经得到 了这个transaction,将会把这个transaction删除;
直到driver返回item_done信号。此时,uvm_do宏才算是执行完毕,返回后开始执行下一个uvm_do,并产生新的transaction;
相比于get_next_item, try_next_item的行为更加接近真实driver的行为
22.sequence的启动
1.手工启动
首先创建一个my_sequence的实例seq,之后调用start任务
my_sequence seq; phase.raise_objection(this); seq = my_sequence::type_id::create("seq"); seq.start(i_agt.sqr);
start任务的参数是一个sequencer指针
2.default_sequence
在某个component(如my_env)的build_phase中设置
uvm_config_db#(uvm_object_wrapper)::set (this, "i_agt.sqr.main_phase", "default_sequence", my_sequence::type_id::get());
不需要在sequencer中手工写一些get相关的代码
3.通常只在sequence出现的地方才提起和撤销 objection;
在uvm_sequence这个基类中,有一个变量名为starting_phase,它的类型是uvm_phase, 可以在sequence中使用starting_phase进行提起和撤销objection;
23.base_test
1.base_test派生自uvm_test,使用uvm_component_utils宏来注册到factory中
2.基本实现功能
在build_phase中实例化my_env,并设置sequencer的 default_sequence;
设置整个验证平台的超时退出时间;
通过config_db设置验证平台中某些参数的值;