先来看一看,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中。
<sim command> +UVM_PHASE_TRACE
在指定phase设置verbosity:
<sim command> +uvm_set_verbosity=<comp>,<id>,<verbosity>,<phase>
例如: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> 不需要phase后缀,如上面例子将uvm_test_top.env.mdl的main_phase中打印的信息屏蔽掉,命令行里用的是+uvm_set_verbosity=uvm_test_top.env.mdl,my_model,UVM_NONE,main 。
设置timeout时间
1. 通过命令行:<sim command> +UVM_TIMEOUT=<timeout>,<overridable>
如<sim command> +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仿真的开始和结束,我们接下来则分别介绍这些方法的应用。
网友评论