Part 9 · Register Model (RAL) · Intermediate
uvm_reg_map Addressing: Offsets, Endianness, and Byte Width
How map parameters define address interpretation: base offsets, n_bytes granularity, endianness, and disciplined add_reg usage.
Why map parameters matter
A register model can have perfectly correct fields but still fail if map metadata is wrong . Address offsets, byte width, and endianness jointly define how abstract reads/writes become bus transactions.
Misconfigured map values are dangerous because accesses still execute, but hit wrong byte lanes, wrong aligned addresses, or incorrect multi-byte ordering. Failures then look random unless map assumptions are explicit.
[RAL] create_map key inputs
create_map(name, base_addr, n_bytes, endian)
base_addr : local base for this map
n_bytes : bus byte width per access unit
endian : byte ordering for packed data transfer
add_reg(reg, offset, rights):
absolute_addr = map_base + offset (+ parent submap offsets)[UVM][RAL][BUS] addressing path
[UVM] test calls reg.write(data)
│
▼
[RAL] map resolves local offset + hierarchy bases
│
▼
[RAL] adapter receives uvm_reg_bus_op(addr,data,kind)
│
▼
[BUS] driver sends protocol item to DUTOffsets are relative to the map where add_reg is called.
n_bytes must match the bus data granularity used for frontdoor accesses.
Endianness defines byte packing order for multi-byte fields and registers.
Offsets and add_reg discipline
Use add_reg consistently with spec-defined offsets. Keep one source of truth for offsets to avoid drift between documentation and model implementation.
class cfg_block extends uvm_reg_block;
`uvm_object_utils(cfg_block)
rand cfg_ctrl_reg ctrl;
rand cfg_stat_reg stat;
rand cfg_intr_reg intr;
uvm_reg_map default_map;
localparam int unsigned CTRL_OFS = 'h000;
localparam int unsigned STAT_OFS = 'h004;
localparam int unsigned INTR_OFS = 'h008;
function new(string name = "cfg_block");
super.new(name, UVM_NO_COVERAGE);
endfunction
virtual function void build();
ctrl = cfg_ctrl_reg::type_id::create("ctrl");
stat = cfg_stat_reg::type_id::create("stat");
intr = cfg_intr_reg::type_id::create("intr");
ctrl.configure(this); ctrl.build();
stat.configure(this); stat.build();
intr.configure(this); intr.build();
default_map = create_map("default_map", 'h0, 4, UVM_LITTLE_ENDIAN);
default_map.add_reg(ctrl, CTRL_OFS, "RW");
default_map.add_reg(stat, STAT_OFS, "RO");
default_map.add_reg(intr, INTR_OFS, "RW");
endfunction
endclass[RAL] offset table example
register offset rights note
----------------------------------------------
ctrl 0x000 RW control and mode bits
stat 0x004 RO status and sticky indicators
intr 0x008 RW interrupt mask/status controls
gaps are legal; overlapping offsets are not[BUS] overlap failure pattern
if ctrl and stat both mapped at 0x004:
- writes to ctrl unexpectedly alter stat location
- mirror mismatches look non-deterministic
- bit-bash fails unrelated fields
add map collision checks early during buildUse named constants for offsets and keep them grouped by block.
Assign map rights to match expected access style, even if field policy is richer.
Validate no overlap and no accidental misalignment in integration tests.
Endianness and byte width walkthrough
Endianness bugs often surface only on multi-byte fields. Demonstrate and test byte-lane assumptions explicitly, especially on bridges where register bus width and CPU width differ.
function void build_maps();
// APB control domain: 32-bit little-endian
apb_map = create_map("apb_map", 'h0, 4, UVM_LITTLE_ENDIAN);
// Legacy sideband domain: 16-bit big-endian
sb_map = create_map("sb_map", 'h0, 2, UVM_BIG_ENDIAN);
apb_map.add_reg(ctrl, 'h00, "RW");
sb_map.add_reg(legacy_cfg, 'h20, "RW");
endfunction[RAL][BUS] byte-lane view for 32'hA1B2C3D4
little-endian (n_bytes=4):
addr+0 -> D4
addr+1 -> C3
addr+2 -> B2
addr+3 -> A1
big-endian (n_bytes=4):
addr+0 -> A1
addr+1 -> B2
addr+2 -> C3
addr+3 -> D4[UVM] practical verification tactic
1) choose a pattern with distinct bytes (A1 B2 C3 D4)
2) write through frontdoor
3) monitor observed [BUS] bytes/lane placement
4) compare against map endian assumption
5) run readback to ensure mirror and DUT agreeUse distinct-byte patterns for endian debug, not symmetric values like 0x0000FFFF.
Confirm adapter bus item fields preserve address and byte-enable semantics.
Document map width assumptions per bus domain and review during integration.
Debugging map/addressing failures
Addressing bugs manifest as wrong register hits, stale mirror values, and unpredictable bit-bash failures. Correlate requested register name, resolved address, adapter output, and monitor capture in one log stream.
task debug_one_write(uvm_reg rg, uvm_reg_data_t d);
uvm_status_e status;
`uvm_info("RAL_ADDR_DBG",
$sformatf("writing %s with data 0x%0h", rg.get_full_name(), d),
UVM_MEDIUM)
rg.write(status, d, UVM_FRONTDOOR, .parent(this));
endtask[RAL] address debug checklist
expected:
reg full name
map name
local offset
effective absolute address
adapter item address/data
monitor observed address/data
if any stage differs, isolate at that boundaryKey takeaways
Map correctness is foundational: offsets, n_bytes, and endianness define real hardware touch points.
Use disciplined add_reg tables with named constants and collision checks.
Exercise endian assumptions using distinct-byte patterns and monitor correlation.
When debuging failures, trace name->map->adapter->monitor as one chain.
Common pitfalls
Copying offsets from outdated spec revision - silent incorrect accesses.
Mismatching n_bytes with actual bus width - split/packed transfers break.
Assuming adapter logic can compensate for wrong map configuration.
Ignoring right/readonly mismatch - tests write fields that should never move.