phase机制是uvm最重要的几个机制之一,它使得uvm的运行仿真层次化,使得各种例化先后次序正确,保证了验证环境与DUT的正确交互。
目录
一、phase机制概述
二、phase执行顺序
(1)时间顺序
(2)空间顺序
三、phase机制中uvm树的遍历
四、phase的super
五、phase的跳转
六、phase的调试和超时退出
总结
uvm中的phase按照是否消耗仿真时间分为function phase和task phase两类,不消耗仿真时间的为function phase,而消耗仿真时间的为task phase。
uvm中的phase:
phase机制的意义是使得UVM的运行层次化,使得各种例化先后次序正确。UVM的phase主要有9个,外加12个小phase,这12个小的phase称为run-time phase。其中除了run phase和12个小的phase是task之外,其余的都是function。比较常用的phase有build_phase、connect_phase、reset_phase、main_phase、run_phase、report_phase、final_phase等。
为什么要引入这12个小的phase?分成小的phase是为了实现更加精细化的控制。reset、configure、main、shutdown四个phase是核心,这四个phase通常模拟DUT的正常工作方式:
(1)在reset_phase对DUT进行复位、初始化等操作。
(2)在configure_phase则进行DUT的配置。
(3)在main_phase主要是DUT的运行。
(4)在shutdown_phase主要是一些与DUT断电相关的操作。
通过这种细分实现对DUT更加精确的控制,同时也可以让我们实现在phase间的跳转,更便于去构建一些场景。
phase的执行顺序可以分为时间和空间。
先说时间,在时间层面所有的phase会按照顺序自上向下依次执行。其中12个小的phase与run_phase是并行的关系。
run_phase和main_phase之间的关系:
objection机制是UVM中唯一可以控制仿真开始和结束的方式。在task phase中,至少有一个task phase要在消耗第一条消耗仿真时间的语句执行之前挂起objection。
(1)如果12个分支中有一个phase(比如main_phase)挂起了objection ,那么run_phase 中则不需要挂起objection 就可以执行其中的代码;但是这时,run_phase的运行时间被动地受这个挂起objection的分支phase的控制。
(2)而如果在run_phase中挂起了objection,没有在main_phase中挂起,main_phase中的操作则不会执行,此时的运行时间完全由run_phase控制。
请注意不要混淆,这里的时间顺序并不是指消耗仿真时间,只有task phase才会消耗仿真时间!所有的function phase都会在0时刻完成。
对于task phase,uvm内置了phase的同步。只有同一个phase全部执行完毕,才会执行下一个phase。但是从整个环境的角度来看,各个task phase之间是没有空白的。
再说空间,uvm中规定层次越高,优先级越高,层次即为在uvm树中的位置,uvm_top是树根,越靠近树根,则层次越高。
我们可以再思考一下,层次越高,就会越接近用户。我们会在uvm_test_top(也就是testcase)中启动不同的sequence,来实现不同的测试场景,而env则是在uvm_test_top中才会完成例化,向下依此类推。
值得注意的是,uvm的设计哲学就是在build_phase中完成例化,在connect_phase中完成连接,这种分块式设计。所以build_phase比较特殊,由于树形结构实例化必须从树根开始,也就是自顶向下运行。但是例如connect_phase则是自底向上运行,因为组件的连接必须从最基础的部件开始。所以对于空间来说:
只有build phase和final phase是自顶向下运行,其余phase都是自底向上运行。
可以使用print_topology来打印树形结构,可以清晰看到各个组件之间的关联。例如:
UVM_INFO @ 33626.00ns: reporter [UVMTOP] UVM testbench topology:
------------------------------------------------------------------
Name Type Size Value
------------------------------------------------------------------
uvm_test_top tc_sanity - @471
fifo_env env - @479
rd_agt rd_agent - @495
drv rd_driver - @621
rsp_port uvm_analysis_port - @638
seq_item_port uvm_seq_item_pull_port - @629
mon rd_monitor - @770
ap uvm_analysis_port - @780
sqr rd_sequencer - @647
rsp_export uvm_analysis_export - @655
seq_item_export uvm_seq_item_pull_imp - @761
arbitration_queue array 0 -
lock_queue array 0 -
num_last_reqs integral 32 'd1
num_last_rsps integral 32 'd1
rdmon_scb_fifo uvm_tlm_analysis_fifo #(T) - @564
analysis_export uvm_analysis_imp - @608
get_ap uvm_analysis_port - @599
get_peek_export uvm_get_peek_imp - @581
put_ap uvm_analysis_port - @590
put_export uvm_put_imp - @572
scb scoreboard - @503
rd_port uvm_blocking_get_port - @803
wr_port uvm_blocking_get_port - @794
wr_agt wr_agent - @487
drv wr_driver - @939
rsp_port uvm_analysis_port - @956
seq_item_port uvm_seq_item_pull_port - @947
mon wr_monitor - @965
ap uvm_analysis_port - @975
sqr wr_sequencer - @816
rsp_export uvm_analysis_export - @824
seq_item_export uvm_seq_item_pull_imp - @930
arbitration_queue array 0 -
lock_queue array 0 -
num_last_reqs integral 32 'd1
num_last_rsps integral 32 'd1
wrmon_scb_fifo uvm_tlm_analysis_fifo #(T) - @511
analysis_export uvm_analysis_imp - @555
get_ap uvm_analysis_port - @546
get_peek_export uvm_get_peek_imp - @528
put_ap uvm_analysis_port - @537
put_export uvm_put_imp - @519
------------------------------------------------------------------
我们可以用build_phase和connect_phase来举例,比如:
UVM_INFO @ 0: reporter [RNTST] Running test tc_sanity...
UVM_INFO ../tc/tc_sanity.sv(30) @ 0.00ns: uvm_test_top [uvm_test_top] build_phase active.
UVM_INFO ../ver/env.sv(33) @ 0.00ns: uvm_test_top.fifo_env [fifo_env] build_phase active.
UVM_INFO ../ver/scoreboard.sv(36) @ 0.00ns: uvm_test_top.fifo_env.scb [scb] build_phase active.
UVM_INFO ../ver/wr_agent/wr_agent.sv(32) @ 0.00ns: uvm_test_top.fifo_env.wr_agt [wr_agt] build_phase active.
UVM_INFO ../ver/wr_agent/wr_driver.sv(31) @ 0.00ns: uvm_test_top.fifo_env.wr_agt.drv [drv] build_phase active.
UVM_INFO ../ver/wr_agent/wr_monitor.sv(29) @ 0.00ns: uvm_test_top.fifo_env.wr_agt.mon [mon] build_phase active.
UVM_INFO ../ver/wr_agent/wr_sequencer.sv(28) @ 0.00ns: uvm_test_top.fifo_env.wr_agt.sqr [sqr] build_phase active.
UVM_INFO ../ver/scoreboard.sv(43) @ 0.00ns: uvm_test_top.fifo_env.scb [scb] connect_phase active.
UVM_INFO ../ver/wr_agent/wr_driver.sv(39) @ 0.00ns: uvm_test_top.fifo_env.wr_agt.drv [drv] connect_phase active.
UVM_INFO ../ver/wr_agent/wr_monitor.sv(38) @ 0.00ns: uvm_test_top.fifo_env.wr_agt.mon [mon] connect_phase active.
UVM_INFO ../ver/wr_agent/wr_sequencer.sv(33) @ 0.00ns: uvm_test_top.fifo_env.wr_agt.sqr [sqr] connect_phase active.
UVM_INFO ../ver/wr_agent/wr_agent.sv(42) @ 0.00ns: uvm_test_top.fifo_env.wr_agt [wr_agt] connect_phase active.
UVM_INFO ../ver/env.sv(46) @ 0.00ns: uvm_test_top.fifo_env [fifo_env] connect_phase active.
UVM_INFO ../tc/tc_sanity.sv(36) @ 0.00ns: uvm_test_top [uvm_test_top] connect_phase active.
可以清晰的看到build_phase的运行是自顶向下,而connect_phase的运行是自底向上。
其实在上面的代码中能够看到,build_phase中scoreboard执行是要先于wr_agent的,这是因为uvm中字典序所导致的。scoreboard的s在字典序中要先于w,如果将scoreboard的例化名称改为z_scoreboard,则会晚于wr_agent执行。实际使用中这种顺序理论上不应该产生影响,但是假如要求一定是某种固定的执行顺序,那么这种代码存在很高风险,应立即进行修改。
从树形结构上来说,scoreboard级别是高于agent中的组件的,但是他们build_phase的执行顺序其实并不确定。UVM中采用的是深度优先的原则,即会先执行完某一条树枝,才会去执行下一条树枝。例如agent和scoreboard,他们的层次相同,在执行时会先将agent中包含的组件全部执行完成,才去执行scoreboard,这就是深度优先原则。我们可以总结为:
横向按照字典序,纵向按照深度原则。
通过super对父类的内容进行继承,其中build_phase的super语句最重要且必须存在,其他phase的super则可以省略。查看uvm源码,可以看到除了build_phase之外其他的phase几乎没有做任何相关的事情:
// phase methods
//--------------
// these are prototypes for the methods to be implemented in user components
// build_phase() has a default implementation, the others have an empty default
function void uvm_component::build_phase(uvm_phase phase);
m_build_done = 1;
build();
endfunction
// Backward compatibility build function
function void uvm_component::build();
m_build_done = 1;
apply_config_settings(print_config_matches);
if(m_phasing_active == 0) begin
uvm_report_warning("UVM_DEPRECATED", "build()/build_phase() has been called explicitly, outside of the phasing system. This usage of build is deprecated and may lead to unexpected behavior.");
end
endfunction
// these phase methods are common to all components in UVM. For backward
// compatibility, they call the old style name (without the _phse)
function void uvm_component::connect_phase(uvm_phase phase);
connect();
return;
endfunction
function void uvm_component::start_of_simulation_phase(uvm_phase phase);
start_of_simulation();
return;
endfunction
function void uvm_component::end_of_elaboration_phase(uvm_phase phase);
end_of_elaboration();
return;
endfunction
task uvm_component::run_phase(uvm_phase phase);
run();
return;
endtask
function void uvm_component::extract_phase(uvm_phase phase);
extract();
return;
endfunction
function void uvm_component::check_phase(uvm_phase phase);
check();
return;
endfunction
function void uvm_component::report_phase(uvm_phase phase);
report();
return;
endfunction
// These are the old style phase names. In order for runtime phase names
// to not conflict with user names, the _phase postfix was added.
function void uvm_component::connect(); return; endfunction
function void uvm_component::start_of_simulation(); return; endfunction
function void uvm_component::end_of_elaboration(); return; endfunction
task uvm_component::run(); return; endtask
function void uvm_component::extract(); return; endfunction
function void uvm_component::check(); return; endfunction
function void uvm_component::report(); return; endfunction
function void uvm_component::final_phase(uvm_phase phase); return; endfunction
// these runtime phase methods are only called if a set_domain() is done
task uvm_component::pre_reset_phase(uvm_phase phase); return; endtask
task uvm_component::reset_phase(uvm_phase phase); return; endtask
task uvm_component::post_reset_phase(uvm_phase phase); return; endtask
task uvm_component::pre_configure_phase(uvm_phase phase); return; endtask
task uvm_component::configure_phase(uvm_phase phase); return; endtask
task uvm_component::post_configure_phase(uvm_phase phase); return; endtask
task uvm_component::pre_main_phase(uvm_phase phase); return; endtask
task uvm_component::main_phase(uvm_phase phase); return; endtask
task uvm_component::post_main_phase(uvm_phase phase); return; endtask
task uvm_component::pre_shutdown_phase(uvm_phase phase); return; endtask
task uvm_component::shutdown_phase(uvm_phase phase); return; endtask
task uvm_component::post_shutdown_phase(uvm_phase phase); return; endtask
不过为了代码风格的统一,我还是会给每个phase都加上。
这里先说一下跳转,后面在专门讨论复位的时候会再次用到它,因为跳转对于复位实现来说是必要的。跳转中最难的地方在于清理和准备工作,组件之间的fifo需要全部清空,组件内部的队列也需要全部清空,如果清空做不好可能会引发很多未知问题。
跳转使用jump函数,比如:
begin
@(negedge this.wr_drv_if.wrst_n);
phase.jump(uvm_reset_phase::get());
`uvm_info(this.name,"2nd reset activated.",UVM_LOW)
end
这里就是复位信号到来跳转至reset_phase。
当然能够做跳转的phase也有限制,在uvm_pre_reset_phase::get() 后的所有phase都可以:
uvm_build_phase::get();
uvm_connect_phase::get();
uvm_end_of_elaboration_phase::get();
uvm_start_of_simulation_phase::get();
uvm_run_phase::get();
uvm_pre_reset_phase::get();
// ----------------------------------------------------------
uvm_reset_phase::get();
uvm_post_reset_phase::get();
uvm_pre_configure_phase::get();
uvm_configure_phase::get();
uvm_post_configure_phase::get();
uvm_pre_main_phase::get();
uvm_main_phase::get();
uvm_post_main_phase::get();
uvm_pre_shutdown_phase::get();
uvm_shutdown_phase::get();
uvm_post_shutdown_phase::get();
uvm_extract_phase::get();
uvm_check_phase::get();
uvm_report_phase::get();
uvm_final_phase::get();
除了使用uvm_info打印信息来帮助检查问题之外,uvm还提供命令行参数UVM_PHASE_TRACE来对phase机制进行调试。需要在仿真的命令后面加入:+UVM_PHASE_TRACE,或者加在makefile当中。
UVM_INFO /opt/Synopsys/VCS2014/etc/uvm-1.1/base/uvm_phase.svh(1381) @ 33626.00ns: reporter [PH/TRC/DONE] Phase 'common.report' (id=271) Completed phase
UVM_INFO /opt/Synopsys/VCS2014/etc/uvm-1.1/base/uvm_phase.svh(1403) @ 33626.00ns: reporter [PH/TRC/SCHEDULED] Phase 'common.final' (id=283) Scheduled from phase common.report
UVM_INFO /opt/Synopsys/VCS2014/etc/uvm-1.1/base/uvm_phase.svh(1124) @ 33626.00ns: reporter [PH/TRC/STRT] Phase 'common.final' (id=283) Starting phase
UVM_INFO /opt/Synopsys/VCS2014/etc/uvm-1.1/base/uvm_phase.svh(1381) @ 33626.00ns: reporter [PH/TRC/DONE] Phase 'common.final' (id=283) Completed phase
UVM_INFO /opt/Synopsys/VCS2014/etc/uvm-1.1/base/uvm_phase.svh(1403) @ 33626.00ns: reporter [PH/TRC/SCHEDULED] Phase 'common.common_end' (id=180) Scheduled from phase common.final
UVM_INFO /opt/Synopsys/VCS2014/etc/uvm-1.1/base/uvm_phase.svh(1124) @ 33626.00ns: reporter [PH/TRC/STRT] Phase 'common.common_end' (id=180) Starting phase
UVM_INFO /opt/Synopsys/VCS2014/etc/uvm-1.1/base/uvm_phase.svh(1381) @ 33626.00ns: reporter [PH/TRC/DONE] Phase 'common.common_end' (id=180) Completed phase
挂死是有时候会遇到的问题,一般是陷入了死循环无法终止。出现挂死的时候设置的drain_time就不会起作用,导致仿真无法结束。在uvm中通过uvm_root的set_timeout函数可以设置超时时间来强制结束(在top中使用$finish或者$stop是同样的效果)。
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
uvm_top.set_timeout(500ns, 0);
endfunction
set_timeout函数有两个参数,第一个参数是要设置的时间,第二个参数表示此设置是否可以被其后的其他set_timeout语句覆盖。如果达到规定时间后测试用例还没有运行完毕,则会给出一条uvm_fatal的提示信息,并退出仿真。
phase机制是uvm最重要的几个机制之一,它使得uvm的运行仿真层次化,使得各种例化先后次序正确,保证了验证环境与DUT的正确交互。
验证平台是很复杂的,要搭建出一个验证平台是一件相当繁杂的事情,要正确地掌握并理顺这些步骤是一个相当艰难的过程。在不同时间做不同的事情,这就是uvm中phase的设计哲学。但是仅仅划分成phase是不够的,phase的自动执行功能极大地方便了用户。同时,phase机制的引入在很大程度上解决了因代码顺序杂乱可能会引发的问题,也在很大程度上减少了验证平台开发者的工作量。