// -------------------------------------------------------------
// Copyright 2010 AMD
// Copyright 2012 Accellera Systems Initiative
// Copyright 2010-2018 Cadence Design Systems, Inc.
// Copyright 2017 Cisco Systems, Inc.
// Copyright 2014-2017 Intel Corporation
// Copyright 2021 Marvell International Ltd.
// Copyright 2010-2020 Mentor Graphics Corporation
// Copyright 2014-2020 NVIDIA Corporation
// Copyright 2014 Semifore
// Copyright 2004-2018 Synopsys, Inc.
//    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; 
 `ifdef UVM_VCS_INDEX_BY_ID
   // if indexing by name or id, we need to store the object itself
   uvm_reg                rg;
   uvm_mem                mem;
`endif
 
  // 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
`ifdef UVM_VCS_FOREACH_OPT
// strct to hold range information for batch processing
class uvm_range_info extends uvm_void;
  uvm_reg_map_info info;
  uvm_reg_addr_t addr;
  uvm_reg_map_addr_range range;
  bit is_mem;

   //constructor
   function new(uvm_reg_map_info i, uvm_reg_addr_t addr, uvm_reg_map_addr_range range, bit is_mem);
     info = i;
     this.addr = addr;
     this.range = range;
     this.is_mem = is_mem;
   endfunction

  function int get_offset();
    return range.min;
  endfunction

  // only support for registers
  function bit overlapping_reg(uvm_reg_addr_t min, uvm_reg_addr_t max);
    if (is_mem) return 0;
    return (addr >= min && addr <= max);
  endfunction

   // only support for memories
   function bit overlapping_mem(uvm_reg_addr_t min, uvm_reg_addr_t max);
      if (!is_mem) return 0;
      return (min <= range.max && max >= range.max ||
             min <= range.min && max >= range.min ||
             min >= range.min && max <= range.max);
   endfunction
endclass

class avl_node;
  typedef uvm_range_info T;
  int unsigned offset;
  T node_data;
  avl_node left, right;
  int height;

  function new(uvm_reg_addr_t addr, T data);
    offset = addr;
    node_data = data;
    height = 1;
  endfunction

  function bit overlapping_reg(uvm_reg_addr_t min, uvm_reg_addr_t max);
    return node_data.overlapping_reg(min, max);
  endfunction

   function bit overlapping_mem(uvm_reg_addr_t min, uvm_reg_addr_t max);
      return node_data.overlapping_mem(min, max);
   endfunction
endclass


class avl_tree;;
  typedef uvm_range_info T;
  typedef avl_node avl_node_t;
  avl_node_t root;

  // Utility: Get height
  function int get_height(avl_node_t n);
    return (n == null) ? 0 : n.height;
  endfunction

  // Utility: Get balance factor
  function int get_balance(avl_node_t n);
    return (n == null) ? 0 : get_height(n.left) - get_height(n.right);
  endfunction

  // Utility: Max of two integers
   function int max(int a, int b);
      return (a > b) ? a : b;
   endfunction

  // Right rotate
  function avl_node_t rotate_right(avl_node_t y);
    avl_node_t x = y.left;
    avl_node_t T2 = x.right;

    x.right = y;
    y.left = T2;

    y.height = 1 + max(get_height(y.left), get_height(y.right));
    x.height = 1 + max(get_height(x.left), get_height(x.right));

    return x;
  endfunction

  // Left rotate
  function avl_node_t rotate_left(avl_node_t x);
    avl_node_t y = x.right;
    avl_node_t T2 = y.left;

    y.left = x;
    x.right = T2;

    x.height = 1 + max(get_height(x.left), get_height(x.right));
    y.height = 1 + max(get_height(y.left), get_height(y.right));

    return y;
  endfunction

  // Insert node
  function avl_node_t insert_node(avl_node_t node, uvm_reg_addr_t offset, T data);
    int balance;
    if (node == null) begin
      avl_node_t new_node;
      new_node = new(offset, data);
      return new_node;
    end  

    if (offset < node.offset)
      node.left = insert_node(node.left, offset, data);
    else if (offset > node.offset)
      node.right = insert_node(node.right, offset, data);
    else
      return node; // Duplicate offset, skip or update

    node.height = 1 + max(get_height(node.left), get_height(node.right));
    balance = get_balance(node);

    // Balancing cases
    if (balance > 1 && offset < node.left.offset)
      return rotate_right(node);
    if (balance < -1 && offset > node.right.offset)
      return rotate_left(node);
    if (balance > 1 && offset > node.left.offset) begin
      node.left = rotate_left(node.left);
      return rotate_right(node);
    end
    if (balance < -1 && offset < node.right.offset) begin
      node.right = rotate_right(node.right);
      return rotate_left(node);
    end

    return node;
  endfunction
  

function void insert(uvm_reg_addr_t offset, T data);
    root = insert_node(root, offset, data);
  endfunction

  function T find_by_key(uvm_reg_addr_t offset);
    avl_node_t curr = root;
    while (curr != null) begin
      if (offset == curr.offset)
        return curr.node_data;
      else if (offset < curr.offset)
        curr = curr.left;
      else
        curr = curr.right;
    end
    return null;
  endfunction

// implement traveral to find all overlappint regs
function void find_overlapping_regs(uvm_reg_addr_t min, uvm_reg_addr_t max, ref T result[$]);
   avl_node_t curr = root;
   while (curr != null) begin
     if (curr.overlapping_reg(min, max))
       result.push_back(curr.node_data);
     if (curr.left != null && min < curr.offset)
       curr = curr.left;
     else
       curr = curr.right;
   end
endfunction

// implement traveral to find all overlapping mems
function void find_overlapping_mems(uvm_reg_addr_t min, uvm_reg_addr_t max, ref T result[$]);
   avl_node_t curr = root;
   while (curr != null) begin
     if (curr.overlapping_mem(min, max))
       result.push_back(curr.node_data);
     if (curr.left != null && min < curr.offset)
       curr = curr.left;
     else
       curr = curr.right;
   end
endfunction

  function void replace_by_key(int unsigned offset,T data );
    avl_node_t curr = root;
    while (curr != null) begin
      if (offset == curr.offset) begin
        curr.node_data = data;
        return;
      end else if (offset < curr.offset)
        curr = curr.left;
      else
        curr = curr.right;
    end
  endfunction


endclass

`endif // UVM_VCS_FOREACH_OPT 


