UVM基础-Phase机制(二)

3.1 objection机制概念说明

       objection机制往往控制着uvm phase的运行,objection的字面意思为反对,实际上,UVM验证方法学在phase机制上的建模,使用了一种“举手”的机制,也就是在由各个组件(uvm_component)组成的成员中,通过投票的方式,决定当前阶段是否通过,如果所有组件在某个阶段都参与了举手投票,即存在phase.raise_objection(this),那么就一定要等到“放手”,也就是phase.drop_objection(this)。才能表明这个组件同意完成该phase阶段,只有所有的组件都完成“放手”,这个phase阶段才是真正结束,才可以进行下一个phase的举手表决。

       如果不参与投票的组件phase,uvm自然也就不会考虑这个组件是否“放手”,即不会考虑该组件是否同意结束组件,因为其并没有参与投票。也就是说,如果其他组件有参与到投票机制中,个别组件没有参与投票,但是会在当前phase中做一些内容或者动作,那么uvm只会考虑参与投票的phase是否全部同意该phase结束,如果都执行了drop_objection,那么就会结束该phase的运行,并不会考虑为参与投票的phase结果,如果未参与投票组件的phase运行完成,那么就是正常完成,如果未参与投票的组件phase未完成,(一般的,会在这样的组件phase中写while 1循环),那么uvm也不会管他,直接结束该phase的运行。

       如果所有的组件在某个phase中都没有过raise和drop objection,会怎么样?uvm会认为当前phase无投票方,会直接结束该phase,进入下一个phase,也即是说,在同一个phase阶段,需要至少有一个组件参与到投票,也就是要raise objection和drop objection一次。

3.2 objection机制对于task phase的影响

       在动态运行时间的phase中,使用raise objection和drop objection,可以很好的控制仿真的运行,结合不同的task phase,说明这个过程,假设在driver中存在main phase,且参与举手投票。

1.	class driver extends uvm_driver#(uvm_sequence_item);
2.	      ......
3.	      extern virtual task drive_pkt();
4.	      extern virtual task reset_phase(uvm_phase phase);
5.	      extern virtual task main_phase(uvm_phase phase);
6.	      ......
7.	endclass
8.	
9.	......
10.	task driver::main_phase(uvm_phase phase);
11.	      super.main_phase(phase);
12.	      phase.raise_objection(this);
13.	      seq_item_port.get_next_item(seq);
14.	      drive_pkt();
15.	      seq_item_port.item_done();
16.	      phase.drop_objection(this);
17.	endtask

而在monitor中:

1.	class monitor extends uvm_monitor;
2.	      ......
3.	      extern virtual task sample_pkt();
4.	      extern virtual task reset_phase(uvm_phase phase);
5.	      extern virtual task main_phase(uvm_phase phase);
6.	      ......
7.	endclass
8.	
9.	......
10.	task monitror::main_phase(uvm_phase phase);
11.	      while (1) begin
12.	            sample_pkt();
13.	      end
14.	endtask

        此时monitor中的main_phase没有raise和drop objection,而是存在死循环,那么uvm会执行完driver的main_phase的举手机制后,也就是执行完drive_pkt后的drop_objection,就会进入后续phase(post_main_phase),同时直接kill掉monitor main_phase中的死循环。

如果monitor中没有死循环,其内容也会正常执行,只不过如果monitor内部的代码运行时间超过driver的raise和drop,那么剩余的phase代码则不会执行,而是直接kill,如果monitor中运行时间没有超过driver,那么则会正常执行完,因为monitor在driver执行放手之前就已经执行完该phase了。

       那么对于run_phase,情况会怎么样?实际上,run_phase和12个子phase之间是并行执行的,也就是说,run_phase如果没有raise和drop objection,那么仿真运行完全靠12个子phase中的raise和drop控制。那么如果run_phase中存在raise和drop,那么仿真启动和停止,完全由run_phase控制,比如driver中有如下代码:

