2.12 phase - timeout
前言
本文以uvm-1.2/examples/simple/phases/timeout为例,通过代码了解UVM中的phases机制。通过这个例子可以基本了解以下知识点:
- phase的“举手”和“放手”机制
- phase运行的时间控制方法
一、基本介绍
在UVM中,执行仿真通过phase控制,phase的分类和执行顺序在《2.11 phase - basic》节中有具体介绍。在每个phase内部,则是通过raise_objection()和drop_objection()这对函数来控制,即我们常说的“举手”和“放手”,这两个函数通常成对出现,这一点和《2.10 objections》节类似,但是这里是通过phase来进行控制。
二、代码分析
这个测试用例一共有三个文件,包括:test.sv、tb_env.svh和tb_timer.svh。下面自顶向下分别介绍这三个文件的代码。
2.1 test.sv
program test;
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "tb_timer.svh"
`include "tb_env.svh"
tb_env env;
class test extends uvm_test;
`uvm_component_utils(test)
function new(string name, uvm_component parent = null);
super.new(name, parent);
endfunction
task pre_main_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask
task main_phase(uvm_phase phase);
phase.raise_objection(this);
// Will cause a time-out
// because we forgot to drop the objection
//phase.drop_objection(this);
endtask
task shutdown_phase(uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask
endclass
initial
begin
env = new("env");
run_test("test");
end
endprogram
第1行,通过一个program关键字,将测试代码打成一个test包;
第3到6行,导入UVM包,以及另外tb_env.svh和tb_timer.svh两个文件;
第8行,声明tb_env,tb_env的实现位于tb_env.svh文件中;
第11到36行,test测试用例的实现,分别在pre_main_phase、main_phase和shutdown_phase中,通过phase.raise_objection(this)加入仿真时间,但是在main_phase中估计丢掉了;phase.drop_objection(this)函数,让其仿真无法结束;
第39到43行,实例化env并启动test的仿真。
2.2 tb_env.svh
class tb_env extends uvm_env;
`uvm_component_utils(tb_env)
function new(string name, uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
uvm_config_db#(time)::set(null, "global_timer.*", "timeout", 1000);
uvm_config_db#(time)::set(null, "global_timer.main", "timeout", 3000);
uvm_config_db#(time)::set(null, "global_timer.run", "timeout", 0);
endfunction
task reset_phase(uvm_phase phase);
phase.raise_objection(this);
#20;
phase.drop_objection(this);
endtask
task configure_phase(uvm_phase phase);
phase.raise_objection(this);
#200;
phase.drop_objection(this);
endtask
task main_phase(uvm_phase phase);
phase.raise_objection(this);
#1000;
phase.drop_objection(this);
endtask
task shutdown_phase(uvm_phase phase);
phase.raise_objection(this);
#10;
phase.drop_objection(this);
endtask
endclass
这个文件是tb_env的具体实现。
其中,值得注意的是:
第8到12行,在build_phase中通过config_db机制,配置了global_timer中各个phase机制的timeout参数,global_timer的实现在tb_timer.svh中;
第15到37行,分别在reset_phase、configure_phase、main_phase、shutdown_phase中,通过phase.raise_objection(this)和phase.drop_objection(this)这对函数,加入了仿真时间。
2.3 tb_timer.svh
//
// Generic phase timer
//
// All time-out values are interprted in ns
//
// To set time-out values:
//
// - For one phase:
//
// uvm_config_db#(time)::set(null, "global_timer.main", "timeout", 100);
//
// - For multiple phases:
//
// uvm_config_db#(time)::set(null, "global_timer.pre*", "timeout", 100);
//
class tb_timer extends uvm_component;
`uvm_component_utils(tb_timer)
local static tb_timer m_global = new("global_timer", null);
function new(string name, uvm_component parent = null);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "run", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in run phase")
end
endtask
task pre_reset_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "pre_reset", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in pre_reset phase")
end
endtask
task reset_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "reset", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in reset phase")
end
endtask
task post_reset_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "post_reset", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in post_reset phase")
end
endtask
task pre_configure_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "pre_configure", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in pre_configure phase")
end
endtask
task configure_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "configure", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in configure phase")
end
endtask
task post_configure_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "post_configure", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in post_configure phase")
end
endtask
task pre_main_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "pre_main", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in pre_main phase")
end
endtask
task main_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "main", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in main phase")
end
endtask
task post_main_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "post_main", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in post_main phase")
end
endtask
task pre_shutdown_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "pre_shutdown", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in pre_shutdown phase")
end
endtask
task shutdown_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "shutdown", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in shutdown phase")
end
endtask
task post_shutdown_phase(uvm_phase phase);
time t;
if (uvm_config_db#(time)::get(this, "post_shutdown", "timeout", t) &&
t > 0) begin
#(t * 1ns);
`uvm_fatal("TIMEOUT", "Time-out expired in post_shutdown phase")
end
endtask
endclass
这个文件主要是tb_timer 的具体实现。
其中,值得注意的是:
第19行,将tb_timer注册到UVM的工厂机制中,这是为什么在env中没有例化m_global,却能够传递参数过来的原因;
第27到142行,代码实现上基本上都类似,意思是在每个phase中通过config_db机制获取到一个timeout的变量,并检测改phase执行的时间,是否超过了timeout的值,如果phase提前结束,则不会打印uvm_fatal相关的信息,否则打印在哪个phase超时的fatal信息。
2.4 仿真结果
UVM_INFO @ 0: reporter [RNTST] Running test test...
UVM_FATAL tb_timer.svh(126) @ 3320: global_timer [TIMEOUT] Time-out expired in main phase
UVM_INFO ../../../../src/base/uvm_report_server.svh(847) @ 3320: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---
** Report counts by severity
UVM_INFO : 2
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 1
** Report counts by id
[RNTST] 1
[TIMEOUT] 1
[UVM/RELNOTES] 1
$finish called from file "../../../../src/base/uvm_root.svh", line 135.
$finish at simulation time 3320
V C S S i m u l a t i o n R e p o r t
Time: 3320 ns
通过仿真结果,可以看到打印了一个UVM_FATAL的信息,提示main phase执行超时,这是因为在test.sv文件的main phase中,我们故意丢了一个phase.drop_objection(this)函数。
另外,值得注意的是,仿真结束的时间是3320ns,这个时间是tb_env中reset_phase的20ns,加上tb_env中configure_phase的200ns,加上test中pre_main_phase的100ns,加上tb_env中build_phase给global_timer.main配置的3000ns,一共是20+200+100+3000=3320ns。
总结
通过这个测试用例,可以比较直观的看到,在phase内部,我们通过phase.raise_objection(this)和phase.drop_objection(this)这对函数来控制仿真执行的时间;另外,通过tb_timer 这个单独的类,来控制各个phase执行的时间,一旦超时则报UVM FATAL的错误信息。