UVM Tutorial for Candy Lovers – 36. Register Callbacks

In some design, when one register is written, another register takes a new value. This article will explain how to model this behavior using a register callback.

Registers in Jelly Bean Taster

In Register Abstraction, we defined two registers; RECIPE and TASTE. The RECIPE register has four fields (sour, sugar_free, color, and flavor) and they are write-only. The TASTE register has one field (taste) and it is read-only.

Registers in Jelly Bean Taster
Registers in Jelly Bean Taster
After reset, both the DUT and the register model have the following register values.
Register Values after Reset
Register Values after Reset
Now let’s write the RECIPE register with a sour-green apple recipe (line 24).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class jelly_bean_reg_sequence extends uvm_reg_sequence;
   `uvm_object_utils( jelly_bean_reg_sequence )
 
   function new( string name = "" );
      super.new( name );
   endfunction: new
 
   virtual task body();
      jelly_bean_reg_block       jb_reg_block;
      jelly_bean_types::flavor_e flavor;
      jelly_bean_types::color_e  color;
      bit                        sugar_free;
      bit                        sour;
      jelly_bean_types::taste_e  taste;
      uvm_status_e               status;
      uvm_reg_data_t             value;
 
      $cast( jb_reg_block, model );
      flavor     = jelly_bean_types::APPLE;
      color      = jelly_bean_types::GREEN;
      sugar_free = 0;
      sour       = 1;
 
      jb_reg_block.jb_recipe_reg.write( status, { sour, sugar_free, color, flavor } );      taste = jelly_bean_types::taste_e'( jb_reg_block.jb_taste_reg.taste.get_mirrored_value() );
      `uvm_info( "body", $sformatf( "taste=%s", taste.name() ), UVM_NONE )
   endtask: body    
endclass: jelly_bean_reg_sequence

After writing the register, the register values in the DUT become like this:

Register Values in the DUT after Write
Register Values in the DUT after Write
Note that the DUT also updated the TASTE register in response to the recipe write. However, the TASTE register in the register model remains NO_TASTE as you see in the log, because the model does not know how to update the TASTE register.
Register Values in the Model after Write
Register Values in the Model after Write
UVM_INFO sequences.svh(59) @ 20: uvm_test_top.jb_env.jb_agent.jb_seqr@@jb_reg_seq [body] taste=NO_TASTE

Defining a Register Callback

In order to update the TASTE register, we are going to define a callback class for the RECIPE register (jelly_bean_recipe_reg_callback). The callback updates the TASTE register every time the RECIPE register is written. We extend the uvm_reg_cbs class (line 1), which defines pre_write, post_write, pre_read, and post_read tasks. As the names suggest, the pre_write is called before a register write operation, the post_write is called after a write operation, the pre_read is called before a read operation, and the post_read is called after a read operation.

In this article, we define the post_write task only (line 10). When the write task of the uvm_reg is called, the task creates a uvm_reg_item object and calls the post_write. The write value is stored in the value[0] of the uvm_reg_item object. The lines 14 and 15 extract some field values from the register. Finally, the task updates the taste field of the TASTE register based on the written values (lines 18 to 21).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class jelly_bean_recipe_reg_callback extends uvm_reg_cbs;  `uvm_object_utils( jelly_bean_recipe_reg_callback )
 
  jelly_bean_taste_reg jb_taste_reg;
 
  function new( string name = "jelly_bean_recipe_reg_callback" );
    super.new( name );
  endfunction: new
 
  virtual task post_write( uvm_reg_item rw );    jelly_bean_types::flavor_e flavor;
    bit                        sour;
 
    flavor = jelly_bean_types::flavor_e'( rw.value[0][2:0] ); // rw.value[0] holds the written value    sour   = rw.value[0][6];    `uvm_info( "post_write", $sformatf( "%s, flavor=%s sour=%b", rw.convert2string(), flavor.name(), sour ), UVM_DEBUG )
 
    if ( flavor == jelly_bean_types::CHOCOLATE && sour )      assert( jb_taste_reg.taste.predict( jelly_bean_types::YUCKY ) );    else      assert( jb_taste_reg.taste.predict( jelly_bean_types::YUMMY ) );  endtask: post_write
 
endclass: jelly_bean_recipe_reg_callback

Using the Register Callback

Let’s update the jelly_bean_reg_sequence we saw earlier to use the callback. The lines 26 and 27 create a callback object and let it know the TASTE register. The line 28 adds the callback to the RECIPE register.

uvm_reg_cb on the line 28 is nothing but a convenience type definition of uvm_callbacks:

typedef uvm_callbacks#(uvm_reg, uvm_reg_cbs) uvm_reg_cb;

