Jump to content

RAL. Mirroring registers with the same addresses


Recommended Posts

I have DUT which contains 2 registers ( A and B ) with the same addres. DUT also has the third register ( C ) which control to what register write: to A or to B.

Using RAL we have problems with mirroring registers A and B, because they have the same physical addres.
How caw we solve it?

I found a way which works:
1) give registers A and B unique addresses
2) make register adapter to change address using register C

Link to comment
Share on other sites

For your specific case, you might consider an alternative frontdoor path. With this method, you create a sequence that runs on--well, any sequencer really--and is assigned to the A & B registers using set_frontdoor. This sequence extends uvm_reg_frontdoor and contains an 'rw_info' variable. This rw_info variable holds the information of the transaction. Based on the value of C, the registers can then be manipulated like any other.

 

Consider:

uvm_reg chosen_reg;
uvm_reg_status status;

chosen_reg = (p_sequencer.reg_block.C.SELECTOR.get() == <A_SELECTED>)? 
              p_sequencer.reg_block.A :
              p_sequencer.reg_block.B;

if(rw_info.kind == WRITE)
  chosen_reg.write(status, rw_info.value[0]);
else begin
  uvm_reg_data_t value;
  rw_info.value = new[1];
  chosen_reg.mirror(status, value);
  rw_info.value[0] = value;
end

It's something like that.

 

Not to be too self-aggrandizing, but my book demonstrates this method more clearly:

http://tinyurl.com/AdvancedUVM.

or the e-book: http://tinyurl.com/AdvancedUVM-ebook.

Link to comment
Share on other sites

There's a section in the User Guide talking about indexed registers. Have a look at that.

 

 

For this comment, I think we can try to make it this way:

// -----------------------------------------
// Real DUT register
// way field controls which data register
// will be ascessed
// ------------------------------------------
class ctrl_reg extends uvm_reg;
  uvm_reg_field   some_field;
  uvm_reg_field  way; // if 0, access to data0 reg, if 1, access to data1 reg
endclass

// -----------------------------------------
// Virtual register, idx field is a handle
// to way field of ctrl_reg
// ------------------------------------------
class idx_reg extends uvm_reg;
  uvm_reg_field   idx;
endclass

// -----------------------------------------
// data0 and data1 share the same physical
// address
// ------------------------------------------
class data0_reg extends uvm_reg;
  uvm_reg_field field0;
endclass

class data1_reg extends uvm_reg;
  uvm_reg_field field0;
  uvm_reg_field field1;
endclass

// -----------------------------------------
// Indirect register for accesing data0 and
// data1 registers.
// The number of bits in each register in 
// the register array must be equal to n_bits 
// of this register.
// ------------------------------------------
class data_reg extends uvm_reg_indirect_data;
// No fields
endclass
class reg_model extends uvm_reg_block;
  ctrl_reg  ctrl;
  idx_reg   idx;
  data0_reg data0;
  data1_reg data1;
  data_reg  data;
  uvm_reg   reg_ar[];
  
  virtual function build();
    reg_ar = new[2];
    
    data0 = data0_reg::type_id::create();
    data0.configure(this);
    data.build();
 
    data1 = data1_reg::type_id::create();
    data1.configure(this);
    data.build();   
    
    reg_ar[0] = data0;
    reg_ar[1] = data1;

    ctrl = ctrl_reg::type_id::create();
    ctrl.configure(this);
    ctrl.build();

    idx = idx_reg::type_id::create();
    idx.configure(this);
    idx.build();
  
    idx.idx = ctrl.way;
    
    data = data_reg::type_id::create();
    data.configure(idx, reg_ar, this, null);
    data.build();
    
    default_map = create_map(““, 0, 4, UVM_BIG_ENDIAN);
    default_map.add_reg(ctrl, 0);
    default_map.add_reg(data, 4);

  endfunction
  
  endclass

P.S. this is just concept

Link to comment
Share on other sites

What you can also do is define your own "indirect data register" class that is more tailored to your situation:

class some_indirect_data_reg extends uvm_reg_indirect_data;
  ctrl_reg ctrl;
  data0_reg data0;
  data1_reg data1;

  // override configure
  function void configure (ctrl_reg ctrl,
                           data0_reg data0,
                           data1_reg data1,
                           uvm_reg_block blk_parent,
                           uvm_reg_file regfile_parent = null);
    // ...
    m_tbl = new[2];
    m_tbl[0] = data0;
    m_tbl[1] = data1;
  endfunction

  virtual function uvm_reg get_indirect_reg(string  fname = "",
                                            int     lineno = 0);
    int unsigned idx = ctrl.way.get_mirrored_value();
    return(m_tbl[idx]);
  endfunction

  // do prediction and starting stuff using 'get_indirect_reg'
endclass

Encapsulating it like this means less code in your register block where you instantiate your regs, which will make it more readable. Everything else is just implementation detail which should be hidden. It's also more compile-time safe, because you force the reg block to connect regs of the appropriate types (ctrl_reg, data0_reg and data1_reg) instead of the generic uvm_reg type.

 

