Part 9 · Register Model (RAL) · Intermediate

Nested Blocks and Submaps: Multi-Block Address Composition

Composing large RAL trees with child block maps and add_submap at subsystem/chip integration layers.

Why nested submaps scale

Large systems cannot be modeled as a flat register list. Nested blocks with submaps preserve IP-level encapsulation while enabling top-level address composition.

Each child block owns its internal offsets. Parent blocks decide where that entire map lands in the broader address space through add_submap base offsets.

diagram
[RAL] address composition formula

 absolute_addr(reg) =
   top_base
 + parent_submap_base
 + child_submap_base
 + ... (nested levels)
 + reg_offset_in_leaf_map
diagram
[UVM][RAL] hierarchy composition example

 chip_block.default_map
   ├─ add_submap(periph_ss.default_map, 0x0000_0000)
   └─ add_submap(media_ss.default_map,  0x1000_0000)

 periph_ss.default_map
   ├─ add_submap(uart0.default_map, 0x0000_1000)
   └─ add_submap(spi0.default_map,  0x0000_2000)

 uart0.ctrl offset = 0x000
 -> absolute = 0x0000_1000 + 0x000
  • Leaf blocks should never need top-level absolute addresses baked into their code.

  • Submaps provide clean reuse when same IP appears multiple times.

  • Integration teams own base placement while IP teams own local offsets.


Building nested maps step-by-step

Implement and validate one hierarchy level at a time: leaf map first, subsystem composition second, chip map last.

systemverilog
class periph_subsystem_block extends uvm_reg_block;
  `uvm_object_utils(periph_subsystem_block)

  rand uart_block uart0;
  rand spi_block  spi0;
  uvm_reg_map default_map;

  function new(string name = "periph_subsystem_block");
    super.new(name, UVM_NO_COVERAGE);
  endfunction

  virtual function void build();
    uart0 = uart_block::type_id::create("uart0");
    uart0.configure(this);
    uart0.build();

    spi0 = spi_block::type_id::create("spi0");
    spi0.configure(this);
    spi0.build();

    default_map = create_map("default_map", 'h0, 4, UVM_LITTLE_ENDIAN);
    default_map.add_submap(uart0.default_map, 'h1000);
    default_map.add_submap(spi0.default_map,  'h2000);
  endfunction
endclass
systemverilog
class chip_block extends uvm_reg_block;
  `uvm_object_utils(chip_block)

  rand periph_subsystem_block periph_ss;
  rand media_subsystem_block  media_ss;
  uvm_reg_map default_map;

  function new(string name = "chip_block");
    super.new(name, UVM_NO_COVERAGE);
  endfunction

  virtual function void build();
    periph_ss = periph_subsystem_block::type_id::create("periph_ss");
    periph_ss.configure(this);
    periph_ss.build();

    media_ss = media_subsystem_block::type_id::create("media_ss");
    media_ss.configure(this);
    media_ss.build();

    default_map = create_map("default_map", 'h0, 4, UVM_LITTLE_ENDIAN);
    default_map.add_submap(periph_ss.default_map, 'h0000_0000);
    default_map.add_submap(media_ss.default_map,  'h1000_0000);
  endfunction
endclass
diagram
[RAL] staged verification approach

 stage 1: verify leaf block offsets
 stage 2: verify subsystem submap bases
 stage 3: verify top-level absolute addresses
 stage 4: run access seqs across all windows

 isolate failures by level instead of debugging whole tree at once
  • Keep map widths/endianness consistent when composing submaps unless intentionally mixed.

  • Name submaps by integration domain for easier debug traces.

  • Test one representative register per child block for quick sanity.


Multi-instance reuse and alias maps

Submaps excel when one IP appears multiple times (channels, lanes, tiles). Instantiate block class repeatedly and place each map at unique base offsets.

systemverilog
class dma_ss_block extends uvm_reg_block;
  `uvm_object_utils(dma_ss_block)
  rand dma_channel_block ch[4];
  uvm_reg_map default_map;

  function new(string name = "dma_ss_block");
    super.new(name, UVM_NO_COVERAGE);
  endfunction

  virtual function void build();
    default_map = create_map("default_map", 'h0, 4, UVM_LITTLE_ENDIAN);

    foreach (ch[i]) begin
      ch[i] = dma_channel_block::type_id::create($sformatf("ch%0d", i));
      ch[i].configure(this);
      ch[i].build();
      default_map.add_submap(ch[i].default_map, i * 'h1000);
    end
  endfunction
endclass
diagram
[UVM][RAL] multi-instance pattern

 same class: dma_channel_block
 instances: ch0 ch1 ch2 ch3
 bases:     0x0000 0x1000 0x2000 0x3000

 benefits:
   - one class definition
   - deterministic placement pattern
   - scalable test utilities via index-based loops
diagram
[RAL] alias-map caution

 some designs expose same register space through multiple bus windows.
 if alias maps are modeled:
   - define clear primary prediction path
   - prevent duplicate predictor updates for same transaction
   - document consistency expectations for each alias
  • Multi-instance submaps are ideal for channelized IP blocks.

  • Index-based placement helps generate repeatable tests and diagnostics.

  • Alias windows require careful predictor and scoreboard policy.


Walkthrough: debugging nested address issues

When a top-level write hits the wrong register, inspect address composition level by level. Failures often come from wrong submap base, wrong map chosen, or child map using unexpected local offsets.

diagram
[RAL][BUS] debug decomposition

 symptom:
   write to periph_ss.uart0.ctrl appears on bus as 0x0000_2000
 expected:
   0x0000_1000

 investigate:
 1) uart0.ctrl local offset?          (leaf)
 2) periph_ss submap base for uart0?  (subsystem)
 3) chip submap base for periph_ss?   (top)
 4) adapter rewrite of address?        (bridge)
systemverilog
function void log_submap_bases(chip_block model);
  `uvm_info("SUBMAP_DBG",
    $sformatf("periph base=0x%0h media base=0x%0h",
      'h0000_0000, 'h1000_0000), UVM_LOW)
  // Add project-specific map introspection helpers here.
endfunction
diagram
[UVM] triage order for nested map bugs

 first: static map construction review
 second: model lookup and expected address calc
 third: adapter item address logging
 fourth: monitor observed address confirmation

 stop when first mismatch appears

Key takeaways

  • Submaps let integration teams compose reusable IP-level models without rewriting leaf offsets.

  • Absolute address is a sum across hierarchy levels; debug each level independently.

  • Multi-instance designs benefit from repeated submap patterns and indexed utilities.

  • Nested map verification should be staged from leaf to top for fast fault isolation.

Common pitfalls

  • Embedding absolute addresses inside leaf IP blocks - destroys reuse.

  • Changing submap base without regression of affected windows.

  • Assuming single-level offsets in a multi-level hierarchy during debug.

  • Ignoring alias-map predictor interactions - mirror double-updates occur.