UVM知识点总结
Tags: protocol
当UVM启动后, 会自动执行build_phase(仿真0时刻)UVM通过run_test语句实例化了一个脱离了top_tb层次结构的实例, 建立了一个新的层次结构在UVM验证平台中, 只要一个类使用uvm_component_utils注册且此类被实例化了, 那么这个类的main_phase就会自动被调用在每个phase中, UVM会检查是否有objection被提起 ( raise_objection) , 如果有, 那么等待这个objection被撤销( drop_objection) 后停止仿真; 如果没有, 则马上结束当前phase。模拟的数据包,是有生命周期,仿真的某一时间产生,并于check对比后结束
-
验证平台中所有的组件均应派生自UVM中的类。
-
factory机制:必须用宏 uvm_component_utils,uvm_object_utils, uvm_object_utils_begin … end 注册后才能使用。
-
UVM factory机制会维护一个注册表,这些宏可以把用户定义的类注册到该表中。
-
在uvm_top中使用run_test(“your_test_name”),它会自动创建和运行类,该对象的名字是uvm_test_top。也可以在run的命令行中添加:+UVM_TESTNAME=my_case 来指定运行某个case。
-
config_db 机制:由于UVM通过run_test语句实例化了一个脱离了top tb层次结构的实例,建立了脱离了硬件世界的软件天地,为了对这片天地里的信号等进行配置,UVM引入了config_db机制。
-
config_db分为set和get两部操作。
-
在main_phase中,raise_objection() 应该放在第一个消耗时间的语句前,否则无法起到作用。当然,标准的基于uvm 的testbench只应该在sequence里 raise/drop objection.
-
在build_phase时是从树根到叶子,有在每级节点创建完成后才开始下级结点的创建。同一级下的component创建先后顺序不确定。在connect_phase时是从叶子到树根的顺序.
-
interface在class中使用会引起编译错误。为了在class中使用,SystemVerilog创造了virtual interface。
-
使用UVM宏语句的结尾不用分号结尾也可,如 uvm_info, uvm_fatal, uvm_component_utils 等。
-
UVM test类的三个主要作用:例化顶层验证环境 2.通过factory overrides或config db的方式配置验证环境 3.通过引入Sequence来完成test的具体测试内容。
-
UVM env类: 一个soc top env下可能由许多 dedicated的env,如PCIe Env, USB env, Memory controller Env等。
-
filed automation机制: 提供copy(),compare(),print()等函数,以及pack_bytes(),unpack_bytes()等。
-
pack_bytes(data_q)将tr中所有的字段变成byte流放入data_q中,其放入顺序是根据uvm_filed系列宏的书写顺序排列的。
-
unpack_bytes(data_array) 函数将data_array数组中的byte流转化成tr中已被filed automation注册过的各个字段
-
在定义component 时使用参数化的定义之名使用的transcation类型,这样可以使用其预定义好的成员变量,如driver中的req
-
sequence不属于验证平台的任何一部分。sequence就像弹夹,里面的子弹是transcation,而sqeuencer是一把枪。弹夹用完就扔掉没用了,但枪只有战斗结束才不用。
-
uvm_macros.svh文件通过include语句包含进来。 这是UVM中的一个文件, 里面包含了众多的宏定义, 只需要包含一次。
-
import语句将整个uvm_pkg导入验证平台中。 只有导入了这个库, 编译器在编译my_driver.sv文件时才会认识其中的uvm_driver等类名。
-
注意事项:在UVM定义中,组件或者object的注册都应该放在变量的声明之后。
-
注意事项:uvm_driver中有成员变量seq_item_port, 而在uvm_sequencer中有成员变量seq_item_export, 这两者之间可以建立一个“通道”, 通道中传递的transaction类型就是定义my_sequencer和my_driver时指定的transaction类型。
-
注意事项:uvm_top是一个全局变量,可以直接使用uvm_top。它是uvm_root的一个实例,得到它的指针:top=uvm_root::get()
-
uvm_do宏做如下事:
- 创建一个transcation的实例。
- 将其随机化。
- 最终将其送给sequencer。
- driver从sequencer中获取item的方法:
- get_next_item(req)是阻塞的
- try_next_item(req)是非阻塞的
- try_next_item的行为更接近真实的driver的行为,当由数据时就驱动数据到总线,否则总线就处于空闲状态。
- 通过set default_sequence的方式启动sequence。
- starting_phase是uvm_sequence基类中的uvm_phase类型的成员变量。sequencer在启动default_sequence时,会将自身的phase赋值给starting_phase. 因此sequence中可以使用starting_phase进行raise/drop objection。
-
uvm_transaction和uvm_sequence_item有什么区别?
- uvm_transaction是从uvm_object派生的用于对事务进行建模的基类。
- sequence item是在uvm_transaction的基础上还添加了一些其他信息的类,例如:sequence id。建议使用uvm_sequence_item实现基于sequence的激励
-
sequencer和driver之间的握手协议
在sequence端:
- start_item():请求sequencer访问driver
- finish_item():使driver接收sequence item,这是阻塞调用,在driver调用item_done()方法后才返回。
在driver端:
- get_next_item(req):这是driver中的一个阻塞方法,直到接收到sequence item,driver可以将其转换为引脚级协议信号。
- item_done(req):向sequencer发出信号,表明它可以接受新的sequence请求,使得sequence解除对finish_item()方法的阻塞。
-
backdoor和frontdoor访问有什么区别?
backdoor访问:通过RTL信号路径访问,不消耗仿真时间
frontdoor访问:通过数据总线协议访问,消耗仿真时间
-
Phase的运行
所有不耗费仿真时间的phase( 即function phase) 都是自下而上执行的,按照层次结构,同一层次的兄弟结构按照字典序的顺序执行。
对于不同的component,同一个run_phase(或reset_phase)是自下而上的启动,同时运行。
对于同一component来说, 其12个run-time的phase是顺序执行的。即对于每个component来说,总体12个小的run_phase之间可能会有间隔,但是对于,系统来说,所有component的12个小run_phase是顺序,无间隔的执行完毕。 -
不能直接把my_monitor中的analysis_port和my_model中的blocking_get_port相连吗?需要一个fifo呢?
因为AP的writer函数是非阻塞的,调用完后马上返回,需要一个缓冲来存储端口数据
-
transaction,sequencer与driver的区别?
Transaction 继承与uvm_sequence_item类型,是object类型。模拟的是一个数据包,有生命周期,从产生开始到比较完毕结束。
Driver接收transaction,负责把transaction级别的数据转变成DUT的端口级别, 并驱动给DUT,driver只负责驱动transaction, 而不负责产生transaction。uvm_driver也是一个参数化的类,其参数是定义的transaction类型,component类型
Sequencer作为sequence与driver的桥梁,做仲裁,: 第一, 检测仲裁队列里是否有某个sequence发送transaction的请求;(start_item) 第二, 检测driver是否申请transaction。(get—next_item) 。uvm_sequencer是一个参数化的类,派生自uvm_sequencer。 其参数是my_transaction,component类型sequence不属于验证平台的任何一部分,object类型 ,也是一个参数化的类,其参数是定义的transaction类型,
它与sequencer之间有密切的联系, 这点从二者的名字就可以看出来。 只有在sequencer的帮助下, sequence产生出的transaction才能最终送给driver; 同样, sequencer只有在sequence出现的情况下才能体现其价值, 如果没有sequence, sequencer就几乎没有任何作用。 sequence就像是一个弹夹, 里面的子弹是transaction, 而sequencer是一把枪。 弹夹只有放入枪中才有意义, 枪只有在放入弹夹后才能发挥威力。
每一个sequence都有一个body任务, 当一个sequence启动之后, 会自动执行body中的代码。 在上面的例子中, 用到了一个全新的宏:uvm_do。[`uvm_do(m_trans)]它用于: ①创建一个my_transaction的实例m_trans; ②将其随机化; ③最终将其送给sequencer。 -
UVM工厂机制作用
工厂的三大作用,注册,创建,重载
- 方便component类型生成树形结构
- 为object类型和component类型提供override功能
- 提供了对象的copy和clone,compare等方法
-
New()和type_id::creat的区别?在什么时候做?
Type_id::creat会间接的调用new函数,在调用之前会检查目标类型是否被覆盖,
Type_id::creat和new的创建不同不会影响到phase的运行,因为,如build和connect的执行和组建之间的层次关系有关,而层次关系的形成与组件的new()函数相关。两种方式都会调用new()函数 -
联合数组是什么?
联合数组是SystemVerilog中定义的一种非常有用的数据类型,在验证平台中经常使用。UVM对其提供了良好的支持,与联合数组相关的uvm_field宏有:
define uvm_field_aa_int_string(ARG, FLAG)
define uvm_field_aa_string_string(ARG, FLAG)
define uvm_field_aa_object_string(ARG, FLAG)
define uvm_field_aa_int_int(ARG, FLAG)
……
联合数组有两大识别标志,一是存储数据的类型,二是索引的类型,
在这一系列uvm_field系列宏中,出现的第一个类型是存储数据类型,第二个类型是索引类型,如uvm_field_aa_int_string用于声明那些存储的数据是int,而其索引是string类型的联合数组。 -
m_sequencer和p_sequencer有什么区别?
m_sequencer和p_sequencer有什么区别?
M_sequence 是uvm_sequence_item的成员变量,可理解为一个句炳,指向此sequence所挂载的sequencer。m_sequencer是uvm_sequencer_base类型,如果要访问my_sequencer的成员变量,需要做类型转化,将m_sequence转化为p_sequence。使用`uvm_declare_p_sequencer宏声明p_sequencer,实际做的事情就是声明了p_sequence并实现父类句柄转化为子类句柄