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_phase(uvm_phase phase);
	super.build_phase(phase);
	if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
		`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
		`uvm_error("my_driver", "UVM_ERROR test")
endfunction

# UVM_ERROR my_driver.sv(16) @ 0: uvm_test_top.env.i_agt.drv [my_driver] UVM_ERROR test
# UVM_FATAL @ 0: reporter [BUILDERR] stopping due to build errors

这里给出的uvm_fatal是UVM内部自定义的。在end_of_elaboration_phase及其前的phase中,如果出现了一个或多个UVM_ERROR,那么UVM就认为出现了致命错误,会调用uvm_fatal结束仿真。

UVM的这个特性在小型设计中体现不出优势,但在大型设计中非常有用。大型设计中真正仿真前的编译、优
化可能会花费一个多小时的时间。完成编译、优化后开始仿真,几秒钟后出现一个uvm_fatal就停止仿真。当修复这个问题后再次运行,发现又有一个uvm_fatal出现。如此反复可能会耗费大量时间。但如果将这些uvm_fatal替换为uvm_error,将所有类似的问题一次性暴露出来,一次性修复,这会极大缩减时间,提高效率。

*phase的跳转

前面所有表述中各个phase都是顺序执行的,前一个phase执行完才执行后一个。并没有介绍过当后一个phase执行后还可以再执行一次前面的phase。而“跳转”这个词则完全打破了这种观念:phase之间可以互相跳来跳去

phase的跳转是比较高级的功能,举一个最简单的例子,实现main_phase到reset_phase的跳转:假如在验证平台中监测到reset_n信号为低电平, 则马上从main_phase跳转到reset_phase。 driver的代码如下:

task my_driver::reset_phase(uvm_phase phase);
	phase.raise_objection(this);
	`uvm_info("driver", "reset phase", UVM_LOW)
	vif.data <= 8'b0;
	vif.data <= 1'b0;
	while(!vif.rst_n)
		@(posedge vif.clk);
	phase.drop_objection(this);
endtask
task my_driver::main_phase(uvm_phase phase);
	`uvm_info("driver", "main phase", UVM_LOW)
	fork
		while(1) begin
			seq_item_port.get_next_item(req);
			drive_one_pkt(req);
			seq_item_port.item_done();
		end
		begin
			@(negedge vif.rst_n);
			phase.jump(uvm_reset_phase::get());
		end
	join
endtask

reset_phase主要做一些清理工作并等待复位完成。main_phase中一旦监测到reset_n为低电平,则马上跳转到reset_phase。在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_case中控制objection代码如下:

task my_case0::reset_phase(uvm_phase phase);
	`uvm_info("case0", "reset_phase", UVM_LOW)
endtask
task my_case0::main_phase(uvm_phase phase);
	phase.raise_objection(this);
	`uvm_info("case0", "main_phase", UVM_LOW)
	#10000;
	phase.drop_objection(this);
endtask

运行上述的例子,则显示:

# UVM_INFO my_case0.sv(15) @ 0: uvm_test_top [case0] reset_phase
# UVM_INFO my_driver.sv(25) @ 0: uvm_test_top.env.i_agt.drv [driver] reset phase
# UVM_INFO my_case0.sv(20) @ 1100: uvm_test_top [case0] main_phase
# UVM_INFO my_driver.sv(34) @ 1100: uvm_test_top.env.i_agt.drv [driver] main phase
# UVM_INFO /home/landy/uvm/uvm-1.1d/src/base/uvm_phase.svh(1314) @ 4000: repo-rter[PH_JUMP] phase main
# UVM_WARNING @ 4000: main_objection [OBJTN_CLEAR] Object 'uvm_top' cleared
ob jection counts for main_objection
# UVM_INFO my_case0.sv(15) @ 4000: uvm_test_top [case0] reset_phase
# UVM_INFO my_driver.sv(25) @ 4000: uvm_test_top.env.i_agt.drv [driver] reset phase
# UVM_INFO my_case0.sv(20) @ 7100: uvm_test_top [case0] main_phase
# UVM_INFO my_driver.sv(34) @ 7100: uvm_test_top.env.i_agt.drv [driver] main phase

整个验证平台都从main_phase跳转到了reset_phase。运行结果中出现了一个UVM_WARNING。这是因为在my_driver中调用jump时,并没有把my_case0中提起的objection进行撤销。加入跳转后整个验证平台phase的运行图实现变为如图所示的形式:
在这里插入图片描述
灰色区域的phase在整个运行图中出现了两次。跳转中最难的地方在于跳转前后的清理和准备工作。如上面的运行结果中的警告信息就是因为没有及时对objection进行清理。对于scoreboard来说这个问题可能尤其严重。跳转前scoreboard的expect_queue中的数据应该清空,同时要容忍跳转后DUT可能输出一些异常数据

在my_driver中使用了jump函数,它的原型是:function void uvm_phase::jump(uvm_phase phase);

jump函数的参数必须是一个uvm_phase类型的变量。在UVM中,这样的变量共有如下几个:

