文章目录
一、phase机制
UVM中的phase机制是将仿真阶段层次化,各个phase按照先后顺序执行,并且同一层次的phase中的层次化组件之间按照顺序执行
1.1 为什么要使用phase机制?
从上图中可以看出UVM提供了很多phase,但是在一般情况下是不会把所有的phase都使用上的,使用频率最高的是build_phase、connect_phase和main_phase。这里能够很方便验证工程师将不同的代码写在不同的phase上,还有利与其他验证学向UVM迁移。
- build phase——实现验证平台的创建、连接、配置;
- run phase——产生激励并运行仿真,需要消耗仿真事件;
- clean up phase——测试用例结果收集与报告;
1.2 phase是如何运行的?
在1.1节中笼统说了phase是自上而下执行的,但是这里需要注意的是针对不同阶段的phase,这个自上而下的概念会不一样。在build phase中的自上而下执行是指它在实例化时进行的方式,例如针对driver和monitor,它都是agent的成员变量,所以它必须现实现agent的实例化才能实现它们的实例化。如果在agent实例化之前,driver想要实例化,那么会报错。
除了build phase之外,所有不耗费仿真时间的phase都是自下而上的执行,例如connect_phase需要先执行driver和monitor的connect_phase才能够执行agent的connect_phase。
无论自上而下还是自下而上,都只适应UVM树中有直系关系的component,对于同一层次、有兄弟关系的component,例如driver和monitor它在执行顺序是按照字典执行的。
针对非父子关系的component,UVM采用的是深度优先的执行顺序,如果active_agent如果比passive_agent先执行,那么driver、monitor、sequencer全部执行完后,passive_agent才会执行
针对UVM树来说,build_phase阶段自顶向下执行,创建空间,搭建UVM数;
其余阶段自下而上执行,耗时阶段自下而上执行。
class case_0 extends base_test;
`uvm_component_utils(case_0);
function new(string name = "case_0",uvm_component parent = null);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this,"v_sqr.main_phase","default_sequence",vseq_0::type_id::get());
`uvm_info(get_full_name(),"build_phase",UVM_MEDIUM);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
`uvm_info(get_full_name(),"connect_phase",UVM_MEDIUM);
endfunction
virtual task reset_phase(uvm_phase phase);
super.rest_phase(phase);
phase.raise_objection(this);
#1;
`uvm_info(get_full_name(),"rest_phase",UVM_MEDIUM)
phase.drop_objection(this);
endtask
virtual task main_phase(uvm_phase phase);
//phase.raise_objection(this);
#1;
`uvm_info(get_full_name(),"main_phase",UVM_MEDIUM)
//phase.drop_objection(this);
endtask
virtual task shutdown_phase(uvm_phase phase);
//phase.raise_objection(this);
#1;
`uvm_info(get_full_name(),"shutdown_phase",UVM_MEDIUM)
//phase.drop_objection(this);
endtask
virtual task run_phase(uvm_phase phase);
#1;
`uvm_info(get_full_name(),"run_phase",UVM_MEDIUM)
endtask
virtual function void report_phase(uvm_phase phase);
super.report_phase(phase);
`uvm_info(get_full_name(),"report_phase",UVM_MEDIUM)
endfunction
- 这里先执行run_phase再执行reset_phase,不会因为声明函数的先后而影响他们的执行顺序,而是按照run_phase与run-time并行执行的顺序进行的。这里的run_phase虽然消耗了一个时间单位
#1
,但是由于它没有raise_objection,所以与run-time的raise同时启动,别的组件中的run_phase(例如driver)都已经raise了,那么这里case_0的run_phase就可以先进行打印,这里仿真器任务仿真器。但是如果是别的组件的raise都删掉了,只有run_phase的raise那么就run-time就不会成立这个执行顺序;
后面的monitor的打印的问题
class apb_monitor extends uvm_monitor;
`uvm_component_utils(apb_monitor)
virtual apb_interface vif;
uvm_analysis_port#(apb_trans) apb_mon_port;
function new(string name = "apb_monitor",uvm_component parent)l
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual apb_interface)::get(this,"","vif",vif))begin
`uvm_fatal("NOVIF/VIP/MON","NO ")
end
apb_mon_port = new("apb_mon_port",this);
endfunction
virtual task main_phase(uvm_phase phase);
super.main_phase(phase);
`uvm_info(get_full_name(),$sformatf("psel = %x",vif.psel),UVM_MEDIUM
`uvm_info(get_full_name(),$sformatf("penable = %x",vif.penable),UVM_MEDIUM)
`uvm_info(get_full_name(),$sformatf("pwrite = %x",vif.pwrite),UVM_MEDIUM)
`uvm_info(get_full_name(),$sformatf("paddr = %x",vif.paddr),UVM_MEDIUM)
`uvm_info(get_full_name(),$sformatf("pwdata = %x",vif.pwdata),UVM_MEDIUM)
`uvm_info(get_full_name(),$sformatf("prdata = %x",vif.prdata),UVM_MEDIUM)
#1;
`uvm_info(get_full_name(),$sformatf("pwdata2 = %x",vif.pwdata),UVM_MEDIUM)
endtask
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
endtask
endclass
- 这里的main_phase没有raise和drop的,别的任何的main_phase都没有raise和drop。但是仍然会执行monitor里面的run_phase打印,并且#1后不会打印。也就是说我们在main_phase中是会检测是否有raise/drop_objection就不会执行任何消耗时间单位的语句,但是不消耗时间的就会执行
virtual task main_phase(uvm_phase phase);
phase.raise_objection(this);
#1;
`uvm_info(get_full_name(),"main_phase",UVM_MEDIUM)
phase.drop_objection(this);
endtask
如果在main_phase中加了raise/drop之后,那么延时#1的就可以打印出来,因为这里的main_phase有进行raise/drop。
但是如果将消耗时间由#1
变为#2
,那么就不会打印出来,这时因为在#1时钟单位后就drop了。
1.3 phase的调试
<sim command>+UVM_PHASE_TRACE//1、可以对phase机制进行调试
//2、设置防止测试用例挂起的超时时间
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env",this);
uvm_top.set_timeout(500ns,0);//第一个参数设置时间,第二个参数设定是否可以被其他的set_timeout语句覆盖
endfunction
//3、命令行设置超时
<sim command>+UVM_TIMEOUT=<timeout>,<overridable>
1.4 如何使用super.xxx_phase的执行
我们首先需要知道super.xxx_phase的作用是执行父类的内容。
1.4.1 在component中使用super.xxx_phase的执行
class apb_driver extends uvm_driver#(apb_trans);
...
virtual protected task run_phase(uvm_phase phase);
//super.run_phase(phase);这里的可以删掉
forever begin
seq.item_port.get_next_item(req);
send(req);
seq.item_port.item_done();
end
endtask
上面的代码中如果将super.run_phase(phase)
删掉不会影响的,这时因为apb_driver是派生自uvm_driver,uvm_driver的run_phase是没有东西的,有时候还有进行消耗时间。如果写另一个driver去继承这个apb_driver,那么如果在它的run_phase中也执行父类的forever
循环,导致一直无法跳出。通常task中不添加super.xxx_phase
。
1.4.2 在object中使用super.xxx_phase的执行
class base_test extends uvm_test;
`uvm_component_utils(base_test)
apb_environment apb_env;
virtual_sequencer v_sqr;
...
virtual function void build_phase(uvm_phase phase);;
super.build_phase(phase);
apb_env = apb_environment::type_id::create("apb_env",this);
v_sqr = virtual_sequencer::type_id::create("v_sqr",this);
sb = scoreboard::type_id::create("sb",this);
endfunction
virtual function void connect_phase(uvm_phase phase);
v_sqr.apb_sqr = apb_env.act_agt.sqr;
...
这里的base_test中的除了build_phase
进行了例化,在connect_phase
中也进行了操作,例如v_sqr.apb_sqr = apb_env.act_agt.sqr
那么我们在base_test派生的类中就需要进行super.
class case_0 extends base_test;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
...
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
...