// 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-2020 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
`ifdef UVM_VCS_INDEX_BY_ID
   local uvm_reg_map_info   m_regs_info[int];
   local uvm_reg_map_info   m_mems_info[int];
   local int            m_regs_info_q[$]; // To avoid foreach on associative array
`else

  local uvm_reg_map_info   m_regs_info[uvm_reg];
  local uvm_reg_map_info   m_mems_info[uvm_mem];
`endif

  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;

  `ifdef UVM_SPARSE_ARRAY
  local uvm_reg_map_info   m_reg_arrays_info[uvm_reg_file];
  local uvm_reg_file       m_reg_arrays_by_offset[uvm_reg_addr_t];
 `endif
`ifdef UVM_VCS_INDEX_BY_ID
`ifdef UVM_VCS_FOREACH_OPT
   local avl_tree addr_tree_reg;
   local avl_tree addr_tree_mem;
`endif // UVM_VCS_FOREACH_OPT

   local bit              m_scaling_computed;
`ifdef UVM_REG_BYTE_ADDRESSING_FIX
   local real              k; // scaling factor between this map and parent map
`else
   local int              k; // scaling factor between this map and parent map
`endif
`endif

  extern /*local*/ function void Xinit_address_mapX();
    
    static local uvm_reg_map   m_backdoor;


    // @uvm-ieee 1800.2-2020 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-2020 auto 18.2.3.1
    extern function new(string name="uvm_reg_map");



  // @uvm-ieee 1800.2-2020 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-2020 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);
   `ifdef UVM_SPARSE_ARRAY
   // Function: add_reg_array
   //
   // Add a register
   //
   // Add the specified register array instance ~rg~ to this address map.
   //
   // The register array is located at the specified address ~offset~ from
   // this maps configured base address.
   //
   // The ~rights~ specify the register array's accessibility via this map.
   // Valid values are "RW", "RO", and "WO". Whether a register field
   // can be read or written depends on both the field's configured access
   // policy (see <uvm_reg_field::configure> and the register's rights in
   // the map being used to access the field. 
   //
   // The number of consecutive physical addresses occupied by the register array
   // depends on the width of the register and the number of bytes in the
   // physical interface corresponding to this address map and the number of
   // registers in the array.
   //
   // If ~unmapped~ is TRUE, the register array does not occupy any
   // physical addresses and the base address is ignored.
   // Unmapped register arrays require a user-defined ~frontdoor~ to be specified.
   //
   // A register array may be added to multiple address maps
   // if it is accessible from multiple physical interfaces.
   // A register array may only be added to an address map whose parent block
   // is the same as the register array's parent block.
   //
   extern virtual function void add_reg_array (uvm_reg_file rg,
                                         uvm_reg_addr_t    offset,
                                         string            rights = "RW",
                                         bit               unmapped=0,
                                         uvm_reg_frontdoor frontdoor=null);

   `endif





  // @uvm-ieee 1800.2-2020 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-2020 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-2020 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-2020 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-2020 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-2020 auto 18.2.3.9
  extern virtual function void   set_base_addr (uvm_reg_addr_t  offset);



  // @uvm-ieee 1800.2-2020 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-2020 auto 18.2.4.1
  extern virtual function uvm_reg_map get_root_map();



  // @uvm-ieee 1800.2-2020 auto 18.2.4.2
  extern virtual function uvm_reg_block get_parent();



  // @uvm-ieee 1800.2-2020 auto 18.2.4.3
  extern virtual function uvm_reg_map           get_parent_map();



  // @uvm-ieee 1800.2-2020 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();

