目录
一、概述
二、9个phase执行机制
三、12个分支phase
四、UVM编译和运行顺序
UVM仿真开始
UVM仿真结束
一、概述
SV的验证环境构建中,传统的硬件设计模型在仿真开始前,已经完成例化和连接了,而SV的软件部分对象例化则需要在仿真开始后执行。虽然对象例化通过调用构建函数new()来实现,但是单单通过new()函数无法解决在验证环境实现层次化时,无法保证例化的先后关系,以及各个组件在例化后的连接的顺序。
如果需要实现高级功能,例如在顶层到到底层的配置时,SV也无法在底层组件例化之前完成对底层的配置逻辑。因此,UVM在验证环境构建时,引入了phase机制,通过该机制可以将UVM仿真阶段层次化,不单单是各个phase的先后执行顺序,而且处于同一phase中的层次化组件之间的phase也有先后关系。
二、9个phase执行机制
9个phase对应9个方法,且只有component具备这9个phase。也就是说如果类型是组件类型,则该类型一定有phase机制。
对于UVM组件,主要关心各个phase执行的先后顺序。在定义了各个phase虚方法后,UVM环境会按照phase的顺序分别调用这些方法。
这9个phase对于一个测试环境的生命周期而言,是有固定的先后执行顺序的,同时对于同一个phase中的组件,执行也会按照层次的顺序或者自顶向下或者自顶向上来执行。
对于build_phase,执行顺序按照自顶向下,这符合验证结构建设的逻辑,因为只有先例化高层组件,才会创建空间来容纳底层组件。
只有uvm_component及其继承于uvm_component的子类,才会按照phase机制将上面的9个phase先后执行完毕,这些phase在uvm_component中通过_phase的后缀完成了虚方法的定义,比如build_phase()可以定义一些组件例化和配置的任务。
class subcomp extends uvm_component; //1:继承c0mp0nent`uvm_component_utils(subcomp) //:2:宏注册function new(string name, uvm_component parent); //:3:newsuper.new(name, parent);endfunction//下面是九个阶段phasefunction void build_phase(uvm_phase phase); //默认形式,把参数传进来`uvm_info("build_phase", "", UVM_LOW)endfunctionfunction void connect_phase(uvm_phase phase);`uvm_info("connect_phase", "", UVM_LOW)endfunctionfunction void end_of_elaboration_phase(uvm_phase phase);`uvm_info("end_of_elaboration_phase", "", UVM_LOW)endfunctionfunction void start_of_simulation_phase(uvm_phase phase);`uvm_info("start_of_simulation_phase", "", UVM_LOW)endfunctiontask run_phase(uvm_phase phase);`uvm_info("run_phase", "", UVM_LOW)endtaskfunction void extract_phase(uvm_phase phase);`uvm_info("extract_phase", "", UVM_LOW)endfunctionfunction void check_phase(uvm_phase phase);`uvm_info("check_phase", "", UVM_LOW)endfunctionfunction void report_phase(uvm_phase phase);`uvm_info("report_phase", "", UVM_LOW)endfunctionfunction void final_phase(uvm_phase phase);`uvm_info("final_phase", "", UVM_LOW)endfunction
endclassclass topcomp extends subcomp; //继承了subcomp上面九个phasesubcomp c1, c2;...function void build_phase(uvm_phase phase);`uvm_info("build_phase", "", UVM_LOW)c1 = subcomp::type_id::create("c1", this); //利用工厂创建对象 c1:实例名称 c2即parent:实例句柄c2 = subcomp::type_id::create("c2", this);endfunction
endclassclass test1 extends uvm_test;topcomp t1;...function void build_phase(uvm_phase phase);t1 = topcomp::type_id::create("t1", this);endfunction
endclass
上方代码层次结构:
仿真结果:
总结:
这9个phase对于一个测试环境的生命周期而言,是有固定的先后执行顺序的,同时对于同一个phase中的组件,执行也会按照层次的顺序或者自顶向下或者自顶向上来执行。
从输出结果来看,build是一个自顶向下的执行顺序,先把顶层的t1创建出来,然后再创建t1里面的c1和c2,然后connect是自底向上的执行顺序,先创建c1和c2然后再创建t1,其他也同样按照phase所规定的执行顺序进行创建。
在所有的phase中,只有run_phase方法是一个可以耗时的任务,这意味着该方法可以完成一些等待、激励、采样的任务。对于其它phase对于的方法都是函数,必须立即返回(0耗时)。在run_phase中,如果要完成测试,通常需要组织下面的激励序列:上电、复位、寄存器配置、发送主要测试内容、等待DUT完成测试。
三、12个分支phase(run_phase的细分phase)
发送激励的一种简单方式是,在run_phase中完成上面所有的激励,另一种方式是,可以将上面几种典型序列划分到不同区间,让对应的激励按区间顺序发送的话,可以让测试更有层次。因此run_phase又可以分为以下12个phase:
- pre_reset_phase
- reset_phase
- post_reset_phase
- pre_configure_phase
- configure_phase
- post_configure_phase
- pre_main_phase
- main_phase
- post_main_phase
- pre_shutdown_phase
- shutdown_phase
- post_shutdown_phase
实际上run_phase任务和上面细分的12个phase是并行的,即在start_of_simulation_phase任务执行以后,run_phase和reset_phase开始执行,而在shutdown_phase执行完成之后,需要等待run_phase执行完才可以进入extract_phase。
注:路桑建议工作中可以不用12细分phase,那9个phase足够用了
四、UVM编译和运行顺序
0时刻前:首先在加载硬件模型调用仿真器之前,需要完成编译和建模阶段。
run0或0时刻:接下来在开始仿真之前,会分别执行硬件的always/initial语句,以及UVM的调用测试方法run_test和几个phase,分别是build、connect、end_of_elaboration、start_of_simulation。
开始仿真:在开始仿真后,将会执行run_phase或者对应的12个细分phase。
结束仿真:在仿真结束后,将会执行剩余的phase,分别是extract、check、report和final。
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名称传递,在仿真时也可以从顶层覆盖已指定的test。此方式不需要执行编译,灵活选择test。
无论上面哪一种方式,都必须在顶层调用全局函数run_test()
,而全局函数run_test()
的重要性,正是从uvm_root
创建了一个UVM世界。
task run_test(string test_name="");uvm_root top;uvm_coreservice_t cs;cs = uvm_coreservice_t::get();top = cs.get_root();top.run_test(test_name);
endtask
UVM顶层类uvm_root,该类也继承于uvm_component,它也是UVM环境结构中的一员,而它可以作为顶层结构类。它提供了一些像run_test()的这种方法,来充当了UVM世界中的核心角色。在uvm_pkg中,有且只有一个顶层类uvm_root所例化的对象,即uvm_top。
uvm_top的核心作用包括:
- 作为隐形的UVM验证结构的顶层,任何其它的组件实例都在它之下,通过创建组件时指定parent来构成层次。如果parent设置为null,那么它将作为uvm_top的子组件。
- 控制所有组件的phase顺序。
- 索引功能。通过层次名称来索引组件实例。
- 报告配置。通过uvm_top来全局配置报告的繁简度。
- 全局报告设备。由于可以全局访问到uvm_top实例,所以UVM报告设备在组件内部和组件外部(例如module和sequence)都可以访问。
通过uvm_top调用方法run_test(test_name),uvm_top做了如下的初始化:
- 得到正确的test_name。
- 初始化objection机制(控制仿真退出)。
- 创建uvm_test_top实例。
- 调用phase控制方法,安排所有组件的phase方法执行顺序。
- 等待所有phase执行结束,关闭phase控制进程。
- 报告总结和结束仿真。
UVM仿真结束
UVM结束仿真的机制有且只有一种,那就是利用objection挂起机制来控制仿真结束。uvm_objection类提供了一种供所有component和sequence共享的计数器,如果有组件来挂起objection,那么它还应该记得落下objection。参与到objection机制中的参与组件,可以独立的各自挂起objection,来防止run_phase退出,但是只有这些组件都落下objection后,uvm_objection共享的counter才会变为0,这意味run_phase退出的条件满足,因此可以退出run_phase。
对于uvm_objection类,用来反停止的控制方法包括:
//挂起objection
raise_objection(uvm_object obj=null, string description="", int count=1)
//落下objection
drop_objection(uvm_object obj=null, string description="", int count=1)
//设置退出时间
set_drain_time(uvm_object obj=null, time drain)
这几种方法,对于component()而言,可以在run_phase()中使用phase.raise_objection()或者phase.drop_objection()来控制run_phase退出。最好为参数description字符串提供说明,利于后期的调试,应该使用默认count值,对于uvm_top或者uvm_test_top应该尽可能少地使用set_drain_time()。
objection防止仿真退出
class test1 extends uvm_test;...task run_phase(uvm_phase phase);phase.raise_objection(this); //this:当前的组件 当前的组件在当前的phase挂起了objection`uvm_info("run_phase", "entered...", UVM_LOW)#1us; //1us后`uvm_info("run_phase", "exited...", UVM_LOW)phase.drop_objection(this); //当前的组件在当前的phase了落下endtask
endclasshou