zhiharev

RAL. Mirroring registers with the same addresses

5 posts in this topic

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

zhiharev likes this

Share this post


Link to post
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.

Share this post


Link to post
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

Share this post


Link to post
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.

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