UVM_PHASE的执行顺序

1.build_phase(function phase)

                                                             

        build_phase是特殊的,uvm的设计哲学就是在build_phase中进行uvm_component及其派生类的实例化工作。所以build_phase的执行顺序是自上而下进行的。以agentdriver为例,如果在agent的build_phase执行之前调用driver的build_phase,此时driver还没有实例化,所以调用driver的build_phase只会报错。

        为了验证以上所说,用uvm搭建了test.v的整体架构,并在每一个component组件中的build_phase的开始和结束添加以下代码用以在结果中辨别执行顺序。

`uvm_info("component name","build_phase enter...",UVM_LOW)
`uvm_info("component name","build_phase exiting...",UVM_LOW)

执行结果如下:

       build_phase属于function phaae,并不消耗时间,所以这里的时间均为0。

      根据运行结果得出,“最上边的”test是最先执行的,然后是environment的build_phase,agent的build_phase,然后是drivermonitorsequencer的build_phase,最后是scoreboard的build_phase执行。   

        build_phase的自上而下执行是正确的,但是drivermonitorsequencer在uvm树中属于同级,都是agent的孩子,这些孩子组件相互之间是怎么确定执行顺序的呢?并且scoreboard属于agent的那个级别,是driver等组件的叔叔辈,为什么会在最后执行build_phase呢?   

        书中给出解答:对于同一层次,且具有兄弟关系的component,执行顺序是按照字典顺序的,字典排序的依据是实例化时指定的名字。        

        做如下实验: drivermonitorsequencer 是在agent的build_phase中进行例化的, 执行过程确实是按照字典顺序进行的,d,m,s,将其替换为更为明显的a,b,c       

function void build_phase(uvm_phase phase);
    	super.build_phase(phase);
      `uvm_info("agent","build_phase enter...",UVM_LOW)
    	if(is_active==UVM_ACTIVE) begin
    	    sqr = sequencer::type_id::create("a",this);
    	    drv = driver::type_id::create("c",this);
    	end
    	    mon = monitor::type_id::create("b",this);
      `uvm_info("agent","build_phase exiting...",UVM_LOW)
    endfunction

        如果字典序是正确的,那么更改后的执行顺序应该是之前的倒序。sequencer最先执行,然后是monitor,最后是driver

        运行结果显示agent下的执行顺序与预期相符合。对于同一层次的component组件,其执行顺序是字典顺序。

对于scoreboard的结果,应该明白这两个概念:

广度优先:agent的build_phase执行完毕后,接下来执行的是其同层次的兄弟component的build_phase,当所有兄弟的component执行完毕后,在执行agent孩子driver等的build_phase。

深度优先:agent的build_phase执行完毕之后,接下来执行其孩子的build_phase,一直执行到以agent为树根的uvm子树的build_phase执行完毕,然后再去执行agent兄弟component的build_phase。

很明显,根据之前的结果,uvm采用的是深度优先。根据agentscoreboard的字典序,agent先执行。

