zhiharev Posted December 23, 2015 Report Share Posted December 23, 2015 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 addresses2) make register adapter to change address using register C zhiharev 1 Quote Link to comment Share on other sites More sharing options...
tudor.timi Posted December 23, 2015 Report Share Posted December 23, 2015 There's a section in the User Guide talking about indexed registers. Have a look at that. Quote Link to comment Share on other sites More sharing options...
bhunter1972 Posted December 23, 2015 Report Share Posted December 23, 2015 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. Quote Link to comment Share on other sites More sharing options...
zhiharev Posted December 28, 2015 Author Report Share Posted December 28, 2015 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 Quote Link to comment Share on other sites More sharing options...
tudor.timi Posted December 29, 2015 Report Share Posted December 29, 2015 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. Quote Link to comment Share on other sites More sharing options...
ashleywinn Posted August 20, 2021 Report Share Posted August 20, 2021 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 Quote Link to comment Share on other sites More sharing options...
york Posted March 21 Report Share Posted March 21 I think inside do_predict(), the idx should be from the mirrored value. int unsigned idx = m_idx_fld.get_mirrored_value(); Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.