UVM Tutorial for Candy Lovers – 29. Backdoor HDL Path

Our jelly-bean tasting business became very successful, so we decided to expand our business into partnership with another jelly-bean taster. During the process of the merger, however, we found that the partner had slightly different register structure than ours. We want to access the partner’s registers through the back door without modifying the register model we already have. But, how do we specify the HDL paths?

Partnership (DUT)

This is how the new DUT (jelly_bean_partnership) looks.

Partnership module (DUT)
Partnership module (DUT)

The partnership module includes our old jelly_bean_taster as well as the new jelly_bean_partner. We also added a new signal called taster_id, which selects either taster or partner, to the jelly_bean_if.

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
module jelly_bean_partnership( jelly_bean_if.slave_mp jb_if );
   import jelly_bean_pkg::*;
 
   jelly_bean_if jb_if0( jb_if.clk );
   jelly_bean_if jb_if1( jb_if.clk );
 
   jelly_bean_taster  taster ( jb_if0 );   jelly_bean_partner partner( jb_if1 ); 
   always @* begin
      jb_if0.flavor     = jb_if.flavor;
      jb_if1.flavor     = jb_if.flavor;
      jb_if0.color      = jb_if.color;
      jb_if1.color      = jb_if.color;
      jb_if0.sugar_free = jb_if.sugar_free;
      jb_if1.sugar_free = jb_if.sugar_free;
      jb_if0.sour       = jb_if.sour;
      jb_if1.sour       = jb_if.sour;
 
      if ( jb_if.taster_id == 0 ) begin
	 jb_if0.command = jb_if.command;
	 jb_if1.command = NO_OP;
	 jb_if.taste    = jb_if0.taste;
      end else begin
	 jb_if0.command = NO_OP;
	 jb_if1.command = jb_if.command;
	 jb_if.taste    = jb_if1.taste;
      end
   end // always @ *
 
endmodule: jelly_bean_partnership

Partner

The partner module does jelly-bean tasting in a similar way as the jelly_bean_taster. It has the equivalent registers as the jelly_bean_taster, but its structure is different. In particular:

  • The partner has one 5-bit consolidated color_and_flavor register (line 5).
  • The sour and sugar_free registers are within another module instance called extra (lines 37 and 38).
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
36
37
38
39
module jelly_bean_partner( jelly_bean_if.slave_mp jb_if );
   import jelly_bean_pkg::*;
 
   reg [1:0] taste;
   reg [4:0] color_and_flavor;   reg [1:0] command;
 
   jelly_bean_extra extra();
 
   initial begin
      color_and_flavor = 0;
      extra.sugar_free = 0;
      extra.sour       = 0;
      command          = 0;
      taste            = 0;
   end
 
   always @ ( posedge jb_if.clk ) begin
      command < = jb_if.command;
      if ( jb_if.command == JB_WRITE ) begin
	 color_and_flavor <= { jb_if.color, jb_if.flavor };
	 extra.sugar_free <= jb_if.sugar_free;
	 extra.sour       <= jb_if.sour;
      end else if ( jb_if.command == JB_READ ) begin
	 jb_if.taste <= #2ns taste;
      end
   end
 
   always @ ( posedge jb_if.clk ) begin
      if ( jb_if.flavor == CHOCOLATE && jb_if.sour ) taste <= YUCKY;
      else if ( jb_if.flavor != NO_FLAVOR )          taste <= YUMMY;
   end
 
endmodule: jelly_bean_partner
 
module jelly_bean_extra;
   reg sugar_free;   reg sour;endmodule: jelly_bean_extra

Register Model

The top-level register block (jelly_bean_partnership_reg_block) instantiates two jelly_bean_reg_blocks we created before; one (jb_reg_blocks[0]) for the taster and the other (jb_reg_block[1]) for the partner.

Top-level register model
Top-level register model

