Jump to content
markeduda

Purpose of uvm_reg_map::backdoor() and Related Warning with Backdoor Write of UVM_MEM

Recommended Posts

I recently came upon an issue where attempting to write a memory via a backdoor yields a warning similar to the following:

"reporter [RegModel] Memory 'tst_reg_blk.test_mem' is not contained within map 'Backdoor' (called from get_access())"

In addition to the warning, it appears that due rw.map being overwritten (as explained in the second link), Read-Only memories can be written to via the backdoor, since get_access end up returning the default value of "RW".  This appears to be in conflict with the Spec:

Quote

18.6.5.1 write

[...]

The write may be performed using either front-door or back-door operations (as defined by path). If back
door is specified, the effect of writing the memory through a physical access is mimicked. For example,
read-only memory will remain unchanged.

Researching this warning turned up a couple prior forum posts from 2014:

http://forums.accellera.org/topic/2104-question-about-backdoor-map/

http://forums.accellera.org/topic/2107-problem-with-backdoor-access-to-uvm_mem/

Neither of which had an associated answer.

 

 I came up with a test case to reproduce the warning:

//#######################################################################
// Backdoor accesses to a UVM_MEM that is a member of more than 1 map
// causes a UVM_WARNING that that the memory is not contained within
// the 'Backdoor' register map.
//
// This appears to happen due to the uvm_mem::Xcheck_accessX call in
// uvm_mem::do_write, which replaces rw.map with the pseudo-map 'Backdoor'.
// This replacement precedes the get_access(rw.map) call later in do_write. 
// The warning is generated when map membership is checked in get_local_map
// which is called from get_access in the case that there is > 1 maps to
// which the memory belongs.
//
// Below is a test case that reproduces the warning, as well as a workaround
// that replaces the overwritten map (if WORKAROUND is defined).
//#######################################################################

