// ------------------------------------------------------------- // Copyright 2010-2020 Mentor Graphics Corporation // Copyright 2014 Semifore // Copyright 2014-2017 Intel Corporation // Copyright 2004-2018 Synopsys, Inc. // Copyright 2010-2018 Cadence Design Systems, Inc. // Copyright 2010 AMD // Copyright 2014-2020 NVIDIA Corporation // Copyright 2017 Cisco Systems, Inc. // Copyright 2012 Accellera Systems Initiative // All Rights Reserved Worldwide // // Licensed under the Apache License, Version 2.0 (the // "License"); you may not use this file except in // compliance with the License. You may obtain a copy of // the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in // writing, software distributed under the License is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See // the License for the specific language governing // permissions and limitations under the License. // ------------------------------------------------------------- // // Class -- NODOCS -- uvm_reg_transaction_order_policy // Not in LRM. class uvm_reg_map_info; uvm_reg_addr_t offset; string rights; bit unmapped; uvm_reg_addr_t addr[]; uvm_reg_frontdoor frontdoor; uvm_reg_map_addr_range mem_range; // if set marks the uvm_reg_map_info as initialized, prevents using an uninitialized map (for instance if the model // has not been locked accidently and the maps have not been computed before) bit is_initialized; endclass // Class -- NODOCS -- uvm_reg_transaction_order_policy virtual class uvm_reg_transaction_order_policy extends uvm_object; function new(string name = "policy"); super.new(name); endfunction // Function -- NODOCS -- order // the order() function may reorder the sequence of bus transactions // produced by a single uvm_reg transaction (read/write). // This can be used in scenarios when the register width differs from // the bus width and one register access results in a series of bus transactions. // the first item (0) of the queue will be the first bus transaction (the last($) // will be the final transaction pure virtual function void order(ref uvm_reg_bus_op q[$]); endclass // Extends virtual class uvm_sequence_base so that it can be constructed: class uvm_reg_seq_base extends uvm_sequence_base; `uvm_object_utils(uvm_reg_seq_base) function new(string name = "uvm_reg_seq_base"); super.new(name); endfunction endclass //------------------------------------------------------------------------------ // // Class -- NODOCS -- uvm_reg_map // // :Address map abstraction class // // This class represents an address map. // An address map is a collection of registers and memories // accessible via a specific physical interface. // Address maps can be composed into higher-level address maps. // // Address maps are created using the // method. //------------------------------------------------------------------------------ // @uvm-ieee 1800.2-2017 auto 18.2.1 class uvm_reg_map extends uvm_object; `uvm_object_utils(uvm_reg_map) // info that is valid only if top-level map local uvm_reg_addr_t m_base_addr; local int unsigned m_n_bytes; local uvm_endianness_e m_endian; local bit m_byte_addressing; local uvm_object_wrapper m_sequence_wrapper; local uvm_reg_adapter m_adapter; local uvm_sequencer_base m_sequencer; local bit m_auto_predict; local bit m_check_on_read; local uvm_reg_block m_parent; local int unsigned m_system_n_bytes; local uvm_reg_map m_parent_map; local uvm_reg_addr_t m_submaps[uvm_reg_map]; // value=offset of submap at this level local string m_submap_rights[uvm_reg_map]; // value=rights of submap at this level local uvm_reg_map_info m_regs_info[uvm_reg]; local uvm_reg_map_info m_mems_info[uvm_mem]; local uvm_reg m_regs_by_offset[uvm_reg_addr_t]; // Use only in addition to above if a RO and a WO // register share the same address. local uvm_reg m_regs_by_offset_wo[uvm_reg_addr_t]; local uvm_mem m_mems_by_offset[uvm_reg_map_addr_range]; local uvm_reg_transaction_order_policy policy; extern /*local*/ function void Xinit_address_mapX(); static local uvm_reg_map m_backdoor; // @uvm-ieee 1800.2-2017 auto 18.2.2 static function uvm_reg_map backdoor(); if (m_backdoor == null) m_backdoor = new("Backdoor"); return m_backdoor; endfunction //---------------------- // Group -- NODOCS -- Initialization //---------------------- // @uvm-ieee 1800.2-2017 auto 18.2.3.1 extern function new(string name="uvm_reg_map"); // @uvm-ieee 1800.2-2017 auto 18.2.3.2 extern function void configure(uvm_reg_block parent, uvm_reg_addr_t base_addr, int unsigned n_bytes, uvm_endianness_e endian, bit byte_addressing = 1); // @uvm-ieee 1800.2-2017 auto 18.2.3.3 extern virtual function void add_reg (uvm_reg rg, uvm_reg_addr_t offset, string rights = "RW", bit unmapped=0, uvm_reg_frontdoor frontdoor=null); // @uvm-ieee 1800.2-2017 auto 18.2.3.4 extern virtual function void add_mem (uvm_mem mem, uvm_reg_addr_t offset, string rights = "RW", bit unmapped=0, uvm_reg_frontdoor frontdoor=null); // NOTE THIS isnt really true because one can add a map only to another map if the // map parent blocks are either the same or the maps parent is an ancestor of the submaps parent // also AddressUnitBits needs to match which means essentially that within a block there can only be one // AddressUnitBits // @uvm-ieee 1800.2-2017 auto 18.2.3.5 extern virtual function void add_submap (uvm_reg_map child_map, uvm_reg_addr_t offset); // Function -- NODOCS -- set_sequencer // // Set the sequencer and adapter associated with this map. This method // ~must~ be called before starting any sequences based on uvm_reg_sequence. // @uvm-ieee 1800.2-2017 auto 18.2.3.6 extern virtual function void set_sequencer (uvm_sequencer_base sequencer, uvm_reg_adapter adapter=null); // Function -- NODOCS -- set_submap_offset // // Set the offset of the given ~submap~ to ~offset~. // @uvm-ieee 1800.2-2017 auto 18.2.3.8 extern virtual function void set_submap_offset (uvm_reg_map submap, uvm_reg_addr_t offset); // Function -- NODOCS -- get_submap_offset // // Return the offset of the given ~submap~. // @uvm-ieee 1800.2-2017 auto 18.2.3.7 extern virtual function uvm_reg_addr_t get_submap_offset (uvm_reg_map submap); // Function -- NODOCS -- set_base_addr // // Set the base address of this map. // @uvm-ieee 1800.2-2017 auto 18.2.3.9 extern virtual function void set_base_addr (uvm_reg_addr_t offset); // @uvm-ieee 1800.2-2017 auto 18.2.3.10 extern virtual function void reset(string kind = "SOFT"); /*local*/ extern virtual function void add_parent_map(uvm_reg_map parent_map, uvm_reg_addr_t offset); /*local*/ extern virtual function void Xverify_map_configX(); /*local*/ extern virtual function void m_set_reg_offset(uvm_reg rg, uvm_reg_addr_t offset, bit unmapped); /*local*/ extern virtual function void m_set_mem_offset(uvm_mem mem, uvm_reg_addr_t offset, bit unmapped); //--------------------- // Group -- NODOCS -- Introspection //--------------------- // Function -- NODOCS -- get_name // // Get the simple name // // Return the simple object name of this address map. // // Function -- NODOCS -- get_full_name // // Get the hierarchical name // // Return the hierarchal name of this address map. // The base of the hierarchical name is the root block. // extern virtual function string get_full_name(); // @uvm-ieee 1800.2-2017 auto 18.2.4.1 extern virtual function uvm_reg_map get_root_map(); // @uvm-ieee 1800.2-2017 auto 18.2.4.2 extern virtual function uvm_reg_block get_parent(); // @uvm-ieee 1800.2-2017 auto 18.2.4.3 extern virtual function uvm_reg_map get_parent_map(); // @uvm-ieee 1800.2-2017 auto 18.2.4.4 extern virtual function uvm_reg_addr_t get_base_addr (uvm_hier_e hier=UVM_HIER); // Function -- NODOCS -- get_n_bytes // // Get the width in bytes of the bus associated with this map. If ~hier~ // is ~UVM_HIER~, then gets the effective bus width relative to the system // level. The effective bus width is the narrowest bus width from this // map to the top-level root map. Each bus access will be limited to this // bus width. // extern virtual function int unsigned get_n_bytes (uvm_hier_e hier=UVM_HIER); // Function -- NODOCS -- get_addr_unit_bytes // // Get the number of bytes in the smallest addressable unit in the map. // Returns 1 if the address map was configured using byte-level addressing. // Returns otherwise. // extern virtual function int unsigned get_addr_unit_bytes(); // @uvm-ieee 1800.2-2017 auto 18.2.4.7 extern virtual function uvm_endianness_e get_endian (uvm_hier_e hier=UVM_HIER); // @uvm-ieee 1800.2-2017 auto 18.2.4.8 extern virtual function uvm_sequencer_base get_sequencer (uvm_hier_e hier=UVM_HIER); // @uvm-ieee 1800.2-2017 auto 18.2.4.9 extern virtual function uvm_reg_adapter get_adapter (uvm_hier_e hier=UVM_HIER); // @uvm-ieee 1800.2-2017 auto 18.2.4.10 extern virtual function void get_submaps (ref uvm_reg_map maps[$], input uvm_hier_e hier=UVM_HIER); // @uvm-ieee 1800.2-2017 auto 18.2.4.11 extern virtual function void get_registers (ref uvm_reg regs[$], input uvm_hier_e hier=UVM_HIER); // @uvm-ieee 1800.2-2017 auto 18.2.4.12 extern virtual function void get_fields (ref uvm_reg_field fields[$], input uvm_hier_e hier=UVM_HIER); // @uvm-ieee 1800.2-2017 auto 18.2.4.13 extern virtual function void get_memories (ref uvm_mem mems[$], input uvm_hier_e hier=UVM_HIER); // @uvm-ieee 1800.2-2017 auto 18.2.4.14 extern virtual function void get_virtual_registers (ref uvm_vreg regs[$], input uvm_hier_e hier=UVM_HIER); // @uvm-ieee 1800.2-2017 auto 18.2.4.15 extern virtual function void get_virtual_fields (ref uvm_vreg_field fields[$], input uvm_hier_e hier=UVM_HIER); extern virtual function uvm_reg_map_info get_reg_map_info(uvm_reg rg, bit error=1); extern virtual function uvm_reg_map_info get_mem_map_info(uvm_mem mem, bit error=1); extern virtual function int unsigned get_size(); // @uvm-ieee 1800.2-2017 auto 18.2.4.16 extern virtual function int get_physical_addresses(uvm_reg_addr_t base_addr, uvm_reg_addr_t mem_offset, int unsigned n_bytes, ref uvm_reg_addr_t addr[]); // @uvm-ieee 1800.2-2017 auto 18.2.4.17 extern virtual function uvm_reg get_reg_by_offset(uvm_reg_addr_t offset, bit read = 1); // @uvm-ieee 1800.2-2017 auto 18.2.4.18 extern virtual function uvm_mem get_mem_by_offset(uvm_reg_addr_t offset); //------------------ // Group -- NODOCS -- Bus Access //------------------ // @uvm-ieee 1800.2-2017 auto 18.2.5.2 function void set_auto_predict(bit on=1); m_auto_predict = on; endfunction // @uvm-ieee 1800.2-2017 auto 18.2.5.1 function bit get_auto_predict(); return m_auto_predict; endfunction // @uvm-ieee 1800.2-2017 auto 18.2.5.3 function void set_check_on_read(bit on=1); m_check_on_read = on; foreach (m_submaps[submap]) begin submap.set_check_on_read(on); end endfunction // Function -- NODOCS -- get_check_on_read // // Gets the check-on-read mode setting for this map. // function bit get_check_on_read(); return m_check_on_read; endfunction // Task -- NODOCS -- do_bus_write // // Perform a bus write operation. // extern virtual task do_bus_write (uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); // Task -- NODOCS -- do_bus_read // // Perform a bus read operation. // extern virtual task do_bus_read (uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); // Task -- NODOCS -- do_write // // Perform a write operation. // extern virtual task do_write(uvm_reg_item rw); // Task -- NODOCS -- do_read // // Perform a read operation. // extern virtual task do_read(uvm_reg_item rw); extern function void Xget_bus_infoX (uvm_reg_item rw, output uvm_reg_map_info map_info, output int size, output int lsb, output int addr_skip); extern virtual function string convert2string(); extern virtual function uvm_object clone(); extern virtual function void do_print (uvm_printer printer); extern virtual function void do_copy (uvm_object rhs); //extern virtual function bit do_compare (uvm_object rhs, uvm_comparer comparer); //extern virtual function void do_pack (uvm_packer packer); //extern virtual function void do_unpack (uvm_packer packer); // @uvm-ieee 1800.2-2017 auto 18.2.5.5 function void set_transaction_order_policy(uvm_reg_transaction_order_policy pol); policy = pol; endfunction // @uvm-ieee 1800.2-2017 auto 18.2.5.4 function uvm_reg_transaction_order_policy get_transaction_order_policy(); return policy; endfunction // ceil() function local function automatic int unsigned ceil(int unsigned a, int unsigned b); int r = a / b; int r0 = a % b; return r0 ? (r+1): r; endfunction /* * translates an access from the current map ~this~ to an address ~base_addr~ (within the current map) with a * length of ~n_bytes~ into an access from map ~parent_map~. * if ~mem~ and ~mem_offset~ are supplied then a memory access is assumed * results: ~addr~ contains the set of addresses and ~byte_offset~ holds the number of bytes the data stream needs to be shifted * * this implementation assumes a packed data access */ extern virtual function int get_physical_addresses_to_map(uvm_reg_addr_t base_addr, uvm_reg_addr_t mem_offset, int unsigned n_bytes, // number of bytes ref uvm_reg_addr_t addr[], // array of addresses input uvm_reg_map parent_map, // translate till parent_map is the parent of the actual map or NULL if this is a root_map ref int unsigned byte_offset, input uvm_mem mem =null ); // performs all bus operations ~accesses~ generated from ~rw~ via adapter ~adapter~ on sequencer ~sequencer~ extern task perform_accesses(ref uvm_reg_bus_op accesses[$], input uvm_reg_item rw, input uvm_reg_adapter adapter, input uvm_sequencer_base sequencer); // performs all necessary bus accesses defined by ~rw~ on the sequencer ~sequencer~ utilizing the adapter ~adapter~ extern task do_bus_access (uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); // unregisters all content from this map recursively // it is NOT expected that this leads to a fresh new map // it rather removes all knowledge of this map from other objects // so that they can be reused with a fresh map instance // @uvm-ieee 1800.2-2017 auto 18.2.3.11 virtual function void unregister(); uvm_reg_block q[$]; uvm_reg_block::get_root_blocks(q); foreach(q[idx]) q[idx].set_lock(0); foreach(q[idx]) q[idx].unregister(this); foreach (m_submaps[map_]) map_.unregister(); m_submaps.delete(); m_submap_rights.delete(); foreach(m_regs_by_offset[i]) m_regs_by_offset[i].unregister(this); m_regs_by_offset.delete(); m_regs_by_offset_wo.delete(); m_mems_by_offset.delete(); m_regs_info.delete(); m_mems_info.delete(); m_parent_map =null; endfunction virtual function uvm_reg_map clone_and_update(string rights); if(m_parent_map!=null) `uvm_error("UVM/REG/CLONEMAPWITHPARENT","cannot clone a map which already has a parent") if(m_submaps.size() != 0) `uvm_error("UVM/REG/CLONEMAPWITHCHILDREN","cannot clone a map which already has children") begin uvm_reg_map m; uvm_reg_block b = get_parent(); uvm_reg qr[$]; uvm_mem qm[$]; m = b.create_map(get_name(),0,m_n_bytes,m_endian,m_byte_addressing); foreach(m_regs_by_offset[i]) begin uvm_reg rg=m_regs_by_offset[i]; uvm_reg_map_info info = get_reg_map_info(rg); m.add_reg(rg,info.offset,rights, info.unmapped, info.frontdoor); end foreach(m_mems_by_offset[i]) begin uvm_mem rg=m_mems_by_offset[i]; uvm_reg_map_info info = get_mem_map_info(rg); m.add_mem(rg,info.offset,rights, info.unmapped, info.frontdoor); end return m; end endfunction endclass: uvm_reg_map //--------------- // Initialization //--------------- // new function uvm_reg_map::new(string name = "uvm_reg_map"); super.new((name == "") ? "default_map" : name); m_auto_predict = 0; m_check_on_read = 0; endfunction // configure function void uvm_reg_map::configure(uvm_reg_block parent, uvm_reg_addr_t base_addr, int unsigned n_bytes, uvm_endianness_e endian, bit byte_addressing=1); m_parent = parent; m_n_bytes = n_bytes; m_endian = endian; m_base_addr = base_addr; m_byte_addressing = byte_addressing; endfunction: configure // add_reg function void uvm_reg_map::add_reg(uvm_reg rg, uvm_reg_addr_t offset, string rights = "RW", bit unmapped=0, uvm_reg_frontdoor frontdoor=null); if (m_regs_info.exists(rg)) begin `uvm_error("RegModel", {"Register '",rg.get_name(), "' has already been added to map '",get_name(),"'"}) return; end if (rg.get_parent() != get_parent()) begin `uvm_error("RegModel", {"Register '",rg.get_full_name(),"' may not be added to address map '", get_full_name(),"' : they are not in the same block"}) return; end rg.add_map(this); begin uvm_reg_map_info info = new; info.offset = offset; info.rights = rights; info.unmapped = unmapped; info.frontdoor = frontdoor; info.is_initialized=0; m_regs_info[rg]=info; end endfunction // m_set_reg_offset function void uvm_reg_map::m_set_reg_offset(uvm_reg rg, uvm_reg_addr_t offset, bit unmapped); if (!m_regs_info.exists(rg)) begin `uvm_error("RegModel", {"Cannot modify offset of register '",rg.get_full_name(), "' in address map '",get_full_name(), "' : register not mapped in that address map"}) return; end begin uvm_reg_map_info info = m_regs_info[rg]; uvm_reg_block blk = get_parent(); uvm_reg_map top_map = get_root_map(); uvm_reg_addr_t addrs[]; // if block is not locked, Xinit_address_mapX will resolve map when block is locked if (blk.is_locked()) begin // remove any existing cached addresses if (!info.unmapped) begin foreach (info.addr[i]) begin if (!top_map.m_regs_by_offset_wo.exists(info.addr[i])) begin top_map.m_regs_by_offset.delete(info.addr[i]); end else begin if (top_map.m_regs_by_offset[info.addr[i]] == rg) begin top_map.m_regs_by_offset[info.addr[i]] = top_map.m_regs_by_offset_wo[info.addr[i]]; uvm_reg_read_only_cbs::remove(rg); uvm_reg_write_only_cbs::remove(top_map.m_regs_by_offset[info.addr[i]]); end else begin uvm_reg_write_only_cbs::remove(rg); uvm_reg_read_only_cbs::remove(top_map.m_regs_by_offset[info.addr[i]]); end top_map.m_regs_by_offset_wo.delete(info.addr[i]); end end end // if we are remapping... if (!unmapped) begin string rg_acc = rg.Xget_fields_accessX(this); // get new addresses void'(get_physical_addresses(offset,0,rg.get_n_bytes(),addrs)); // make sure they do not conflict with others foreach (addrs[i]) begin uvm_reg_addr_t addr = addrs[i]; if (top_map.m_regs_by_offset.exists(addr)) begin uvm_reg rg2 = top_map.m_regs_by_offset[addr]; string rg2_acc = rg2.Xget_fields_accessX(this); // If the register at the same address is RO or WO // and this register is WO or RO, this is OK if (rg_acc == "RO" && rg2_acc == "WO") begin top_map.m_regs_by_offset[addr] = rg; uvm_reg_read_only_cbs::add(rg); top_map.m_regs_by_offset_wo[addr] = rg2; uvm_reg_write_only_cbs::add(rg2); end else if (rg_acc == "WO" && rg2_acc == "RO") begin top_map.m_regs_by_offset_wo[addr] = rg; uvm_reg_write_only_cbs::add(rg); uvm_reg_read_only_cbs::add(rg2); end else begin string a; a = $sformatf("%0h",addr); `uvm_warning("RegModel", {"In map '",get_full_name(),"' register '", rg.get_full_name(), "' maps to same address as register '", top_map.m_regs_by_offset[addr].get_full_name(),"': 'h",a}) end end else top_map.m_regs_by_offset[addr] = rg; foreach (top_map.m_mems_by_offset[range]) begin if (addrs[i] >= range.min && addrs[i] <= range.max) begin string a; a = $sformatf("%0h",addrs[i]); `uvm_warning("RegModel", {"In map '",get_full_name(),"' register '", rg.get_full_name(), "' overlaps with address range of memory '", top_map.m_mems_by_offset[range].get_full_name(),"': 'h",a}) end end end info.addr = addrs; // cache it end end if (unmapped) begin info.offset = -1; info.unmapped = 1; end else begin info.offset = offset; info.unmapped = 0; end end endfunction // add_mem function void uvm_reg_map::add_mem(uvm_mem mem, uvm_reg_addr_t offset, string rights = "RW", bit unmapped=0, uvm_reg_frontdoor frontdoor=null); if (m_mems_info.exists(mem)) begin `uvm_error("RegModel", {"Memory '",mem.get_name(), "' has already been added to map '",get_name(),"'"}) return; end if (mem.get_parent() != get_parent()) begin `uvm_error("RegModel", {"Memory '",mem.get_full_name(),"' may not be added to address map '", get_full_name(),"' : they are not in the same block"}) return; end mem.add_map(this); begin uvm_reg_map_info info = new; info.offset = offset; info.rights = rights; info.unmapped = unmapped; info.frontdoor = frontdoor; m_mems_info[mem] = info; end endfunction: add_mem // m_set_mem_offset function void uvm_reg_map::m_set_mem_offset(uvm_mem mem, uvm_reg_addr_t offset, bit unmapped); if (!m_mems_info.exists(mem)) begin `uvm_error("RegModel", {"Cannot modify offset of memory '",mem.get_full_name(), "' in address map '",get_full_name(), "' : memory not mapped in that address map"}) return; end begin uvm_reg_map_info info = m_mems_info[mem]; uvm_reg_block blk = get_parent(); uvm_reg_map top_map = get_root_map(); uvm_reg_addr_t addrs[]; // if block is not locked, Xinit_address_mapX will resolve map when block is locked if (blk.is_locked()) begin // remove any existing cached addresses if (!info.unmapped) begin foreach (top_map.m_mems_by_offset[range]) begin if (top_map.m_mems_by_offset[range] == mem) top_map.m_mems_by_offset.delete(range); end end // if we are remapping... if (!unmapped) begin uvm_reg_addr_t addrs[],addrs_max[]; uvm_reg_addr_t min, max, min2, max2; int unsigned stride; void'(get_physical_addresses(offset,0,mem.get_n_bytes(),addrs)); min = (addrs[0] < addrs[addrs.size()-1]) ? addrs[0] : addrs[addrs.size()-1]; min2 = addrs[0]; void'(get_physical_addresses(offset,(mem.get_size()-1), mem.get_n_bytes(),addrs_max)); max = (addrs_max[0] > addrs_max[addrs_max.size()-1]) ? addrs_max[0] : addrs_max[addrs_max.size()-1]; max2 = addrs_max[0]; // address interval between consecutive mem locations stride = mem.get_n_bytes()/get_addr_unit_bytes(); // make sure new offset does not conflict with others foreach (top_map.m_regs_by_offset[reg_addr]) begin if (reg_addr >= min && reg_addr <= max) begin string a,b; a = $sformatf("[%0h:%0h]",min,max); b = $sformatf("%0h",reg_addr); `uvm_warning("RegModel", {"In map '",get_full_name(),"' memory '", mem.get_full_name(), "' with range ",a, " overlaps with address of existing register '", top_map.m_regs_by_offset[reg_addr].get_full_name(),"': 'h",b}) end end foreach (top_map.m_mems_by_offset[range]) begin if (min <= range.max && max >= range.max || min <= range.min && max >= range.min || min >= range.min && max <= range.max) begin string a,b; a = $sformatf("[%0h:%0h]",min,max); b = $sformatf("[%0h:%0h]",range.min,range.max); `uvm_warning("RegModel", {"In map '",get_full_name(),"' memory '", mem.get_full_name(), "' with range ",a, " overlaps existing memory with range '", top_map.m_mems_by_offset[range].get_full_name(),"': ",b}) end end begin uvm_reg_map_addr_range range = '{ min, max, stride}; top_map.m_mems_by_offset[range] = mem; info.addr = addrs; info.mem_range = range; end end end if (unmapped) begin info.offset = -1; info.unmapped = 1; end else begin info.offset = offset; info.unmapped = 0; end end endfunction // add_submap function void uvm_reg_map::add_submap (uvm_reg_map child_map, uvm_reg_addr_t offset); uvm_reg_map parent_map; if (child_map == null) begin `uvm_error("RegModel", {"Attempting to add NULL map to map '",get_full_name(),"'"}) return; end parent_map = child_map.get_parent_map(); // Cannot have more than one parent (currently) if (parent_map != null) begin `uvm_error("RegModel", {"Map '", child_map.get_full_name(), "' is already a child of map '", parent_map.get_full_name(), "'. Cannot also be a child of map '", get_full_name(), "'"}) return; end // this check means that n_bytes cannot change in a map hierarchy, that should work with 5446 begin : n_bytes_match_check if (m_n_bytes > child_map.get_n_bytes(UVM_NO_HIER)) begin `uvm_warning("RegModel", $sformatf("Adding %0d-byte submap '%s' to %0d-byte parent map '%s'", child_map.get_n_bytes(UVM_NO_HIER), child_map.get_full_name(), m_n_bytes, get_full_name())) end end child_map.add_parent_map(this,offset); set_submap_offset(child_map, offset); endfunction: add_submap // reset function void uvm_reg_map::reset(string kind = "SOFT"); uvm_reg regs[$]; get_registers(regs); foreach (regs[i]) begin regs[i].reset(kind); end endfunction // add_parent_map function void uvm_reg_map::add_parent_map(uvm_reg_map parent_map, uvm_reg_addr_t offset); if (parent_map == null) begin `uvm_error("RegModel", {"Attempting to add NULL parent map to map '",get_full_name(),"'"}) return; end if (m_parent_map != null) begin `uvm_error("RegModel", $sformatf("Map \"%s\" already a submap of map \"%s\" at offset 'h%h", get_full_name(), m_parent_map.get_full_name(), m_parent_map.get_submap_offset(this))) return; end m_parent_map = parent_map; parent_map.m_submaps[this] = offset; endfunction: add_parent_map // set_sequencer function void uvm_reg_map::set_sequencer(uvm_sequencer_base sequencer, uvm_reg_adapter adapter=null); if (sequencer == null) begin `uvm_error("REG_NULL_SQR", "Null reference specified for bus sequencer") return; end if (adapter == null) begin `uvm_info("REG_NO_ADAPT", {"Adapter not specified for map '",get_full_name(), "'. Accesses via this map will send abstract 'uvm_reg_item' items to sequencer '", sequencer.get_full_name(),"'"},UVM_MEDIUM) end m_sequencer = sequencer; m_adapter = adapter; endfunction //------------ // get methods //------------ // get_parent function uvm_reg_block uvm_reg_map::get_parent(); return m_parent; endfunction // get_parent_map function uvm_reg_map uvm_reg_map::get_parent_map(); return m_parent_map; endfunction // get_root_map function uvm_reg_map uvm_reg_map::get_root_map(); return (m_parent_map == null) ? this : m_parent_map.get_root_map(); endfunction: get_root_map // get_base_addr function uvm_reg_addr_t uvm_reg_map::get_base_addr(uvm_hier_e hier=UVM_HIER); uvm_reg_map child = this; if (hier == UVM_NO_HIER || m_parent_map == null) return m_base_addr; get_base_addr = m_parent_map.get_submap_offset(this); get_base_addr += m_parent_map.get_base_addr(UVM_HIER); endfunction // get_n_bytes function int unsigned uvm_reg_map::get_n_bytes(uvm_hier_e hier=UVM_HIER); if (hier == UVM_NO_HIER) return m_n_bytes; return m_system_n_bytes; endfunction // get_addr_unit_bytes function int unsigned uvm_reg_map::get_addr_unit_bytes(); return (m_byte_addressing) ? 1 : m_n_bytes; endfunction // get_endian function uvm_endianness_e uvm_reg_map::get_endian(uvm_hier_e hier=UVM_HIER); if (hier == UVM_NO_HIER || m_parent_map == null) return m_endian; return m_parent_map.get_endian(hier); endfunction // get_sequencer function uvm_sequencer_base uvm_reg_map::get_sequencer(uvm_hier_e hier=UVM_HIER); if (hier == UVM_NO_HIER || m_parent_map == null) return m_sequencer; return m_parent_map.get_sequencer(hier); endfunction // get_adapter function uvm_reg_adapter uvm_reg_map::get_adapter(uvm_hier_e hier=UVM_HIER); if (hier == UVM_NO_HIER || m_parent_map == null) return m_adapter; return m_parent_map.get_adapter(hier); endfunction // get_submaps function void uvm_reg_map::get_submaps(ref uvm_reg_map maps[$], input uvm_hier_e hier=UVM_HIER); foreach (m_submaps[submap]) maps.push_back(submap); if (hier == UVM_HIER) foreach (m_submaps[submap_]) begin uvm_reg_map submap=submap_; submap.get_submaps(maps); end endfunction // get_registers function void uvm_reg_map::get_registers(ref uvm_reg regs[$], input uvm_hier_e hier=UVM_HIER); foreach (m_regs_info[rg]) regs.push_back(rg); if (hier == UVM_HIER) foreach (m_submaps[submap_]) begin uvm_reg_map submap=submap_; submap.get_registers(regs); end endfunction // get_fields function void uvm_reg_map::get_fields(ref uvm_reg_field fields[$], input uvm_hier_e hier=UVM_HIER); foreach (m_regs_info[rg_]) begin uvm_reg rg = rg_; rg.get_fields(fields); end if (hier == UVM_HIER) foreach (this.m_submaps[submap_]) begin uvm_reg_map submap=submap_; submap.get_fields(fields); end endfunction // get_memories function void uvm_reg_map::get_memories(ref uvm_mem mems[$], input uvm_hier_e hier=UVM_HIER); foreach (m_mems_info[mem]) mems.push_back(mem); if (hier == UVM_HIER) foreach (m_submaps[submap_]) begin uvm_reg_map submap=submap_; submap.get_memories(mems); end endfunction // get_virtual_registers function void uvm_reg_map::get_virtual_registers(ref uvm_vreg regs[$], input uvm_hier_e hier=UVM_HIER); uvm_mem mems[$]; get_memories(mems,hier); foreach (mems[i]) mems[i].get_virtual_registers(regs); endfunction // get_virtual_fields function void uvm_reg_map::get_virtual_fields(ref uvm_vreg_field fields[$], input uvm_hier_e hier=UVM_HIER); uvm_vreg regs[$]; get_virtual_registers(regs,hier); foreach (regs[i]) regs[i].get_fields(fields); endfunction // get_full_name function string uvm_reg_map::get_full_name(); if (m_parent == null) return get_name(); else return {m_parent.get_full_name(), ".", get_name()}; endfunction // get_mem_map_info function uvm_reg_map_info uvm_reg_map::get_mem_map_info(uvm_mem mem, bit error=1); if (!m_mems_info.exists(mem)) begin if (error) `uvm_error("REG_NO_MAP",{"Memory '",mem.get_name(),"' not in map '",get_name(),"'"}) return null; end return m_mems_info[mem]; endfunction // get_reg_map_info function uvm_reg_map_info uvm_reg_map::get_reg_map_info(uvm_reg rg, bit error=1); uvm_reg_map_info result; if (!m_regs_info.exists(rg)) begin if (error) `uvm_error("REG_NO_MAP",{"Register '",rg.get_name(),"' not in map '",get_name(),"'"}) return null; end result = m_regs_info[rg]; if(!result.is_initialized) `uvm_warning("RegModel",{"map '",get_name(),"' does not seem to be initialized correctly, check that the top register model is locked()"}) return result; endfunction //---------- // Size and Overlap Detection //--------- // set_base_addr function void uvm_reg_map::set_base_addr(uvm_reg_addr_t offset); if (m_parent_map != null) begin m_parent_map.set_submap_offset(this, offset); end else begin m_base_addr = offset; if (m_parent.is_locked()) begin uvm_reg_map top_map = get_root_map(); top_map.Xinit_address_mapX(); end end endfunction // get_size function int unsigned uvm_reg_map::get_size(); int unsigned max_addr; int unsigned addr; // get max offset from registers foreach (m_regs_info[rg_]) begin uvm_reg rg = rg_; addr = m_regs_info[rg].offset + ((rg.get_n_bytes()-1)/m_n_bytes); if (addr > max_addr) max_addr = addr; end // get max offset from memories foreach (m_mems_info[mem_]) begin uvm_mem mem = mem_; addr = m_mems_info[mem].offset + (mem.get_size() * (((mem.get_n_bytes()-1)/m_n_bytes)+1)) -1; if (addr > max_addr) max_addr = addr; end // get max offset from submaps foreach (m_submaps[submap_]) begin uvm_reg_map submap=submap_; addr = m_submaps[submap] + submap.get_size(); if (addr > max_addr) max_addr = addr; end return max_addr + 1; endfunction function void uvm_reg_map::Xverify_map_configX(); // Make sure there is a generic payload sequence for each map // in the model and vice-versa if this is a root sequencer bit error; uvm_reg_map root_map = get_root_map(); if (root_map.get_adapter() == null) begin `uvm_error("RegModel", {"Map '",root_map.get_full_name(), "' does not have an adapter registered"}) error++; end if (root_map.get_sequencer() == null) begin `uvm_error("RegModel", {"Map '",root_map.get_full_name(), "' does not have a sequencer registered"}) error++; end if (error) begin `uvm_fatal("RegModel", {"Must register an adapter and sequencer ", "for each top-level map in RegModel model"}) return; end endfunction // NOTE: if multiple memory addresses would fall into one bus word then the memory is addressed 'unpacked' // ie. every memory location will get an own bus address (and bits on the bus larger than the memory width are discarded // otherwise the memory access is 'packed' // // same as get_physical_addresses() but stops at the specified map function int uvm_reg_map::get_physical_addresses_to_map( uvm_reg_addr_t base_addr, // in terms of the local map aub uvm_reg_addr_t mem_offset, // in terms of memory words int unsigned n_bytes, // number of bytes for the memory stream ref uvm_reg_addr_t addr[], // out: set of addresses required for memory stream in local map aub input uvm_reg_map parent_map, // desired target map ref int unsigned byte_offset, // leading byte offset (due to shifting within address words) input uvm_mem mem=null ); int bus_width = get_n_bytes(UVM_NO_HIER); uvm_reg_map up_map; uvm_reg_addr_t local_addr[]; uvm_reg_addr_t lbase_addr; // `uvm_info("RegModel",$sformatf("this=%p enter base=0x%0x mem_offset=0x%0d request=%0dbytes byte_enable=%0d byte-offset=%0d", // this,base_addr,mem_offset,n_bytes,m_byte_addressing,byte_offset),UVM_HIGH) // `uvm_info("RegModel",$sformatf("addressUnitBits=%0d busWidthBits=%0d",get_addr_unit_bytes()*8,bus_width*8),UVM_HIGH) up_map = get_parent_map(); lbase_addr = up_map==null ? get_base_addr(UVM_NO_HIER): up_map.get_submap_offset(this); // `uvm_info("RegModel",$sformatf("lbase =0x%0x",lbase_addr),UVM_HIGH) if(up_map!=parent_map) begin uvm_reg_addr_t lb; // now just translate first address and request same number of bytes // may need to adjust addr,n_bytes if base_addr*AUB is not a multiple of upmap.AUB // addr=5,aub=8 and up.aub=16 and n_bytes=1 which is translated addr=2,n_bytes=2 uvm_reg_addr_t laddr; begin // adjust base_addr to find the base of memword(mem_offset) if(mem_offset) begin base_addr+=mem_offset*mem.get_n_bytes()/get_addr_unit_bytes(); end laddr=lbase_addr + base_addr*get_addr_unit_bytes()/up_map.get_addr_unit_bytes(); // start address in terms of the upper map lb = (base_addr*get_addr_unit_bytes()) % up_map.get_addr_unit_bytes(); // potential byte offset on top of the start address in the upper map byte_offset += lb; // accumulate! end return up_map.get_physical_addresses_to_map(laddr, 0, n_bytes+lb, addr,parent_map,byte_offset); end else begin uvm_reg_addr_t lbase_addr2; // first need to compute set of addresses // each address is for one full bus width (the last beat may have less bytes to transfer) local_addr= new[ceil(n_bytes,bus_width)]; lbase_addr2 = base_addr; if(mem_offset) if(mem!=null && (mem.get_n_bytes() >= get_addr_unit_bytes())) begin // packed model lbase_addr2 = base_addr + mem_offset*mem.get_n_bytes()/get_addr_unit_bytes(); byte_offset += (mem_offset*mem.get_n_bytes() % get_addr_unit_bytes()); end else begin lbase_addr2 = base_addr + mem_offset; end // `uvm_info("UVM/REG/ADDR",$sformatf("gen addrs map-aub(bytes)=%0d addrs=%0d map-bus-width(bytes)=%0d lbase_addr2=%0x", // get_addr_unit_bytes(),local_addr.size(),bus_width,lbase_addr2),UVM_DEBUG) case (get_endian(UVM_NO_HIER)) UVM_LITTLE_ENDIAN: begin foreach (local_addr[i]) begin local_addr[i] = lbase_addr2 + i*bus_width/get_addr_unit_bytes(); end end UVM_BIG_ENDIAN: begin foreach (local_addr[i]) begin local_addr[i] = lbase_addr2 + (local_addr.size()-1-i)*bus_width/get_addr_unit_bytes() ; end end UVM_LITTLE_FIFO: begin foreach (local_addr[i]) begin local_addr[i] = lbase_addr2; end end UVM_BIG_FIFO: begin foreach (local_addr[i]) begin local_addr[i] = lbase_addr2; end end default: begin `uvm_error("UVM/REG/MAPNOENDIANESS", {"Map has no specified endianness. ", $sformatf("Cannot access %0d bytes register via its %0d byte \"%s\" interface", n_bytes, bus_width, get_full_name())}) end endcase // foreach(local_addr[idx]) // `uvm_info("UVM/REG/ADDR",$sformatf("local_addr idx=%0d addr=%0x",idx,local_addr[idx]),UVM_DEBUG) // now need to scale in terms of upper map addr = new [local_addr.size()] (local_addr); foreach(addr[idx]) addr[idx] += lbase_addr; // foreach(addr[idx]) // `uvm_info("UVM/REG/ADDR",$sformatf("top %0x:",addr[idx]),UVM_DEBUG) end endfunction // NOTE the map argument could be made an arg with a default value. didnt do that to present the function signature function int uvm_reg_map::get_physical_addresses(uvm_reg_addr_t base_addr, uvm_reg_addr_t mem_offset, int unsigned n_bytes, // number of bytes ref uvm_reg_addr_t addr[]); int unsigned skip; return get_physical_addresses_to_map(base_addr, mem_offset, n_bytes, addr,null,skip); endfunction //-------------- // Get-By-Offset //-------------- // set_submap_offset function void uvm_reg_map::set_submap_offset(uvm_reg_map submap, uvm_reg_addr_t offset); if (submap == null) begin `uvm_error("REG/NULL","set_submap_offset: submap handle is null") return; end m_submaps[submap] = offset; if (m_parent.is_locked()) begin uvm_reg_map root_map = get_root_map(); root_map.Xinit_address_mapX(); end endfunction // get_submap_offset function uvm_reg_addr_t uvm_reg_map::get_submap_offset(uvm_reg_map submap); if (submap == null) begin `uvm_error("REG/NULL","set_submap_offset: submap handle is null") return -1; end if (!m_submaps.exists(submap)) begin `uvm_error("RegModel",{"Map '",submap.get_full_name(), "' is not a submap of '",get_full_name(),"'"}) return -1; end return m_submaps[submap]; endfunction // get_reg_by_offset function uvm_reg uvm_reg_map::get_reg_by_offset(uvm_reg_addr_t offset, bit read = 1); if (!m_parent.is_locked()) begin `uvm_error("RegModel", $sformatf("Cannot get register by offset: Block %s is not locked.", m_parent.get_full_name())) return null; end if (!read && m_regs_by_offset_wo.exists(offset)) return m_regs_by_offset_wo[offset]; if (m_regs_by_offset.exists(offset)) return m_regs_by_offset[offset]; return null; endfunction // get_mem_by_offset function uvm_mem uvm_reg_map::get_mem_by_offset(uvm_reg_addr_t offset); if (!m_parent.is_locked()) begin `uvm_error("RegModel", $sformatf("Cannot memory register by offset: Block %s is not locked.", m_parent.get_full_name())) return null; end foreach (m_mems_by_offset[range]) begin if (range.min <= offset && offset <= range.max) begin return m_mems_by_offset[range]; end end return null; endfunction // Xinit_address_mapX function void uvm_reg_map::Xinit_address_mapX(); int unsigned bus_width; uvm_reg_map top_map = get_root_map(); if (this == top_map) begin top_map.m_regs_by_offset.delete(); top_map.m_regs_by_offset_wo.delete(); top_map.m_mems_by_offset.delete(); end foreach (m_submaps[l]) begin uvm_reg_map map=l; map.Xinit_address_mapX(); end foreach (m_regs_info[rg_]) begin uvm_reg rg = rg_; m_regs_info[rg].is_initialized=1; if (!m_regs_info[rg].unmapped) begin string rg_acc = rg.Xget_fields_accessX(this); uvm_reg_addr_t addrs[]; bus_width = get_physical_addresses(m_regs_info[rg].offset,0,rg.get_n_bytes(),addrs); foreach (addrs[i]) begin uvm_reg_addr_t addr = addrs[i]; if (top_map.m_regs_by_offset.exists(addr) && (top_map.m_regs_by_offset[addr] != rg)) begin uvm_reg rg2 = top_map.m_regs_by_offset[addr]; string rg2_acc = rg2.Xget_fields_accessX(this); // If the register at the same address is RO or WO // and this register is WO or RO, this is OK if (rg_acc == "RO" && rg2_acc == "WO") begin top_map.m_regs_by_offset[addr] = rg; uvm_reg_read_only_cbs::add(rg); top_map.m_regs_by_offset_wo[addr] = rg2; uvm_reg_write_only_cbs::add(rg2); end else if (rg_acc == "WO" && rg2_acc == "RO") begin top_map.m_regs_by_offset_wo[addr] = rg; uvm_reg_write_only_cbs::add(rg); uvm_reg_read_only_cbs::add(rg2); end else begin string a; a = $sformatf("%0h",addr); `uvm_warning("RegModel", {"In map '",get_full_name(),"' register '", rg.get_full_name(), "' maps to same address as register '", top_map.m_regs_by_offset[addr].get_full_name(),"': 'h",a}) end end else top_map.m_regs_by_offset[addr] = rg; foreach (top_map.m_mems_by_offset[range]) begin if (addr >= range.min && addr <= range.max) begin string a,b; a = $sformatf("%0h",addr); b = $sformatf("[%0h:%0h]",range.min,range.max); `uvm_warning("RegModel", {"In map '",get_full_name(),"' register '", rg.get_full_name(), "' with address ",a, "maps to same address as memory '", top_map.m_mems_by_offset[range].get_full_name(),"': ",b}) end end end m_regs_info[rg].addr = addrs; end end foreach (m_mems_info[mem_]) begin uvm_mem mem = mem_; if (!m_mems_info[mem].unmapped) begin uvm_reg_addr_t addrs[],addrs_max[]; uvm_reg_addr_t min, max, min2, max2; int unsigned stride; int unsigned bo; bus_width = get_physical_addresses_to_map(m_mems_info[mem].offset,0,mem.get_n_bytes(),addrs,null,bo,mem); min = (addrs[0] < addrs[addrs.size()-1]) ? addrs[0] : addrs[addrs.size()-1]; // foreach(addrs[idx]) // `uvm_info("UVM/REG/ADDR",$sformatf("idx%0d addr=%0x",idx,addrs[idx]),UVM_DEBUG) void'(get_physical_addresses_to_map(m_mems_info[mem].offset,(mem.get_size()-1),mem.get_n_bytes(),addrs_max,null,bo,mem)); max = (addrs_max[0] > addrs_max[addrs_max.size()-1]) ? addrs_max[0] : addrs_max[addrs_max.size()-1]; stride = mem.get_n_bytes()/get_addr_unit_bytes(); // foreach(addrs_max[idx]) // `uvm_info("UVM/REG/ADDR",$sformatf("idx%0d addr=%0x",idx,addrs_max[idx]),UVM_DEBUG) // `uvm_info("UVM/REG/ADDR",$sformatf("mem %0d x %0d in map aub(bytes)=%0d n_bytes=%0d",mem.get_size(),mem.get_n_bits(), // get_addr_unit_bytes(),get_n_bytes(UVM_NO_HIER)),UVM_DEBUG) /* if (uvm_report_enabled(UVM_DEBUG, UVM_INFO,"UVM/REG/ADDR")) begin uvm_reg_addr_t ad[]; for(int idx=0;idx get_addr_unit_bytes()) if(mem.get_n_bytes() % get_addr_unit_bytes()) begin `uvm_warning("UVM/REG/ADDR",$sformatf("memory %s is not matching the word width of the enclosing map %s \ (one memory word not fitting into k map addresses)", mem.get_full_name(),get_full_name())) end if(mem.get_n_bytes() < get_addr_unit_bytes()) if(get_addr_unit_bytes() % mem.get_n_bytes()) `uvm_warning("UVM/REG/ADDR",$sformatf("the memory %s is not matching the word width of the enclosing map %s \ (one map address doesnt cover k memory words)", mem.get_full_name(),get_full_name())) if(mem.get_n_bits() % 8) `uvm_warning("UVM/REG/ADDR",$sformatf("this implementation of UVM requires memory words to be k*8 bits (mem %s \ has %0d bit words)",mem.get_full_name(),mem.get_n_bits())) foreach (top_map.m_regs_by_offset[reg_addr]) begin if (reg_addr >= min && reg_addr <= max) begin string a; a = $sformatf("%0h",reg_addr); `uvm_warning("RegModel", {"In map '",get_full_name(),"' memory '", mem.get_full_name(), "' maps to same address as register '", top_map.m_regs_by_offset[reg_addr].get_full_name(),"': 'h",a}) end end foreach (top_map.m_mems_by_offset[range]) begin if (min <= range.max && max >= range.max || min <= range.min && max >= range.min || min >= range.min && max <= range.max) if(top_map.m_mems_by_offset[range]!=mem) // do not warn if the same mem is located at the same address via different paths begin string a; a = $sformatf("[%0h:%0h]",min,max); `uvm_warning("RegModel", {"In map '",get_full_name(),"' memory '", mem.get_full_name(), "' overlaps with address range of memory '", top_map.m_mems_by_offset[range].get_full_name(),"': 'h",a}) end end begin uvm_reg_map_addr_range range = '{ min, max, stride}; top_map.m_mems_by_offset[ range ] = mem; m_mems_info[mem].addr = addrs; m_mems_info[mem].mem_range = range; end end end // If the block has no registers or memories, // bus_width won't be set if (bus_width == 0) bus_width = m_n_bytes; m_system_n_bytes = bus_width; endfunction //----------- // Bus Access //----------- function void uvm_reg_map::Xget_bus_infoX(uvm_reg_item rw, output uvm_reg_map_info map_info, output int size, output int lsb, output int addr_skip); if (rw.get_element_kind() == UVM_MEM) begin uvm_mem mem; if(rw.get_element() == null || !$cast(mem,rw.get_element())) `uvm_fatal("REG/CAST", {"uvm_reg_item 'element_kind' is UVM_MEM, ", "but 'element' does not point to a memory: ",rw.get_name()}) map_info = get_mem_map_info(mem); size = mem.get_n_bits(); end else if (rw.get_element_kind() == UVM_REG) begin uvm_reg rg; if(rw.get_element() == null || !$cast(rg,rw.get_element())) `uvm_fatal("REG/CAST", {"uvm_reg_item 'element_kind' is UVM_REG, ", "but 'element' does not point to a register: ",rw.get_name()}) map_info = get_reg_map_info(rg); size = rg.get_n_bits(); end else if (rw.get_element_kind() == UVM_FIELD) begin uvm_reg_field field; if(rw.get_element() == null || !$cast(field,rw.get_element())) `uvm_fatal("REG/CAST", {"uvm_reg_item 'element_kind' is UVM_FIELD, ", "but 'element' does not point to a field: ",rw.get_name()}) map_info = get_reg_map_info(field.get_parent()); size = field.get_n_bits(); lsb = field.get_lsb_pos(); addr_skip = lsb/(get_n_bytes()*8); end endfunction // do_write(uvm_reg_item rw) task uvm_reg_map::do_write(uvm_reg_item rw); uvm_sequence_base tmp_parent_seq; uvm_reg_map system_map = get_root_map(); uvm_reg_adapter adapter = system_map.get_adapter(); uvm_sequencer_base sequencer = system_map.get_sequencer(); uvm_reg_seq_base parent_proxy; if (adapter != null && adapter.parent_sequence != null) begin uvm_object o; uvm_sequence_base seq; o = adapter.parent_sequence.clone(); if (o == null) `uvm_fatal("REG/CLONE", {"failed to clone adapter's parent sequence: '", adapter.parent_sequence.get_full_name(), "' (of type '", adapter.parent_sequence.get_type_name(), "')"}) if (!$cast(seq, o)) `uvm_fatal("REG/CAST", {"failed to cast: '", o.get_full_name(), "' (of type '", o.get_type_name(), "') to uvm_sequence_base!"}) seq.set_parent_sequence(rw.get_parent_sequence()); rw.set_parent_sequence(seq); tmp_parent_seq = seq; end if (rw.get_parent_sequence() == null) begin parent_proxy = new("default_parent_seq"); rw.set_parent_sequence(parent_proxy); tmp_parent_seq = parent_proxy; end if (adapter == null) begin uvm_event#(uvm_object) end_event ; uvm_event_pool ep; ep = rw.get_event_pool(); end_event = ep.get("end") ; rw.set_sequencer(sequencer); tmp_parent_seq = rw.get_parent_sequence(); tmp_parent_seq.start_item(rw,rw.get_priority()); tmp_parent_seq.finish_item(rw); end_event.wait_on(); end else begin do_bus_write(rw, sequencer, adapter); end if (tmp_parent_seq != null) sequencer.m_sequence_exiting(tmp_parent_seq); endtask // do_read(uvm_reg_item rw) task uvm_reg_map::do_read(uvm_reg_item rw); uvm_sequence_base tmp_parent_seq; uvm_reg_map system_map = get_root_map(); uvm_reg_adapter adapter = system_map.get_adapter(); uvm_sequencer_base sequencer = system_map.get_sequencer(); uvm_reg_seq_base parent_proxy; if (adapter != null && adapter.parent_sequence != null) begin uvm_object o; uvm_sequence_base seq; o = adapter.parent_sequence.clone(); if (o == null) `uvm_fatal("REG/CLONE", {"failed to clone adapter's parent sequence: '", adapter.parent_sequence.get_full_name(), "' (of type '", adapter.parent_sequence.get_type_name(), "')"}) if (!$cast(seq, o)) `uvm_fatal("REG/CAST", {"failed to cast: '", o.get_full_name(), "' (of type '", o.get_type_name(), "') to uvm_sequence_base!"}) seq.set_parent_sequence(rw.get_parent_sequence()); rw.set_parent_sequence(seq); tmp_parent_seq = seq; end if (rw.get_parent_sequence() == null) begin parent_proxy = new("default_parent_seq"); rw.set_parent_sequence(parent_proxy); tmp_parent_seq = parent_proxy; end if (adapter == null) begin uvm_event#(uvm_object) end_event ; uvm_event_pool ep; ep = rw.get_event_pool(); end_event = ep.get("end") ; rw.set_sequencer(sequencer); tmp_parent_seq = rw.get_parent_sequence(); tmp_parent_seq.start_item(rw,rw.get_priority()); tmp_parent_seq.finish_item(rw); end_event.wait_on(); end else begin do_bus_read(rw, sequencer, adapter); end if (tmp_parent_seq != null) sequencer.m_sequence_exiting(tmp_parent_seq); endtask // do_bus_write task uvm_reg_map::do_bus_write (uvm_reg_item rw, uvm_sequencer_base sequencer, uvm_reg_adapter adapter); do_bus_access(rw, sequencer, adapter); endtask task uvm_reg_map::perform_accesses(ref uvm_reg_bus_op accesses[$], input uvm_reg_item rw, input uvm_reg_adapter adapter, input uvm_sequencer_base sequencer); string op; uvm_reg_data_logic_t data; uvm_endianness_e endian; op=(rw.get_kind() inside {UVM_READ,UVM_BURST_READ}) ? "Read" : "Wrote"; endian=get_endian(UVM_NO_HIER); // if set utilize the order policy if(policy!=null) policy.order(accesses); // perform accesses foreach(accesses[i]) begin uvm_reg_bus_op rw_access=accesses[i]; uvm_sequence_item bus_req; uvm_sequence_base rw_parent_seq; uvm_reg_map rw_map; uvm_status_e rw_status; if ((rw_access.kind == UVM_WRITE) && (endian == UVM_BIG_ENDIAN)) begin { >> { rw_access.data }} = { << byte { rw_access.data}}; end adapter.m_set_item(rw); bus_req = adapter.reg2bus(rw_access); 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_seq = rw.get_parent_sequence(); rw_parent_seq.start_item(bus_req,rw.get_priority()); if (rw_parent_seq != null && i == 0) rw_parent_seq.mid_do(rw); rw_parent_seq.finish_item(bus_req); begin uvm_event#(uvm_object) end_event ; uvm_event_pool ep; ep = bus_req.get_event_pool(); end_event = ep.get("end") ; end_event.wait_on(); end 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_seq.get_base_response(bus_rsp,bus_req.get_transaction_id()); adapter.bus2reg(bus_rsp,rw_access); end else begin adapter.bus2reg(bus_req,rw_access); end if ((rw_access.kind == UVM_READ) && (endian == UVM_BIG_ENDIAN)) begin { >> { rw_access.data }} = { << byte { rw_access.data}}; end rw.set_status(rw_access.status); begin data = rw_access.data & ((1< This converts our byte stream (p) into a bit stream, // but it makes '{'hC7,'h3F} into 'b1100_0111__0011_1111. // Note that the endianness of the bytes has changed, ie. // the high order byte ('h3F) is now the low order byte. // {<<8{ ... } -> This takes the bit stream and corrects the endianness // 8 bits at a time. bits = {<< 8{ {<< {p}} }}; repeat(bit_shift) bits.push_front(1'b0); // This operation is the exact opposite of above, converting the // bitstream into a byte stream, and then reversing the endianness of // the byte stream. p = {<< 8{ {<< {bits}} }}; end /* if (uvm_report_enabled(UVM_NONE, UVM_INFO, "UVM/REG/ADDR")) begin `uvm_info("UVM/REG/ADDR", $sformatf("bit_shift = %0d", bit_shift), UVM_NONE) foreach(be[idx]) `uvm_info("UVM/REG/ADDR",$sformatf("idx %0d en=%0d",idx,be[idx]),UVM_NONE) foreach(adr[idx]) `uvm_info("UVM/REG/ADDR",$sformatf("mem-adr %0x byte-offset=%0d",adr[idx],byte_offset),UVM_NONE) foreach(values[idx]) `uvm_info("UVM/REG/ADDR", $sformatf("idx %0d mem-val=%0x", idx, values[idx]), UVM_NONE) foreach(p[idx]) `uvm_info("UVM/REG/ADDR",$sformatf("idx %0d data=%x enable=%0d",idx,p[idx],be[idx]),UVM_NONE) foreach(rw.value[idx]) `uvm_info("UVM/REG/ADDR",$sformatf("original idx=%0d %0x",idx,rw.value[idx]),UVM_NONE) end */ // transform into accesses per address accesses.delete(); foreach(adr[i]) begin uvm_reg_bus_op rw_access; uvm_reg_data_t data; uvm_reg_map tmp_map = rw.get_map(); for(int i0=0;i0=0;i--) begin if(rw_access.byte_en[i]==0) rw_access.n_bits-=8; else break; end accesses.push_back(rw_access); end perform_accesses(accesses, rw, adapter, sequencer); // for reads copy back to rw.value if(rw.get_kind() inside {UVM_READ,UVM_BURST_READ}) begin int rw_value_size; p.delete(); foreach(accesses[i0]) for(int i1=0;i1