Then we set the HDL path of the jb_reg_blocks[0] to be "taster" (line 16), which matches the instance name of the jelly_bean_taster in the jelly_bean_partnership module (see the line 7 of the jelly_bean_partnership above). Similarly, we set the HDL path of the jb_reg_blocks[1] to be "partner" (line 21), which matches the instance name of the jelly_bean_partner in the jelly_bean_partnership module (see the line 8 of the jelly_bean_partnership above).

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
class jelly_bean_partnership_reg_block extends uvm_reg_block;
   `uvm_object_utils( jelly_bean_partnership_reg_block )
 
   rand jelly_bean_reg_block jb_reg_blocks[2];   uvm_reg_map               reg_map;
 
   function new( string name = "jelly_bean_partnership_reg_block" );
      super.new( .name( name ), .has_coverage( UVM_NO_COVERAGE ) );
   endfunction: new
 
   virtual function void build();
      reg_map = create_map( .name( "reg_map" ), .base_addr( 8'h00 ), 
                            .n_bytes( 1 ), .endian( UVM_LITTLE_ENDIAN ) );
 
      jb_reg_blocks[0] = jelly_bean_reg_block::type_id::create( "jb_reg_blocks[0]" );
      jb_reg_blocks[0].configure( .parent( this ), .hdl_path( "taster" ) );      jb_reg_blocks[0].build();
      reg_map.add_submap( .child_map( jb_reg_blocks[0].reg_map ), .offset( 0 ) );
 
      jb_reg_blocks[1] = jelly_bean_reg_block::type_id::create( "jb_reg_blocks[1]" );
      jb_reg_blocks[1].configure( .parent( this ), .hdl_path( "partner" ) );      jb_reg_blocks[1].build();
      reg_map.add_submap( .child_map( jb_reg_blocks[1].reg_map ), .offset( 2 ) );
   endfunction: build
endclass: jelly_bean_partnership_reg_block

Setting HDL Paths

Now we are ready to instantiate the top-level register model. Firstly, we set the HDL path of the top-level register block to be "top.dut" (line 18 of jelly_bean_base_test below) assuming we have the following top-level test-bench .

module top;
  import uvm_pkg::*;
 
  reg clk;
  jelly_bean_if          jb_if( clk );
  jelly_bean_partnership dut( jb_if ); // DUT 
  // ... omit ...
 
endmodule: top

Then, we set the HDL path slices to the partner’s recipe register (lines 22 to 24). Note that we cleared the HDL path of the jb_recipe_reg on the line 21 before setting the slices. This is because the reused jelly_bean_recipe_reg already has HDL path slices, which do not match the HDL paths of the partner (If you are interested, please see the lines 66 to 69 of the jelly_bean_recipe_reg in Register Access through the Back Door.).

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
class jelly_bean_base_test extends uvm_test;
   `uvm_component_utils( jelly_bean_base_test )
 
   jelly_bean_env                   jb_env;
   jelly_bean_env_config            jb_env_cfg;
   jelly_bean_agent_config          jb_agent_cfg;
   jelly_bean_partnership_reg_block jb_partnership_reg_block;
 
   function new( string name, uvm_component parent );
      super.new( name, parent );
   endfunction: new
 
   function void build_phase( uvm_phase phase );
      jelly_bean_recipe_reg jb_recipe_reg;
 
      super.build_phase( phase );
      jb_partnership_reg_block = jelly_bean_partnership_reg_block::type_id::create( "jb_partnership_reg_block" );
      jb_partnership_reg_block.configure( .hdl_path( "top.dut" ) );      jb_partnership_reg_block.build();
      jb_recipe_reg = jb_partnership_reg_block.jb_reg_blocks[1].jb_recipe_reg; // shorthand
      jb_recipe_reg.clear_hdl_path();      jb_recipe_reg.add_hdl_path_slice( .name( "color_and_flavor" ), .offset( 0 ), .size( 5 ) );      jb_recipe_reg.add_hdl_path_slice( .name( "extra.sugar_free" ), .offset( 5 ), .size( 1 ) );      jb_recipe_reg.add_hdl_path_slice( .name( "extra.sour"       ), .offset( 6 ), .size( 1 ) );      jb_partnership_reg_block.lock_model(); // finalize the address mapping
 
   // ... omit ...
 
