核心基类
uvm_object
UVM中所有的类都是从一个uvm_void类中继承过来的,这类没有任何的方法和变量,只是一个虚类,等待着他的子类去开发他。继承于uvm_void的分别为uvm_object和uvm_port_base。
事务接口继承于uvm_port_base类,其他所有的类都继承于uvm_object,包括uvm_component类。
uvm_object提供的方法
从uvm_object提供的方法和相关的宏操作来看,它的核心方法主要提供与数据操作的相关服务:Copy、Clone 、Compare 、 Print 、Pack/npaCk 。在SV模块的学习中,我们懂得了什么是句柄拷贝和对象拷贝。因 此无论是copy或者clone。都需要确保在操作的过程中有source object和target object。
域的自动化
从UVM通过域的自动化,使得用户在注册UVM类的同时也可以声明今后会参与到对象拷贝、克隆、打印等操作的成员变量, 域的自动化解放了verifier的双手,这使得在使用uvm_object提 供的一些预定义方法时,非常便捷,而无需再实现自定义方法。
在域的自动化里面,copy这些函数都不需要自己再声明,系统会帮我们准备好。 如果有一些数据没有通过域的自动化来声明的话,它们也将不会自动参与到数据的拷贝、打印等操作,除非用户自己去定义这些数据操作方法。
拷贝(copy)
在UVM的数据操作中,需要对copy和clone加以区分。 前者默认已经创建好了对象。只需要对数据进行拷贝;后者则会自动创建对象并对Source object进行数据拷贝,再返回target object句柄。
无论是copy或者clone,都需要对数据进行复制。 但是如果数据成员包括句柄,那么拷贝的时候,是否只拷贝该成 员句柄本身,还是也额外创建新的对象,拷贝该句柄指向的对象? 。在进行Copy时,默认进行的是深拷贝 (deel copy),即会执行copy()和do_copy
新添加了一个类ball 并且在box中例化了一个ball的对象。 在拷贝过程中,box的其它成员都正常拷贝了,但对于box:b的拷贝 则通过了ball的深拷贝方式进行。
即先执行自动拷贝copy()来拷贝允许拷贝的域,由于ballcolor不允许拷贝,斤以只拷贝了bal:diametero
接下来,再执行do_copy(函数,这个函数是需要用户定义的回调函数(calbaCk function),即在copy()执行完后会执行 do_copy() 如果用户没有定义该函数,那么则不会执行额外的数据操作。 从ball:do_Copy()函数可以看到,如果被持贝对象的diameter小 于20,那么则将自身的diameter设置为20。因此,最后对象 b2.b的成员与b1.b的成员数值不同。
比较(compare)
function bit compare (uvm_object rhs , uvm_comparer comparer = null);
默认情况下,如果不对比较的情况作出额外配置,用户可以在调用compare()方法时,省略第二项参数,即采用默认的比较配置。
在上面的两个对象比较中,会将每一个自动化的域进行比较,所以在执行compare()函数时,内置的比较方法也会将比较错误输出,从结果来看,比较发生了错误,返回0值。 那么,bl.Color和 b2.Color虽然不相同,为什么没有比较错误的信息呢? 原因在于默认的比较器 uvm_package:UVm_default_comparer当比较错误发生时候错,不会再进行后续的比较。
打印(print)
打印方法是核心基类提供的另外一种便于开发和调试的功能。通过field automation,使得声明之后的各个成员域会在调用 uvm_object:print()函数时自动打印出来。
只要在field automation中声明过的域,在稍后的print()函数执行时, 都将打印出它们的类型、大小和数值, 如果用户不对打印的格式做出修改,那么在打印时,UM会按照uvm_default_printer规定的格式来打印。
打包和解包
function bit pack (ref bit bisttream[], input uvm_packer packer= null);
function bit unpack (ref bit bisttream[], input uvm_packer packer= null);
pack是为了将自动化声明后的域(标量) 打包为比特流 (bit stream)即将各个散乱的数据,整理到bit数据串中,类似于 Struct packed的整理方式,但又能充分利用数据空间,也更容易与硬件之间进行数据传递和比对。unpack与pack相反,即将串行数据解包为原有的各自域。该操作适用于从硬件接收串行数据,进行校验之后,还原为软件对象中各自对应的成员变量。(用的比较少)
phase机制
SV在构建验证环境中,在硬件设计和仿真之前就完成了例化和连接,软件部分的例化则需要在仿真开始的时候执行。这种验证环境在实现层次化的时候,无法保证例化的先后顺序,以及各个组件在例化之后的连接。因此在UVM验证环境中我们引入了phase机制,可以很清晰的将UVM仿真阶段层次化。
9个主要phase
9个phase中,只有run_phase方法是一个可以耗时的任务,这意味着这个方法可以完成一些等待、激励、采样的任务。其他的phase对应的方法都是函数,必须立即返回0.
phase机制执行顺序
只有component组件才具备,除了build和final是自顶向下的执行顺序,其他的都是自底向上
12个分支phase
run_phase和12个小的分支phase是并行的,都是在start_of_elba任务执行以后开始执行,在12个小的phase执行结束以后,需要等待run_phase才能进入extract_phase。不建议使用这12个小的phase
uvm编译和运行顺序
uvm_top
UVM仿真的结束
UVM-1.1之后,结束仿真的机制只有一种,就是利用objection挂起机制来控制仿真的结束。raise_objection语句必须是在main_phase中第一个消耗仿真时间的语句之前,$display语句是不消耗仿真时间的,可以放在raise_objection之前。
config机制
为了验证环不境的复用性,通过外部的参数配置,使得环境在创建 时可以根据不同参数来选择创建的组件类型、组件实例数目、组件之间的连接以及组件的运行模式等。 比起重新编译来调节变量。如果在仿真中可以通过变量设置来修改环境,那么就更灵活了,而UVM config机制正提供了这样的便捷。
常用的uvm_config_db类的使用方法包括:船夫virtual interface到环境中去,设置单一变量值,例如int、string、enum等,传递对象配置(config object)到环境中去。
top_tb.sv
uvm_config_db#(virtual my_if)::set(null,
"uvm_test_top",
"vif",
input_if);
如果是类的话,就用this。
interface的传递
interface传递可以很好地解决了连接硬件世界和软件世界。而在之前Sv验证模块中,虽然可以通过层次化的interface的索引来完成了传递,但是这种方式不利于软件环境的封装和复用,UVM的uvm_Config_db使得接口的传递和获取彻底分离开来,需要注意的是:
接口传递应该发生在run_test()之前,这保证了在进入build_phase之前,virtual interface就已经传入uvm_config_db里面了
应该注意interface和virtual interface的区别,在传递过程中的类型应该为virtual interface,即实际接口的句柄。
object传递
在test配置中,需要配置的参数不只是数量多,而且可能还分属于不同的组件
在使用set()/get()方法的时候,传递的参数类型应该上下保持一致,对于uvm_object等实例的转换,如果get类型和set类型不一样,应该首先通过$cast()完成类型转换,再对类型转换后的对象进行操作。
set()/get()方法传递的参数可以使用通配符“*”来表示任意层次,类似于正则表达式的用法。*.comp1 与 *comp1 的区别。前者表示的是目前层次下所有名为comp1的组件,后者为当前层次以及当前层次以下所有名为comp1 的组件。
消息管理
消息方法
在UVM环境中或者环境外,只要引入uvm_pkg,均可以通过以下方法按照消息的严重级别和冗余程度来打印消息:
包含严重级别,冗余度、消息ID、消息、文件名和行号这些信息
消息处理
消息宏
uvm_error出现以后,仿真就会默认停止,这是因为设置了UVM_COUNT数量为1导致的,可以通过set_max_quit_count来修改UVM_COUNT的值。
回调函数