`ifdef UVM_REG_BYTE_ADDRESSING_FIX
   // Function: get_byte_addressing
   //
   // Get the address granularity of the map
   // Returns 1 if the address map was configured using byte-level addressing.
   // Returns 0 otherwise.
   //
   extern virtual function bit get_byte_addressing();
`endif


  // @uvm-ieee 1800.2-2020 auto 18.2.4.7
  extern virtual function uvm_endianness_e get_endian (uvm_hier_e hier=UVM_HIER);



  // @uvm-ieee 1800.2-2020 auto 18.2.4.8
  extern virtual function uvm_sequencer_base get_sequencer (uvm_hier_e hier=UVM_HIER);



  // @uvm-ieee 1800.2-2020 auto 18.2.4.9
  extern virtual function uvm_reg_adapter get_adapter (uvm_hier_e hier=UVM_HIER);



  // @uvm-ieee 1800.2-2020 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-2020 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-2020 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-2020 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-2020 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-2020 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);
   `ifdef UVM_SPARSE_ARRAY
   extern virtual function uvm_reg_map_info get_reg_array_map_info(uvm_reg_file rga,  bit error=1);
   `endif

  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-2020 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-2020 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-2020 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-2020 auto 18.2.5.2
  function void set_auto_predict(bit on=1); m_auto_predict = on; endfunction


  
  // @uvm-ieee 1800.2-2020 auto 18.2.5.1
  function bit  get_auto_predict(); return m_auto_predict; endfunction


  
  // @uvm-ieee 1800.2-2020 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-2020 auto 18.2.5.5
  function void set_transaction_order_policy(uvm_reg_transaction_order_policy pol);
    policy = pol;
  endfunction
  

  // @uvm-ieee 1800.2-2020 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-2020 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 = 1'b0;
          m_check_on_read = 1'b0;
          `ifdef UVM_VCS_INDEX_BY_ID
          `ifdef UVM_VCS_FOREACH_OPT
          addr_tree_reg = new();
          addr_tree_mem = new();
          `endif // UVM_VCS_FOREACH_OPT
          `endif
          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);
`ifdef UVM_VCS_INDEX_BY_ID
   int obj_id;
   obj_id = rg.get_inst_id();
   if (m_regs_info.exists(obj_id)) begin
      `uvm_error("RegModel", {"Register '",rg.get_name(),
                 "' has already been added to map '",get_name(),"'"})
      return;
   end
`else

  if (m_regs_info.exists(rg)) begin
    `uvm_error("RegModel", {"Register '",rg.get_name(),
                            "' has already been added to map '",get_full_name(),"'"})
    return;
  end
`endif  

  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;
    `ifdef UVM_VCS_INDEX_BY_ID
    info.rg = rg;
    m_regs_info[obj_id] = info;
    m_regs_info_q.push_back(obj_id);
    `else
    info.is_initialized=1'b0;
    m_regs_info[rg]=info;	   
    `endif    
  end
endfunction