endclass: jelly_bean_base_test

The following table summarizes the overall HDL paths we have defined for the partner’s recipe register.

Model jb_partnership_reg_block jb_reg_block[1] jb_recipe_reg
Path top.dut partner color_and_flavor
Path top.dut partner extra.sugar_free
Path top.dut partner extra.sour

The partner’s taste register uses the same HDL path (taste) we had already defined in the jelly_bean_taste_reg.

Model jb_partnership_reg_block jb_reg_block[1] jb_taste_reg
Path top.dut partner taste

FYI, the following tables summarize the overall HDL paths we have for the taster.

Model jb_partnership_reg_block jb_reg_block[0] jb_recipe_reg
Path top.dut taster flavor
Path top.dut taster color
Path top.dut taster sugar_free
Path top.dut taster sour
Model jb_partnership_reg_block jb_reg_block[0] jb_taste_reg
Path top.dut taster taste

Register Sequence

Let’s verify the HDL paths using the following sequence. We have three backdoor writes in the sequence (highlighted).

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
36
37
38
39
40
41
42
43
44
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_partnership_reg_block jb_partnership_reg_block;
      jelly_bean_reg_block             partner_reg_block;
      flavor_e                         flavor;
      color_e                          color;
      bit                              sugar_free;
      bit                              sour;
      uvm_status_e                     status;
      uvm_reg_data_t                   value;
 
      $cast( jb_partnership_reg_block, model );
      partner_reg_block = jb_partnership_reg_block.jb_reg_blocks[1]; // shorthand
 
      sugar_free = 0;
      sour       = 1;
 
      // back-door writes
      flavor = BLUEBERRY;
      color  = BLUE;
      poke_reg( partner_reg_block.jb_recipe_reg, status,                 { sour, sugar_free, color, flavor } ); // 'h5A      #10ns ;
 
      flavor = BUBBLE_GUM;
      color  = GREEN;
      write_reg( partner_reg_block.jb_recipe_reg, status,                  { sour, sugar_free, color, flavor }, UVM_BACKDOOR ); // 'h53      #10ns ;
 
      flavor = CHOCOLATE;
      color  = RED;
      partner_reg_block.jb_recipe_reg.write( status,         { sour, sugar_free, color, flavor }, UVM_BACKDOOR, .parent( this ) ); // 'h4C      #10ns ;
   endtask: body
 
endclass: jelly_bean_reg_sequence

Simulation

When you run a simulation, you should see the result like this:

# KERNEL: UVM_INFO /home/build/vlib1/vlib/uvm-1.2/src/reg/uvm_reg.svh(2820) @ 0:
 reporter [RegModel] Poked register "jb_partnership_reg_block.jb_reg_blocks[1].jb_recipe_reg": 'h000000000000005a# KERNEL: UVM_INFO @ 10: reporter [RegModel] Wrote register via DPI backdoor: 
 jb_partnership_reg_block.jb_reg_blocks[1].jb_recipe_reg=0x53# KERNEL: UVM_INFO @ 20: reporter [RegModel] Wrote register via DPI backdoor: 
 jb_partnership_reg_block.jb_reg_blocks[1].jb_recipe_reg=0x4c

I hope this article helped you to clarify the HDL path.

EDA Playground

You can view and run the code on EDA Playground.

2 thoughts on “UVM Tutorial for Candy Lovers – 29. Backdoor HDL Path”

  1. Hello Keisuke,

    All your tutorials are very helpful.

    In the file “jelly_bean_reg_sequence”, I am getting error in the line number 11 and 12. And the error is:

    # ** Error: jelly_bean_reg_sequence.sv(11): near “;”: syntax error, unexpected ‘;’, expecting ‘(‘
    # ** Error: jelly_bean_reg_sequence.sv(12): near “;”: syntax error, unexpected ‘;’, expecting ‘(‘

    Please help me to rectify this error. I am using the Questsim.

    Thanks
    Sunil

Leave a Reply