(6)UVM phase机制
概述
SV的验证环境构建中,可以发现,传统的硬件设计模型在仿真开始前,已经完成例化和连接了;而SV的软件部分对象例化则需要在仿真开始后执行。
虽然对象例化通过调用构建函数new()来实现,但是单单通过new()函数无法解决一个重要问题,那就是验证环境在实现层次化时,如何保证例化的先后关系,以及各个组件在例化后的连接。此外如果需要实现高级功能,例如在项层到底层的配置时,SV也无法在底层组件例化之前完成对底层的配置逻辑。
因此UVM在验证环境构建时,引入了phase机制,通过该机制我们可以很清晰地将UVM仿真阶段层次化
这里的层次化,不单单是各个phase的先后执行顺序,而且处于同一phase中的层次化组件之间的phase也有先后关系。
phase机制
执行机制
如果暂时抛开phase的机制剖析,对于UVM组件的开发者而言,他们主要关心各个phase执行的先后顺序。在定义了各个phase虚方法后,UVM环境会按照phase的顺序分别调用这些方法。
class subcomp extends uvm_component;
`uvm_ccomponent_uti1s(subcomp)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
`uvm_info("build phase","", UVM_LOW)
endfunction
function void connect_phase(uvm_phase phase);
`uvm_info("connect phase","", UVM_LOW)
endfunction
function void end_of_elaboration_phase(uvm_phase phase);
`uvm_info("end_of_elaboration_phase","", UVL_LOW)
endfunction
function void start_of_simulation_phase(uvm_phase phase);
`uvm_info("start of simulation phase","", UVM_LOW)
endfunction task run_phase(uvm_phase phase);
`uvm_info("run phase","", UVM_LOW)
endtask
function void extract_phase(uvm_phase phase);
`uvm_info("extract phase","", UVM_LOW)
endfunction
function void check_phase(uvm_phase phase);
`uvm_info("check phase","", UVM_LOW)
endfunction
function void report_phase(uvm_phase phase);
`uvm_info("report_phase","", UVM_LOW)
endfunction
function void final_phase(uvm_phase phase);
`uvm_info("final phase","",UVM_LOW)
endfunction
endclass
class topcomp extends subcomp;
subcomp c1,c2;
function void build_phase(uvm_phase phase);
`uvm_info("build phase","",UVM LOW)
c1=subcomp::type id::create("c1",this);
c2=subcomp::type id::create("c2",this);
endfunction
endclass
class test1 extends uvm_test;
topcomp t1;
...
function void build_phase(uvm_phase phase);
t1=topcomp::type id::create("t1",this);
endfunction
endclass
topcomp继承了subcomp,且在其中定义了c1和c2test继承了uvm test且定义了t1,在t1里面有c1和c2
task phase和function phase
UVM中的phase,按照其是否消耗仿真时间($time打印出的时间)的特性,可以分成两大类,一类是function phase,如build phase、connect phase等,这些phase都不耗费仿真时间,通过函数来实现;另外一类是task phase,如run phase等,它们耗费仿真时间,通过任务来实现。给DUT施加激励、监测DUT的输出都是在这些phase中完成的。在下图中,灰色背景所示的是task phase,其他为function phase。
但是task_phase中,run_phase和pre_reset_phase等12个小的phase并行运行。后者称为动态运行(run-time)的phase。对于其它phase对应的方法都是函数,必须立即返回(0耗时)。
UVM编译和运行顺序
要在仿真开始时建立验证环境,用户可以考虑选择下面几种方式:
- 可以通过全局函数(由uvm_pkg提供)run_test)来选择性地指定要运行哪一个uvm_test。这里的test类均继承于uvm_test。这样的话,指定的test类将被例化并指定为顶层的组件。一般而言,run_test()函数可以在合适module/program中的initial进程块中调用。
- 如果没有任何参数传递给run_test(),那么用户可以在仿真时通过传递参数
+UVM_TESTNAME=<test_name>
,来指定仿真时调用的uvm_test。当然,即便run_test()函数在调用时已经有test名称传递,在仿真时+UVM_TESTNAME=<test name>
也可以从顶层覆盖已指定的test。这种方式使得仿真不需要通过再次修改run_test()调用的test名称和重复编译,就可以灵活选定test。
无论上面哪一种方式,都必须在顶层调用全局函数run_test(),用户可以考虑不传递test名称作为参数,而在仿真时通过传递参数+UVM_TESTNAME=<test_name>
来选择test。
全局函数run_test()的重要性,正是从uvm_root创建了一个UVM世界。
phase的执行顺序
对于UVM树来说,共有三种顺序可以选择,一是自上而下,二是自下而上。
UVM使用自上而下的方式执行build_phase。UVM的设计哲学就是在build_phase中做实例化的工作。若非自上而下执行:driver和monitor都是agent的成员变量,所以它们的实例化都要在agent的build_phase中执行。如果在agent的build phase之前执行driver的build phase,此时driver还根本没有实例化,所以调用driver.build_phase只会引发错误。
除了build_phase之外,所有不耗费仿真时间的phase(即function phase)都是自下而上执行的。如对于connect_phase即先执行driver和monitor的connect phase,再执行agent的connect phase。
无论是自上而下还是自下而上,都只适应于UVM树中有直系关系的component。对于同一层次的、具有兄弟关系的component,如driver与monitor,它们的执行顺序是按照字典序的。这里的字典序的排序依据new时指定的名字。假如monitor在new时指定的名字为aaa,而driver的名字为bbb,那么将会先执行monitor的build_phase。反之若monitor为mon,driver为drv,那么将会先执行driver的build_phase。
这篇笔记参考《UVM实战》、《芯片验证漫游指南》和某验证视频整理而成,仅作学习心得交流,如果涉及侵权烦请请告知,我将第一时间处理。