《UVM实战》学习笔记——第九章 UVM中代码的可重用性 (callback机制)


前言

2023.3.6 加油接着学
2023.3.12 植树节


一、callback机制

在父类定义方法时,预留回调函数入口,使得在继承的子类中填充回调函数,就可以完成对父类方法的修改。
预留回调函数入口,定义回调类和类中函数,例化及添加回调类的实例

1、用途

  • 提高验证平台的可重用性:如post_randomize、pre_body、post_body等函数
  • 用于构建异常的测试用例(之前的factory机制也可以用来构建)

2、callback机制的原理

使用时只要从A派生一个类并将其实例化,然后重新定义其pre_tran函数。把实例A放到A_pool。假设这个类称为A_pool,意思就是专门存放A或者A的派生类的一个池子。UVM约定会执行这个池子中所有实例的pre_tran函数/任务。

task my_driver::main_phase();while(1) begin
		seq_item_port.get_next_item(req);
		A.pre_tran(req);
		…
	end
endtask

3、callback机制的使用

对于VIP的开发者

  • 定义一个A类;
  • 声明一个A_pool类;
  • 在要预留callback函数/任务接口的类中调用uvm_register_cb宏;
  • 在要调用callback函数/任务接口的函数/任务中,使用uvm_do_callbacks宏。

对于VIP的使用者

首先从A派生一个类,然后在测试用例中将my_callback实例化,并将其加入A_pool中(是在case层次的connect_phase完成这个任务)。

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

4、子类继承父类的callback机制

应用场景:前后两次项目,大部分代码都相同,只是去修改某个组件,例如my_driver。
首先,使用uvm_set_super_type宏,把子类和父类关联在一起;然后在main_phase中调用uvm_do_callbacks宏时,其第一个参数是my_driver而不是new_driver,即调用方式与在my_driver中一样。

class new_driver extends my_driver;
	`uvm_component_utils(new_driver)
	`uvm_set_super_type(new_driver, my_driver)
	…
endclass
task new_driver::main_phase(uvm_phase phase);while(1) begin
		seq_item_port.get_next_item(req);
		`uvm_info("new_driver", "this is new driver", UVM_MEDIUM)
		`uvm_do_callbacks(my_driver, A, pre_tran(this, req))
		drive_one_pkt(req);
		seq_item_port.item_done();
	end
endtask

在my_agent中实例化此new_driver

function void my_agent::build_phase(uvm_phase phase);
	super.build_phase(phase);
	if (is_active == UVM_ACTIVE) begin
		sqr = my_sequencer::type_id::create("sqr", this);
		drv = new_driver::type_id::create("drv", this);
	end
	mon = my_monitor::type_id::create("mon", this);
endfunction

5、使用callback函数/任务实现所有sequence

6、举例

定义回调函数类,绑定回调函数类和组件,插入回调函数

`uvm_register_cb(comp1,cb1)  //绑定
`uvm_do_callbacks(comp1,cb1,do_trans(d))   //插入

`uvm_callbacks #(comp1)::add(c1,m_cb1)    //添加

在这里插入图片描述

在这里插入图片描述

call_back是按照顺序执行的
uvm_do_callbacks(T, CB, METHOD):会循环执行和该对象结对的回调函数类的方法

顶层声明例化两个回调函数类,并添加回调函数
uvm_callbacks #(T, CB):类的静态方法add(comp, cb1)来添加成对的object和cb对象

在这里插入图片描述

7、完整举例

// Developer Code
class my_seq_item extends uvm_sequence_item;
  rand logic [7:0] addr;
  rand logic [7:0] data;
  constraint addr_range_cn {
    addr inside { [10:20]};
  }
  constraint data_range_cn {
    data inside { [100:200]};
  }
  `uvm_object_utils_begin (my_seq_item)
    `uvm_field_int (addr, UVM_ALL_ON| UVM_DEC)
    `uvm_field_int (data, UVM_ALL_ON| UVM_DEC)
  `uvm_object_utils_end
  function new (string name="my_seq_item");
    super.new (name);
  endfunction : new
  virtual function string convert2string ();
    convert2string = $sformatf ("addr=%0d, data=%0d", addr, data);
  endfunction : convert2string
endclass : my_seq_item

class my_sequencer extends uvm_sequencer # (my_seq_item);
  `uvm_component_utils (my_sequencer)
  function new (string name="my_sequencer", uvm_component parent=null);
    super.new (name, parent);
  endfunction : new
endclass : my_sequencer

