UVM——phase objection

0. 介绍

​ UVM中,component的task phase是消耗仿真时间的,各个components需要在不同层次的task phase中同步信息。UVM中通过objection mechanism来控制phase的执行,通过raise or drop objection来决定phase中事件的开始和停止。

1. objection控制phase的执行

1.1 phase中收集到objection

​ 当程序根据real-time phase的执行顺序,进入到某一个real-time phase中时,它会收集此phase提出的所有的objection(每个component都可以raise objection),当这个phase中所有的objection都被撤销(drop)之后,那么这个phase就执行结束。

​ 比如说进入到main_phase后,driver中 raise objection,monitor的main phase没有raise objection,那么driver和monitor的main phase都会执行。monitor的的main phase的执行受driver的影响,当driver中drop objection之后,monitor的main phase也停止执行。

1
2
3
4
5
6
7
8
9
10
//// driver中提起objection
task driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
.....//some activity
phase.drop_objection(this);
endtask
//// monitor中不提起objection
task monitor::main_phase(uvm_phase phase);
....
endtask

1.2 phase没有收集到objection

​ 如果进入某个real-time phase后没有收集到objection,也就是说所有的component的这个real-time phase没有没raise objection,那么不会执行phase。

1.3 run_phase的执行

run_phase和real-time phase是并行执行关系,如果在12个real-time phase有任何一个phase提起了objection,那么run_phase都会执行;当然也可以在run_phase中提起objection。

外国人的代码中一般是在run_phase中进行操作,而且run_phase和main_phase的作用差不多,推荐run_phase。

2. 在sequence中提起objection

​ 在验证平台中通常是在sequence 中来控制phase的执行,并且一般是在root sequence(没有parent sequence的sequence),比如嵌套sequence、virtual sequence。

2.1 设置phase的default sequence

在uvm_sequence中有一个starting_phase成员,它是uvm_phase类型。如果我们通过设置run phase的default sequence来启动一个sequence,那么会默认将run phase的phase传递给这个sequence的starting_phase.

1
2
3
4
5
function void my_case::build_phase(uvm_phase phase);
...
uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.run_phase","default_sequence",sequence::type_id::get());
...
endfunction

sequencer在启动sequence时候会默认做如下操作:

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

这样我们就可以在sequence中控制objection

1
2
3
4
5
6
7
8
9
10
11
12
13
class sequence extends uvm_sequence#(transaction);
task pre_body(); //重载pre_body
if(starting_phase!=null)
starting_phase.raise_objection(this);
endtask
task body();
...
endtask
task post_body();
if(starting_phase!=null)
starting_phase.drop_objection(this);
endtask
endclass

2.2 直接将task phase的phase传递给sequence

如果我们是用start函数来启动sequence,可以通过直接将phase传递给sequence的starting_phase。

1
2
3
4
5
task my_case::run_phase(uvm_phase phase):
...
seq.starting_phase = phase;
seq.start(env.i_agt.sqr);
endtask

在实际应用中不写seq.starting_phase=phase这一句也可以,但是在UVM UG中,写了。

3. 在哪控制objection

可以有两种方法。

3.1 在sequence中控制

之前讲的就是在sequence中控制,将phase传递给sequence的starting_phase

3.2 在scoreboard中控制

我们控制run phase任务运行的目的是想要正确的发送和接受激励序列。

​ 发送的激励序列一方面传递给了参考模型,一方面传递给了DUT,参考模型产生预测数据,DUT产生真实的输出响应,所以,如果我们将DUT的所有合理的输出都在scoreboard完成了,那么我们的run phase其实就可以结束了。基于这个思想我们可以在scoreboard中控制objection。

​ 如果我们有pkg_num个激励,那么我们可以在scoreboard中收集pkg_num个真实DUT输出就可以raise_objection。这个pkg_num可以通过config_db的get得到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
task scoreboard::run_phase(uvm_phase phase);
...
phase.raise_objection(this); //提起objection
fork
while(1) begin
exp_port.get(exp_tr); //从参考模型中拿到预测值
exp_queue.push_back(tr);//将预测值都保存在队列中,为了后面与真实值比较
end
forint i=0;i<pkg_num;i++) begin
act_port.get(act_tr); //得到真实值
tmp_tr=exp_queue.pop_front(); //逐次与预测值比较
result=exp_tr.compare(tmp_tr);//比较
end
join_any// 注意
phase.drop_objection(this);
endtask

上面用的是fork join_any,fork里面有两个进程,第一个是个while循环会一直执行,所以当第二个进程for循环执行完之后,就会跳出fork,执行phase.drop_objection(this);结束任务。

4. set_drain_time

phase.phase_done.set_drain_time(this,200);

​ 任何功能模块DUT都有处理延迟,我们发送transaction给DUT的输入到DUT产生输出,这中间会有延迟,如果我们在sequence中发送完最后一个transaction之后,立马drop_objection,那么可能最后发送的几个transaction还没有在DUT中产生输出,也就不会被scoreboard收集到。

​ 为了让task phase在drop_objection后不立马结束,延迟一段时间,等待所有的transaction都在DUT产生输出,我们可以设置set_drain_time。

1
2
3
4
5
6
7
8
9
10
11
task main_phase(uvm_phase phase);
phase.raise_objection(this);
#1000;//这是1000ns,打印时候是ps为单位
phase.drop_objection(this);
endtask

task post_main_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("case0","this post_main_phase",UVM_LOW);
phase.drop_objection(this);
endtask
1
UVM_INFO case/cases.sv(31) @ 1000000: uvm_test_top [case0] this post_main_phase

延迟1000ns之后调用drop_objection,立马进入post_main_phase.

如果我们使用了set_drain_time:

1
2
3
4
5
6
7
8
9
10
11
12
task main_phase(uvm_phase phase);
phase.raise_objection(this);
#1000;
phase.phase_done.set_drain_time(this,200);
phase.drop_objection(this);
endtask

task post_main_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("case0","this post_main_phase",UVM_LOW);
phase.drop_objection(this);
endtask

输出:

1
UVM_INFO case/cases.sv(31) @ 1200000: uvm_test_top [case0] this post_main_phase

延迟了200ns才进入post_main_phase。

这里的phase_done是uvm_phase的一个成员,它是uvm_objection类型,我们在调用phase.raise_objection(this)的时候,其实调用的是phase_done.raise_objection(this)。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!