1.	class driver extends uvm_driver#(uvm_sequence_item);
2.	      ......
3.	      extern virtual task drive_pkt();
4.	      extern virtual task run_phase(uvm_phase phase);
5.	      extern virtual task main_phase(uvm_phase phase);
6.	      ......
7.	endclass
8.	
9.	......
10.	task driver::main_phase(uvm_phase phase);
11.	      super.main_phase(phase);
12.	      phase.raise_objection(this);
13.	      drive_pkt();
14.	      phase.drop_objection(this);
15.	endtask
16.	
17.	task driver::run_phase(uvm_phase phase);
18.	      super.run_phase(phase);
19.	      while (1) begin
20.	            #10;
21.	      end
22.	endtask

        那么当main_phase运行结束后,run_phase也就结束了。

       如果将main_phase的raise和drop放到run_phase中,那么main_phase将不会执行,因为验证组件中所有的组件在main_phase中都没有参与投票,uvm直接跳过main_phase,在run_phase中存在raise和drop,那么就由run_phase控制仿真的结束。

       那么如果在run_phase和main_phase中都有raise和drop呢?此时就取决于raise和drop之间的延时内容了,也即是说,如果run_phase的时间超过了12个子phase的所有raise和drop的时长之和,那么运行完run_phase,仿真才结束;如果run_phase的raise和drop时长较短,短到来不及执行组件的main_phase,那么自然main_phase没来的及执行,仿真就会结束。也就是说,如果run_phase中存在raise和drop,那么验证环境的phase运行完全由run_phase控制。

总结一下:

  • 同一个phase中,参与投票的组件,uvm会等到所有的组件结束raise和drop,会直接跳转到下一个phase,而对于没有参与投票的组件,uvm会直接终止该组件phase的运行,raise和drop是通过phase的传参uvm_phase phase控制。
  • 如果所有组件在某个phase上都没有参与投票,那么uvm会直接结束该phase,进入下一个phase,即使组件在当前phase中存在延时类的代码,因此phase如果像运行,要至少进行一次raise和drop。
  • 如果一个验证环境中,某个组件的run_phase存在raise和drop,那么仿真环境完全由run_phase控制,其他子phase中如果由raise和drop,那么完全取决于和run_phase之间的运行时间,时间run_phase较长,那么会执行main_phase,如果run_phase较短以至于来不及执行main_phase,那么仿真也会直接停止。
  • 一般情况下,不会在run_phase中控制仿真的运行。
  • 可以想象,如果在raise和drop objection之间写了死循环,会直接导致仿真挂死,raise和drop objection写在死循环内部则不会导致挂死。或者如果在raise和drop objection之间写了阻塞语句,如等待某个事件或者信号的触发,或者阻塞性的从端口获取某个数据包,都会导致仿真挂死。或者,在sequence的启动同样需要提起objection,那么在如果存在driver和sequence通过get_response和put_response的方式做响应交互,如果sequence在get_response的时候,一直等不到driver put_reponse,或者sequence没有得到相应数量的rsp(通常rsp仲裁队列深度为8),都会从在objection挂住的可能性。

3.3 延时phase运行延时-set_drain_time

       想象一个情况,在仿真过程中,DUT往往具备一定的时延,也就是说,一个激励输入给DUT,DUT往往会经过一定时间的处理,那么如果在driver驱动完时序后,phase机制的运行直接将仿真结束,那么将无法采样到正确的结果,因为DUT此时还没有输出,从而得到环境bug,即如下图所示。

uvm考虑了这种情况,为每个phase设置了单独的set_drain_time方法。例如要在main_phase中增加延时:

1.	class driver extends uvm_driver#(uvm_sequence_item);
2.	      ......
3.	      extern virtual task drive_pkt();
4.	      extern virtual task run_phase(uvm_phase phase);
5.	      extern virtual task main_phase(uvm_phase phase);
6.	      ......
7.	endclass
8.	
9.	......
10.	task driver::main_phase(uvm_phase phase);
11.	      super.main_phase(phase);
12.	      phase.phase_done.set_drain_time(this, 200);
13.	      phase.raise_objection(this);
14.	      drive_pkt();
15.	      phase.drop_objection(this);
16.	endtask
17.	
18.	endtask
19.	

 

如上述代码第12行,使用phase.phase_done.set_drain_time(this,200),第二个参数为要设置的drain_time时间,也就是main_phase在结束drop后,会在延时200ns。

这里解释下原理,实际上,在raise和drop的过程中,phase会调用phase_done的raise和drop,phase_done是uvm_phase类的一个成员变量,通常情况下,set_drain_time为0,不会进行drain,如果设置了drain_time,执行drop objection的时候就会有延时。

