创建测试用例
1、加入base_test UVM使用的是一种树形结构,书中最初这棵树的树根是my_driver,后来由于要放置其他component,树根变成 了my_env。但在实际应用的UVM验证平台中my_env并不是树根,通常树根是一个基于uvm_test派生的类。先讲述base_test,真正的测试用例都是基于base_test派生的一个类:
class base_test extends uvm_test;
my_env env;
function new(string name = "base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
extern virtual function void buil_phase(uvm_phase phase);
extern virtual function void repor_phase(uvm_phase phase);
`uvm_component_utils(base_test)
endclass
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
uvm_config_db#(uvm_object_wrapper)::set(this, "env.i_agt.sqr.main_phase",
"default_sequence", my_sequence::type_id::get());
endfunction
function void base_test::report_phase(uvm_phase phase);
uvm_report_server server;
int err_num;
super.report_phase(phase);
server = get_report_server();
err_num = server.get_serverity_count(UVM_ERROR);
if(err_num != 0) begin
$display("TEST CASE FAILED");
end
else begin
$display("TEST CASE PASSED");
end
endfunction
- 通常在base_test中做如下事情:第一,设置整个验证平台的超时退出时间;第二,通过config_db设置验证平台中某些参数的值。 这些根据不同的验证平台及不同的公司而不同,没有统一的答案。
- 在把my_env放入base_test中之后,UVM树的层次结构如图所示:
- top_tb中run_test的参数从my_env变成了base_test,并且config_db中设置virtual interface的路径参数要做如下改变:
initial begin
run_test("base_test");
end
intial begin
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt.drv", "vif", input_if);
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt.mon", "vif", input_if);
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.o_agt.mon", "vif", output_if);
end
2、UVM中测试用例的启动
- 测试DUT是否按照预期工作,需要对其施加不同的激励,这些激励称为测试向量或pattern。一种激励作为一个测试用例,不同激励就是不同的测试用例。测试用例的数量是衡量验证人员工作成果的最直接目标。
- 伴随着验证的进行,测试用例的数量一直在增加,在增加的过程中重要的是保证后加的测试用例不影响已建的测试用例。前面通过设置default_sequence的形式启动my_sequence。假如存在my_sequence2,如何在不影响my_sequence的前提下将其启动:最理想的办法是在命令行中指定参数来启动不同的测试用例。
- 无论是在my_env中设置default_sequence,还是在base_test中或top_tb中设置,都必须修改相关的设置代码才能启动my_sequence2。为了解决这个问题,先看两个不同的测试用例。my_case0的定义如下:
class case0_sequence extends uvm_sequence #(my_transaction);
my_transaction m_trans;
...
virtual task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat(10) begin
`uvm_do(m_trans)
end
#100;
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
...
endclass
class my_case0 extends base_test;
function new(string name = "my_case0", uvm_componet parent = null);
super.new(name, parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
`uvm_component_utils(my_case0)
endclass
function void my_case0::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this, "env.i_agt.sqr.main_phase",
"default_sequence", case0_sequence::type_id::get());
endfunction
my_case1的定义如下:
class case1_sequence extends uvm_sequence #(my_transaction);
my_transaction m_trans;
...
virtual task body();
if(starting_phase != null)
starting_phase.raise_objection(this);
repeat(10) begin
`uvm_do_with(m_trans, {m_trans.pload.size() == 60;})
end
#100;
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask
...
endclass
class my_case1 extends base_test;
function new(string name = "my_case1", uvm_component parent = null);
super.new(name, parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
`uvm_component_utils(my_cas1)
endclass
function void my_case1::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this, "env.i_agt_sqr.main_phase",
"default_sequence", case1_sequence::type_id::get());
endfunction
- 要启动my_case0,需要在top_tb中更改run_test的参数:(启动my_case1也同理)
initial begin
run_test("my_case0");
end
当my_case0运行的时候需要修改代码,重新编译后才能运行;当my_case1运行时也需如此,这相当不方便。UVM也提供对不加参数的run_test的支持:
initial begin
run_test();
end
在这种情况下,UVM会利用UVM_TEST_NAME从命令行中寻找测试用例的名字,创建它的实例并运行。下面代码可以启动my_case0:
<sim command>
… +UVM_TEST_NAME=my_case1
整个启动及执行的流程如图所示: 启动后,整棵UVM树的结构如图所示:与前面UVM树结构的唯一区别在于树根的类型从base_teset变成了my_casen。(书中应该打印错误,这里应该是my_case0)
|