1. objection与phase
前文中我们了解了uvm的phase机制:数字验证:一文弄懂UVM的phase机制-CSDN博客,而objection与phase之间关系紧密。objection字面的意思就是反对、异议。在验证平台中,可以通过raise_objection来提起“反对”,通过drop_objection来通知系统可以关闭验证平台。当然,一定是raise在drop之前:
task main_phase(uvm_phase phase);
phase.raise_objection(this);
…
phase.drop_objection(this);
endtask
在进入到某一phase时,UVM会收集此phase提出的所有objection,并且实时监测所有objection是否已经被撤销了,当发现所有都已经撤销后,那么就会关闭此phase,开始进入下一个phase。当所有的phase都执行完毕后,就会调用$finish来将整个的验证平台关掉。
如果UVM发现此phase没有提起任何objection,那么将会直接跳转到下一个phase中。假如验证平台中只有driver中提起了异议,而monitor等都没有提起,代码如下所示:
task driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask
task monitor::main_phase(uvm_phase phase);
while(1) begin
…
end
endtask
driver中的代码是肯定可以执行的,那么monitor中的代码能够执行吗?
可以。当进入到monitor后,系统会监测到已经有objection被提起了,所以会执行monitor中的代码。当过了100个时间单位之后,driver中的objection被撤销。此时,UVM监测发现所有的objection都被撤销了(因为只有driver raise_objection),于是UVM会直接杀死monitor中的无限循环进程,并跳到下一个phase,即post_main_phase()。
如果driver里也没有就没有raise_objection,并且所有其他component的main_phase里面也没有raise_objection,即driver变成如下情况:
task driver::main_phase(uvm_phase phase);
#100;
endtask
那么在进入main_phase时,UVM发现没有任何objection被提起,于是虽然driver中有一个延时100个单位时间的代码,monitor中有一个无限循环,UVM也都不理会,它会直接跳转到post_main_phase,假设进入main_phase的时刻为0,那么进入post_main_phase的时刻还是为0。
2. objection的使用
对于12个小phase来说:如果想执行一些耗费时间的代码,那么要在此phase下任意一个component中至少提起一次objection。
task my_case0::main_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask
task my_case0::run_phase(uvm_phase phase);
for(int i = 0; i < 9; i++) begin
#10;
`uvm_info("case0", "run_phase is executed", UVM_LOW)
end
endtask
可以看到在main_phase中提起了objection,但为什么run_phase的输出仍然有效呢?因为run_phase与12个小phase是并行运行的,当小phase中有objection被提起,run_phase也可以自动执行。但是反过来,main_phase则不会有输出出现。
这是因为对于run_phase来说,有两种运行方式:
(1)其他动态运行的phase中有objection提起。此时运行时间受小phase的objection控制。
(2)run_phase中提起objection。此时运行时间完全受run_phase自身控制。
raise_phase语句也必须在第一个消耗仿真时间的语句之前声明。
3. 参数phase的必要性
在UVM中所有phase的函数/任务参数中,都有一个phase:
task main_phase(uvm_phase phase);
这个输入参数中的phase是什么意思?为什么要加入这样一个东西?因为要便于在任何component的main_phase中都能raise_objection,而要raise_objection则必须通过phase.raise_objection来完成,所以必须将phase作为参数传递到main_phase等任务中。如果没有这个phase参数,那么想要提起一个objection就会比较麻烦了。
那么类似build_phase等function phase是否可以提起和撤销objection呢?
不会报错,但是一般不会这么用。phase的引入是为了解决何时结束仿真的问题,它更多面向main_phase等task phase,而不是面向function phase。
4. objection的控制
在整棵UVM树中,在什么地方控制objection最合理呢?driver中、monitor中、agent中、scoreboard中抑或是env中?
在drv,monitor,ref model等component都是通过while(1)无限循环的,因此一般不在其中控制objection。
一般来说,控制objection的策略有两个:
(1)在scoreboard中进行控制。如果要在scoreboard中控制objection,则需要去除scb中的无限循环,通过config_db::set的方式设置收集到的transaction的数量pkt_num,当收集到足够数量的transaction后跳出循环:
task my_scoreboard::main_phase(uvm_phase phase);
phase.raise_objection(this);
fork
while (1) begin
exp_port.get(get_expect);
expect_queue.push_back(get_expect);
end
for(int i = 0; i < pkt_num; i++) begin
act_port.get(get_actual);
…
end
join_any
phase.drop_objection(this);
endtask
(2)在sequence中提起sequencer的objection,sequence完成后再撤销objection。
UVM更提倡采用第二种方法,全部由sequence控制激励的生成。
5. set_drain_time的使用
无论任何功能的模块,都有其处理延时。如下图所示,0时刻DUT开始接收输入,直到p时刻才有数据输出。
在sequence中,n时刻发送完毕最后一个transaction,如果此时立刻drop_objection,那么最后在n+p时刻DUT输出的包将无法接收到。因此,在sequence中,最后一个包发送完毕后,要延时p时间才能drop_objection。
virtual task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat (10) begin
`uvm_do(m_trans)
end
#100;
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
延长的时间与激励有很大关系,因此只能根据激励选择一个最大的延时。这种延时对于所有sequence都是必须的,但对每个sequence都进行这样的处理不够合理,如果DUT处理的延时变大,那么要修改所有的延时大小,非常麻烦。
因此UVM设置了drain_time这一属性:当所有的objection被drop之后,不会马上进入下一层,而是等待一段时间。drain_time的设置方式为:
task base_test::main_phase(uvm_phase phase);
phase.phase_done.set_drain_time(this, 200);
endtask
当调用phase.raise_objection或者phase.drop_objection时,其实质是调用phase_done的raise_objection和drop_objection。当UVM在main_phase检测到所有的objection被撤销后,接下来会检查有没有设置drain_time。如果没有设置,则马上进入到post_main_phase,否则延迟drain_time后再进入post_main_phase。如果在post_main_phase及其后都没有提起objection,那么最终会前进到final_phase,结束仿真。
drain_time属于uvm_objection的一个特性,一个drain_time对应一个phase,并不是所有共享,在没有设置时会默认取0.