typedef class my_driver;
class driver_cb extends uvm_callback;
  `uvm_object_utils (driver_cb)
  function new (string name="driver_cb");
    super.new (name);
  endfunction : new
  // callback method shall be virtual.
  virtual task inject_err (my_driver drv, my_seq_item tr);
  endtask : inject_err
endclass : driver_cb

class my_driver extends uvm_driver # (my_seq_item);
  `uvm_component_utils (my_driver)
  // Register Callback
  `uvm_register_cb (my_driver,driver_cb)
  function new (string name="my_driver", uvm_component parent=null);
    super.new (name, parent);
  endfunction : new
  function void build_phase (uvm_phase phase);
    super.build_phase (phase);
  endfunction : build_phase
  virtual task inject_err (my_seq_item tr);
    `uvm_do_callbacks (my_driver, driver_cb, inject_err (this,tr))
  endtask : inject_err
  task run_phase (uvm_phase phase);
    forever
      begin
        #5;
        seq_item_port.get_next_item (req);
        inject_err (req);
        `uvm_info (get_name (),
          $sformatf ("After passing throguh callback in driver my_seq_item= %s",
          req.convert2string ()), UVM_LOW);
        #5;
        seq_item_port.item_done ();
      end
   endtask : run_phase
endclass : my_driver

// End User Code

class my_seq extends uvm_sequence # (my_seq_item);
  `uvm_object_utils (my_seq)
  function new (string name="my_seq");
    super.new (name);
  endfunction : new
  task body ();
    `uvm_create (req)
    if (!req.randomize ())
      begin
        `uvm_fatal (get_name (), $sformatf ("Randomization failed"))
      end
    `uvm_info (get_name (),
      $sformatf ("After randomizating in my_seq my_seq_item= %s",
      req.convert2string ()), UVM_LOW)
    `uvm_send (req)
   endtask : body
endclass : my_seq

class my_dri_cb extends driver_cb;
  `uvm_object_utils (my_dri_cb)
  static bit drop = 1;
  function new (string name="my_dri_cb");
    super.new (name);
  endfunction : new
  task inject_err (my_driver drv, my_seq_item tr);
    tr.addr = tr.addr + 10;
    tr.data = tr.data + 10;
   endtask : inject_err
endclass : my_dri_cb

typedef uvm_callbacks # (my_driver,driver_cb) bus_driver_cbs_t;

class my_test extends uvm_test;
  `uvm_component_utils (my_test)
  my_sequencer sqr;
  my_driver drv;
  my_dri_cb cb;
  
function new (string name="my_test", uvm_component parent=null);
    super.new (name, parent);
endfunction : new

function void build_phase (uvm_phase phase);
   super.build_phase (phase);
   sqr = my_sequencer :: type_id :: create ("sqr", this);
   drv = my_driver :: type_id :: create ("drv", this);
   cb = my_dri_cb :: type_id :: create ("cb");
endfunction : build_phase

function void connect_phase(uvm_phase phase); 
   super.connect_phase(phase); 
   drv.seq_item_port.connect(sqr.seq_item_export); 
endfunction: connect_phase 

task run_phase(uvm_phase phase); 
   my_seq seq; 
   phase.raise_objection(this); 
   #5; 
   seq = my_seq::type_id::create("seq"); 
   seq.start(sqr); 
   #50; 
   bus_driver_cbs_t::add(drv, cb); 
   `uvm_info(get_name(), "----------------------Added Callback----------------------", UVM_LOW) 
   seq = my_seq::type_id::create("seq"); 
   seq.start(sqr); 
   bus_driver_cbs_t::delete(drv, cb); 
   `uvm_info(get_name(), "----------------------Deleted Callback--------------------", UVM_LOW) 
   #50; 
   seq = my_seq::type_id::create("seq"); 
   seq.start(sqr); 
   phase.drop_objection(this); 
endtask 

endclass

module top();
	`include "uvm_macros.svh"
	import uvm_pkg::*;
	initial begin
		run_test("my_test");
	end
endmodule: top

在这里插入图片描述

二、UVM中一些可重用性的地方

1、factory机制:把一些操作写成可重载的函数/任务,这样代码在验证环境中只要写一次,其他地方要用的话就对它去重载。
2、sequence:把实现单个功能的部分做成一个个小的sequence
3、参数化类:如设置总线位宽为参数,参数化的类方便修改从而产生不同的类
提高复用率,减少代码量
4、寄存器模型的重用
5、call back机制

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值