天天看點

uvm-1.2 examples —— 2.13 callbacks2.13 callbacks前言一、基本介紹二、代碼分析總結

2.13 callbacks

文章目錄

  • 2.13 callbacks
  • 前言
  • 一、基本介紹
    • 1.1 預留callback函數步驟
    • 1.2 使用callback函數步驟
  • 二、代碼分析
    • 2.1 驗證環境開發者預留callback函數
    • 2.2 驗證環境使用者使用callback函數
    • 2.3 仿真結果分析
  • 總結

前言

本文以uvm-1.2/examples/simple/callbacks為例,通過代碼了解UVM中的callback機制。通過這個例子可以基本了解以下知識點:

  • 什麼是callback機制
  • 如何預留callback函數
  • 如何使用callback函數

一、基本介紹

在UVM驗證平台中,callback機制常用于提高驗證平台的可重用性,以及建構異常測試用例。

廣義的callback機制包括pre_randomize(),post_randomize(),pre_body(),post_body()等,這些函數會自動執行,可以被使用者重構,在随機或者仿真前後去實作特定的功能。

UVM中的callback機制分為兩個層次:驗證環境的開發者,驗證環境的使用者。驗證環境的開發者需要考慮,如何利用UVM的callback機制預留callback函數;驗證環境的使用者需要考慮,如何使用開發者預留的callback函數。

1.1 預留callback函數步驟

驗證環境的開發者,預留一個callback函數需要做以下幾步:

1、從uvm_callback派生出一個類A,并定義需要預留的callback任務;

class A extends uvm_callback;//派生類A
	virtual task pre_tran(my_driver drv, ref my_transaction tr);//預留的callback任務
	endtask
endclass
           

2、聲明一個A類型的池子;

3、對A進行注冊;

typedef class A;
class my_driver extends uvm_driver#(my_transaction);
...
	`uvm_register_cb(my_driver, A) //對從uvm_callback派生出的類A進行注冊
...
endclass
           

4、調用callback

task my_driver::main_phase(uvm_phase phase);
...
	while(1) begin
		seq_item_port.get_next_item(req);
		`uvm_do_callbacks(my_driver, A, pre_tran(this, req));//通過宏調用callback任務
		dirver_one_pkt(req);
		seq_item_port.item_done();
	end
...
endtask
           

1.2 使用callback函數步驟

驗證環境的使用者,使用callback函數需要做以下幾步:

1、從A派生一個類,并定義回調的任務;

class my_callback extends A; //從A派生一個類

virtual task pre_tran(my_driver drv, my_transaction tr); //在類中實作回調任務要執行的内容
	`uvm_info("my_callback", "this is pre_tran task", UVM_MEDIUM);
endtask

endclass
           

2、執行個體化A派生的類,并加入到A_pool這個池子中;

function void my_case::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
           

值得注意的是:在使用callback函數時,要保證仿真先執行從上述步驟2;再去執行callback函數中的内容。

上述示例代碼中,上述步驟2在connect_phase中執行,而callback函數在main_phase中執行,connect_phase在main_phase之前執行,滿足要求。

二、代碼分析

這一節将分别從驗證環境的開發者,和驗證環境的使用者,這兩個不同的角度,結合代碼進一步說明,如何預留callback函數,以及如何使用callback函數。

2.1 驗證環境開發者預留callback函數

`include "uvm_macros.svh"

//------------------------------------------------------------------------------
// Title: Typical Callback Application
//
// This example demonstrates callback usage. The component developer defines a
// driver and driver-specific callback class. The callback class defines the
// hooks available for users to override. The component using the callbacks
// (i.e. calling the callback methods) also defines corresponding virtual
// methods for each callback hook. The developer implements each virtual methods
// to call the corresponding callback method in all registered callback objects
// using default algorithm. The end-user may then define either a callback or
// driver subtype to extend driver behavior.
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//
// Group: Component Developer Use Model
//
//------------------------------------------------------------------------------
// Component developers defines transaction, driver, and callback classes.
//------------------------------------------------------------------------------

package bus_driver_pkg;

import uvm_pkg::*;

typedef class bus_driver;
typedef class bus_driver_cb;
typedef uvm_callbacks #(bus_driver,bus_driver_cb) bus_driver_cbs_t;


//------------------------------------------------------------------------------
//
// CLASS: bus_tr
//
// A basic bus transaction. 
//------------------------------------------------------------------------------

