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.
[RAL] address composition formula
absolute_addr(reg) =
top_base
+ parent_submap_base
+ child_submap_base
+ ... (nested levels)
+ reg_offset_in_leaf_map[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 + 0x000Leaf 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.
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
endclassclass 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[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 onceKeep 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.
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[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[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 aliasMulti-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.
[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)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[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 appearsKey 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.