How UVM Callback works?

Callback mechanism is used for altering the behavior of the transactor/BFM without modifying the existing BFM/transactor. Callback gives flexibility to plug-and-play and reuse the components i.e. driver, monitor etc.. 

Sometimes requirements are often unpredictable when the BFM/transactor is first written. So a transactor should provide some kind of hooks for executing the code which is defined afterward or to add or alter the existing functionality of BFM or Transactor. In uvm, these hooks are created using callback methods.

For instance, a driver is developed and an empty method is called before driving a set of sequences to the DUT. Initially, this empty method does nothing. As the implementation goes, the user may realize that he needs to print the state of the sequence or to delay the transaction driving to DUT or inject an error into a sequence. The callback mechanism allows executing the user-defined code in place of the empty callback method without changing the behavior of the transactor or BFM.

Sometimes we are using third party VIP in our verification environment. Third party wants their code to be encrypted and also wants to give facility to their customers to change the behavior of driver/BFM without allowing to modify driver code. In this case they provide hooks at some particular places like just after getting transaction from sequence (after get_next_item), just before start driving to the interface, etc. These hooks are created using callback

Another example of callback usage is in monitor. Callbacks can be used in a monitor for collecting coverage information or for hooking up to scoreboard to pass transactions for self checking. With this, user will able to control the behavior of the transactor in verification environment and individual test cases without doing any modifications to the existing transactor code.

Let’s understand through callback example in which error is injected.

`include "uvm_macros.svh"
import uvm_pkg::*;
//-------------------------------------------------------------------
// 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

on Facebook on Google+

About the author

The Art of Verification

Hi, I’m Hardik, and welcome to The Art of Verification.

I’m a Verification Engineer who loves to crack complex designs and here to help others commit to mastering Verification Skills through self-learning, System Verilog, UVM, and most important to develop that thought process that every verification engineer should have.

I’ve made it my mission to give back and serve others beyond myself.

I will NEVER settle for less than I can be, do, give, or create.

View all posts