关连
UVM实战卷I学习笔记8——UVM验证平台的运⾏(2)
⽬录
*build阶段出现UVM_ERROR停⽌仿真
之前的代码中,如果使⽤config_db::get⽆法得到virtual interface,就会直接调⽤uvm_fatal结束仿真。由于
virtual interface对于⼀个driver来说是必须的,所以这种uvm_fatal直接退出的使⽤⽅式是⾮常常见的。但如果这⾥使⽤uvm_error,也会退出:
virtual function void build_pha(uvm_pha pha);
super.build_pha(pha);
if(!uvm_config_db#(virtual my_if)::get(this,"","vif", vif))
`uvm_fatal("my_driver","virtual interface must be t for vif")
`uvm_error("my_driver","UVM_ERROR test")
endfunction
# UVM_ERROR my_driver.sv(16) @ 0: uvm_v.i_agt.drv [my_driver] UVM_ERROR test
# UVM_FATAL @ 0: reporter [BUILDERR] stopping due to build errors
这⾥给出的uvm_fatal是UVM内部⾃定义的。在end_of_elaboration_pha及其前的pha中,如果出现了⼀个或多个UVM_ERROR,那么UVM就认为出现了致命错误,会调⽤uvm_fatal结束仿真。
UVM的这个特性在⼩型设计中体现不出优势,但在⼤型设计中⾮常有⽤。⼤型设计中真正仿真前的编译、优
化可能会花费⼀个多⼩时的时间。完成编译、优化后开始仿真,⼏秒钟后出现⼀个uvm_fatal就停⽌仿真。当修复这个问题后再次运⾏,发现⼜有⼀个uvm_fatal出现。如此反复可能会耗费⼤量时间。但如果将这些uvm_fatal替换为uvm_error,将所有类似的问题⼀次性暴露出来,⼀次性修复,这会极⼤缩减时间,提⾼效率。
真丝面料
*pha的跳转
前⾯所有表述中各个pha都是顺序执⾏的,前⼀个pha执⾏完才执⾏后⼀个。并没有介绍过当后⼀
个pha执⾏后还可以再执⾏⼀次前⾯的pha。⽽“跳转”这个词则完全打破了这种观念:pha之间可以互相跳来跳去。
pha的跳转是⽐较⾼级的功能,举⼀个最简单的例⼦,实现main_pha到ret_pha的跳转:假如在验证平台中监测到ret_n信号为低电平, 则马上从main_pha跳转到ret_pha。 driver的代码如下:
task my_driver::ret_pha(uvm_pha pha);
pha.rai_objection(this);探究式学习
`uvm_info("driver","ret pha", UVM_LOW)
vif.data <=8'b0;
vif.data <=1'b0;
while(!vif.rst_n)
mxf格式@(podge vif.clk);
pha.drop_objection(this);
endtask
task my_driver::main_pha(uvm_pha pha);
`uvm_info("driver","main pha", UVM_LOW)
fork
while(1) begin
q__next_item(req);
drive_one_pkt(req);
q_item_port.item_done();
end
begin
@(negedge vif.rst_n);
pha.jump(uvm_ret_pha::get());
end
join
endtask
工作励志
ret_pha主要做⼀些清理⼯作并等待复位完成。main_pha中⼀旦监测到ret_n为低电平,则马上跳转到ret_pha。在top_tb 中,控制复位信号代码如下:
initial begin
rst_n =1'b0;
#1000;
rst_n =1'b1;
#3000;
rst_n =1'b0;
#3000;
rst_n =1'b1;
end
在my_ca中控制objection代码如下:
task my_ca0::ret_pha(uvm_pha pha);
`uvm_info("ca0","ret_pha", UVM_LOW)
endtask
task my_ca0::main_pha(uvm_pha pha);
pha.rai_objection(this);阑尾图片
`uvm_info("ca0","main_pha", UVM_LOW)
#10000;
pha.drop_objection(this);
endtask
运⾏上述的例⼦,则显⽰:
# UVM_INFO my_ca0.sv(15) @ 0: uvm_test_top [ca0] ret_pha
# UVM_INFO my_driver.sv(25) @ 0: uvm_v.i_agt.drv [driver] ret pha
# UVM_INFO my_ca0.sv(20) @ 1100: uvm_test_top [ca0] main_pha
# UVM_INFO my_driver.sv(34) @ 1100: uvm_v.i_agt.drv [driver] main pha
# UVM_INFO /home/landy/uvm/uvm-1.1d/src/ba/uvm_pha.svh(1314) @ 4000: repo-rter[PH_JUMP] pha main
中西方文化的差异# UVM_WARNING @ 4000: main_objection [OBJTN_CLEAR] Object 'uvm_top' cleared
ob jection counts for main_objection
# UVM_INFO my_ca0.sv(15) @ 4000: uvm_test_top [ca0] ret_pha
# UVM_INFO my_driver.sv(25) @ 4000: uvm_v.i_agt.drv [driver] ret pha
# UVM_INFO my_ca0.sv(20) @ 7100: uvm_test_top [ca0] main_pha
# UVM_INFO my_driver.sv(34) @ 7100: uvm_v.i_agt.drv [driver] main pha
整个验证平台都从main_pha跳转到了ret_pha。运⾏结果中出现了⼀个UVM_WARNING。这是因为在my_driver中调⽤jump时,并没有把my_ca0中提起的objection进⾏撤销。加⼊跳转后整个验证平台pha的运⾏图实现变为如图所⽰的形式:
灰⾊区域的pha在整个运⾏图中出现了两次。跳转中最难的地⽅在于跳转前后的清理和准备⼯作。如上⾯的运⾏结果中的警告信息就是因为没有及时对objection进⾏清理。对于scoreboard来说这个问题可能尤其严重。跳转前scoreboard的expect_queue中的数据应该清空,同时要容忍跳转后DUT可能输出⼀些异常数据。
在my_driver中使⽤了jump函数,它的原型是:function void uvm_pha::jump(uvm_pha pha);
jump函数的参数必须是⼀个uvm_pha类型的变量。在UVM中,这样的变量共有如下⼏个:
uvm_build_pha::get();
uvm_connect_pha::get();
uvm_end_of_elaboration_pha::get();
uvm_start_of_simulation_pha::get();
uvm_run_pha::get();
//分⽔岭
uvm_pre_ret_pha::get();
uvm_ret_pha::get();
uvm_post_ret_pha::get();
uvm_pre_configure_pha::get();
uvm_configure_pha::get();
uvm_post_configure_pha::get();
uvm_pre_main_pha::get();
uvm_main_pha::get();
uvm_post_main_pha::get();
uvm_pre_shutdown_pha::get();
uvm_shutdown_pha::get();
uvm_post_shutdown_pha::get();
uvm_extract_pha::get();
uvm_check_pha::get();
uvm_report_pha::get();
uvm_final_pha::get();
并⾮所有的pha都可以作为jump的参数。如果将上⾯代码jump的参数由uvm_ret_pha::get()替换为uvm_build_pha::get(),那么运⾏验证平台后会给出如下结果:UVM_FATAL /home/landy/uvm/uvm-1.1d/src/ba/uvm_root.svh(922) @ 4000: reporte r [RUNPHSTIME] The run ph
所以往前跳转到从build到start_of_simulation的function pha是不可⾏的。如果把参数替换为uvm_run_pha:: get()也是不可⾏的:UVM会提⽰run_pha不是main_pha的先驱pha或后继pha。因为run_pha是与12个动态运⾏的pha并⾏运⾏的,不存在任何先驱或后继的关系。
uvm_pre_ret_pha::get()后的所有pha都可以作为jump的参数。从main_pha跳转到ret_pha
是向前跳转,这种向前跳转只能是main_pha前的动态运⾏pha中的⼀个。也可以向后跳转:如从main_pha跳转到shutdown_pha。在向后跳转中除了动态运⾏的pha外,还可以是函数pha,如可以从main_pha跳转到final_pha。
pha机制的必要性
共享汽车怎么租车怎么收费Verilog中有⾮阻塞赋值和阻塞赋值,相对应的在仿真器中要实现分为NBA区域和Active区域,这样在不同区域做不同事情,可以避免因竞争关系导致的变量值不确定的情况。同样,验证平台是很复杂的,要搭建⼀个验证平台是⼀件相当繁杂的事情,要正确地掌握并理顺这些步骤是⼀个相当艰难的过程。
举⼀个最简单的例⼦,⼀个env下⾯会实例化agent、scoreboard、reference model等,agent下⾯⼜会有quencer、driver、monitor。并且这些组件之间还有连接关系,如agent中monitor的输出要送给scoreboard或reference model,这种通信的前提是要先将reference model和scoreboard连接在⼀起。那么可以:
scoreboard = new;
reference_model = new;
t(scoreboard);
agent = new;
agent.driver = new;
最后⼀句话⼀定要放在最后写,因为连接的前提是所有组件已经实例化。t(scoreboard)的要求则没有那么⾼,只需要在上述代码中reference_model=new之后任何⼀个地⽅编写即可。可以看出代码的书写顺序会影响代码的实现。若要将代码顺序的影响降低到最低,可以按照如下⽅式编写:
scoreboard = new;
reference_model = new;
agent = new;
agent.driver = new;
t(scoreboard);
只要将连接语句放在最后两⾏写就没有关系了。UVM采⽤了这种⽅法,将前⾯实例化的部分都放在build_pha来做,⽽连接关系放在connect_pha来做,这就是pha最初始的来源。
在不同时间做不同事情是UVM中pha的设计哲学。但仅仅划分成pha是不够的,pha的⾃动执⾏功能才极⼤⽅便了⽤户。上⾯代码当new语句执⾏完成后,后⾯的connect语句肯定就会⾃动执⾏。现引⼊pha的概念,将前⾯new的部分包裹进build_pha⾥⾯,把后⾯的connect语句包裹进connect_pha⾥⾯,很⾃然的,当build_pha执⾏结束就应该⾃动执⾏connect_pha。
pha的引⼊在很⼤程度上解决了因代码顺序杂乱可能会引发的问题。遵循UVM的代码顺序划分原则(如build_pha做实例化⼯
作,connect_pha做连接⼯作等),可以在很⼤程度上减少验证平台开发者的⼯作量, 使其从⼀部分杂乱的⼯作中解脱出来。pha的调试
UVM的pha机制如此复杂,如果碰到问题后每次都使⽤uvm_info在每个pha打印不同的信息显然是不能满⾜要求的。UVM提供命令⾏参数UVM_PHASE_TRACE对pha机制进⾏调试,其使⽤⽅式为:
<sim command>+UVM_PHASE_TRACE
这个命令的输出⾮常直观,下⾯列出了部分输出信息:
超时退出
验证平台运⾏时,有时测试⽤例会出现挂起(hang up)的情况。在这种状态下仿真时间⼀直向前⾛,driver或者monitor并没有发出或收到transaction,也没有UVM_ERROR出现。⼀个测试⽤例的运⾏时间是可以预计的,如果超出了这个时间,那么通常就是出错了。在UVM 中通过uvm_root的t_timeout函数可以设置超时时间:
function void ba_test::build_pha(uvm_pha pha);
super.build_pha(pha);
env = my_env::type_id::create("env", this);
uvm_top.t_timeout(500ns,0);
endfunction
t_timeout函数有两个参数:要设置的时间和此设置是否可以被其后的其他t_timeout语句覆盖。代码将超时的时间定为
500ns,500ns时测试⽤例还没有运⾏完毕,则会给出⼀条uvm_fatal的提⽰信息并退出仿真。默认的超时退出时间是9200s,是通过宏UVM_DEFAULT_TIMEOUT指定:`define UVM_DEFAULT_TIMEOUT 9200s