UVM Tutorial for Candy Lovers – 24. Register Access through the Back Door

This post will add back-door access to the registers defined in Register Abstraction. With a few additional lines of code, you can access the registers through the back door.

DUT

We use the same DUT (jelly_bean_taster) as defined in Register Abstraction. The DUT has two registers as shown below.

DUT Registers

DUT Registers

The DUT defines each field of the registers as reg (lines 4 to 8).

module jelly_bean_taster( jelly_bean_if.slave_mp jb_if );
  import jelly_bean_pkg::*;
 
  reg [1:0] taste;  // TASTE  register
  reg [2:0] flavor; // RECIPE register
  reg [1:0] color;
  reg       sugar_free;
  reg       sour;
 
  reg [1:0] command;
 
  initial begin
    flavor     = 0;
    color      = 0;
    sugar_free = 0;
    sour       = 0;
    command    = 0;
    taste      = 0;
  end
 
  always @ ( posedge jb_if.clk ) begin
    if ( jb_if.command == JB_WRITE ) begin
      flavor     < = jb_if.flavor;
      color      <= jb_if.color;
      sugar_free <= jb_if.sugar_free;
      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_taster

Testbench

The top-level testbench instantiates the jelly_bean_taster as the dut (line 6).

module top;
  import uvm_pkg::*;
 
  reg clk;
  jelly_bean_if     jb_if( clk );
  jelly_bean_taster dut( jb_if ); // DUT
 
  initial begin // clock
    clk = 1;
    forever #5ns clk = ! clk;
  end
 
  initial begin // waveform
    $dumpfile( "dump.vcd" );
    $dumpvars( 0, top );
  end
 
  initial begin
    uvm_config_db#( virtual jelly_bean_if )::set( .cntxt( null ),
                                                  .inst_name( "uvm_test_top*" ),
                                                  .field_name( "jb_if" ),
                                                  .value( jb_if ) );
    run_test();
  end
endmodule: top

Register Block

To access the DUT registers through the back door, we need to inform the register block about its corresponding HDL path (line 27). In our case, the hierarchical HDL path corresponding to the register block is "top.dut".

class jelly_bean_reg_block extends uvm_reg_block;
  `uvm_object_utils( jelly_bean_reg_block )
 
  rand jelly_bean_recipe_reg jb_recipe_reg;
  rand jelly_bean_taste_reg  jb_taste_reg;
  uvm_reg_map                reg_map;
 
  function new( string name = "jelly_bean_reg_block" );
    super.new( .name( name ), .has_coverage( UVM_NO_COVERAGE ) );
  endfunction: new
 
  virtual function void build();
    jb_recipe_reg = jelly_bean_recipe_reg::type_id::create( "jb_recipe_reg" );
    jb_recipe_reg.configure( .blk_parent( this ) );
    jb_recipe_reg.build();
 
    jb_taste_reg = jelly_bean_taste_reg::type_id::create( "jb_taste_reg" );
    jb_taste_reg.configure( .blk_parent( this ) );
    jb_taste_reg.build();
 
    reg_map = create_map( .name( "reg_map" ), .base_addr( 8'h00 ), 
                          .n_bytes( 1 ), .endian( UVM_LITTLE_ENDIAN ) );
    reg_map.add_reg( .rg( jb_recipe_reg ), .offset( 8'h00 ), .rights( "WO" ) );
    reg_map.add_reg( .rg( jb_taste_reg  ), .offset( 8'h01 ), .rights( "RO" ) );
 
    // for back-door access
    add_hdl_path( .path( "top.dut" ) );
 
    lock_model(); // finalize the address mapping
  endfunction: build
endclass: jelly_bean_reg_block

Registers

We also need to inform each register abstraction class about the HDL path to the register field (line 31). In our case, the taste register field corresponds to the taste reg in the DUT.

class jelly_bean_taste_reg extends uvm_reg;
  `uvm_object_utils( jelly_bean_taste_reg )
 
  rand uvm_reg_field taste;
 
  //----------------------------------------------------------------------------
  // Function: new
  //----------------------------------------------------------------------------
 
  function new( string name = "jelly_bean_taste_reg" );
     super.new( .name( name ), .n_bits( 2 ), .has_coverage( UVM_NO_COVERAGE ) );
  endfunction: new
 
  //----------------------------------------------------------------------------
  // Function: build
  //----------------------------------------------------------------------------
 
  virtual function void build();
    taste = uvm_reg_field::type_id::create( "taste" );
    taste.configure( .parent                 ( this ), 
                     .size                   ( 2    ), 
                     .lsb_pos                ( 0    ), 
                     .access                 ( "RO" ), 
                     .volatile               ( 1    ),
                     .reset                  ( 0    ), 
                     .has_reset              ( 1    ), 
                     .is_rand                ( 0    ), 
                     .individually_accessible( 0    ) );
 
    // for back-door access
    add_hdl_path_slice( .name( "taste" ), .offset( 0 ), .size( 2 ) );
  endfunction: build
endclass: jelly_bean_taste_reg

We set the HDL paths to the RECIPE register, too (lines 66 to 69). In this case, we add four HDL paths (one path per DUT reg).

