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的代碼都是開源的,有興趣的讀者可以進一步深入分析源代碼,做到知其然,知其是以然。