Jump to content

ashleywinn

Members
  • Posts

    2
  • Joined

  • Last visited

ashleywinn's Achievements

Member

Member (1/2)

0

Reputation

  1. 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
  2. In the UVM register model, if you create a register or a field which is wider than the bus width there is no way to control what order the multiple bus operations occur to update the register/field value. In my case I was using BIG_ENDIAN addressing and the register model would always write the largest address first. My design spec required that the largest address be written last. I searched in vain for a way to change this but ended up overriding the 'do_bus_write' function in 'uvm_reg_map'. If you've run into a similar issue, here's my workaround. reversed_reg_order_map.sv `ifndef __REVERSED_REG_ORDER_MAP_SV__ `define __REVERSED_REG_ORDER_MAP_SV__ class reversed_reg_order_map extends uvm_reg_map; `uvm_object_utils(reversed_reg_order_map) function new(string name="reversed_reg_order_map"); super.new(name); endfunction extern virtual task do_write_bus_ops(uvm_reg_bus_op bus_ops[$], uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); extern virtual task do_bus_write(uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); endclass task reversed_reg_order_map::do_write_bus_ops(uvm_reg_bus_op bus_ops[$], uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); foreach (bus_ops[i]) begin uvm_sequence_item bus_req; uvm_reg_bus_op rw_access; adapter.m_set_item(rw); bus_req = adapter.reg2bus(bus_ops[i]); adapter.m_set_item(null); if (bus_req == null) `uvm_fatal("RegMem",{"adapter [",adapter.get_name(),"] didnt return a bus transaction"}); bus_req.set_sequencer(sequencer); rw.parent.start_item(bus_req,rw.prior); if (rw.parent != null && i == 0) rw.parent.mid_do(rw); rw.parent.finish_item(bus_req); bus_req.end_event.wait_on(); if (adapter.provides_responses) begin uvm_sequence_item bus_rsp; uvm_access_e op; // TODO: need to test for right trans type, if not put back in q rw.parent.get_base_response(bus_rsp); adapter.bus2reg(bus_rsp,rw_access); end else begin adapter.bus2reg(bus_req,rw_access); end if (rw.parent != null && i == bus_ops.size()-1) rw.parent.post_do(rw); rw.status = rw_access.status; `uvm_info(get_type_name(), $sformatf("Wrote 'h%0h at 'h%0h via map \"%s\": %s...", bus_ops[i].data, bus_ops[i].addr, rw.map.get_full_name(), rw.status.name()), UVM_FULL) if (rw.status == UVM_NOT_OK) break; end endtask task reversed_reg_order_map::do_bus_write (uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); uvm_reg_addr_t addrs[$]; uvm_reg_map system_map = get_root_map(); int unsigned bus_width = get_n_bytes(); uvm_reg_byte_en_t byte_en = -1; uvm_reg_map_info map_info; int n_bits; int lsb; int skip; int unsigned curr_byte; int n_access_extra, n_access; int n_bits_init; uvm_reg_bus_op bus_ops[$]; Xget_bus_infoX(rw, map_info, n_bits_init, lsb, skip); addrs = map_info.addr; // `UVM_DA_TO_QUEUE(addrs,map_info.addr) // if a memory, adjust addresses based on offset if (rw.element_kind == UVM_MEM) foreach (addrs[i]) addrs[i] = addrs[i] + map_info.mem_range.stride * rw.offset; foreach (rw.value[val_idx]) begin: foreach_value uvm_reg_data_t value = rw.value[val_idx]; curr_byte = 0; /* calculate byte_enables */ if (rw.element_kind == UVM_FIELD) begin int temp_be; int idx; n_access_extra = lsb%(bus_width*8); n_access = n_access_extra + n_bits_init; temp_be = n_access_extra; value = value << n_access_extra; while(temp_be >= 8) begin byte_en[idx++] = 0; temp_be -= 8; end temp_be += n_bits_init; while(temp_be > 0) begin byte_en[idx++] = 1; temp_be -= 8; end byte_en &= (1<<idx)-1; for (int i=0; i<skip; i++) void'(addrs.pop_front()); while (addrs.size() > (n_bits_init/(bus_width*8) + 1)) void'(addrs.pop_back()); end curr_byte=0; n_bits= n_bits_init; foreach(addrs[i]) begin: foreach_addr uvm_reg_bus_op rw_access; uvm_reg_data_t data; data = (value >> (curr_byte*8)) & ((1'b1 << (bus_width * 8))-1); curr_byte += bus_width; `uvm_info(get_type_name(), $sformatf("Writing 'h%0h at 'h%0h via map \"%s\"...", data, addrs[i], rw.map.get_full_name()), UVM_FULL); if (rw.element_kind == UVM_FIELD) begin for (int z=0;z<bus_width;z++) rw_access.byte_en[z] = byte_en[curr_byte+z]; end rw_access.kind = rw.kind; rw_access.addr = addrs[i]; rw_access.data = data; rw_access.n_bits = (n_bits > bus_width*8) ? bus_width*8 : n_bits; rw_access.byte_en = byte_en; n_bits -= bus_width * 8; bus_ops.push_back(rw_access); end: foreach_addr foreach (addrs[i]) addrs[i] = addrs[i] + map_info.mem_range.stride; end: foreach_value // reverse the write order begin uvm_reg_bus_op reversed_bus_op_list[$]; foreach (bus_ops[i]) reversed_bus_op_list.push_front(bus_ops[i]); do_write_bus_ops(reversed_bus_op_list, rw, sequencer, adapter); end endtask `endif // `ifndef __REVERSED_REG_ORDER_MAP_SV__ Then I used a factory type override: set_type_override_by_type(uvm_reg_map::get_type(), reversed_reg_order_map::get_type());
×
×
  • Create New...