class bus_tr extends uvm_transaction;
  rand int addr;
  rand int data;
  virtual function string convert2string();
    convert2string = $sformatf("addr=%0h data=%0h",addr,data);
  endfunction
endclass


//------------------------------------------------------------------------------
//
// CLASS: bus_driver_cb
//
//------------------------------------------------------------------------------
// The callback class defines an interface consisting of one or more function
// or task prototypes. The signatures of each method have no restrictions.
// The component developer knows best the intended semantic of multiple
// registered callbacks. Thus the algorithm for traversal the callback queue
// should reside in the callback class itself. We could provide convenience
// macros that implement the most common traversal methods, such as sequential
// in-order execution.
//------------------------------------------------------------------------------

virtual class bus_driver_cb extends uvm_callback; 

  virtual function bit trans_received(bus_driver driver, bus_tr tr);
    return 0;
  endfunction

  virtual task trans_executed(bus_driver driver, bus_tr tr);
  endtask

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

  static string type_name = "bus_driver_cb";

  virtual function string get_type_name();
    return type_name;
  endfunction

endclass


//------------------------------------------------------------------------------
//
// CLASS: bus_driver
//
//------------------------------------------------------------------------------
// With the following implementation of bus_driver, users can implement
// the callback "hooks" by either...
//
// - extending bus_driver and overriding one or more of the virtual
//   methods, trans_received or trans_executed. Then, configure the
//   factory to use the new type via a type or instance override.
//
// - extending bus_driver_cb and overriding one or more of the virtual
//   methods, trans_received or trans_executed. Then, register an
//   instance of the new callback type with an instance of bus_driver.
//   This requires access to the handle of the bus_driver.
//------------------------------------------------------------------------------

class bus_driver extends uvm_component;

  uvm_blocking_put_imp #(bus_tr,bus_driver) in;

  `uvm_register_cb(bus_driver, bus_driver_cb)

  function new (string name, uvm_component parent=null);
    super.new(name,parent);
    in = new("in",this);
  endfunction

  static string type_name = "bus_driver";

  virtual function string get_type_name();
    return type_name;
  endfunction

  virtual function bit trans_received(bus_tr tr);
    `uvm_do_callbacks_exit_on(bus_driver,bus_driver_cb,trans_received(this,tr),1)
  endfunction

  virtual task trans_executed(bus_tr tr);
    `uvm_do_callbacks(bus_driver,bus_driver_cb,trans_executed(this,tr))
  endtask

  virtual task put(bus_tr t);
    uvm_report_info("bus_tr received",t.convert2string());
    if (!trans_received(t)) begin
      uvm_report_info("bus_tr dropped",
          "user callback indicated DROPPED\n");
      return;
    end
    #100;
    trans_executed(t);
    uvm_report_info("bus_tr executed",{t.convert2string(),"\n"});
  endtask

endclass

endpackage // bus_driver_pkg

           

64到83行,從uvm_callback派生出一個類bus_driver_cb,并定義需要預留的callback任務trans_received()和trans_executed()。

31行,聲明一個bus_driver_cb類型的池子bus_driver_cbs_t。

108行,對bus_driver_cb進行注冊。

122行和126行,分别調用trans_received()和trans_executed()的callback。值得注意的是trans_received()調用的是宏`uvm_do_callbacks_exit_on(T, CB, METHOD, VAL),這個宏可以進一步控制執行回調函數的層次,簡單來講,回調函數會一直執行,直到傳回值與給入的VAL值相同就立刻傳回,這一點使得回調方法執行順序上面有了更多的選擇。

2.2 驗證環境使用者使用callback函數

//------------------------------------------------------------------------------
//
// Group: End-User Use Model
//
//------------------------------------------------------------------------------
// The end-user simply needs to extend the callback base class, overriding any or
// all of the prototypes provided in the developer-supplied callback interface.
// Then, register an instance of the callback class with any object designed to
// use the base callback type.
//------------------------------------------------------------------------------

import uvm_pkg::*;
import bus_driver_pkg::*;

//------------------------------------------------------------------------------
//
// CLASS: my_bus_driver_cb
//
//------------------------------------------------------------------------------
// This class defines a subtype of the driver developer's base callback class.
// In this case, both available driver callback methods are defined. The 
// ~trans_received~ method randomly chooses whether to return 0 or 1. When 1,
// the driver will "drop" the received transaction.
//------------------------------------------------------------------------------

class my_bus_driver_cb extends bus_driver_cb;

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

  virtual function bit trans_received(bus_driver driver, bus_tr tr);
    static bit drop = 0;
    driver.uvm_report_info("trans_received_cb",
      {"  bus_driver=",driver.get_full_name()," tr=",tr.convert2string()});
    drop = 1 - drop;
    return drop;
  endfunction

  virtual task trans_executed(bus_driver driver, bus_tr tr);
    driver.uvm_report_info("trans_executed_cb",
      {"  bus_driver=",driver.get_full_name()," tr=",tr.convert2string()});
  endtask

  virtual function string get_type_name();
    return "my_bus_driver_cb";
  endfunction

endclass


//------------------------------------------------------------------------------
//
// CLASS: my_bus_driver_cb2
//
//------------------------------------------------------------------------------
// This class defines a subtype of the driver developer's base callback class.
// In this case, only one of the two available methods are defined.
//------------------------------------------------------------------------------

class my_bus_driver_cb2 extends bus_driver_cb;

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

  virtual task trans_executed(bus_driver driver, bus_tr tr);
    driver.uvm_report_info("trans_executed_cb2",
      {"  bus_driver=",driver.get_full_name()," tr=",tr.convert2string()});
  endtask

  virtual function string get_type_name();
    return "my_bus_driver_cb2";
  endfunction

endclass


//------------------------------------------------------------------------------
//
// MODULE: top
//
//------------------------------------------------------------------------------
// In this simple example, we don't build a complete environment, but this does
// not detract from the example's purpose. In the top module, we instantiate
// the bus_driver, and one instance each of our custom callback classes. 
// To register the callbacks with the driver, we get the global callback pool
// that is typed to our specific driver-callback combination. We associate
// (register) the callback objects with driver using the callback pool's 
// ~add_cb~ method. After calling ~display~ just to show that the
// registration was successful, we push several transactions into the driver.
// Our custom callbacks get called as the driver receives each transaction.
//------------------------------------------------------------------------------

module top;
  import uvm_pkg::*;
  import bus_driver_pkg::*;

  bus_tr            tr     = new;
  bus_driver        driver = new("driver");
  my_bus_driver_cb  cb1    = new("cb1");
  my_bus_driver_cb2 cb2    = new("cb2");

  initial begin
    bus_driver_cbs_t::add(driver,cb1);
    bus_driver_cbs_t::add(driver,cb2);
    bus_driver_cbs_t::display();
    for (int i=1; i<=5; i++) begin
      tr.addr = i;
      tr.data = 6-i;
      driver.in.put(tr);
    end
    begin
       uvm_report_server svr;
       svr = uvm_report_server::get_server();
       svr.summarize();
    end
  end

endmodule

           

27到50行,從bus_driver_cb派生出回調類my_bus_driver_cb,在其中定義并實作了回調函數trans_received()和trans_executed()。

62到77行,從bus_driver_cb派生出回調類my_bus_driver_cb2,在其中定義并實作了回調函數trans_received()。

102行和103行,執行個體化派生的兩個回調函數。

106行和107行,将回調函數加入到bus_driver_cbs_t這個池子中。

2.3 仿真結果分析

仿真log如下:

UVM_INFO ../../../src/base/uvm_callback.svh(428) @ 0: reporter [UVM/CB/DISPLAY] Registered callbacks for all instances of bus_driver
---------------------------------------------------------------
cb1   driver on   ON
cb2   driver on   ON

UVM_INFO @ 0: driver [bus_tr received] addr=1 data=5
UVM_INFO @ 0: driver [trans_received_cb]   bus_driver=driver tr=addr=1 data=5
UVM_INFO @ 100: driver [trans_executed_cb]   bus_driver=driver tr=addr=1 data=5
UVM_INFO @ 100: driver [trans_executed_cb2]   bus_driver=driver tr=addr=1 data=5
UVM_INFO @ 100: driver [bus_tr executed] addr=1 data=5

UVM_INFO @ 100: driver [bus_tr received] addr=2 data=4
UVM_INFO @ 100: driver [trans_received_cb]   bus_driver=driver tr=addr=2 data=4
UVM_INFO @ 100: driver [bus_tr dropped] user callback indicated DROPPED

UVM_INFO @ 100: driver [bus_tr received] addr=3 data=3
UVM_INFO @ 100: driver [trans_received_cb]   bus_driver=driver tr=addr=3 data=3
UVM_INFO @ 200: driver [trans_executed_cb]   bus_driver=driver tr=addr=3 data=3
UVM_INFO @ 200: driver [trans_executed_cb2]   bus_driver=driver tr=addr=3 data=3
UVM_INFO @ 200: driver [bus_tr executed] addr=3 data=3