class jelly_bean_recipe_reg extends uvm_reg;
  `uvm_object_utils( jelly_bean_recipe_reg )
 
  rand uvm_reg_field flavor;
  rand uvm_reg_field color;
  rand uvm_reg_field sugar_free;
  rand uvm_reg_field sour;
 
  constraint flavor_color_con {
    flavor.value != NO_FLAVOR;
    flavor.value == APPLE     -> color.value != BLUE;
    flavor.value == BLUEBERRY -> color.value == BLUE;
    flavor.value < = CHOCOLATE;
  }
 
  function new( string name = "jelly_bean_recipe_reg" );
    super.new( .name( name ), .n_bits( 7 ), .has_coverage( UVM_NO_COVERAGE ) );
  endfunction: new
 
  virtual function void build();
    flavor = uvm_reg_field::type_id::create( "flavor" );
    flavor.configure( .parent                 ( this ), 
                      .size                   ( 3    ), 
                      .lsb_pos                ( 0    ), 
                      .access                 ( "WO" ), 
                      .volatile               ( 0    ),
                      .reset                  ( 0    ), 
                      .has_reset              ( 1    ), 
                      .is_rand                ( 1    ), 
                      .individually_accessible( 0    ) );
 
    color = uvm_reg_field::type_id::create( "color" );
    color.configure( .parent                 ( this ), 
                     .size                   ( 2    ), 
                     .lsb_pos                ( 3    ), 
                     .access                 ( "WO" ), 
                     .volatile               ( 0    ),
                     .reset                  ( 0    ), 
                     .has_reset              ( 1    ), 
                     .is_rand                ( 1    ), 
                     .individually_accessible( 0    ) );
 
    sugar_free = uvm_reg_field::type_id::create( "sugar_free" );
    sugar_free.configure( .parent                 ( this ), 
                          .size                   ( 1    ), 
                          .lsb_pos                ( 5    ), 
                          .access                 ( "WO" ), 
                          .volatile               ( 0    ),
                          .reset                  ( 0    ), 
                          .has_reset              ( 1    ), 
                          .is_rand                ( 1    ), 
                          .individually_accessible( 0    ) );
 
    sour = uvm_reg_field::type_id::create( "sour" );
    sour.configure( .parent                 ( this ), 
                    .size                   ( 1    ), 
                    .lsb_pos                ( 6    ), 
                    .access                 ( "WO" ), 
                    .volatile               ( 0    ),
                    .reset                  ( 0    ), 
                    .has_reset              ( 1    ), 
                    .is_rand                ( 1    ), 
                    .individually_accessible( 0    ) );
 
    // for back-door access
    add_hdl_path_slice( .name( "flavor"     ), .offset( 0 ), .size( 3 ) );
    add_hdl_path_slice( .name( "color"      ), .offset( 3 ), .size( 2 ) );
    add_hdl_path_slice( .name( "sugar_free" ), .offset( 5 ), .size( 1 ) );
    add_hdl_path_slice( .name( "sour"       ), .offset( 6 ), .size( 1 ) );
  endfunction: build
endclass: jelly_bean_recipe_reg

That’s about all you need. Let’s test the back door.

Register Sequence

This sequence demonstrates several ways to access the registers through the back door. As a refresher, we access the registers through the front door first.

  1. The line 24 uses the write_reg task of the uvm_reg_sequence class to write to the RECIPE register.
  2. The line 27 uses the read_reg task of the uvm_reg_sequence class to read from the TASTE register.

Then, we write the RECIPE register through the back door in three different ways.

  1. The line 32 uses the poke_reg task of the uvm_reg_sequence class.
  2. The line 36 uses the write_reg task of the uvm_reg_sequence class with the UVM_BACKDOOR option.
  3. The line 41 uses the write task of the uvm_reg class with the UVM_BACKDOOR option.

Similarly, we read the TASTE register through the back door in three different ways.

  1. The line 46 uses the peek_reg task of the uvm_reg_sequence class.
  2. The line 49 uses the read_reg task of the uvm_reg_sequence class with the UVM_BACKDOOR option.
  3. The line 52 uses the read task of the uvm_reg class with the UVM_BACKDOOR option.
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;
    flavor_e             flavor;
    color_e              color;
    bit                  sugar_free;
    bit                  sour;
    uvm_status_e         status;
    uvm_reg_data_t       value;
 
    $cast( jb_reg_block, model );
    flavor     = APPLE;
    color      = GREEN;
    sugar_free = 0;
    sour       = 1;
 
    // front-door write
    write_reg( jb_reg_block.jb_recipe_reg, status, { sour, sugar_free, color, flavor } ); 
 
    // front-door read
    read_reg( jb_reg_block.jb_taste_reg, status, value );
    #20ns ;
 
    // back-door writes
    flavor = BLUEBERRY;
    poke_reg( jb_reg_block.jb_recipe_reg, status, { sour, sugar_free, color, flavor } ); 
    #10ns ;
 
    flavor = BUBBLE_GUM;
    write_reg( jb_reg_block.jb_recipe_reg, status, { sour, sugar_free, color, flavor },
               UVM_BACKDOOR ); 
    #10ns ;
 
    flavor = CHOCOLATE;
    jb_reg_block.jb_recipe_reg.write( status, { sour, sugar_free, color, flavor },
                                      UVM_BACKDOOR, .parent( this ) );
    #10ns ;
 
    // back-door reads
    peek_reg( jb_reg_block.jb_taste_reg, status, value );
    assert( value == YUMMY );
 
    read_reg( jb_reg_block.jb_taste_reg, status, value, UVM_BACKDOOR );
    assert( value == YUMMY );
 
    jb_reg_block.jb_taste_reg.read( status, value, UVM_BACKDOOR, .parent( this ) );
    assert( value == YUMMY );
    #10ns ;
  endtask: body
 
endclass: jelly_bean_reg_sequence

Simulation

Here is an annotated waveform. The front-door access uses the jb_if, whereas the back-door access directly updates the register value of the DUT. Note that back-door writing CHOCOLATE to the flavor field does not update the taste field of the DUT even though the DUT is supposed to respond YUCKY to the combination of sour and CHOCOLATE. This is because the DUT updates the taste field in response to the value on the jb_if, but not to the internal RECIPE register values.

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页