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.
After reset, both the DUT and the register model have the following register values.
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:
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.
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 ofuvm_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. Theuvm_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.
Simulation
After registering the callback, the register model updates the value of the TASTE register, too, when the RECIPE register is written.
UVM_INFO sequences.svh(59) @ 20: uvm_test_top.jb_env.jb_agent.jb_seqr@@jb_reg_seq [body] taste=YUMMY |
You can view and run the code on EDA Playground.
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()’.
That’s a good point. Thank you for your comment. I implemented a callback (for the
flavor
field) using thepost_predict
as follows:And here is a new sequence that uses the callback.
As you know the
post_predict
is called by theuvm_reg_field::predict()
function, so a similar callback needs to be implemented for thesour
field too (not shown).