`ifdef UVM_SPARSE_ARRAY
// add_reg_array

function void uvm_reg_map::add_reg_array(uvm_reg_file rg, 
                                   uvm_reg_addr_t offset,
                                   string rights = "RW",
                                   bit unmapped=0,
                                   uvm_reg_frontdoor frontdoor=null);

   uvm_reg default_reg;
   if (m_reg_arrays_info.exists(rg)) begin
      `uvm_error("RegModel", {"Register array '",rg.get_name(),
                 "' has already been added to map '",get_name(),"'"})
      return;
   end
    
   if (rg.get_parent() != get_parent()) begin
      `uvm_error("RegModel",
         {"Register array '",rg.get_full_name(),"' may not be added to address map '",
          get_full_name(),"' : they are not in the same block"})
      return;
   end
   
   default_reg = rg.get_default_reg();
   default_reg.add_map(this);

   begin
   uvm_reg_map_info info = new;
   info.offset   = offset;
   info.rights   = rights;
   info.unmapped = unmapped;
   info.frontdoor = frontdoor;
   m_reg_arrays_info[rg] = info;
   end
endfunction


// set_reg_map_info

function void uvm_reg_map::set_reg_map_info(uvm_reg rg,
                                            uvm_reg_map_info info);
`ifdef UVM_VCS_INDEX_BY_ID
   int obj_id;
   obj_id = rg.get_inst_id();
   if (m_regs_info.exists(obj_id)) begin
      `uvm_error("RegModel", {"Register '",rg.get_name(),
                 "' has already been added to map '",get_name(),"'"})
      return;
   end
`else

   if (m_regs_info.exists(rg)) begin
      `uvm_error("RegModel", {"Register '",rg.get_name(),
                 "' has already been added to map '",get_name(),"'"})
      return;
   end
`endif   

   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

 `ifdef UVM_VCS_INDEX_BY_ID
   m_regs_info[obj_id] = info;
   m_regs_info[obj_id].rg = rg;
   m_regs_info_q.push_back(obj_id);
`else
  
   m_regs_info[rg] = info;
`endif   
endfunction
`endif //UVM_SPARSE_ARRAY

//m_set_reg_offset

function void uvm_reg_map::m_set_reg_offset(uvm_reg rg, 
                                            uvm_reg_addr_t offset,
                                            bit unmapped);
`ifdef UVM_VCS_INDEX_BY_ID
int obj_id;
obj_id = rg.get_inst_id();
if (!m_regs_info.exists(obj_id)) 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
`else

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
`endif

  begin
  `ifdef UVM_VCS_INDEX_BY_ID
  uvm_reg_map_info info    = m_regs_info[obj_id];
  `else
  uvm_reg_map_info info    = m_regs_info[rg];
  `endif

    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);
`ifdef UVM_VCS_INDEX_BY_ID
int obj_id;
obj_id = mem.get_inst_id();
if (m_mems_info.exists(obj_id)) begin
`uvm_error("RegModel", {"Memory '",mem.get_name(),
        "' has already been added to map '",get_name(),"'"})
return;
end
`else
if (m_mems_info.exists(mem)) begin
`uvm_error("RegModel", {"Memory '",mem.get_name(),
        "' has already been added to map '",get_full_name(),"'"})
return;
end
`endif  

  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;
`ifdef UVM_VCS_INDEX_BY_ID
    info.mem = mem;
    m_mems_info[obj_id] = info;
`else
    m_mems_info[mem] = info;
`endif    
    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);
`ifdef UVM_VCS_INDEX_BY_ID
   int obj_id;
   obj_id = mem.get_inst_id();
   if (!m_mems_info.exists(obj_id)) 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
`else

  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
`endif  

  begin
`ifdef UVM_VCS_INDEX_BY_ID
      uvm_reg_map_info info    = m_mems_info[obj_id];
      `else

    uvm_reg_map_info info    = m_mems_info[mem];
`endif    
    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


`ifdef UVM_REG_BYTE_ADDRESSING_FIX
// get_byte_addressing

function bit uvm_reg_map::get_byte_addressing();
   return m_byte_addressing;
endfunction
`endif

// 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);
`ifdef UVM_VCS_INDEX_BY_ID
   foreach(m_regs_info[obj_id])
     regs.push_back(m_regs_info[obj_id].rg);
`else

  foreach (m_regs_info[rg])
    regs.push_back(rg);
`endif    

  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);
`ifdef UVM_VCS_INDEX_BY_ID
   foreach (m_regs_info[obj_id_]) begin
     uvm_reg rg = m_regs_info[obj_id_].rg;
`else

  foreach (m_regs_info[rg_]) begin
    uvm_reg rg = rg_;
`endif    
    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);