uvm_build_phase::get();
uvm_connect_phase::get();
uvm_end_of_elaboration_phase::get();
uvm_start_of_simulation_phase::get();
uvm_run_phase::get();
//分水岭
uvm_pre_reset_phase::get();
uvm_reset_phase::get();
uvm_post_reset_phase::get();
uvm_pre_configure_phase::get();
uvm_configure_phase::get();
uvm_post_configure_phase::get();
uvm_pre_main_phase::get();
uvm_main_phase::get();
uvm_post_main_phase::get();
uvm_pre_shutdown_phase::get();
uvm_shutdown_phase::get();
uvm_post_shutdown_phase::get();
uvm_extract_phase::get();
uvm_check_phase::get();
uvm_report_phase::get();
uvm_final_phase::get();

并非所有的phase都可以作为jump的参数。如果将上面代码jump的参数由uvm_reset_phase::get()替换为uvm_build_phase::get(),那么运行验证平台后会给出如下结果:UVM_FATAL /home/landy/uvm/uvm-1.1d/src/base/uvm_root.svh(922) @ 4000: reporte r [RUNPHSTIME] The run ph

所以往前跳转到从build到start_of_simulation的function phase是不可行的。如果把参数替换为uvm_run_phase:: get()也是不可行的:UVM会提示run_phase不是main_phase的先驱phase或后继phase。因为run_phase是与12个动态运行的phase并行运行的,不存在任何先驱或后继的关系。

uvm_pre_reset_phase::get()后的所有phase都可以作为jump的参数。从main_phase跳转到reset_phase是向前跳转,这种向前跳转只能是main_phase前的动态运行phase中的一个。也可以向后跳转:如从main_phase跳转到shutdown_phase。在向后跳转中除了动态运行的phase外,还可以是函数phase,如可以从main_phase跳转到final_phase。

phase机制的必要性

Verilog中有非阻塞赋值和阻塞赋值,相对应的在仿真器中要实现分为NBA区域和Active区域,这样在不同区域做不同事情,可以避免因竞争关系导致的变量值不确定的情况。同样,验证平台是很复杂的,要搭建一个验证平台是一件相当繁杂的事情,要正确地掌握并理顺这些步骤是一个相当艰难的过程。

举一个最简单的例子,一个env下面会实例化agent、scoreboard、reference model等,agent下面又会有sequencer、driver、monitor。并且这些组件之间还有连接关系,如agent中monitor的输出要送给scoreboard或reference model,这种通信的前提是要先将reference model和scoreboard连接在一起。那么可以:

scoreboard = new;
reference_model = new;
reference_model.connect(scoreboard);
agent = new;
agent.driver = new;
agent.monitor = new;
agent.monitor.connect(scoreboard);

最后一句话一定要放在最后写,因为连接的前提是所有组件已经实例化。reference_model.connect(scoreboard)的要求则没有那么高,只需要在上述代码中reference_model=new之后任何一个地方编写即可。可以看出代码的书写顺序会影响代码的实现。若要将代码顺序的影响降低到最低,可以按照如下方式编写:

scoreboard = new;
reference_model = new;
agent = new;
agent.driver = new;
agent.monitor = new;
reference_model.connect(scoreboard);
agent.monitor.connect(scoreboard);

只要将连接语句放在最后两行写就没有关系了。UVM采用了这种方法,将前面实例化的部分都放在build_phase来做,而连接关系放在connect_phase来做,这就是phase最初始的来源

不同时间做不同事情是UVM中phase的设计哲学。但仅仅划分成phase是不够的,phase的自动执行功能才极大方便了用户。上面代码当new语句执行完成后,后面的connect语句肯定就会自动执行。现引入phase的概念,将前面new的部分包裹进build_phase里面,把后面的connect语句包裹进connect_phase里面,很自然的,当build_phase执行结束就应该自动执行connect_phase。

phase的引入在很大程度上解决了因代码顺序杂乱可能会引发的问题。遵循UVM的代码顺序划分原则(如build_phase做实例化工作,connect_phase做连接工作等),可以在很大程度上减少验证平台开发者的工作量, 使其从一部分杂乱的工作中解脱出来。

phase的调试

UVM的phase机制如此复杂,如果碰到问题后每次都使用uvm_info在每个phase打印不同的信息显然是不能满足要求的。UVM提供命令行参数UVM_PHASE_TRACE对phase机制进行调试,其使用方式为:

<sim command> +UVM_PHASE_TRACE

这个命令的输出非常直观,下面列出了部分输出信息:
在这里插入图片描述

超时退出

验证平台运行时,有时测试用例会出现挂起(hang up)的情况。在这种状态下仿真时间一直向前走,driver或者monitor并没有发出或收到transaction,也没有UVM_ERROR出现。一个测试用例的运行时间是可以预计的,如果超出了这个时间,那么通常就是出错了。在UVM中通过uvm_root的set_timeout函数可以设置超时时间

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);
endfunction

set_timeout函数有两个参数:要设置的时间和此设置是否可以被其后的其他set_timeout语句覆盖。代码将超时的时间定为500ns,500ns时测试用例还没有运行完毕,则会给出一条uvm_fatal的提示信息并退出仿真。默认的超时退出时间是9200s,是通过宏UVM_DEFAULT_TIMEOUT指定:`define UVM_DEFAULT_TIMEOUT 9200s

除了可以在代码中设置超时退出时间外,还可以在命令行中设置:其中timeout是要设置的时间,overridable表示能否被覆盖,其值可以是YES或NO。如将超时退出时间设置为300ns且可被覆盖,代码如下:

<sim command> +UVM_TIMEOUT=<timeout>,<overridable>
<sim command> +UVM_TIMEOUT="300ns, YES"
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值