| // ------------------------------------------------------------- |
| // Copyright 2010-2018 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-2018 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 <uvm_reg_block::create_map()> |
| // 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 <get_n_bytes()> 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<mem.get_size();idx++) begin |
| void'(get_physical_addresses_to_map(m_mems_info[mem].offset,idx,1,ad,null,bo,mem)); |
| |
| `uvm_info("UVM/REG/ADDR",$sformatf("idx%d addr=%x",idx,ad[0]),UVM_DEBUG) |
| end |
| end |
| */ |
| |
| if(mem.get_n_bytes()<get_addr_unit_bytes()) |
| `uvm_warning("UVM/REG/ADDR",$sformatf("this version of UVM does not properly support memories with \ |
| a smaller word width than the enclosing map. map %s has n_bytes=%0d aub=%0d while the mem has get_n_bytes %0d. \ |
| multiple memory words fall into one bus address. if that happens memory addressing will be unpacked.", |
| get_full_name(),get_n_bytes(UVM_NO_HIER),get_addr_unit_bytes(),mem.get_n_bytes())) |
| |
| if(mem.get_n_bytes() > 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.element_kind == UVM_MEM) begin |
| uvm_mem mem; |
| if(rw.element == null || !$cast(mem,rw.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.element_kind == UVM_REG) begin |
| uvm_reg rg; |
| if(rw.element == null || !$cast(rg,rw.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.element_kind == UVM_FIELD) begin |
| uvm_reg_field field; |
| if(rw.element == null || !$cast(field,rw.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.parent); |
| rw.parent = seq; |
| tmp_parent_seq = seq; |
| end |
| |
| if (rw.parent == null) begin |
| parent_proxy = new("default_parent_seq"); |
| rw.parent = parent_proxy; |
| tmp_parent_seq = rw.parent; |
| 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); |
| rw.parent.start_item(rw,rw.prior); |
| rw.parent.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.parent); |
| rw.parent = seq; |
| tmp_parent_seq = seq; |
| end |
| |
| if (rw.parent == null) begin |
| parent_proxy = new("default_parent_seq"); |
| rw.parent = parent_proxy; |
| tmp_parent_seq = rw.parent; |
| 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); |
| rw.parent.start_item(rw,rw.prior); |
| rw.parent.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.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; |
| |
| 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.start_item(bus_req,rw.prior); |
| |
| if (rw.parent != null && i == 0) |
| rw.parent.mid_do(rw); |
| |
| rw.parent.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.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.status = rw_access.status; |
| |
| begin |
| data = rw_access.data & ((1<<get_n_bytes()*8)-1); // mask the upper bits |
| |
| if(rw.kind inside {UVM_READ,UVM_BURST_READ}) |
| if (rw.status == UVM_IS_OK && (^data) === 1'bx) |
| rw.status = UVM_HAS_X; |
| |
| rw_access.data=data; |
| end |
| |
| `uvm_info("UVM/REG/ADDR", |
| $sformatf("%s 'h%0h at 'h%0h via map \"%s\": %s...",op, |
| rw_access.data, rw_access.addr, rw.map.get_full_name(), rw.status.name()), UVM_FULL) |
| |
| if (rw.status == UVM_NOT_OK) |
| break; |
| |
| if (rw.parent != null && i == accesses.size()-1) |
| rw.parent.post_do(rw); |
| |
| accesses[i]=rw_access; |
| end |
| endtask |
| |
| // do_bus_read |
| |
| task uvm_reg_map::do_bus_access (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; |
| uvm_reg_bus_op accesses[$]; |
| // int n_bits_init; |
| string op; |
| uvm_reg_addr_t adr[]; |
| int unsigned byte_offset; |
| int unsigned num_stream_bytes; |
| int unsigned n_bytes; |
| int unsigned bytes_per_value; |
| int unsigned bit_shift; |
| int unsigned extra_byte; |
| |
| Xget_bus_infoX(rw, map_info, n_bits, lsb, skip); |
| addrs=map_info.addr; |
| op = (rw.kind inside {UVM_READ,UVM_BURST_READ} ? "Reading" : "Writing"); |
| |
| case(rw.element_kind) |
| UVM_MEM: begin |
| uvm_mem mem; |
| $cast(mem,rw.element); |
| void'(get_physical_addresses_to_map(m_mems_info[mem].offset,rw.offset,rw.value.size()*mem.get_n_bytes(),adr,null,byte_offset,mem)); |
| num_stream_bytes =rw.value.size()*mem.get_n_bytes(); |
| n_bytes=mem.get_n_bytes(); |
| bytes_per_value=mem.get_n_bytes(); |
| end |
| UVM_FIELD: begin |
| uvm_reg_field f; |
| uvm_reg_addr_t ad; |
| $cast(f,rw.element); |
| |
| // adjust adr bit skipped bytes; still need to shift data by byte fractions (lsb) |
| void'(get_physical_addresses_to_map(m_regs_info[f.get_parent()].offset+skip,0,ceil(f.get_n_bits(),8),adr,null,byte_offset)); |
| num_stream_bytes =ceil(f.get_n_bits(),8); |
| n_bytes=get_n_bytes(UVM_NO_HIER); |
| bytes_per_value=ceil(f.get_n_bits(),8); |
| bit_shift=lsb % (get_n_bytes()*8); |
| if(((bit_shift+f.get_n_bits()) /8) != ((f.get_n_bits()) /8)) |
| extra_byte=1; |
| // `uvm_info("UVM/REG/ADDR",$sformatf("need to byte skip %0d and bit shift %0d",skip,bit_shift),UVM_DEBUG) |
| end |
| UVM_REG: begin |
| uvm_reg r; |
| uvm_reg_addr_t ad; |
| $cast(r,rw.element); |
| |
| void'(get_physical_addresses_to_map(m_regs_info[r].offset,0,r.get_n_bytes(),adr,null,byte_offset)); |
| num_stream_bytes =r.get_n_bytes(); |
| n_bytes=get_n_bytes(UVM_NO_HIER); |
| bytes_per_value=r.get_n_bytes(); |
| end |
| endcase |
| |
| begin |
| bit be[$]; |
| byte p[$]; |
| |
| // adjust bytes if there is a leading bit shift |
| num_stream_bytes+=extra_byte; |
| |
| repeat(byte_offset) be.push_back(1'b0); // TODO rewrite |
| repeat(num_stream_bytes) be.push_back(1'b1); |
| repeat(bus_width) be.push_back(1'b0); |
| |
| // now shift data to match the alignment |
| repeat(byte_offset) p.push_back(8'b0); |
| foreach(rw.value[idx]) |
| for(int i=0;i<bytes_per_value;i++) |
| p.push_back(rw.value[idx][8*i+:8]); |
| |
| |
| if(bit_shift) begin |
| uvm_reg_data_t ac; |
| ac='0; |
| foreach(p[idx]) begin |
| uvm_reg_data_t n; |
| n = (ac | (p[idx] << bit_shift)) & 'hff; |
| ac=(p[idx]>>bit_shift) & 'hff; |
| p[idx]=n; |
| end |
| if(extra_byte) |
| p.push_back(ac); |
| end |
| |
| /* |
| if (uvm_report_enabled(UVM_DEBUG, UVM_INFO, "UVM/REG/ADDR")) begin |
| foreach(be[idx]) |
| `uvm_info("UVM/REG/ADDR",$sformatf("idx %0d en=%0d",idx,be[idx]),UVM_DEBUG) |
| |
| foreach(adr[idx]) |
| `uvm_info("UVM/REG/ADDR",$sformatf("mem-adr %0x byte-offset=%0d",adr[idx],byte_offset),UVM_DEBUG) |
| |
| foreach(p[idx]) |
| `uvm_info("UVM/REG/ADDR",$sformatf("idx %0d data=%x enable=%0d",idx,p[idx],be[idx]),UVM_DEBUG) |
| |
| foreach(rw.value[idx]) |
| `uvm_info("UVM/REG/ADDR",$sformatf("original idx=%0d %0x",idx,rw.value[idx]),UVM_DEBUG) |
| |
| end |
| */ |
| |
| // transform into accesses per address |
| accesses.delete(); |
| foreach(adr[i]) begin |
| uvm_reg_bus_op rw_access; |
| uvm_reg_data_t data; |
| |
| for(int i0=0;i0<bus_width;i0++) |
| data[i0*8+:8]=p[i*bus_width+i0]; |
| |
| `uvm_info("UVM/REG/ADDR", |
| $sformatf("%s 'h%0h at 'h%0h via map \"%s\"...",op, |
| data, adr[i], rw.map.get_full_name()), UVM_FULL) |
| |
| for (int z=0;z<bus_width;z++) |
| rw_access.byte_en[z] = be[bus_width*i+z]; |
| |
| rw_access.kind = rw.kind; |
| rw_access.addr = adr[i]; |
| rw_access.data = data; |
| |
| rw_access.n_bits=8*bus_width; |
| for(int i=bus_width-1;i>=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.kind inside {UVM_READ,UVM_BURST_READ}) begin |
| p.delete(); |
| foreach(accesses[i0]) |
| for(int i1=0;i1<bus_width;i1++) |
| p.push_back(accesses[i0].data[i1*8+:8]); |
| |
| repeat(byte_offset) void'(p.pop_front()); |
| foreach(rw.value[i]) rw.value[i]=0; |
| |
| if(bit_shift) begin |
| uvm_reg_data_t ac; |
| ac='0; |
| for(int i=0;i<p.size();i++) begin |
| byte nv; |
| nv=(p[i] >> bit_shift); |
| if(i!=p.size()-1) |
| nv |= (p[i+1]<<bit_shift); |
| |
| p[i] = nv; |
| end |
| if(extra_byte) |
| void'(p.pop_back()); |
| end |
| |
| foreach(rw.value[idx]) |
| for(int i0=0;i0<bytes_per_value;i0++) |
| rw.value[idx][i0*8+:8]= p[idx*bytes_per_value+i0]; |
| |
| if(rw.element_kind == UVM_FIELD) begin |
| uvm_reg_field f; |
| uvm_reg_data_t m; |
| $cast(f,rw.element); |
| |
| m = (1 << f.get_n_bits())-1; |
| foreach(rw.value[idx]) |
| rw.value[idx] &= m; |
| end |
| |
| /* |
| if (uvm_report_enabled(UVM_DEBUG, UVM_INFO, "UVM/REG/ADDR")) |
| foreach(rw.value[idx]) |
| ` uvm_info("UVM/REG/ADDR",$sformatf("read return idx=%0d %0x",idx,rw.value[idx]),UVM_DEBUG) |
| */ |
| |
| end |
| end |
| endtask |
| |
| task uvm_reg_map::do_bus_read (uvm_reg_item rw, |
| uvm_sequencer_base sequencer, |
| uvm_reg_adapter adapter); |
| |
| do_bus_access(rw, sequencer, adapter); |
| |
| endtask: do_bus_read |
| |
| |
| |
| //------------- |
| // Standard Ops |
| //------------- |
| |
| // do_print |
| |
| function void uvm_reg_map::do_print (uvm_printer printer); |
| uvm_reg regs[$]; |
| uvm_vreg vregs[$]; |
| uvm_mem mems[$]; |
| uvm_endianness_e endian; |
| uvm_reg_map maps[$]; |
| string prefix; |
| uvm_sequencer_base sqr=get_sequencer(); |
| |
| super.do_print(printer); |
| |
| endian = get_endian(UVM_NO_HIER); |
| |
| printer.print_generic("endian","",-2,endian.name()); |
| printer.print_field_int("n_bytes", get_n_bytes(UVM_NO_HIER), 64, UVM_DEC); |
| printer.print_field_int("byte addressing",get_addr_unit_bytes()==1,64,UVM_DEC); |
| |
| if(sqr!=null) |
| printer.print_generic("effective sequencer",sqr.get_type_name(),-2,sqr.get_full_name()); |
| |
| get_registers(regs,UVM_NO_HIER); |
| foreach (regs[j]) |
| printer.print_generic(regs[j].get_name(), regs[j].get_type_name(),-2,$sformatf("@%0d +'h%0x",regs[j].get_inst_id(),regs[j].get_address(this))); |
| |
| |
| get_memories(mems); |
| foreach (mems[j]) |
| printer.print_generic(mems[j].get_name(), mems[j].get_type_name(),-2,$sformatf("@%0d +'h%0x",mems[j].get_inst_id(),mems[j].get_address(0,this))); |
| |
| get_virtual_registers(vregs); |
| foreach (vregs[j]) |
| printer.print_generic(vregs[j].get_name(), vregs[j].get_type_name(),-2,$sformatf("@%0d +'h%0x",vregs[j].get_inst_id(),vregs[j].get_address(0,this))); |
| |
| get_submaps(maps); |
| foreach (maps[j]) |
| printer.print_object(maps[j].get_name(),maps[j]); |
| endfunction |
| |
| // convert2string |
| |
| function string uvm_reg_map::convert2string(); |
| uvm_reg regs[$]; |
| uvm_vreg vregs[$]; |
| uvm_mem mems[$]; |
| uvm_endianness_e endian; |
| string prefix; |
| |
| $sformat(convert2string, "%sMap %s", prefix, get_full_name()); |
| endian = get_endian(UVM_NO_HIER); |
| $sformat(convert2string, "%s -- %0d bytes (%s)", convert2string, |
| get_n_bytes(UVM_NO_HIER), endian.name()); |
| get_registers(regs); |
| foreach (regs[j]) begin |
| $sformat(convert2string, "%s\n%s", convert2string, |
| regs[j].convert2string());//{prefix, " "}, this)); |
| end |
| get_memories(mems); |
| foreach (mems[j]) begin |
| $sformat(convert2string, "%s\n%s", convert2string, |
| mems[j].convert2string());//{prefix, " "}, this)); |
| end |
| get_virtual_registers(vregs); |
| foreach (vregs[j]) begin |
| $sformat(convert2string, "%s\n%s", convert2string, |
| vregs[j].convert2string());//{prefix, " "}, this)); |
| end |
| endfunction |
| |
| |
| // clone |
| |
| function uvm_object uvm_reg_map::clone(); |
| `uvm_fatal("UVM/REGMAP/NOCLONE","uvm_reg_map doesnt support clone()") |
| return null; |
| endfunction |
| |
| |
| // do_copy |
| |
| function void uvm_reg_map::do_copy (uvm_object rhs); |
| //uvm_reg_map rhs_; |
| //if (!$cast(seq, o)) |
| // `uvm_fatal(...) |
| |
| //rhs_.regs = regs; |
| //rhs_.mems = mems; |
| //rhs_.vregs = vregs; |
| //rhs_.blks = blks; |
| //... and so on |
| endfunction |
| |