`ifdef UVM_VCS_INDEX_BY_ID
   foreach (m_mems_info[obj_id_]) begin
     uvm_mem mem = m_mems_info[obj_id_].mem;
     mems.push_back(mem);
   end  
`else

  foreach (m_mems_info[mem])
    mems.push_back(mem);
`endif  
  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);
`ifdef UVM_VCS_INDEX_BY_ID
    if (!m_mems_info.exists(mem.get_inst_id())) 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.get_inst_id()];   
    `else
    if (!m_mems_info.exists(mem)) begin
if (error)
    `uvm_error("REG_NO_MAP",{"Memory '",mem.get_name(),"' not in map '",get_full_name(),"'"})
    return null;
    end
    return m_mems_info[mem];
    `endif  
endfunction


`ifdef UVM_SPARSE_ARRAY
// get_reg_array_map_info

function uvm_reg_map_info uvm_reg_map::get_reg_array_map_info(uvm_reg_file rga, bit error=1);
  if (!m_reg_arrays_info.exists(rga)) begin
    if (error)
      `uvm_error("REG_NO_MAP",{"Register Array '",rga.get_name(),"' not in map '",get_name(),"'"})
    return null;
  end
  return m_reg_arrays_info[rga];
endfunction
`endif //UVM_SPARSE_ARRAY


// 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;
`ifdef UVM_VCS_INDEX_BY_ID
  int obj_id;
  obj_id = rg.get_inst_id();
    if (!m_regs_info.exists(obj_id)) begin
if (error)
    `uvm_error("REG_NO_MAP",{"Register '",rg.get_name(),"' not in map '",get_name(),"'"})
    return null;
    end
    result = m_regs_info[obj_id];  
    `else
    if (!m_regs_info.exists(rg)) begin
    if (error)
      `uvm_error("REG_NO_MAP",{"Register '",rg.get_name(),"' not in map '",get_full_name(),"'"})
    return null;
  end
  result = m_regs_info[rg];
