1.为什么在TOP里面uvm_config_db set的时候都需要用到uvm_test_top,它针对不同的case产生的实例名字是一样的吗?
是一样的。对于+UVM_TESTNAME=example_case_(0…n),UVM验证平台会创建一个example_case_(0…n)的实例,他们的实例有一个共同的名字:uvm_test_top,所以你在top中congig_db的时候都会自上而下指定UVM component的路径,UVM树的最高就是uvm_test_top
2.怎么可以更好的理解uvm_object,uvm_component,sequence,sequence_item以及config?
我觉得zhangqiang写得《UVM1.1应用指南及源代码分析》一书中比喻的很通俗,原文如下:
uvm_object是一分子,用这个分子可以搭建成许许多多的东西,如既可以搭建成动物,还可以搭建成植物,更加可以搭建成没有生命意识的岩石,空气等。uvm_component就是由uvm_object搭建成的一种高级有生命体或者无生命体,而sequence_item则是由其搭建成的血液或者其他,它流通在各个(uvm_component)之间,sequence则是众多sequence_item的组合,config则是由其搭建的用于规范生命体或者无生命体(uvm_component)行为方式的准则。
3.new和create一个类的实例时有什么区别么?
当你使用了uvm_component_utils注册了之后就可以使用类似于A=example::type_id::create(“a”,this)的形式,create其实是factory重载了new函数,是factory方式实例化;如果你没有使用uvm_component_utils去注册,则只能使用systemverilog的new函数去实例化了,但是使用create产生的实例可以使用uvm的很多功能,比如overide功能(set_type_override_by_type(a1::get_type(),a2::get_type()))。使用上的区别可以这样简单理解,但是,如果觉得还想深入了解请阅读uvm factory源码。
4.UVM常用的phase有哪些?
uvm常用的phase,其实我经常用到的就是build_phase例化,connect_phase连接,run_phase(main_phase)以及report_phase,如果你要使用report_phase,那么建议你最好把要report的变量定义为全局类型的。
5.uvm不同components的同一phase的执行顺序是怎样的?
uvm中对于build_phase是自父类到子类的自上而下的方式去执行的,否则你都没有执行agent的build_phase就去执行driver或者monitor的build_phase那会报错的,因为他们都还没有在agent的build_phase中实例化。除了build_phase,其他的function phase(注意这个地方)都是自下而上的方式,比如connect_phase,都是先执行driver或者monitor的connect_phase再执行agent的connect_phase,以此类推。如果是同一层次的driver和monitor是按照实例化之后的名字的字典顺序执行的,比如driver实例化为“A”,monitor实例化为“B”,那么就先执行driver的phase。对于需要消耗仿真时间的task phase则是自下而上的启动,同时在运行,不存在“等”的情况存在。
6.如果一个component中需要get的变量个数比较多时,怎么可以减少这种uvm_config_db #(type)::get()的工作量呢?
可以你只要在你的component中使用field_automation把需要get的变量都要注册了factory机制,在当前component的build_phase()中调用super.build_phase()就可以了,但是set()中中的第三个和第四个变量名需要一致。
比如
class my_driver extends uvm_driver;
int num_0;
int num_1;
`uvm_component_utils_begin(my_driver)
`uvm_field_int(num_0,UVM_ALL_ON)
`uvm_field_int(num_1,UVM_ALL_ON)
`uvm_component_utils_end
function void build_phase(uvm_phase phase)
Super.build_phase(phase)
(此处就不需要一一get()了啊)
endfunction
endclass
7.寄存器访问为什么要有BACKDOOR的存在呢?
因为有时候用frontdoor是没有办法改变某些寄存器的值的。如在DUT中有一个写清的计数器,当DUT内部发生改变时,这寄存器的值会一直增加。对于用户来说,可以对此寄存器做两种操作,一是通过总线frontdoor来读取这个寄存器的值,二是通过写操作把这个寄存器清零,假设现在要测试一下这个寄存器的进位功能,也即要写入16’hFFFF等值,此时只有通过backdoor的方式来进行。另外,backdoor相对frontdoor来说,是不消耗仿真时间的(即backdoor前后$time的返回值不会改变),backdoor的速度比frontdoor也快的多。
8.寄存器模型是怎么去操作的frontdoor的?
说白了,其实是通过default_map去进行frontdoor操作的
9.常用操作对register model的镜像值和期望值的影响有哪些?
read&write:使用BACKDOOR或FRONTDOOR的方式从DUT中读取或者写入指定寄存器的值,当操作完成后,会根据读写的结果更新register model中的渴望值和镜像值(二者相等)。
peek&poke:peek几乎等同于使用BACKDOOR方式的read,poke几乎等同于BACKDOOR方式的write,与read和write的区别是peek和poke不会模仿寄存器的行为。如对于一个读清的寄存器来说,进行read操作,那么无论BACKDOOR还是FRONTDOOR,那么DUT中寄存器的值依然保持不变。peek和poke操作之后,register model中的渴望值和镜像值都会更新(二者一致)。
get&set:这两个操作都是只针对渴望值的。只有渴望值会改变,镜像值不会改变。
randomize:randomize之后,渴望值将会随机出来的数值,镜像值不会改变。
update:这个操作会检查寄存器的渴望值和镜像值是否一致,如果不一致就会把渴望值写入到DUT中,并且更新新镜像值,使其与渴望值一致。每个由uvm_reg派生出来的类都会有update操作。另外,每个由uvm_reg_block派生出来的类也有update操作,它将会调用加入到此block中的所有寄存器的update。
mirror:它将会读取DUT,并且更新镜像值和渴望值。读取的方式可以选择BACKDOOR或者FRONTDOOR。与update类似,每个uvm_reg派生来的类都有mirror操作,每个由uvm_reg_blcok派生出来的类也有mirror操作。mirror操作可以指定是否报告DUT中寄存器的值与register model中镜像值不一样。如果选择了这一项,那么就可以检查某些计数器的值是否DUT中相应计数器的值一致。
predict:它将会把register model中的镜像值和渴望值都更新为要设置的值。它与set的区别是后者只更新渴望值而不更新镜像值。这个操作非常有用。假如DUT中有一个寄存器用于统计包的数量,当收到一个包时,DUT中寄存器的数值加1,而验证平台中要想同步的更新register model中相关寄存器的值就要用到predict操作。
10.如果我们的环境不在Makefile脚本里面设置打印级别,那么默认的打印severity是什么?
如果我们的环境中不设置打印级别,那么将会直接返回m_max_verbosity_level,它是int整型变量,系统在初始化的时候设置为UVM_MEDIUM。