先来看一看,UVM的phase有哪些?
phase
函数/任务
执行顺序
功能
典型应用
build
函数
自顶向下
创建和配置测试平台的结构
创建组件和寄存器模型,设置或者获取配置
connect
函数
自底向上
建立组件之间的连接
连接TLM/TLM2的端口,连接寄存器模型和adapter
end_of_elaboration
函数
自底向上
测试环境的微调
显示环境结构,打开文件,为组件添加额外配置
start_of_simulation
函数
自底向上
准备测试环境的仿真
显示环境结构,设置断点,设置初始运行的配置值
run
任务
自底向上
激励设计
提供激励、采集数据和数据比较,与OVM兼容
extract
函数
自底向上
从测试环境中收集数据
从测试平台提取剩余数据,从设计观察最终状态
check
函数
自底向上
检查任何不期望的行为
检查不期望的数据
report
函数
自底向上
报告测试结果
报告测试结果,将结果写入到文件中
final
函数
自顶向下
完成测试活动结束仿真
关闭文件,结束联合仿真引擎
1.UVM phase 概览
UVM采用phase机制来自动化运行testbench各个仿真过程。UVM phase支持显示或隐式的同步方案,运行过程中的线程控制和跳转。用户只要把代码填入对应的phase,这些代码就会自动在正确的时间执行。各个phase执行顺序如下图所示:
image.png
相较于OVM,UVM新增了12个小的task phase,如下图:
image.png
其中run_phase和uvm新增加的12个小phase是并行执行的。
2 按是否消耗仿真时间,所有phase可以分成两大类
<1> function phase:不消耗仿真时间,而其也可分成两大类:
a. 继承自uvm_bottomup_phase, 在UVM component树中,自下而上的执行, 如connect_phase
b. 继承自uvm_topdown_phase, 在UVM component树中,自上而下执行, 如build_phase
<2> task phase:消耗仿真时间的,也称动态运行(run-time)phase.
下图是各个phase的继承关系,从中可以看出
image.png
自上而下(top-down) function phase:build和final phase。
自下而上(bottom-up)f unction phase: connect, end_of_elaboration,start_of_simulation, extract, check, report。
task phase: run_phase以及其他12个小phase: pre_reset, reset_phase, post_reset, pre_configure, configure, post_configure,
pre_main, main, post_main, pre_shutdown, shutdown, post_shutdown, 如下图:
image.png
3 task phase的同步
一个UVM验证平台有许多component组成,每个component都有自己的run_phase,以及从pre_reset 到post_shuddown的12个小phase。只有所有component的一个小task phase 完成,整个仿真平台才开始下一个小task phase的执行。 各个component的run_phase之间,以及run_phase于最后一个小phase--post_shutdown_phase之间,都有这样的同步。
image.png
4. super.xxx_phase
除了super.build_phase,其他super.xxx_phase几乎没做任何事情,因此,除了build_phase,其他phase可不必加super.xxx_phase.
在super.build_phase中,主要完成自动获取config_db中参数的功能,如果自定义的component无需获取任何参数,也可省略。
5. phase的跳转
默认情况下各phase是从上到下按时间顺序执行,但可以自定义做必要的跳转,如在main_phase执行过程中,突然遇到reset信号被置起,可以用jump()实现从mian_phase到reset_phase的跳转:
phase.jump(uvm_reset_phase::get())
task my_driver::main_phase(uvm_phase phase);
`uvm_info("driver", "main phase", UVM_LOW)
fork
while(1) begin
seq_item_port.get_next_item(req);
drive_one_pkt(req);
seq_item_port.item_done();
end
begin
@(negedge vif.rst_n);
phase.jump(uvm_reset_phase::get());
end
join
endtask
跳转的限制
不能跳转到build到start_of_function的function phase, 也不能跳转到run_phase. 从pre_reset_phase后的所有phase都可以作为jump()的参数。 除了向前跳转,也可向后跳转。除了向run-time phase跳转,甚至可以向final_phase等function phase跳转。
phase的调试
在命令行加UVM_PHASE_TRACE,可以将进入和退出个phase的信息打印到log中。
+UVM_PHASE_TRACE
在指定phase设置verbosity:
+uvm_set_verbosity=,,,
例如:simv +uvm_set_verbosity=uvm_test_top.env.mdl,my_model,UVM_NONE,main +UVM_TESTNAME=my_case0 +UVM_PHASE_TRACE -l tmp.log
注意,其中参数 不需要phase后缀,如上面例子将uvm_test_top.env.mdl的main_phase中打印的信息屏蔽掉,命令行里用的是+uvm_set_verbosity=uvm_test_top.env.mdl,my_model,UVM_NONE,main 。
设置timeout时间
1. 通过命令行: +UVM_TIMEOUT=,
如 +UVM_TIMEOUT="300ns, YES"
2.通过在base test中使用set_timeout(): uvm_top.set_timeout(500ns,0);
必要时需要修改宏定义:`define UVM_DEFAULT_TIMEOUT 9200s
————————————————
版权声明:本文为CSDN博主「wonder_coole」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wonder_coole/java/article/details/90443373
code
关于执行的顺序,可以从下面这段简单的例码中得到佐证:
module common_phase_order;
import uvm_pkg::*;
`include "uvm_macros.svh"
class subcomp extends uvm_component;
`uvm_component_utils(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", "", UVM_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;
`uvm_component_utils(topcomp)
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)
c1 = subcomp::type_id::create("c1", this);
c2 = subcomp::type_id::create("c2", this);
endfunction
endclass
class test1 extends uvm_test;
topcomp t1;
`uvm_component_utils(test1)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
t1 = topcomp::type_id::create("t1", this);
endfunction
endclass
initial begin
//t1 = new("t1", null);
run_test("test1");
end
endmodule
输出结果:
UVM_INFO @ 0: reporter [RNTST] Running test test1...
UVM_INFO @ 0: uvm_test_top.t1 [build_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [build_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [build_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [connect_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [connect_phase]
UVM_INFO @ 0: uvm_test_top.t1 [connect_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [end_of_elaboration_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [end_of_elaboration_phase]
UVM_INFO @ 0: uvm_test_top.t1 [end_of_elaboration_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [start_of_simulation_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [start_of_simulation_phase]
UVM_INFO @ 0: uvm_test_top.t1 [start_of_simulation_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [run_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [run_phase]
UVM_INFO @ 0: uvm_test_top.t1 [run_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [extract_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [extract_phase]
UVM_INFO @ 0: uvm_test_top.t1 [extract_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [check_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [check_phase]
UVM_INFO @ 0: uvm_test_top.t1 [check_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [report_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [report_phase]
UVM_INFO @ 0: uvm_test_top.t1 [report_phase]
UVM_INFO @ 0: uvm_test_top.t1 [final_phase]
UVM_INFO @ 0: uvm_test_top.t1.c1 [final_phase]
UVM_INFO @ 0: uvm_test_top.t1.c2 [final_phase]
从这个例子可以看出,上面的九个phase,对于一个测试环境的声明周期而言,是有固定的执行先后顺序的;同时,对于处于同一个phase的组件之间,执行也会按照层次的顺序或者自顶向下、或者自底向上来执行。这个简单的环境中,顶层测试组件test1中,例化了一个t1组件,而t1组件内又进一步例化了c1和c2组件。从执行的打印结果来看,需要注意的地方有:
对于build phase,执行顺序按照自顶向下,这符合验证结构建设的逻辑。因为只有先创建高层的组件,才会创建空间来容纳低层的组件。
只有uvm_component及其继承与uvm_component的子类,才会按照phase机制将上面九个phase先后执行完毕。
上面介绍的九个phase中,常用的phase包括build、connect、run和report,它们分别完成了组件的建立、连接、运行和报告。这些phase在uvm_component中通过_phase的后缀完成了虚方法的定义,比如build_phase()中可以定义一些例化组件和配置的任务。在这九个phase中,只有run_phase方法是一个可以耗时的任务,这意味着该方法中可以完成一些等待、激励、采样的任务。对于其它phase对应的方法,都是函数,必须即时返回(0耗时)。
在run_phase中,用户如果要完成测试,则通常需要经历下面的激励序列:
上电
复位
寄存器配置
主要测试内容
等待DUT完成测试
一种简单的方式是,用户在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
上面的12个phase的执行顺序也是前后排列的。那么这12个phase与run_phase是什么关系呢?我们通过这段例码来看看:
module uvm_phase_order;
import uvm_pkg::*;
`include "uvm_macros.svh"
class test1 extends uvm_test;
`uvm_component_utils(test1)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void start_of_simulation_phase(uvm_phase phase);
`uvm_info("start_of_simulation", "", UVM_LOW)
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("run_phase", "entered ..", UVM_LOW)
#1us;
`uvm_info("run_phase", "exited ..", UVM_LOW)
phase.drop_objection(this);
endtask
task reset_phase(uvm_phase phase);
`uvm_info("reset_phase", "", UVM_LOW)
endtask
task configure_phase(uvm_phase phase);
`uvm_info("configure_phase", "", UVM_LOW)
endtask
task main_phase(uvm_phase phase);
`uvm_info("main_phase", "", UVM_LOW)
endtask
task shutdown_phase(uvm_phase phase);
`uvm_info("shutdown_phase", "", UVM_LOW)
endtask
function void extract_phase(uvm_phase phase);
`uvm_info("extract_phase", "", UVM_LOW)
endfunction
endclass
initial begin
run_test("test1");
end
endmodule
输出结果:
UVM_INFO @ 0: reporter [RNTST] Running test test1...
UVM_INFO @ 0: uvm_test_top [start_of_simulation]
UVM_INFO @ 0: uvm_test_top [run_phase] entered ..
UVM_INFO @ 0: uvm_test_top [reset_phase]
UVM_INFO @ 0: uvm_test_top [configure_phase]
UVM_INFO @ 0: uvm_test_top [main_phase]
UVM_INFO @ 0: uvm_test_top [shutdown_phase]
UVM_INFO @ 1000000: uvm_test_top [run_phase] exited ..
UVM_INFO @ 1000000: uvm_test_top [extract_phase]
从这个例子可以看到,实际上,run_phase任务和上面细分的12个phase是并行进行的。在start_of_simulation_phase任务执行以后,run_phase和reset_phase开始执行,而在shutdown_phase执行完之后,需要等待run_phase执行完以后,才能进入extract_phase。关于执行的关系,可以从下面这张图中得出:
image.png
这里需要提醒用户的是,虽然run_phase与细分的12个phase是并行执行的,而12个phase也是按照先后顺序执行的。为了避免不必要的干扰,用户可以选择run_phase,或者12个phase中的若干来完成激励,但是请不要将它们混合起来使用,因为这样容易导致执行关系的不明确。
如果要进一步深入phase机制的话,我们首先需要清晰下面的概念:phase、schedule和domain。
phase即上面介绍的部分,特定的phase会完成特定的功能。
schedule包含phase的关联数组,即若干个phase会由schedule按照安排的顺序先后执行。
domain则内置一个schedule。
schedule类uvm_schedule和domain类uvm_domain均继承于uvm_phase。
上面首先介绍的9个phase,共同构成了common domain;而另外12个phase,则共同构成了uvm domain。无论是domain、还是phase,它们在UVM环境中都只生成一个唯一的对象。关于common domain和uvm domain的联系和区别是:
common domain无法被扩展或者取代,这是UVM phase机制的核心。也就是说,构成它的9个phase的顺序不能更改,也无法添加新的phase。同时,这一核心的domain也是为了与OVM的phase机制保持兼容,方便从OVM代码转换到UVM代码。
uvm domain则包含了上面的12个phase,其调度也是按照先后顺序执行的。对于这一部分,与common domain不同的是,它们的执行是与run_phase同时开始,并且最后共同结束的。同时,用户还可以自定义一些phase,添加到uvm domain中,设置好与其他phase执行的先后关系。
上面的common domain和uvm domain中包含的phase在uvm_pkg中例化的唯一phase实例群如下:
image
在详细介绍完UVM的各个phase,以及它们之间执行的顺序之后,读者可以结合之前硬件和软件的编译和例化部分,来统一理解UVM世界中的编译和运行顺序:
image
首先在加载硬件模型,调用仿真器之前,需要完成编译和建模阶段。
接下来,在开始仿真之前,会分别执行硬件的always/initial语句,以及UVM的调用测试方法run_test和几个phase,分别是build、connect、end_of_elaboration和start_of_simulation。
在开始仿真后,将会执行run_phase或者对应的12个细化phase。
在仿真结束后,将会执行剩余的phase,分别是extract、check、report和final。
对于使用phase机制,这里有一些建议:
避免使用reset_phase()、configure_phase()、main_phase()、shutdown_phase()和其它pre_/post_ phase。这12个phase尽管细化了run_phase(),但是也使得phase的跳转过为冗余,在将来的UVM版本中,这些phase将考虑被废除。为了控制reset、configure、main和shutdown各个阶段的任务调度和同步,用户可以考虑fork-join语句块,或者高级的同步方式,例如uvm_barrier和uvm_event。
避免phase的跳跃。实际上,用户可以指定个别组件的phase执行中,从phaseA跳跃到phaseC,而忽略了phaseB。但是这种方式不容易理解和调试,所以不建议使用这一特性。
避免自定义phase的使用。尽管uvm domain中允许用户自定义phase,并且规定新添加phase的执行顺序,但是目前的这一机制还不方便调试。用户应该尽量将置于新phase中的任务,剥离到新的任务中,并且在已有的phase中调用它们,完成任务调用的模块化。
从之前的例子和上面的图中,读者可以看到,UVM的环境建立和各个phase的先后调用的入口,都是从run_test()进入的。默认情况下,如果run_test()方法执行完毕,那么系统函数$finish则会被调用,来终止仿真。然而,有更多的方法来分别控制UVM仿真的开始和结束,我们接下来则分别介绍这些方法的应用。