Tutorials

Learn More

The callbacks are used to alter the behavior of the component or object without modifying its code. Refer System Verilog callback to have a better understanding. A simple example of callbacks can be the phasing mechanism in UVM.

UVM Callback Usage

  1. Allows plug-and-play mechanism to establish a reusable verification environment.
  2. Based on the hook method call, the user-defined code is executed instead of the empty callback method. This brings various flavors of the component or object.
  3. Callbacks can be used to introduce errors or delays in the components.

UVM Callback Macros

Widely used callback macros alone are mentioned below:

Macros

Description

`uvm_register_cb(T, CB)

Registers the user-defined callback which is extended from uvm_callback.

T – Object type where user-defined callback is used and it must be derived from uvm_object.

CB – user-defined callback type

If a type-callback pair is not registered then a warning is issued for an attempt to call add, delete, etc methods.

`uvm_do_callbacks(T, CB, METHOD)

Calls METHOD of user-defined callback class.

T – Object type where user-defined callback is used and it must be derived from uvm_object.

CB – user-defined callback type

METHOD – callback method call to invoke.

uvm_do_obj_callbacks(T, CB, OBJ, METHOD)

It is similar to `uvm_do_callbacks macro, but it has an additional OBJ argument to specify external object associated with the callback.

Example: For applying callback in a sequence, OBJ could be specified as parent sequence or sequencer.

UVM Callback Classes

The UVM callback provides a set of classes for its implementation.

Classes

Description

uvm_callbacks

provides a base class for callback implementation and is typically used for modifying component behavior without modifying the component class.

uvm_callback_iter

It is an iterator class for iterating over callback queues of a specific type.

uvm_callback

Provides a base class for user-defined callback classes.

UVM Callback Methods

The uvm_callback methods can be called using a scope resolution operator as they are static methods.

For example: uvm_callbacks#(T, CB)::add(obj,cb);

Where,

T : Object type where user-defined callback is used and it must be derived from uvm_object.
CB : user-defined callback type

obj : object handle where user-defined callback is used
cb : a user-defined callback object

Methods

Description

add

Registers the given callback object with the given obj handle.

add_by_name

Registers the given callback object with one or more uvm_components.

delete

Deletes the given callback object.

delete_by_name

Deletes the given callback object with one or more uvm_components.

The uvm_callbacks, uvm_callback_iter, and uvm_callback classes have many other methods that are not discussed here, common methods and macros are described in the above section.

Steps to implement uvm_callback

1. Create a user-defined callback class that extends from the uvm_callback class.

class driver_cb extends uvm_callback;

2. Add an empty callback method

virtual task modify_pkt();
endtask

The driver_cb callback class code:

class driver_cb extends uvm_callback;
  `uvm_object_utils(driver_cb)
  
  function new(string name = "driver_cb");
    super.new(name);
  endfunction
  
  virtual task modify_pkt();
  endtask  
endclass

3. Implement a callback method in the class which is extended from the above user-defined class.

class derived_cb extends driver_cb;
  `uvm_object_utils(derived_cb)
  
  function new(string name = "derived_cb");
    super.new(name);
  endfunction
  
  task modify_pkt; // callback method implementation
    `uvm_info(get_full_name(),"Inside modify_pkt method: Injecting error pkt",UVM_LOW);
    std::randomize(pkt) with {pkt inside {BAD_ERR1, BAD_ERR2};};
  endtask
endclass

4. Register user-defined callback method using `uvm_register_cb in the component or object where callbacks are called (In the below example, it is registered in the driver component).

`uvm_register_cb(driver,driver_cb)

If `uvm_register_cb is not used then a warning is issued.

Example:

UVM_WARNING @ 0: reporter [CBUNREG] Callback drvd_cb cannot be registered with object (*) because callback type derived_cb is not registered with object type uvm_object

5. Place callback hook i.e. calling callback method in the required component or object using `uvm_do_callbacks macro

