phase机制介绍
UVM中的phase,按照其是否消耗仿真时间($time打印出的时间)的特性,可以分成两大类,一类是function phase,如 build_phase、connect_phase等,这些phase都不耗费仿真时间,通过函数来实现;另外一类是task phase,如run_phase等,它们耗费 仿真时间,通过任务来实现。给DUT施加激励、监测DUT的输出都是在这些phase中完成的。在下图中,灰色背景所示的是task phase,其他为function phase。
image-20240228151228432
上面的所有phase都是按顺序自上而下自动执行。
class my_case0 extends base_test; string tID = get_type_name(); virtual function void build_phase(uvm_phase phase); super.build_phase(phase); `uvm_info(tID, "build_phase is executed", UVM_LOW) endfunction virtual function void start_of_simulation_phase(uvm_phase phase); super.start_of_simulation_phase(phase); `uvm_info(tID, "start_of_simulation_phase is executed", UVM_LOW) endfunction virtual task run_phase(uvm_phase phase); `uvm_info(tID, "run_phase is executed", UVM_LOW) endtask virtual task pre_reset_phase(uvm_phase phase); `uvm_info(tID, "pre_reset_phase is executed", UVM_LOW) endtask virtual task post_shutdown_phase(uvm_phase phase); `uvm_info(tID, "post_shutdown_phase is executed", UVM_LOW) endtask virtual function void extract_phase(uvm_phase phase); super.extract_phase(phase); `uvm_info(tID, "extract_phase is executed", UVM_LOW) virtual function void final_phase(uvm_phase phase); super.final_phase(phase); `uvm_info(tID, "final_phase is executed", UVM_LOW) endfunction endclass
运行上述代码,可以看到各phase被依次执行。
需要注意的一点是就是run_phase和右边的12个phase,是并列关系,而不是说run_phase包含右边的12个phase,它们是并行运行的,它们的顺序大致如下:
fork begin run_phase(); end begin 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(); end join
UVM提供了如此多的phase,在一般的应用中,无论是function phase还是task phase都不会将它们全部用上。使用频率最高的 是build_phase、connect_phase和main_phase。这么多phase除了方便验证人员将不同的代码写在不同的phase外,还有利于其他验证 方法学向UVM迁移。一般的验证方法学都会把仿真分成不同的阶段,但是这些阶段的划分通常没有UVM分得这么多、这么细 致。所以一般来说,当其他验证方法学向UVM迁移的时候,总能找到一个phase来对应原来方法学中的仿真阶段,这为迁移提供 了便利。
Phase的执行顺序
build_phase是按照自上而下的顺序执行的,在下图中,先执行uvm_test_top的build_phase,再执行env的build_phase。
除了build_phase,所有不耗费仿真时间的phase都是自下而上执行的,对于connect_phase即先执行driver和monitor的connect_phase,再执行agent的connect_phase。
image-20240228151306612
看到这里,很多同学可能就有三个疑惑。
对于i_agt、mdl、scb、o_agt这几个兄弟关系的component,执行顺序是什么样的?
每个component里面都有build_phase和main_phase,是按component执行还是按phase的顺序执行?
那几个task phase,是会消耗仿真时间的,是按照什么样的顺序执行的?
Answer:
无论是自上而下还是自下而上,都只适应于UVM树中有直系关系的component。对于同一层次的、具有兄弟关系的 component,如driver与monitor,它们是按照字典的顺序执行的,这里的字典序的排序 依据new时指定的名字。假如monitor在new时指定的名字为aaa,而driver的名字为bbb,那么将会先执行monitor的build_phase。反之 若monitor为mon,driver为drv,那么将会先执行driver的build_phase。
我们本节提到的bulid_phase、connect_phase、run_phase这些都是时间的概念,而上面这个图中各个component的关系是空间的概念。在执行时,先把各个component中的build_phase执行完,再执行各个component的connect_phase,再执行各个component的run_phase。
类似run_phase、main_phase等task_phase也都是按照自下而上的顺序执行的。但是与前面function phase自下而上执行不同的是,这种task phase是耗费时间的,所以它并不是等到“下面”的phase(如driver的run_phase)执行完才执行“上面”的phase(如agent 的run_phase),而是将这些run_phase通过fork…join_none的形式全部启动。所以,更准确的说法是自下而上的启动,同时在运行。
但一般我们的环境中,只有driver和monitor里面会有run_phase或者main_phase的定义,像agent这种封装类的component,不会定义task_phase。
对于同一component来说,其12个run-time的phase是顺序执行的,但是它们也仅仅是顺序执行,并不是说前面一个phase执行完 就立即执行后一个phase。以main_phase和post_main_phase为例,对于A component来说,其main_phase在0时刻开始执行,100时刻 执行完毕;对于B component来说,其main_phase在0时刻开始执行,200时刻执行完毕;此时整个验证平台的main_phase才执行完毕,接下来执行post_main_phase,即A和B的post_main_phase都是在200时刻开始执 行。假设A的post_main_phase执行完毕需要300个时间单位,而B只需要200个时间单位,无论是A或者B,其后续都没有其他耗时 间的phase了,整个验证平台会在500时刻关闭。
可以看到对于A来说,main_phase在100时刻结束,其post_main_phase在200时刻开始执行。在100~200时刻,A处于等待B的 状态,除了等待不做任何事情。B的post_main_phase在400时刻结束,之后就处于等待A的状态。
这个过程如下图所示:
image-20240228151317829
无论从A还是B的角度来看,都存在一段空白等待时间。但是从整个验证平台的角度来看,各个task phase之间是没有任何空白的。
除了兄弟关系的component,还有一种叔侄关系的component,如my_scoreboard与my_driver,从树的层次结构上 来说,scoreboard级别是高于driver的,但是,这两者build_phase的执行顺序其实也是不确定的。这两者的执行顺序除了上节提到的 字典序外,还用到了图论中树的遍历方式:广度优先或是深度优先。
UVM中采用的是深度优先的原则,在UVM的树形图中,scoreboard及driver的build_phase的执行顺序,i_agt实例化时名字为“i_agt”, 而scb为“scb”,那么i_agt的build_phase先执行,在执行完毕后,接下来执行driver、monitor及sequencer的build_phase。当全部执行完 毕后再执行scoreboard的build_phase。
常用的三个phase
我们上面也讲过bulid_phase、connect_phase和run_phase是三个最常用的phase,那这三个phase的具体作用是什么?应该如何使用?
run_phase也经常会用main_phase代替。
build_phase
主要用来实例化组件,即创建对象。uvm_component对其做的最重要的事情就是自动获取通过config_db::set设置的参数。
connect_phase
TLM端口连接,实现数据通信。TLM通信我们后面再讲。
run_phase
所有的数据处理,都在run_phase中。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !