Friday 2 June 2017

Advance usage of callback in UVM

Callback is basically used for updating the component's behavior without actually changing the component class. Callback class contains the virtual methods which is used by the component. So, user can update the functionality by extending this callback class which alters the behavior of the component.

Usage of callback class is explained in the UVM user guide (6.3 Callbacks, UVM 1.2 User’s Guide). Here, advanced usage of callback class is explained.User can have multiple class extended from the callback class. All the extended classes will be stored in a queue. So which ever class registered first or added first, its method called first by default followed by the method of the next registered class's. To alter this behavior, user can pass "UVM_PREPEND" in 3rd argument while registering the callback (through "add" method) class which puts the registered class in the top of the queue and its method will be called first before any other callback class's method. 

To check the list of registered callback class, user can call "display" method of "uvm_callbacks" class. It prints the registered callback instances and it's status i.e. ON (enabled) or OFF (disabled). In some cases, user don't want to call the existing functionality implemented through callback. To achieve this functionality, user can disable/enable the callback class by calling "callback_mode(X)" (where X: 0 -> disable, 1-> enable) method of "uvm_callback" class.

Example:

`include "uvm_macros.svh"
package driver_pkg;
import uvm_pkg::*;
typedef class driver;
typedef class driver_cb;
typedef uvm_callbacks #(driver,driver_cb) driver_cbs_t;

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

// Driver callback. All callback class shall be extended from this one to use callback method.
virtual class driver_cb extends uvm_callback;
    ...
    virtual function void trans_received(driver driver, counter_txn tr);
    endfunction
endclass

class driver extends uvm_component;
    uvm_blocking_put_imp #(counter_txn,driver) in;
    `uvm_register_cb(driver, driver_cb)

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

    virtual function void trans_received(counter_txn tr);
        `uvm_do_callbacks(driver,driver_cb,trans_received(this,tr))
    endfunction

    virtual task put(counter_txn t);
        uvm_report_info("counter_txn received",t.convert2string());
        trans_received(t);
        uvm_report_info("counter_txn executed",{t.convert2string(),"\n"});
    endtask
endclass
endpackage // driver_pkg

import uvm_pkg::*;
import driver_pkg::*;

// User's callback class implementation
class my_driver_cb1 extends driver_cb;
    ...
    virtual function void trans_received(driver driver, counter_txn tr);
      driver.uvm_report_info("cb1:trans_received_cb", {"driver:",driver.get_full_name(), "tr:", tr.convert2string()});
    endfunction
endclass

class my_driver_cb2 extends driver_cb;
    ...
    virtual function void trans_received(driver driver, counter_txn tr);
      driver.uvm_report_info("cb2:trans_received_cb", {"driver:",driver.get_full_name(), "tr:", tr.convert2string()});
    endfunction
endclass

class my_driver_cb3 extends driver_cb;
    ...
    virtual function void trans_received(driver driver, counter_txn tr);
      driver.uvm_report_info("cb3:trans_received_cb", {"driver:",driver.get_full_name(), "tr:", tr.convert2string()});
   endfunction
endclass

class my_driver_cb4 extends driver_cb;
    ...
    virtual function void trans_received(driver driver, counter_txn tr);
      driver.uvm_report_info("cb4:trans_received_cb", {"driver:",driver.get_full_name(), "tr:", tr.convert2string()});
    endfunction
endclass

module top();
    driver driver1=new();
    my_driver_cb1 cb1=new();
    my_driver_cb2 cb2=new();
    my_driver_cb3 cb3=new();
    my_driver_cb4 cb4=new();
    counter_txn tr = new();
       
    initial begin
        driver_cbs_t::add(driver1, cb1);
        driver_cbs_t::add(driver1, cb2);        
        driver_cbs_t::add(driver1, cb3, UVM_PREPEND);
        driver_cbs_t::add(driver1, cb4, UVM_PREPEND);
        // Disabling cb1 callback
        cb1.callback_mode(0);

        for (int i=1; i<=3; i++) begin
            tr.cntr = i;
            // To display registered callback instances
            driver_cbs_t::display();
            driver1.in.put(tr);
            // Altering cb1 callback mode i.e. enabling<->disabling
           cb1.callback_mode(i[0]);
        end
    end
endmodule  


Above example shows four callback classes are registered with driver class. First "cb1" class instance is added followed by "cb2". So cb1's  method will be called first before cb2's. cb3 and cb4 class instance are registered as "UVM_PREPEND". So, method's will be called in following schedule:
cb4, cb3, cb1, cb2

cb1 class instance is enabled and disabled by "callback_mode" method and to check the status, "display" method is called which indicates status as ON and OFF of registered callback classes. Below is it's output where status of "cb1" is clearly shown as OFF, ON and OFF and other class's status is ON.

Output:

UVM_INFO /apps/vcsmx/etc/uvm-1.2/src/base/uvm_callback.svh(428) @ 0: reporter [UVM/CB/DISPLAY] Registered callbacks for all instances of driver
---------------------------------------------------------------
cb4 drv on ON
cb3 drv on ON
cb1 drv on OFF
cb2 drv on ON

UVM_INFO @ 0: drv [counter_txn received] cntr=1
UVM_INFO @ 0: drv [cb4:trans_received_cb] driver:drv tr:cntr=1
UVM_INFO @ 0: drv [cb3:trans_received_cb] driver:drv tr:cntr=1
UVM_INFO @ 0: drv [cb2:trans_received_cb] driver:drv tr:cntr=1
UVM_INFO @ 0: drv [counter_txn executed] cntr=1

UVM_INFO /apps/vcsmx/etc/uvm-1.2/src/base/uvm_callback.svh(428) @ 0: reporter [UVM/CB/DISPLAY] Registered callbacks for all instances of driver
---------------------------------------------------------------
cb4 drv on ON
cb3 drv on ON
cb1 drv on ON
cb2 drv on ON

UVM_INFO @ 0: drv [counter_txn received] cntr=2
UVM_INFO @ 0: drv [cb4:trans_received_cb] driver:drv tr:cntr=2
UVM_INFO @ 0: drv [cb3:trans_received_cb] driver:drv tr:cntr=2
UVM_INFO @ 0: drv [cb1:trans_received_cb] driver:drv tr:cntr=2
UVM_INFO @ 0: drv [cb2:trans_received_cb] driver:drv tr:cntr=2
UVM_INFO @ 0: drv [counter_txn executed] cntr=2

UVM_INFO /apps/vcsmx/etc/uvm-1.2/src/base/uvm_callback.svh(428) @ 0: reporter [UVM/CB/DISPLAY] Registered callbacks for all instances of driver
---------------------------------------------------------------
cb4 drv on ON
cb3 drv on ON
cb1 drv on OFF
cb2 drv on ON

UVM_INFO @ 0: drv [counter_txn received] cntr=3
UVM_INFO @ 0: drv [cb4:trans_received_cb] driver:drv tr:cntr=3
UVM_INFO @ 0: drv [cb3:trans_received_cb] driver:drv tr:cntr=3
UVM_INFO @ 0: drv [cb2:trans_received_cb] driver:drv tr:cntr=3
UVM_INFO @ 0: drv [counter_txn executed] cntr=3

3 comments:

  1. sir your explanation is great but i have doubt that
    1.i have to create queue array in uvm_driver_cb.sv right?
    2.and i have problem of declaring and use of queue array while push and pop from and in to array

    ReplyDelete
  2. and also this "class driver extends uvm_component" this is the main driver class?

    ReplyDelete
  3. Why you need to declare queue? It's not necessary. You can pass object, variable anything.
    Yes, driver is the main driver component. You can put it in sequencer or monitor as well.

    ReplyDelete