`endif  
  if(!result.is_initialized)
    `uvm_warning("RegModel",{"map '",get_full_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
  `ifdef UVM_VCS_INDEX_BY_ID
  foreach(m_regs_info[obj_id_]) begin
  int obj_id = obj_id;
  uvm_reg rg = m_regs_info[obj_id].rg;
  addr = m_regs_info[obj_id].offset + ((rg.get_n_bytes()-1)/m_n_bytes);
  if (addr > max_addr) max_addr = addr;
  end
  `else
  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
`endif  

  // get max offset from memories
`ifdef UVM_VCS_INDEX_BY_ID
  foreach (m_mems_info[obj_id_]) begin
      int obj_id = obj_id;
      uvm_mem mem = m_mems_info[obj_id].mem;
      addr = m_mems_info[obj_id].offset + (mem.get_size() * (((mem.get_n_bytes()-1)/m_n_bytes)+1)) -1;
      if (addr > max_addr) max_addr = addr;
  end
  `else

  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
`endif  

  // 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

`ifdef UVM_VCS_INDEX_BY_ID
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_reg_addr_t  addr_q[$];
    uvm_endianness_e endian;
    int unsigned get_addr_unit_bytes_;

    get_addr_unit_bytes_ = get_addr_unit_bytes();
    up_map = get_parent_map();
    endian = get_endian(UVM_NO_HIER);

//	`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)

	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 += 32'(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)];
			int n;
            n = 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 += 32'(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 (endian) 
				UVM_LITTLE_ENDIAN: begin
                        for (int i=0; i<n; i++) begin
                        local_addr.push_back(lbase_addr2 + (i * bus_width/get_addr_unit_bytes_ ));    
					end
				end
				UVM_BIG_ENDIAN: begin
                        for (int i=0; i<n; i++) begin 
                            local_addr.push_back(lbase_addr2 + (local_addr.size()-1-i)*bus_width/get_addr_unit_bytes_);
					end
				end
				UVM_LITTLE_FIFO: begin
                        for (int i=0; i<n; i++) begin 
                            local_addr.push_back(lbase_addr2);
					end
				end
				UVM_BIG_FIFO: begin
                        for (int i=0; i<n; i++) begin
                            local_addr.push_back(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
                    addr_q = local_addr;
                    for (int i = 0; i < addr_q.size(); i++) begin 
                        addr_q[i] += lbase_addr;
                    end
                    addr = addr_q;

// now need to scale in terms of upper map
			
		end	
endfunction
`else

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
`endif
// 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
`ifdef UVM_VCS_INDEX_BY_ID
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


`ifdef UVM_VCS_FOREACH_OPT
   // lets replace existing technique of having foreach nested loops to check for conflicts for each of registers and memories
   // with a more efficient approach of creating an avl tree of addresses and checking for conflicts while inserting into the tree.
   // This should reduce the complexity from O(n^2) to O(n log n) for n registers/memories
   for (int i = 0; i < m_regs_info_q.size(); i++) begin
     int obj_id = m_regs_info_q[i];
     uvm_reg_map_info info = m_regs_info[obj_id];
     uvm_reg rg = info.rg;
     uvm_range_info overlap_mems[$];
     info.is_initialized=1'b1;
     if (!info.unmapped) begin
       string rg_acc = rg.Xget_fields_accessX(this);
       uvm_reg_addr_t addrs[];
       
       bus_width = get_physical_addresses(info.offset,0,rg.get_n_bytes(),addrs);
       foreach (addrs[i]) begin 
         uvm_reg_addr_t addr = addrs[i];
         uvm_range_info matched_range;
         uvm_reg_map_addr_range reg_range;
         uvm_range_info range;
         reg_range = '{addr, addr, 0};
         matched_range = top_map.addr_tree_reg.find_by_key(addr);
         range = new(info, addr, reg_range, 0);
         if (matched_range != null) begin
            uvm_reg rg2 = matched_range.info.rg;
            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;
               addr_tree_reg.replace_by_key(addr,range);
               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 begin
            top_map.m_regs_by_offset[addr] = rg;
            top_map.addr_tree_reg.insert(addr, range);
         end 

         // Also check for memory overlaps
         addr_tree_mem.find_overlapping_mems(addr, addr, overlap_mems);
         foreach (overlap_mems[i]) begin
           uvm_range_info range = overlap_mems[i];
           string a,b;
           a = $sformatf("%0h",addr);
           b = $sformatf("[%0h:%0h]",range.range.min,range.range.max);
           `uvm_warning("RegModel", {"In map '",get_full_name(),"' register '",
               rg.get_full_name(), "' with address ",a,
               "maps to same address as memory '",
               range.info.mem.get_full_name(),"': ",b})
         end
       end
       info.addr = addrs;

      end
   end
          
        
       

   `ifdef UVM_SPARSE_ARRAY
   foreach (m_reg_arrays_info[rg_]) begin
     uvm_reg_file rg = rg_;
     m_reg_arrays_info[rg].is_initialized=1;
     if (!m_reg_arrays_info[rg].unmapped) begin
//        string rg_acc = rg.Xget_fields_accessX(this);
       uvm_reg_addr_t addrs[];
        
       bus_width = get_physical_addresses(m_reg_arrays_info[rg].offset,0,rg.get_size()*rg.get_default_reg().get_n_bytes(),addrs);
        
       foreach (addrs[i]) begin
         uvm_reg_addr_t addr = addrs[i];

         if (top_map.m_reg_arrays_by_offset.exists(addr)) begin
              `uvm_fatal("RegModel","uvm_reg_map lock on UVM SPARSE array is not handled for this condition");  

         end
         else
            top_map.m_reg_arrays_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_reg_arrays_info[rg].addr = addrs;
     end
   end
   `endif //UVM_SPARSE_ARRAY


   foreach (m_mems_info[mem_]) begin
     uvm_reg_map_info info = m_mems_info[mem_];
     uvm_mem mem = info.mem;
     if (!info.unmapped) begin
       uvm_reg_addr_t addrs[],addrs_max[];
       uvm_reg_addr_t min, max, min2, max2;
       int unsigned stride;
       uvm_range_info overlap_regs[$];
       uvm_range_info overlap_mems[$];
       uvm_range_info range;
       int unsigned bo;

       bus_width = get_physical_addresses_to_map(info.offset,0,mem.get_n_bytes(),addrs,null,bo,mem);
       min = (addrs[0] < addrs[addrs.size()-1]) ? addrs[0] : addrs[addrs.size()-1];
//       min2 = addrs[0];

       void'(get_physical_addresses_to_map(info.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();
       // address interval between consecutive mem offsets
//       stride = 32'((max2 - min2)/(mem.get_size()-1));

       top_map.addr_tree_reg.find_overlapping_regs(min, max, overlap_regs);
       foreach (overlap_regs[i]) begin
         string a;
         range = overlap_regs[i];
         a = $sformatf("%0h",range.addr);
           `uvm_warning("RegModel", {"In map '",get_full_name(),"' memory '",
               mem.get_full_name(), "' maps to same address as register '",
               range.info.rg.get_full_name(),"': 'h",a})
       end

       top_map.addr_tree_mem.find_overlapping_mems(min, max, overlap_mems);
       foreach (overlap_mems[i]) begin
         uvm_range_info range = overlap_mems[i];
         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 '",
            range.info.mem.get_full_name(),"': 'h",a})
       end

       begin
         uvm_reg_map_addr_range addr_range = '{ min, max, stride };
         top_map.m_mems_by_offset[ addr_range ] = mem;
         
         info.addr  = addrs;
         info.mem_range = addr_range;
         range = new(info, min, addr_range, 1);
         top_map.addr_tree_mem.insert(min, range);
        end
     end  
   end
   `else // UVM_VCS_FOREACH_OPT
     for (int i = 0; i < m_regs_info_q.size(); i++) begin
       int obj_id = m_regs_info_q[i];
       uvm_reg_map_info info = m_regs_info[obj_id];
       uvm_reg rg = info.rg;
       info.is_initialized=1'b1;
       
       if (!info.unmapped) begin
        string rg_acc = rg.Xget_fields_accessX(this);
       uvm_reg_addr_t addrs[];
        
       bus_width = get_physical_addresses(info.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)) 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
       info.addr = addrs;
     end
   end
   `ifdef UVM_SPARSE_ARRAY
   foreach (m_reg_arrays_info[rg_]) begin
     uvm_reg_file rg = rg_;
     m_reg_arrays_info[rg].is_initialized=1;
     if (!m_reg_arrays_info[rg].unmapped) begin
//        string rg_acc = rg.Xget_fields_accessX(this);
       uvm_reg_addr_t addrs[];
        
       bus_width = get_physical_addresses(m_reg_arrays_info[rg].offset,0,rg.get_size()*rg.get_default_reg().get_n_bytes(),addrs);
        
       foreach (addrs[i]) begin
         uvm_reg_addr_t addr = addrs[i];

         if (top_map.m_reg_arrays_by_offset.exists(addr)) begin
              `uvm_fatal("RegModel","uvm_reg_map lock on UVM SPARSE array is not handled for this condition");  

         end
         else
            top_map.m_reg_arrays_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_reg_arrays_info[rg].addr = addrs;
     end
   end
   `endif //UVM_SPARSE_ARRAY

   foreach (m_mems_info[mem_]) begin
     uvm_reg_map_info info = m_mems_info[mem_];
     uvm_mem mem = info.mem;
     if (!info.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(info.offset,0,mem.get_n_bytes(),addrs,null,bo,mem);
       min = (addrs[0] < addrs[addrs.size()-1]) ? addrs[0] : addrs[addrs.size()-1];

       void'(get_physical_addresses_to_map(info.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];
       // address interval between consecutive mem offsets
       stride = mem.get_n_bytes()/get_addr_unit_bytes();

       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)
             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;
         info.addr  = addrs;
         info.mem_range = range;
       end
     end
   end  
   `endif

   // 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

`else

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

   `ifdef UVM_SPARSE_ARRAY
   foreach (m_reg_arrays_info[rg_]) begin
     uvm_reg_file rg = rg_;
     m_reg_arrays_info[rg].is_initialized=1;
     if (!m_reg_arrays_info[rg].unmapped) begin
//        string rg_acc = rg.Xget_fields_accessX(this);
       uvm_reg_addr_t addrs[];
        
       bus_width = get_physical_addresses(m_reg_arrays_info[rg].offset,0,rg.get_size()*rg.get_default_reg().get_n_bytes(),addrs);
        
       foreach (addrs[i]) begin
         uvm_reg_addr_t addr = addrs[i];

         if (top_map.m_reg_arrays_by_offset.exists(addr)) begin
              `uvm_fatal("RegModel","uvm_reg_map lock on UVM SPARSE array is not handled for this condition");  
//            uvm_reg rg2 = top_map.m_reg_arrays_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_reg_arrays_by_offset[addr]    = rg;
//               uvm_reg_read_only_cbs::add(rg);
//               top_map.m_reg_arrays_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_reg_arrays_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_reg_arrays_by_offset[addr].get_full_name(),"': 'h",a})
//            end
         end
         else
            top_map.m_reg_arrays_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_reg_arrays_info[rg].addr = addrs;
     end
   end
   `endif //UVM_SPARSE_ARRAY

  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
`endif

//-----------
// 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<<get_n_bytes()*8)-1); // mask the upper bits
      
      if(rw.get_kind() inside {UVM_READ,UVM_BURST_READ})
      	if (rw.get_status() == UVM_IS_OK && (^data) === 1'bx)
	      rw.set_status(UVM_HAS_X);
      	
      	rw_access.data=data;    	
      end	

      rw_map = rw.get_map();
      rw_status = rw.get_status();

      `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.get_status() == UVM_NOT_OK)
         break;
       rw_parent_seq = rw.get_parent_sequence();  
        
      if (rw_parent_seq != null && i == accesses.size()-1)
        rw_parent_seq.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;
    
    uvm_reg_data_t rw_value;
    int rw_value_size;
	
	Xget_bus_infoX(rw, map_info, n_bits, lsb, skip);
	addrs=map_info.addr;
	op = (rw.get_kind() inside {UVM_READ,UVM_BURST_READ} ? "Reading" : "Writing");

	case(rw.get_element_kind())
		UVM_MEM: begin
			uvm_mem mem;
			$cast(mem,rw.get_element());
            `ifdef UVM_VCS_INDEX_BY_ID
			void'(get_physical_addresses_to_map(m_mems_info[mem.get_inst_id()].offset,rw.get_offset(),rw.get_value_size()*mem.get_n_bytes(),adr,null,byte_offset,mem));
            `else
			void'(get_physical_addresses_to_map(m_mems_info[mem].offset,rw.get_offset(),rw.get_value_size()*mem.get_n_bytes(),adr,null,byte_offset,mem));
            `endif
			num_stream_bytes =rw.get_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.get_element());

			// adjust adr bit skipped bytes; still need to shift data by byte fractions (lsb)
`ifdef UVM_VCS_INDEX_BY_ID
			void'(get_physical_addresses_to_map(m_regs_info[f.get_parent().get_inst_id()].offset+skip,0,ceil(f.get_n_bits(),8),adr,null,byte_offset));

