UVM建造测试用例

(1)加入base_test

在一个实际应用的UVM验证平台中,my_env并不是树根,通常来说,树根是一个基于uvm_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 build_phase(uvm_phase phase);
   extern virtual function void report_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_severity_count(UVM_ERROR);

   if (err_num != 0) begin
      $display("TEST CASE FAILED");
   end
   else begin
      $display("TEST CASE PASSED");
   end
endfunction

base_test派生自uvm_test,使用uvm_component_utils宏来注册到factory中。在build_phase中实例化my_env,并设置sequencer的 default_sequence。需要注意的是,这里设置了default_sequence,其他地方就不需要再设置了。

除了实例化env外,base_test中做的事情在不同的公司各不相同。上面的代码中出现了report_phase,在report_phase中根据 UVM_ERROR的数量来打印不同的信息。一些日志分析工具可以根据打印的信息来判断DUT是否通过了某个测试用例的检查。report_phase也是UVM内建的一个phase,它在main_phase结束之后执行。

除了上述操作外,还通常在base_test中做如下事情:第一,设置整个验证平台的超时退出时间;第二,通过config_db设置验 证平台中某些参数的值。这些根据不同的验证平台及不同的公司而不同,没有统一的答案。

在把my_env放入base_test中之后,UVM树的层次结构变为如图2-11所示的形式。

top_tb中run_test的参数从my_env变成了base_test,并且config_db中设置virtual interface的路径参数要做如下改变:

initial begin
   run_test("base_test");
end

initial 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的定义如下:

`ifndef MY_CASE0__SV
`define MY_CASE0__SV
class case0_sequence extends uvm_sequence #(my_transaction);
   my_transaction m_trans;

   function  new(string name= "case0_sequence");
      super.new(name);
   endfunction 
   
   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

   `uvm_object_utils(case0_sequence)
endclass


class my_case0 extends base_test;

   function new(string name = "my_case0", uvm_component 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

`endif

my_case1的定义如下:

`ifndef MY_CASE1__SV
`define MY_CASE1__SV
class case1_sequence extends uvm_sequence #(my_transaction);
   my_transaction m_trans;

   function  new(string name= "case1_sequence");
      super.new(name);
   endfunction 

   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

   `uvm_object_utils(case1_sequence)
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_case1)
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

`endif

在case1_sequence中出现了uvm_do_with宏,它是uvm_do系列宏中的一个,用于在随机化时提供对某些字段的约束。

要启动my_case0,需要在top_tb中更改run_test的参数:

initial begin
run_test("my_case0");
end

而要启动my_case1,也需要更改:

initial begin
run_test("my_case1");
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_case0

而如下所示的代码可以启动my_case1:

<sim command>
... +UVM_TEST_NAME=my_case1

整个启动及执行的流程如下图所示

图1 测试用例的启动及执行流程

启动后,整棵UVM树的结构如下图所示。

图2 每个测试用例建立的UVM树

图2-13与图2-11的唯一区别在于树根的类型从base_test变成了my_casen。

相关推荐
apple_ttt6 天前
SystemVerilog学习——构造函数new
fpga开发·fpga·systemverilog·uvm
apple_ttt8 天前
SystemVerilog学习——虚拟接口(Virtual Interface)
fpga开发·fpga·systemverilog·uvm
小妖11601 个月前
uvm_info、uvm_warning,uvm_error、uvm_fatal
uvm·ic验证
逍遥xiaoy5 个月前
SystemVerilog测试框架示例
systemverilog·uvm
谷公子的藏经阁6 个月前
设计模式在芯片验证中的应用——迭代器
设计模式·systemverilog·uvm·芯片验证·design pattern
小邦是名小ICer1 年前
7.2 uvm_resource_db in UVM
uvm
不动明王呀1 年前
uvm白皮书练习_ch2_ch231_加入transaction
uvm
中古传奇1 年前
Pass cfg from cmd to test
uvm
mrbone111 年前
UVM-什么是UVM方法学
systemverilog·uvm·验证·方法学