package test_pkg;
  import uvm_pkg::*;
  `include "uvm_macros.svh"

   typedef class basic_test;
   typedef class test_env;
   typedef class test_reg_block;
   typedef class test_mem_class;
   
  //#########################################
  // Test
  //#########################################
  class basic_test extends uvm_test;
    test_env env;
     
    `uvm_component_utils(basic_test)
     
    //---- New Function ----
    function new(string name = "basic_test", uvm_component parent);
      super.new(name, parent);
    endfunction : new
     
    //---- Build Phase ----
    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      env = test_env::type_id::create("env", this);
    endfunction
     
    //---- Run Phase ----
    virtual task run_phase(uvm_phase phase);
      uvm_reg_data_t data   = 'hA5;
      uvm_reg_addr_t offset = 'h0000;
      uvm_status_e   status; 
      phase.raise_objection(this, "Starting Test");

      `uvm_info(get_type_name(), "Got into Test Run_Phase.", UVM_LOW)
       
      `uvm_info(get_type_name(), $sformatf("Writing 0x%h.", data), UVM_LOW) 
      //This causes a warning if WORKAROUND is not defined
      env.tst_reg_blk.test_mem.write(.status(status),
                                     .offset(offset),
                                     .value(data),
                                     .path(UVM_BACKDOOR),
                                     .map(env.tst_reg_blk.test_reg_map),
                                     .parent(null),
                                     .prior(-1),
                                     .extension(null),
                                     .fname(""),
                                     .lineno(0));
      data = 'h0;
      //Warning does not occur on read
      env.tst_reg_blk.test_mem.read( .status(status),
                                     .offset(offset),
                                     .value(data),
                                     .path(UVM_BACKDOOR),
                                     .map(env.tst_reg_blk.test_reg_map),
                                     .parent(null),
                                     .prior(-1),
                                     .extension(null),
                                     .fname(""),
                                     .lineno(0));
      `uvm_info(get_type_name(), $sformatf("Read 0x%h.", data), UVM_LOW)
      phase.drop_objection(this, "Test Is Done");
    endtask : run_phase
  endclass
  
  //#########################################
  // Basic Environment
  //#########################################
  class test_env extends uvm_env;
    test_reg_block tst_reg_blk;
     
    `uvm_component_utils(test_env)
    
    //---- New Function ----
    function new(input string name, input uvm_component parent=null);
      super.new(name,parent);
    endfunction
     
    //---- Build Phase ----
    function void build_phase(uvm_phase phase);
       super.build_phase(phase);
       tst_reg_blk = test_reg_block::type_id::create("tst_reg_blk");
       tst_reg_blk.configure();
       tst_reg_blk.setup();
       tst_reg_blk.set_hdl_path_root("test_tb");
    endfunction : build_phase
  endclass
  
  //#########################################
  // Register Block Class
  //#########################################
  class test_reg_block extends uvm_reg_block;  
    uvm_reg_map    test_reg_map;
    uvm_reg_map    test_reg_map_2;
    test_mem_class test_mem;
     
    `uvm_object_utils(test_reg_block)
  
    //---- New Function ----
    function new(input string name="test_reg_block");
      super.new(name);
    endfunction // new
  
    //Create Map, Add/Configure Memory, Lock Model
     function void setup();
      test_reg_map = create_map("test_reg_map", 'h00000000, 4, 
                                UVM_LITTLE_ENDIAN, .byte_addressing(0));
	
      test_reg_map_2 = create_map("test_reg_map", 'h00000000, 4, 
                                  UVM_LITTLE_ENDIAN, .byte_addressing(0));
       
      test_mem = test_mem_class::type_id::create("test_mem", , get_full_name());
      test_mem.configure(this, "rtl_mem");
       
      test_reg_map.add_mem( .mem(test_mem),
                            .offset (32'h00000000),
                            .rights ("RO"),
  			                      .unmapped (0));
       
      test_reg_map_2.add_mem( .mem(test_mem),
                              .offset (32'h00000000),
                              .rights ("RO"),
  			                        .unmapped (0));
      this.lock_model();
    endfunction // setup
  endclass
  
  //#########################################
  // Register Block Class
  //#########################################
  class test_mem_class extends uvm_mem;
    `uvm_object_utils(test_mem_class)
  
    //---- New Function ----
    function new(input string name="test_mem_class");
      super.new(.name(name), .size(16), .n_bits(8), .access("RW"), 
                .has_coverage(UVM_NO_COVERAGE));
    endfunction // new

    //---- Bug Workaround ----
    // Override the do_write task grab a local copy of the map.
    // Then override pre_write (which occurs after Xcheck_accessX)
    // to reassign the map with the local copy in the case of a
    // backdoor access. 
    // 
    // An actual fix would involve changing the behaviour of
    // Xcheck_accessX, which is non-virtual.
    `ifdef WORKAROUND
    protected uvm_reg_map backup_map;
    virtual task do_write (uvm_reg_item rw);
      backup_map = rw.map;
      super.do_write(rw);
    endtask
    
    virtual task pre_write (uvm_reg_item rw);
      super.pre_write(rw);
      if(rw.path == UVM_BACKDOOR) begin
        if(backup_map == null)
          rw.map = rw.local_map;
        else
          rw.map = backup_map;
      end
    endtask
    `endif
  endclass
endpackage : test_pkg

//#########################################
// Basic TB
//#########################################
module test_tb();
  import uvm_pkg::*;
  import test_pkg::*;
  `include "uvm_macros.svh"
   
  reg [7:0] rtl_mem [16];
  
  initial run_test("basic_test");
endmodule

 

I was able to come up with a work-around, but I feel that I am missing something.

 

What is the actual purpose of uvm_reg_map::backdoor()? What is it's intended use case? Given that it has no parent block (thus not allowing memories or registers to be added), I do not know how it could be used. 

Thank you in advance for considering my question.

//----------------- Edit ------------------------

I realized after the fact that this is a pre-IEEE issue. I Don't know if the topic can be moved to the correct forum.

However, the question still stands; what is the intended purpose of the Backdoor pseudo-map?

Edited by markeduda
Request to move thread.

Share this post


Link to post
Share on other sites

I think that yours issues was fixed in new UVM 1800.2-2017

EDIT

Note that UVM do not support 8 bits wide memories mapped into map with 32 bits word when byte addressing is off

you will got on 1800.2:

KERNEL: UVM_WARNING uvm_reg_map.svh(1558) @ 0: reporter [UVM/REG/ADDR] this version of UVM does not properly support memories with a smaller word width than the enclosing map. map tst_reg_blk.test_reg_map has n_bytes=4 aub=4 while the mem has get_n_bytes 1. multiple memory words fall into one bus address. if that happens memory addressing will be unpacked.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×