How to support multiple register maps in a SOC system for a block with only one register map?

I have a question of how to reuse a block reg_block to a multi-processor SOC system level.
Usually a block has only one configuration interface(AHB, for example), so in the block level environment, it will instantiate its UVM register block(block register model) which only has one UVM register map.

But when the block is reused to the system, especially for a multi-processor system, there should be multiple register maps, and different register map is for different processor. How can we reuse the block register model to the system level and incorporate multiple register maps?

A graceful(also natural) way in my mind is that we should create a top reg_block for the system level with multiple register maps which has a reference to the block register model, and call add_submap() API to add the UVM register map defined in the block register model into different register maps in system level, but it seems to me that the current UVM can’t support adding one UVM register map into multiple parent register maps. (i.e. UVM reg_map can’t support multiple parents, see below code snippets for details)

Can anyone give some suggestions on how to handle that? Thanks a lot!

P.S the relevant UVM source code snippets are as below:

function void uvm_reg_map::add_submap (uvm_reg_map child_map,
957 uvm_reg_addr_t offset);
958 uvm_reg_map parent_map;
960 if (child_map == null) begin
961 uvm_error("RegModel", {"Attempting to add NULL map to map '",get_full_name(),"'"}) 962 return; 963 end 964 965 parent_map = child_map.get_parent_map(); 966 **967 // Can not have more than one parent (currently) 968 if (parent_map != null) begin 969 uvm_error(“RegModel”, {“Map '”, child_map.get_full_name(),
970 “’ is already a child of map '”,
971 parent_map.get_full_name(),
972 “'. Cannot also be a child of map '”,
973 get_full_name(),
974 “'”})__
975 return;
976 end**
978 begin : parent_block_check
979 uvm_reg_block child_blk = child_map.get_parent();
980 if (child_blk == null) begin
981 uvm_error("RegModel", {"Cannot add submap '",child_map.get_full_name(), 982 "' because it does not have a parent block"}) 983 return; 984 end 985 if (get_parent() != child_blk.get_parent()) begin 986 uvm_error(“RegModel”,
987 {“Submap '”,child_map.get_full_name(),“’ may not be added to this “,
988 “address map, '”, get_full_name(),”', as the submap’s parent block, '”,
989 child_blk.get_full_name(),“', is not a child of this map’s parent block, '”,
990 m_parent.get_full_name(),“'”})
991 return;
992 end
993 end
995 begin : n_bytes_match_check
996 if (m_n_bytes > child_map.get_n_bytes(UVM_NO_HIER)) begin
997 `uvm_warning(“RegModel”,
998 $sformatf(“Adding %0d-byte submap ‘%s’ to %0d-byte parent map ‘%s’”,
999 child_map.get_n_bytes(UVM_NO_HIER), child_map.get_full_name(),
1000 m_n_bytes, get_full_name()));
1001 end
1002 end
1004 child_map.add_parent_map(this,offset);
1006 set_submap_offset(child_map, offset);
1008 endfunction: add_submap

In reply to pinegreen:

I remember trying this out too. The documentation was out of sync, stating that this should be possible, but the BCL gave the nice error message. You could go the long way of mapping all registers/memories into the top level maps and forget the map defined in the module register block:

class module_reg_block extends uvm_reg_block;
  // ...
  virtual function void build();
    default_map.add_mem(...);, ...);

Since we can’t add default map to multiple other maps, we’ll just have to remap:

class system_reg_block extends uvm_reg_block;
  module_reg_block module_block;

  // ...
  virtual function void build();
    master0_map.add_mem(module_block.mem, ...);, ...);
    master1_map.add_mem(module_block.mem, ...);, ...);

You could also refactor mapping register elements into a single function that takes the map as an argument to avoid duplication.

In reply to Tudor Timi:

Thank you Tudor.
It seems that$reg_map, …) is not a pre-defined method for uvm_reg_block, right?

Do you mean we can create a “map($reg_map, …)” method inside a $reg_block which takes $reg_map as a argument and calls $reg_map.add_reg(…) to add all registers to that $reg_block, and in system level, that $$sys_reg_map, …) method can be called to map all registers in the block $reg_block to different reg_maps?

But It seems to me that the following code defined in uvm_reg_map requires that the uvm_reg_map and the uvm_reg have the same parent.
So it seems that the multiple reg_maps needed to be defined and instantiated in each block $reg_block, but it is not good since multiple reg_maps should be invisible in block level from block reuse perspective.

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

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

   if (rg.get_parent() != get_parent()) begin
         {"Register '",rg.get_full_name(),"' may not be added to address map '",
          get_full_name(),"' : they are not in the same block"})

   uvm_reg_map_info info = new;
   info.offset   = offset;
   info.rights   = rights;
   info.unmapped = unmapped; = frontdoor;
   m_regs_info[rg] = info;

In reply to pinegreen:

The trick with the map(…) function I “borrowed” from the UVM user guide.

I didn’t know there was this crazy limitation to only add regs from the same block to a reg map. I guess you could work around it by declaring the system maps inside the system reg block and then pass the module reg blocks as parent arguments:

class system_reg_block extends uvm_reg_block;
  module_reg_block module_block;
  uvm_reg_map system_map;

  virtual function void build();
    system_map = uvm_reg_map::type_id::create("system_map", , module_block.get_full_name());
    system_map.configure(module_block, ...);

It’s kind of like adding the system map inside the module level block, but this way you don’t touch the code for the module register block. You’ll have to see if there are any other repercussions of doing it like this.

In reply to Tudor Timi:

Thank you Tudor for your kindly response. And I summarize your steps to resolve the multiple maps issues and also retain the independence of block level as below:

  1. In block level, map($reg_map, …) function for each block $reg_block class should be defined, which can map all the registers inside that $reg_block to the $reg_map, and take $reg_map as one of the input arguments.

  2. In system level, multiple block reg_maps need to be defined and created for each block, and block $reg_block references should be used as the parent of the block reg_maps, and call $$block_reg_map, …) of each $reg_block to map all internal registers of the $reg_block to that $block_reg_map.

  3. Also in system level, multiple system reg_maps need to be defined and created for system level, and each $block_reg_map need to be added to its corresponding system $system_reg_map using add_submap() function calls.

You workaround seems to work for me, but I still doubt why UVM can’t provide an easier way to support multiple reg_maps, which seems common in a SOC system.
For example, if it can support adding one reg_map to multiple parent reg_maps, things can become much easier.

In reply to pinegreen:

I think you can skip step 3 and just use the maps you created in step 2 directly.

Unfortunately, a lot of things are left unfinished in the BCL, which make it necessary to use workarounds.

In reply to pinegreen:

Could you share an example of your case?
Thank you