`else
            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));
`endif
			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.get_element());
`ifdef UVM_VCS_INDEX_BY_ID
			void'(get_physical_addresses_to_map(m_regs_info[r.get_inst_id()].offset,0,r.get_n_bytes(),adr,null,byte_offset));

`else
			void'(get_physical_addresses_to_map(m_regs_info[r].offset,0,r.get_n_bytes(),adr,null,byte_offset));
`endif            
			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 unsigned p[$];
        uvm_reg_data_t values[];
        typedef bit bit_q_t[$];
        
		// 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);
		rw.get_value_array(values);
		foreach(values[idx]) begin
                    for(int i=0;i<bytes_per_value;i++) begin
			p.push_back(values[idx][8*i+:8]);
                    end
                end
			

		if(bit_shift) begin
                  bit bits[$];
                  // The streaming operator is very useful for converting
                  // a byte stream to a bit stream, but the below line
                  // may be a little confusing.
                  // {<<{p}} -> 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 {bit_q_t'({<< {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 {bit_q_t'({<< {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<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], tmp_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.get_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.get_kind() inside {UVM_READ,UVM_BURST_READ}) begin
				int rw_value_size;
                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());
                rw_value_size = rw.get_value_size();
                for(int i = 0; i < rw_value_size; i++) begin
                  rw.set_value(0,i);
                end

		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	

				rw_value_size = rw.get_value_size();
                for(int idx = 0; idx < rw_value_size; idx++) begin
                    rw_value = rw.get_value(idx);
					for(int i0=0;i0<bytes_per_value;i0++)
						rw_value[i0*8+:8]= p[idx*bytes_per_value+i0];
                    rw.set_value(rw_value, idx);
                end
					
				if(rw.get_element_kind() == UVM_FIELD) begin
						uvm_reg_field f;
						uvm_reg_data_t m;
                        
						$cast(f,rw.get_element());
						
						m = (1 << f.get_n_bits())-1;
                        rw_value_size = rw.get_value_size();
                        for(int idx = 0; idx < rw_value_size; idx++) begin
                          rw_value = rw.get_value(idx);
                          rw_value &= m;
                          rw.set_value(rw_value, idx);
                        end			
				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