Please do not confuse it with uvm_reg_cbs that we used in the previous section. The uvm_callbacks class adds the given callback object to the given object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class jelly_bean_reg_sequence extends uvm_reg_sequence;
   `uvm_object_utils( jelly_bean_reg_sequence )
 
   function new( string name = "" );
      super.new( name );
   endfunction: new
 
   virtual task body();
      jelly_bean_reg_block       jb_reg_block;
      jelly_bean_types::flavor_e flavor;
      jelly_bean_types::color_e  color;
      bit                        sugar_free;
      bit                        sour;
      jelly_bean_types::taste_e  taste;
      uvm_status_e               status;
      uvm_reg_data_t             value;
 
      jelly_bean_recipe_reg_callback jb_recipe_reg_cb;
 
      $cast( jb_reg_block, model );
      flavor     = jelly_bean_types::APPLE;
      color      = jelly_bean_types::GREEN;
      sugar_free = 0;
      sour       = 1;
 
      jb_recipe_reg_cb = jelly_bean_recipe_reg_callback::type_id::create( "jb_recipe_reg_cb" );      jb_recipe_reg_cb.jb_taste_reg = jb_reg_block.jb_taste_reg;      uvm_reg_cb::add( jb_reg_block.jb_recipe_reg, jb_recipe_reg_cb ); 
      jb_reg_block.jb_recipe_reg.write( status, { sour, sugar_free, color, flavor } );
      taste = jelly_bean_types::taste_e'( jb_reg_block.jb_taste_reg.taste.get_mirrored_value() );
      `uvm_info( "body", $sformatf( "taste=%s", taste.name() ), UVM_NONE )
   endtask: body
 
endclass: jelly_bean_reg_sequence

The following diagram shows the classes used in this article. UVM classes are in pink, whereas the jelly-bean classes are in green.

The Classes Used in This Article
The Classes Used in This Article

Simulation

After registering the callback, the register model updates the value of the TASTE register, too, when the RECIPE register is written.

Register Values in the DUT after Write
Register Values in the Model after Write
UVM_INFO sequences.svh(59) @ 20: uvm_test_top.jb_env.jb_agent.jb_seqr@@jb_reg_seq [body] taste=YUMMY

EDA Playground

You can view and run the code on EDA Playground.

2 thoughts on “UVM Tutorial for Candy Lovers – 36. Register Callbacks”

  1. You’re going to have a bad time if you use ‘pre_write()’ like this and want to do any vertical reuse. The ‘pre_write()’ callback is only called after a ‘wriite()’ on the register. In a (sub-) system context register writes are going to be triggered by other design units and not by your testbench. The most versatile way is to use ‘post_predict()’.

    1. That’s a good point. Thank you for your comment. I implemented a callback (for the flavor field) using the post_predict as follows:

      class jelly_bean_recipe_flavor_callback extends uvm_reg_cbs;
        `uvm_object_utils( jelly_bean_recipe_flavor_callback )
       
        jelly_bean_taste_reg jb_taste_reg;
       
        function new( string name = "jelly_bean_recipe_flavor_callback" );
          super.new( name );
        endfunction: new
       
        virtual function void post_predict( input uvm_reg_field  fld,
                                            input uvm_reg_data_t previous,
                                            inout uvm_reg_data_t value,
                                            input uvm_predict_e  kind,
                                            input uvm_path_e     path,
                                            input uvm_reg_map    map );
          jelly_bean_recipe_reg      jb_recipe_reg;
          jelly_bean_types::flavor_e flavor;
          bit                        sour;
       
          flavor = jelly_bean_types::flavor_e'( value );
          $cast( jb_recipe_reg, fld.get_parent() );
          sour = jb_recipe_reg.sour.get();
          `uvm_info( "post_predict", $sformatf( "%s flavor=%s sour=%b", 
                                                jb_recipe_reg.convert2string(), flavor.name(), sour ), UVM_DEBUG )
          if ( kind == UVM_PREDICT_WRITE ) begin
            if ( flavor == jelly_bean_types::CHOCOLATE && sour )
              assert( jb_taste_reg.taste.predict( jelly_bean_types::YUCKY ) );
            else
              assert( jb_taste_reg.taste.predict( jelly_bean_types::YUMMY ) );    
          end
       
        endfunction: post_predict
       
      endclass: jelly_bean_recipe_flavor_callback

      And here is a new sequence that uses the callback.

      class jelly_bean_reg_sequence extends uvm_reg_sequence;
         `uvm_object_utils( jelly_bean_reg_sequence )
       
         function new( string name = "" );
           super.new( name );
         endfunction: new
       
         virtual task body();
           jelly_bean_reg_block       jb_reg_block;
           jelly_bean_types::flavor_e flavor;
           jelly_bean_types::color_e  color;
           bit                        sugar_free;
           bit                        sour;
           jelly_bean_types::taste_e  taste;
           uvm_status_e               status;
           uvm_reg_data_t             value;
       
           jelly_bean_recipe_flavor_callback jb_recipe_flavor_cb;
       
           $cast( jb_reg_block, model );
           flavor     = jelly_bean_types::APPLE;
           color      = jelly_bean_types::GREEN;
           sugar_free = 0;
           sour       = 1;
       
           jb_recipe_flavor_cb = jelly_bean_recipe_flavor_callback::type_id::create( "jb_recipe_flavor_cb" );
           jb_recipe_flavor_cb.jb_taste_reg = jb_reg_block.jb_taste_reg;
           uvm_reg_field_cb::add( jb_reg_block.jb_recipe_reg.flavor, jb_recipe_flavor_cb ); // use uvm_reg_field_cb, not uvm_reg_cb
       
           jb_reg_block.jb_recipe_reg.write( status, { sour, sugar_free, color, flavor } );
           taste = jelly_bean_types::taste_e'( jb_reg_block.jb_taste_reg.taste.get_mirrored_value() );
           `uvm_info( "body", $sformatf( "taste=%s", taste.name() ), UVM_NONE )
         endtask: body
       
      endclass: jelly_bean_reg_sequence

      As you know the post_predict is called by the uvm_reg_field::predict() function, so a similar callback needs to be implemented for the sour field too (not shown).

Leave a Reply