connect_phase是如何执行的呢,因为并不是所有组件都需要connect_phase,在各组件中声明connect_phase时,并不进行操作,只加两个`uvm_info语句。

 connect_phase是自下而上执行的,对于同层次的还是以字典顺序为主。之后的function phase与connect_phase类似,这里不再赘述。

2. task phase

        task_phase是消耗时间的phase,用task来实现。task_phase也是自下而上的顺序执行的。并且并不是等到“下面的”phase执行完才执行“上面的”phase,而是将这些run_phase通过fork join_none的全部启动。更准确的说法是自下而上的启动,同时在运行。

        对于同一个component来说,12个run_time的phase是顺序执行的,并不是说前一个phase执行完毕就立即执行后一个phase。而是要等到整个验证平台的这个phase都执行完毕才会执行下一个phase。uvm中同类型的phase都是同时启动的,所以后一个phase的启动一定要等到前一个phase全部执行完毕。以main_phase和post_main_phase为例子。

         在test的main_phase中raise_objection,并且只在test中raise,当test中执行drop_objection结束main_phase时(时间:1440000ps),整个验证环境的main_phase也就结束了。根据编译结果,在main_phase全部结束后,post_main_phase运行,起始时间就是main_phase结束时间。

         main_phase是在0时刻开始运行的,因为run_time phase只写了main_phase。是以自下而上的,fork join_none的方式启动的。post_main_phase也是在1440000ps时间点以fork join_none,自下而上启动了drivermonitor的post_main_phase。

2.1 run_phase 

         run_phase也是以上的方式执行的,当run_phase和12个run_time phase都存在的时候,是run_phase和12个run_rime 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

 运行结果显示,run_phase和run_time phase是并行运行的。因为是并行运行的,run_time的phase并不需要等到run_phase运行完毕后才能启动。

总结:uvm_phase按照是否消耗时间分为function phase,task phase。

1. function phase中build_phase因为主要执行实例化操作,其执行顺序是uvm_component组件自上而下执行。

其他function phase是自下而上执行。

2. 对于在uvm树中同层次的component,其执行顺序为字典序,按照其实例化名称在字母表中的位置,在前先执行。

3. 对于某一个component,它同层次的component和它孩子component的执行采用深度优先。意思是执行孩子component,再执行与它同层次的。先向下,走完了再回来。

4. 对于task phase,run_phase和run time的phase并行执行。以fork join_none的方式自下而上执行。run_rime的phase每个phase全部执行完毕后才能执行下一个phase,这里涉及到raise/drop objection,放到下篇来说。   

补充:

run_phase和run_time phase(12个小phase)是并行运行的,这12个小phase中,只要有objection被raise,那么run_phase不需要raise_objection就可以自动执行。

需要明确的一点是,如果run_time phase的某一个phase并没有在任何一个component中raise objection,那么不消耗时间的部分比如$display或者`uvm_info等打印语句可以执行,这个phase中消耗时间的部分不会被uvm理会,而是直接跳入下一个phase。

比如,选几个component写pre_reset_phase,在其中加入打印语句,任选一个component中加入延时后的打印,不raise_objection,那么他的打印结果是这样的

    task pre_reset_phase(uvm_phase phase);
        `uvm_info("rtc_driver","pre_reset_phase Enter...",UVM_LOW)
        // phase.raise_objection(this);
        //phase.phase_done.set_drain_time(this,10);
        #10;
        `uvm_info("rtc_driver","pre_reset_phase end",UVM_LOW);
        //phase.drop_objection(this);
    endtask

不截图了吧,截图看不清楚。

UVM_INFO ../env/driver.sv(17) @ 0: uvm_test_top.env.agt.drv [driver] pre_reset_phase Enter...
UVM_INFO ../env/monitor.sv(19) @ 0: uvm_test_top.env.agt.mon [monitor] pre_reset_phase Enter...
UVM_INFO ../test/ral_test.sv(24) @ 0: uvm_test_top [ral_test] pre_reset_phase Enter...
UVM_INFO ../env/scoreboard.sv(87) @ 0: uvm_test_top.env.scb [scoreboard] main_phase Enter...
UVM_INFO ../test/ral_test.sv(16) @ 0: uvm_test_top [ral_test] main_phase Enter...

在添加延时后的打印语句并没有执行,而是直接跳到main_phase这个phase中去了,进入main_phase的时间也是0时刻。也就是延时被uvm直接忽略掉了。

然后将raise/drop objection的注释去掉,执行结果为

UVM_INFO ../env/driver.sv(17) @ 0: uvm_test_top.env.agt.drv [driver] pre_reset_phase Enter...
UVM_INFO ../env/monitor.sv(19) @ 0: uvm_test_top.env.agt.mon [monitor] pre_reset_phase Enter...
UVM_INFO ../test/ral_test.sv(24) @ 0: uvm_test_top [ral_test] pre_reset_phase Enter...
UVM_INFO ../env/driver.sv(21) @ 10000: uvm_test_top.env.agt.drv [driver] pre_reset_phase end
UVM_INFO ../env/scoreboard.sv(87) @ 10000: uvm_test_top.env.scb [scoreboard] main_phase Enter...
UVM_INFO ../test/ral_test.sv(16) @ 10000: uvm_test_top [ral_test] main_phase Enter...

在raise了objection后,消耗时间的部分就可以执行了,pre_reset_phase end语句也打印成功。

如果在monitor中添加以下语句

    task pre_reset_phase(uvm_phase phase);
        `uvm_info("monitor","pre_reset_phase Enter...",UVM_LOW)
        #11;
        $display("monitor raise objection test");
    endtask

因为仿真时间10000时候objection已经drop掉了,所以11000的这条语句不会被打印。

总结来说,如果想在phase中执行一些耗费时间的代码,那么一定要在此phase中任一compnent中至少提起一次objection。

run_phase是贯穿于12个phase的始终的,当12个phase中的objection依次被drop掉之后,并不会直接进入extract_phase,而是去查看run_phase中的objection是否drop掉(如果raise),如果没有,则等到run_phase中的objection drop掉后,进入extract_phase,如果没有raise则直接进入extract_phase。run_phase的寿命至少是12个小phase的寿命,当然run_phase也可以提起objection获得更长的寿命。不变的是只有run_phase和12个小phase都结束了之后,才能进入extract_phase。

参数phase

task main_phase(uvm_phase phase),输入参数带有phase,这是为了便于在任意component中的main_phase中都能raise_objection,因为raise_objection的必须通过phase.raise_objection来完成,所以必须将phase作为参数传入main_phase等任务中,没有这个phase参数,想要提起一个objection就比较麻烦了。

function_phase也是可以提起和撤销objection的,但其中不能有消耗时间的代码,而phase的引入是为了解决何时结束仿真的问题,放入function_phase中没有任何意义,phase的引入更多是面向main_phase等task_phase,而不是funciton_phase。

set_drain_time

uvm中为所有objection设置了drain_time这一属性,在当前phase的objection都drop掉了之后,并不马上进入下一层,而是检查有没有设置drain_time,如果没有设置,马上进入下一phase,否则延迟drain_time后再进入下一phase。在延时期间此phase仍然正常工作,接收数据。

延时与drain_time:

在上面的那个例子中,添加延时后,pre_reset_phase end的打印时间是10000,而如果将#10换为set_drain_time,结果是这样的

    task pre_reset_phase(uvm_phase phase);
        `uvm_info("driver","pre_reset_phase Enter...",UVM_LOW)
         phase.raise_objection(this);
        phase.phase_done.set_drain_time(this,10);
        //#10;
        `uvm_info("driver","pre_reset_phase end",UVM_LOW);
        phase.drop_objection(this);
    endtask
UVM_INFO ../env/driver.sv(17) @ 0: uvm_test_top.env.agt.drv [driver] pre_reset_phase Enter...
UVM_INFO ../env/driver.sv(21) @ 0: uvm_test_top.env.agt.drv [driver] pre_reset_phase end
UVM_INFO ../env/monitor.sv(19) @ 0: uvm_test_top.env.agt.mon [monitor] pre_reset_phase Enter...
UVM_INFO ../test/ral_test.sv(24) @ 0: uvm_test_top [ral_test] pre_reset_phase Enter...
UVM_INFO ../env/scoreboard.sv(87) @ 10000: uvm_test_top.env.scb [scoreboard] main_phase Enter...
UVM_INFO ../test/ral_test.sv(16) @ 10000: uvm_test_top [ral_test] main_phase Enter...

pre_reset_phase打印时间为0,main_phase进入的时间为10000,添加延时是drop_objection会延时后结束,而添加了drain_time后,是drop_objection后延后drain_time进入下一phase。

并且延时可能对于每个sequence都是必须的,如果每个sequence都这样去延时,显然不太合理,而drain_time是属于objection的一个特性,一个phase的所有objection都drop掉了之后phase才会结束,drain_time让此phase的objection都drop掉后再等drain_time,设置了之后,就相当于作用于所有的此phase了。一个phase就对应一个drain_time,所以对于同样的sequence,drain_time只需设置一次。并不是所有phase共享一个drain_time,main_phase中设置了drain_time和shutdown_phase没有任何关系,没有设置的情况下drain_time默认为0。

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值