Unfortunately, the UVM class ("uvm_reg_indirect_data") is poorly written and it can't easily be extended. Ideally it would have relied on calls to "get_indirect_reg(...)" in its own code to figure out which register to use for updating or for checking. This would have made your life much easier, because you would have only needed to override this virtual function and everything else would have worked. You'll have to spend a bit of effort to get it to work like this, because you'll need to override other functions too.

Link to comment
Share on other sites

  • 5 years later...

Thank you Zhiharev and Tudor.Timi. I was facing a similar problem, supporting device with a set of shadow registers which were selected with a single bit field in a control register. Based on your answers above I developed this derived class which extends uvm_reg_indirect_data.

 

typedef class ash_shadow_reg_ftdr_seq;

class ash_shadow_reg extends uvm_reg_indirect_data;
   `uvm_object_utils(ash_shadow_reg);

   uvm_reg_field  m_idx_fld;

   function new(string name = "ash_shadow_reg");
      super.new(.name(name), .n_bits(16), .has_cover(UVM_NO_COVERAGE));
   endfunction: new


   function void configure (uvm_reg_field shadow_en_fld,
                            uvm_reg normal_reg,
                            uvm_reg shadow_reg,
                            uvm_reg_block blk_parent,
                            uvm_reg_file regfile_parent = null);
      uvm_reg reg_pair[] = new[2];
      reg_pair[0] = normal_reg;
      reg_pair[1] = shadow_reg;
      m_idx = shadow_en_fld.get_parent();
      m_tbl = reg_pair;
      m_idx_fld = shadow_en_fld;
      uvm_reg::configure(blk_parent, regfile_parent, "");

      // Not testable using pre-defined sequences
      uvm_resource_db#(bit)::set({"REG::", get_full_name()},
                                 "NO_REG_TESTS", 1);

      // Add a frontdoor to each indirectly-accessed register
      // for every address map this register is in.
      foreach (m_maps[map]) begin
         add_frontdoors(map);
      end
   endfunction

   // Need to override this too because add_frontdoors is
   // local (not virtual) in uvm_reg_indirect
   virtual function void add_map(uvm_reg_map map);
      uvm_reg::add_map(map);
      add_frontdoors(map);
   endfunction

   local function void add_frontdoors(uvm_reg_map map);
      foreach (m_tbl[i]) begin
         ash_shadow_reg_ftdr_seq fd;
         if (m_tbl[i] == null) begin
            `uvm_error(get_full_name(),
                       $sformatf("Indirect register #%0d is NULL", i));
            continue;
         end
         fd = new(m_idx_fld, i, this);
         if (m_tbl[i].is_in_map(map))
           m_tbl[i].set_frontdoor(fd, map);
         else
           map.add_reg(m_tbl[i], -1, "RW", 1, fd);
      end
   endfunction

   virtual function void do_predict (uvm_reg_item      rw,
                                     uvm_predict_e     kind = UVM_PREDICT_DIRECT,
                                     uvm_reg_byte_en_t be = -1);
      if (m_idx_fld.get() >= m_tbl.size()) begin      // changed from uvm_reg_indirect
         `uvm_error(get_full_name(),
                    $sformatf({"Shadow field in %s has a value (%0d) greater than ",
                               "the maximum indirect register array size (%0d)"},
                              m_idx.get_full_name(), m_idx_fld.get(), m_tbl.size()));
         rw.status = UVM_NOT_OK;
         return;
      end

      //NOTE limit to 2**32 registers
      begin
         int unsigned idx = m_idx_fld.get();   // changed from uvm_reg_indirect
         m_tbl[idx].do_predict(rw, kind, be);
      end
   endfunction

   virtual function uvm_reg get_indirect_reg(string  fname = "",
                                             int     lineno = 0);
      int unsigned idx = m_idx_fld.get_mirrored_value();
      return(m_tbl[idx]);
   endfunction
endclass


class ash_shadow_reg_ftdr_seq extends uvm_reg_frontdoor;
   local uvm_reg_field m_addr_fld;
   local uvm_reg       m_data_reg;
   local int           m_idx;

   function new(uvm_reg_field addr_fld,
                int idx,
                uvm_reg data_reg);
      super.new("ash_shadow_reg_ftdr_seq");
      m_addr_fld = addr_fld;
      m_idx      = idx;
      m_data_reg = data_reg;
   endfunction: new

   virtual task body();
      uvm_reg      addr_reg;
      uvm_reg_item rw;

      $cast(rw,rw_info.clone());
      rw.element      = m_addr_fld;
      rw.element_kind = UVM_FIELD;
      rw.kind         = UVM_WRITE;
      rw.value[0]     = m_idx;

      addr_reg = m_addr_fld.get_parent();
      addr_reg.XatomicX(1);
      m_data_reg.XatomicX(1);

      m_addr_fld.do_write(rw);

      if (rw.status == UVM_NOT_OK)
        return;

      $cast(rw,rw_info.clone());
      rw.element = m_data_reg;

      if (rw_info.kind == UVM_WRITE)
        m_data_reg.do_write(rw);
      else begin
        m_data_reg.do_read(rw);
        rw_info.value[0] = rw.value[0];
      end

      addr_reg.XatomicX(0);
      m_data_reg.XatomicX(0);

      rw_info.status = rw.status;
   endtask
endclass

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...