总结一下:

  • uvm phase机制为每一个phase提供了set_drain_time方法,使用为:phase.phase_done.set_drain_time(this, 100)
  • phase_done是uvm_phase类中的一个成员变量,执行phase.raise_objection或者phase.drop_objection,实际上是执行phase_done的raise_objection和drop_objection。
  • drain_time默认值为0,通过set_drain_time方法改变该值,改变过后,会将当前phase延迟一个设定的drain_time时间。

3.4 objection的最佳控制位置和调试

phase机制的启动和控制实际上还是比较复杂的,一般经验的做法是,对于每个task phase,只在一个组件中进行raise和drop,一般在sequence中控制raise和drop会是一种较为简便的方法,如果在其他组件中对phase机制进行控制,会增加组件之间的phase的调试难度,一般不会在run_phase中控制phase的raise和drop。在sequence中控制,实际上是在sequencer的某个动态运行的phase中(一般是main_phase)进行控制,通用的做法是,在sequence的pre_body任务中,将获取到starting_phase的句柄,如果句柄非空,则启动raise_objection,在post_body任务中进行drop,例如如下代码块:

1.	class my_sequence extends uvm_sequence#(uvm_sequence_item);
2.	      my_trainsaction my_tr;
3.	      `uvm_object_utils(my_sequence)
4.	      extern function void new(string name="my_sequence");
5.	      extern virtual task pre_body();
6.	      extern virtual task body();
7.	      extern virtual task post_body();
8.	endclass
9.	
10.	function void my_sequence::new(string name="my_sequence");
11.	      super.new(name);
12.	endfunction:new
13.	
14.	task my_sequence::pre_body();
15.	      if (starting_phase != null) begin
16.	             starting_phase.raise_objection(this);
17.	      end
18.	endtask:pre_body
19.	
20.	task my_sequence::body();
21.	      repeat(5) begin
22.	            `uvm_do(my_tr);
23.	      end
24.	endtask:body
25.	
26.	task my_sequence::post_body();
27.	      if (starting_phase != null) begin
28.	             starting_phase.drop_objection(this);
29.	      end
30.	endtask:pre_body

在tc中,通过default的方式启动,如如下代码:

1.	class my_tc extends base_test;
2.	      my_sequence my_seq;
3.	      `uvm_component_utils(my_tc)
4.	      extern function void new(string name="my_tc", uvm_component parent);
5.	      extern virtual function void build_phase(uvm_phase phase);
6.	......
7.	endclass
8.	
9.	function my_tc::new(string name="my_tc", uvm_component parent);
10.	      super.new(name, parent);
11.	      my_seq = my_sequence::type_id::create("my_seq");
12.	endfunction:new
13.	
14.	function my_tc::build_phase(uvm_phase phase);
15.	      super.build_phase(phase);
16.	      uvm_configdb#(uvm_object_wrapper)::set(this, "env.i_agt.sqr.main_phase", "defualt", my_sequence::type_id::get());
17.	endfunction:build_phase

实际上,执行default sequence的过程,会自动执行sequencer中main phased的:

1.	task sequencer::main_phase(uvm_phase phase);
2.	......      
3.	      seq.starting_phase = phase;
4.	      seq.start(this);
5.	......
6.	endtask:main_phase
7.	      

或者在TC中的main phase直接指定:

1.	class my_tc extends base_test;
2.	      my_sequence my_seq;
3.	      `uvm_component_utils(my_tc)
4.	      function void new(string name="my_tc", uvm_component parent);
5.	             super.new(name, parent);
6.	             my_seq = my_sequence::type_id::create("my_seq");
7.	      endfunction
8.	 ......
9.	      virtual task main_phase(uvm_phase phase);
10.	            phase.phase_done.set_drain_time(this, 200);
11.	            super.main_phase(phase);
12.	            my_seq.starting_phase = phase;
13.	            my_seq.start(env.i_agt.sqr);
14.	      endtask
15.	......
16.	endclass

此外,objection机制还提供了调试手段,可以通过vcs命令行键入:

<vcs command> +UVM_OBJECTION_TRACE

       此时会在屏幕打印objection的调试输出。

       另外,phase.raise_objection(this, “xxxx”, 2)还存在参数,同理drop_objection也是一样:phase.drop_objection(this, “xxxx”, 2)。第二个参数为一个字符串,也可以为空,也就是当前raise/drop objection的名字,第三个参数为当前raise/drop objection的次数。

       uvm通过树形结构管理objection,当一个组件提起objection,uvm会检查当前component一直到最顶层uvm_top的所有objection的数量,统计活跃值。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值