UVM_INFO @ 200: driver [bus_tr received] addr=4 data=2
UVM_INFO @ 200: driver [trans_received_cb]   bus_driver=driver tr=addr=4 data=2
UVM_INFO @ 200: driver [bus_tr dropped] user callback indicated DROPPED

UVM_INFO @ 200: driver [bus_tr received] addr=5 data=1
UVM_INFO @ 200: driver [trans_received_cb]   bus_driver=driver tr=addr=5 data=1
UVM_INFO @ 300: driver [trans_executed_cb]   bus_driver=driver tr=addr=5 data=1
UVM_INFO @ 300: driver [trans_executed_cb2]   bus_driver=driver tr=addr=5 data=1
UVM_INFO @ 300: driver [bus_tr executed] addr=5 data=1

UVM_INFO ../../../src/base/uvm_report_server.svh(847) @ 300: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

** Report counts by severity
UVM_INFO :   23
UVM_WARNING :    0
UVM_ERROR :    0
UVM_FATAL :    0
** Report counts by id
[UVM/CB/DISPLAY]     1
[UVM/RELNOTES]     1
[bus_tr dropped]     2
[bus_tr executed]     3
[bus_tr received]     5
[trans_executed_cb]     3
[trans_executed_cb2]     3
[trans_received_cb]     5

           V C S   S i m u l a t i o n   R e p o r t 
Time: 300 ns

           

通過分析log檔案,總結幾點如下:

1、可以通過bus_driver_cbs_t::display()這樣的方法,來列印目前池子裡面回調函數的狀态。

2、代碼122行,由于trans_received()調用的是宏`uvm_do_callbacks_exit_on(T, CB, METHOD, VAL),是以,會根據trans_received()的傳回值,判斷是否繼續去執行trans_executed()。

如果将122行的代碼改成如下:

那麼執行仿真的log則變成下面這樣:

UVM_INFO ../../../src/base/uvm_callback.svh(428) @ 0: reporter [UVM/CB/DISPLAY] Registered callbacks for all instances of bus_driver
---------------------------------------------------------------
cb1   driver on   ON
cb2   driver on   ON

UVM_INFO @ 0: driver [bus_tr received] addr=1 data=5
UVM_INFO @ 0: driver [trans_received_cb]   bus_driver=driver tr=addr=1 data=5
UVM_INFO @ 0: driver [bus_tr dropped] user callback indicated DROPPED

UVM_INFO @ 0: driver [bus_tr received] addr=2 data=4
UVM_INFO @ 0: driver [trans_received_cb]   bus_driver=driver tr=addr=2 data=4
UVM_INFO @ 0: driver [bus_tr dropped] user callback indicated DROPPED

UVM_INFO @ 0: driver [bus_tr received] addr=3 data=3
UVM_INFO @ 0: driver [trans_received_cb]   bus_driver=driver tr=addr=3 data=3
UVM_INFO @ 0: driver [bus_tr dropped] user callback indicated DROPPED

UVM_INFO @ 0: driver [bus_tr received] addr=4 data=2
UVM_INFO @ 0: driver [trans_received_cb]   bus_driver=driver tr=addr=4 data=2
UVM_INFO @ 0: driver [bus_tr dropped] user callback indicated DROPPED

UVM_INFO @ 0: driver [bus_tr received] addr=5 data=1
UVM_INFO @ 0: driver [trans_received_cb]   bus_driver=driver tr=addr=5 data=1
UVM_INFO @ 0: driver [bus_tr dropped] user callback indicated DROPPED

UVM_INFO ../../../src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

** Report counts by severity
UVM_INFO :   17
UVM_WARNING :    0
UVM_ERROR :    0
UVM_FATAL :    0
** Report counts by id
[UVM/CB/DISPLAY]     1
[UVM/RELNOTES]     1
[bus_tr dropped]     5
[bus_tr received]     5
[trans_received_cb]     5

           V C S   S i m u l a t i o n   R e p o r t 
Time: 0 ns

           

總結

本文結合代碼,分别站在驗證環境的開發者和驗證環境的使用者,這兩個不同的角度,介紹了如何去預留callback函數,以及如何去使用callback函數。

本文沒有深入到callback機制内部,去剖析callback機制的一些宏的具體實作,UVM的代碼都是開源的,有興趣的讀者可以進一步深入分析源代碼,做到知其然,知其是以然。