搭建测试用例的可以用到的三种机制:sequence、factory、callback

本文探讨了UVM测试用例中的sequence、callback、factory三种机制,强调它们在搭建测试用例时的不同作用。sequence机制为生成激励的主要方式,提供强大的重用性和协调功能;callback机制能实现所有测试用例但可能使driver职责不清;factory机制则允许在driver中便捷实现某些特定测试用例。结合使用这三种机制,可以创建高效且灵活的验证平台。
摘要由CSDN通过智能技术生成

前言:

本文基于《UVM实战》学习所得,留痕

1.浅谈测试用例中的sequence、callback、factory机制

从某种程度上来说,callback、sequence、factory机制,都可以搭建测试用例。但是有什么区别呢?

1.1 sequence机制

UVM一直提倡的生成激励的方式,UVM为此做了大量的工作,如构建了许多宏、嵌套的sequence、virtual sequence、可重用性等。

1.2 callback机制

能够实现所有的测试用例,但是某些测试用例用 sequence来实现则更加方便。virtual sequence的协调功能在callback机制中就很难实现。

1.3 factory机制

重载driver使得一些在sequence中比较难实现的测试用例轻易地在driver中实现。如果放弃sequence,只使用factory机制实现测试,似乎一切都回到了起点(在driver中控制发送包的种类、数量,对于objection的控制又 要从sequence中回到driver中)

不推荐单独使用callback机制、factory机制

  • ·引入sequence的原因是将数据流产生的功能从driver中独立出来。取消sequence相当于一种倒退,会使得driver的职能不明 确,与现代编程中模块化、功能化的趋势不合。
  • ·虽然用driver实现某些测试用例比sequence更加方便,但是对于另外一些测试用例,在sequence里做起来会比driver中更加方 便。
  • ·sequence的强大之处在于,它可以在一个sequence中启动另外的sequence,从而可以最大程度地实现不同测试用例之间 sequence的重用。但是对于driver来说,要实现这样的功能,只能将一些基本的产生激励的函数写在基类driver中。用户会发现到最 后这个driver的代码量非常恐怖。
  • ·使用virtual sequence可以协调、同步不同激励的产生。当放弃sequence时,在不同的driver之间完成这样的同步则比较难。

总结

callback机制、sequence机制和factory机制并不是互斥的,三者都能分别实现同一目的。三者互相结合时,将产生许多新的解决问题的方式。如果在建立验证平台和测试用例时,能够择优选择其中最简单的一种实现方式,那么搭建出来的验证平台 一定是足够强大、足够简练的。实现同一事情有多种方式,为用户提供了多种选择,高扩展性是UVM取得成功的一个重要原因。

2.细说测试用例中的sequence、callback、factory机制使用套路

2.1 callback机制

对于VIP的开发者来说,预留一个callback函数/任务接口时需要做以下几步:

  1. 定义一个A类
  2. 声明一个A_pool类
  3. 在要预留callback函数/任务接口的类中调用uvm_register_cb宏
  4. 在要调用callback函数/任务接口的函数/任务中,使用uvm_do_callbacks宏

对于VIP的使用者来说,需要做如下几步:

  • 从A派生一个类,在这个类中定义好pre_tran
  • 在测试用例的connect_phase(或者其他phase)中将从A派 生的类实例化,并将其加入A_pool中

2.1.1 VIP开发者的操作过程:

通过代码注释说明

//callbacks.sv

//1.定义一个A类

//A类一定要从uvm_callback派生,
//另外还需要定义一个pre_tran的任务,此任务的类型一定要是virtual的,
//因为从A派生的类需要重载这个任务。
class A extends uvm_callback;
   virtual task pre_tran(my_driver drv, ref my_transaction tr);
   endtask
endclass

//2.声明一个A_pool

//A_pool的声明,只需要一个typedef语句即可。
//在这个声明中
//要指明这是一个A类型的池子,和这个池子将会被哪个类使用
typedef uvm_callbacks#(my_driver, A) A_pool;

//my_driver.sv

typedef class A;

class my_driver extends uvm_driver#(my_transaction);

   virtual my_if vif;

   `uvm_component_utils(my_driver)

   //3.在要预留callback函数/任务接口的类中调用uvm_register_cb宏
   
   //这个声明与A_pool的类似,要指明 my_driver 和 A
   `uvm_register_cb(my_driver, A)
   function new(string name = "my_driver", uvm_component parent = null);
      super.new(name, parent);
   endfunction

   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!!!")
   endfunction

   extern task main_phase(uvm_phase phase);
   extern task drive_one_pkt(my_transaction tr);
endclass

task my_driver::main_phase(uvm_phase phase);
   vif.data <= 8'b0;
   vif.valid <= 1'b0;
   while(!vif.rst_n)
      @(posedge vif.clk);
   while(1) begin
      seq_item_port.get_next_item(req);
      

      //4.在要调用callback函数/任务接口的函数/任务中,使用uvm_do_callbacks宏
      
      //uvm_do_callbacks宏
      //第一个参数是调用pre_tran的类的名字,这里是 my_driver,
      //第二个参数是哪个类具有pre_tran,这里是 A,
      //第三个参数是调用的是函数/任务,这里是 pre_tran,在指明是pre_tran时,要顺便给出pre_tran的参数
      `uvm_do_callbacks(my_driver, A, pre_tran(this, req))
      drive_one_pkt(req);
      seq_item_port.item_done();
   end
endtask

task my_driver::drive_one_pkt(my_transaction tr);
   byte unsigned     data_q[];
   int  data_size;
   
   data_size = tr.pack_bytes(data_q) / 8; 
   //`uvm_info("my_driver", "begin to drive one pkt", UVM_LOW);
   repeat(3) @(posedge vif.clk);
   for ( int i = 0; i < data_size; i++ ) begin
      @(posedge vif.clk);
      vif.valid <= 1'b1;
      vif.data <= data_q[i]; 
   end

   @(posedge vif.clk);
   vif.valid <= 1'b0;
   //`uvm_info("my_driver", "end drive one pkt", UVM_LOW);
endtask

2.1.1 VIP使用者的操作过程:

//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

//1.从A派生一个类,在这个类中定义好pre_tran

class my_callback extends A;

   virtual task pre_tran(my_driver drv, ref my_transaction tr);
      `uvm_info("my_callback", "this is pre_tran task", UVM_MEDIUM)
   endtask

   `uvm_object_utils(my_callback)
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); 
   extern virtual function void connect_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

//在测试用例的connect_phase中将从A派生的类实例化,并将其加入A_pool中
//(也可以在build_phase中例化,但是一定要在前面callback函数/任务的phase之前)
function void my_case0::connect_phase(uvm_phase phase);
   my_callback my_cb;
   super.connect_phase(phase);

   my_cb = my_callback::type_id::create("my_cb");
   A_pool::add(env.i_agt.drv, my_cb);
endfunction

2.2 factory机制

factory机制重要的一点是提供重载功能。一般来说,如果要用B类重载A类,那么B类是要派生自A类的。在派生时,要保留A 类的大部分代码,只改变其中一小部分。

2.3 sequence机制

放弃建造强大sequence的想法,尽量做到一看见名字就可以知道这个sequence的用处。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰之行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值