`uvm_do_callbacks(driver,driver_cb,modify_pkt());

Driver component code:

typedef enum {GOOD, BAD_ERR1, BAD_ERR2} pkt_type;
class driver extends uvm_component;
  `uvm_component_utils(driver)
  `uvm_register_cb(driver,driver_cb) // callback registration
  
  function new(string name = "driver", uvm_component parent = null);
    super.new(name,parent);
  endfunction
  
  task run_phase(uvm_phase phase); 
    super.run_phase(phase);
    drive();
    `uvm_do_callbacks(driver,driver_cb,modify_pkt()); // callback hook
  endtask
  
  task drive();
    `uvm_info(get_full_name(),"Inside drive method",UVM_LOW);
    std::randomize(pkt) with {pkt == GOOD;};
  endtask
endclass

Output:

1. When base_test is executed (Run Options: +UVM_TESTNAME=base_test)

class base_test extends uvm_test;
  `uvm_component_utils(base_test)
  env env_o;
  
  function new(string name = "base_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    env_o = env::type_id::create("env_o", this);
  endfunction
endclass

A GOOD packet is generated as mentioned below.

UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO driver.sv(17) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Inside drive method
UVM_INFO driver.sv(13) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Driven pkt is GOOD

2. When err_test is executed (Run Options: +UVM_TESTNAME=err_test)

class err_test extends base_test;
  derived_cb drvd_cb;
  `uvm_component_utils(err_test)
  
  function new(string name = "err_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    drvd_cb = derived_cb::type_id::create("drvd_cb", this);
    uvm_callbacks#(driver, driver_cb)::add(env_o.drv, drvd_cb);
  endfunction
endclass

Using UVM callback, an error packet is generated as shown below.

VM_INFO @ 0: reporter [RNTST] Running test err_test...
UVM_INFO driver.sv(17) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Inside drive method
UVM_INFO callbacks.sv(20) @ 0: reporter [drvd_cb] Inside modify_pkt method: Injecting error pkt
UVM_INFO driver.sv(13) @ 0: uvm_test_top.env_o.drv [uvm_test_top.env_o.drv] Driven pkt is BAD_ERR2

UVM callback in uvm_sequence

The steps mentioned to implement callback in uvm_sequence are the same as above.

Note that `uvm_do_obj_callbacks macro is used as a callback hook with associated sequencer.

`uvm_do_obj_callbacks(sequencer,seq_cb,l_seqr,modify_pkt(req));

Where l_seqr handle points to object handle of sequencer class. Instead of this approach, p_seqeuncer can also be used here and p_sequencer must be declared using `uvm_declare_p_sequencer macro.

`uvm_do_obj_callbacks(sequencer,seq_cb,p_sequencer,modify_pkt(req));

UVM callback in uvm_sequence example

class base_seq extends uvm_sequence #(seq_item);
  seq_item req;
  sequencer l_seqr; // Provided sequencer hierarchy from base_test before starting the sequence.
  `uvm_object_utils(base_seq)
  
  function new (string name = "base_seq");
    super.new(name);
  endfunction

  task body();
    `uvm_info(get_type_name(), "Base seq: Inside Body", UVM_LOW);
    req = seq_item::type_id::create("req");
    wait_for_grant();
    assert(req.randomize());
    `uvm_do_obj_callbacks(sequencer,seq_cb,l_seqr,modify_pkt(req));
    send_request(req);
    wait_for_item_done();
  endtask
endclass

Output:

1.When base_test is executed (Run Options: +UVM_TESTNAME=base_test)

class base_test extends uvm_test;
  env env_o;
  base_seq bseq;

  `uvm_component_utils(base_test)
  
  function new(string name = "base_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    env_o = env::type_id::create("env_o", this);
  endfunction
 
  task run_phase(uvm_phase phase);
    phase.raise_objection(this);
    bseq = base_seq::type_id::create("bseq");
    bseq.l_seqr = env_o.agt.seqr;
    bseq.start(env_o.agt.seqr);
    phase.drop_objection(this);
  endtask
endclass

A GOOD packet is generated as mentioned below.

UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO sequences.sv(11) @ 0: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO driver.sv(15) @ 0: uvm_test_top.env_o.agt.drv [uvm_test_top.env_o.agt.drv] Driving pkt =
------------------------------
Name    Type      Size  Value
------------------------------
req     seq_item  -     @578 
 addr  integral  16    'he157
 data  integral  16    'h96b
 pkt   pkt_type  32    GOOD 
------------------------------

UVM_INFO /apps/vcsmx/vcs/Q-2020.03-SP1-1//etc/uvm-1.2/src/base/uvm_objection.svh(1276) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase

2. When err_test is executed (Run Options: +UVM_TESTNAME=err_test)

class err_test extends base_test;
  derived_seq_cb drvd_seq;
  `uvm_component_utils(err_test)
  
  function new(string name = "err_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction
  
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    drvd_seq = derived_seq_cb::type_id::create("drvd_seq", this);
  endfunction
  
  function void end_of_elaboration();
    super.end_of_elaboration();
    uvm_callbacks#(sequencer, seq_cb)::add(env_o.agt.seqr,drvd_seq);
  endfunction : end_of_elaboration
endclass

Using UVM callback, an error packet is generated as shown below.

UVM_INFO @ 0: reporter [RNTST] Running test err_test...
UVM_INFO sequences.sv(11) @ 0: uvm_test_top.env_o.agt.seqr@@bseq [base_seq] Base seq: Inside Body
UVM_INFO callbacks.sv(20) @ 0: reporter [drvd_seq] Inside modify_pkt method: Injecting error in the seq item
--------------------------------
Name    Type      Size  Value  
--------------------------------
req     seq_item  -     @580   
 addr  integral  16    'hffff 
 data  integral  16    'h96b  
 pkt   pkt_type  32    BAD_ERR1
--------------------------------
UVM_INFO driver.sv(15) @ 0: uvm_test_top.env_o.agt.drv [uvm_test_top.env_o.agt.drv] Driving pkt =
--------------------------------
Name    Type      Size  Value  
--------------------------------
req     seq_item  -     @580   
 addr  integral  16    'hffff 
 data  integral  16    'h96b  
 pkt   pkt_type  32    BAD_ERR1
--------------------------------

UVM_INFO /apps/vcsmx/vcs/Q-2020.03-SP1-1//etc/uvm-1.2/src/base/uvm_objection.svh(1276) @ 50: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase