Part 9 · Register Model (RAL) · Intermediate
block build lifecycle: build(), configure(), lock_model()
Step-by-step lifecycle for constructing a robust register model and freezing it at the right time.
Lifecycle intent and ordering
RAL creation is a stateful lifecycle : objects are created, linked, structurally built, map-connected, then frozen. Reordering these steps leads to partial models, missing maps, or runtime errors.
A practical rule: configure children immediately after creation, build local structure before composing parent maps, and lock at the top level once no structural edits remain.
[RAL] lifecycle skeleton
new():
construct object shell
configure(parent):
establish ownership linkage and context
build():
instantiate fields/registers/maps/sub-blocks
lock_model():
freeze structure for runtime access APIs[UVM][RAL] phase placement guideline
build_phase:
create + configure + build + lock_model
connect_phase:
set_sequencer(adapter) + predictor connections
run_phase:
issue accesses (write/read/update/mirror)Treat build/lock as structural setup; never mix with run-time stimulus logic.
Keep lifecycle methods deterministic so model shape does not depend on test order.
Document lifecycle contract in env code for future maintainers.
Step-by-step implementation
This walkthrough shows the complete order of API calls in a block with two registers and one memory, including explicit map setup and lock timing.
class cfg_block extends uvm_reg_block;
`uvm_object_utils(cfg_block)
rand cfg_ctrl_reg ctrl;
rand cfg_stat_reg stat;
rand uvm_mem table_mem;
uvm_reg_map default_map;
function new(string name = "cfg_block");
super.new(name, UVM_NO_COVERAGE);
endfunction
virtual function void build();
// 1) create child objects
ctrl = cfg_ctrl_reg::type_id::create("ctrl");
stat = cfg_stat_reg::type_id::create("stat");
table_mem = uvm_mem::type_id::create("table_mem");
// 2) configure ownership
ctrl.configure(this);
stat.configure(this);
table_mem.configure(this, 64, 32, "RW", UVM_NO_COVERAGE);
// 3) build child internals
ctrl.build();
stat.build();
// 4) create and populate map
default_map = create_map("default_map", 'h0, 4, UVM_LITTLE_ENDIAN);
default_map.add_reg(ctrl, 'h00, "RW");
default_map.add_reg(stat, 'h04, "RO");
default_map.add_mem(table_mem, 'h100, "RW");
endfunction
endclassclass chip_env extends uvm_env;
`uvm_component_utils(chip_env)
cfg_block reg_model;
apb_reg_adapter adapter;
uvm_reg_predictor #(apb_item) predictor;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
reg_model = cfg_block::type_id::create("reg_model");
reg_model.build();
reg_model.lock_model();
adapter = apb_reg_adapter::type_id::create("adapter");
predictor = uvm_reg_predictor#(apb_item)::type_id::create("predictor", this);
endfunction
endclass[RAL] build-to-lock milestone view
build() complete:
- all children exist
- maps created
- offsets assigned
lock_model() complete:
- structure frozen
- lookup and access APIs safe for run phase
- accidental structural mutation blockedKeep creation/configure/build grouped for each child to reduce omissions.
Call lock_model at the top owner after all submaps and registers are added.
Do not defer lock_model to run_phase; lock before any dynamic accesses begin.
Why lock_model is not optional
lock_model validates and freezes the object graph. Without it, some lookup paths and assumptions inside register APIs are undefined, and accidental late add_reg/add_submap calls can produce inconsistent models.
[UVM][RAL] what lock_model protects
before lock:
structure mutable
useful during build composition
after lock:
structure immutable
safe for:
- name/path lookups
- map iteration
- reg sequence traversal
- stable debug dumps[RAL] late mutation anti-pattern
run_phase:
if (feature_x) default_map.add_reg(extra_reg, 'h200, "RW");
problems:
- test-order dependent model shape
- inconsistent mirror behavior
- impossible reproducibility across regressionsfunction void ensure_model_locked(uvm_reg_block blk);
if (!blk.is_locked()) begin
`uvm_fatal("RAL_LOCK", $sformatf("Model %s must be locked before run", blk.get_full_name()))
end
endfunctionLocking is a correctness step, not just a style preference.
Mutable model structure in run-phase causes non-repeatable failures.
Assert lock status early in test startup for fast failure.
Walkthrough: full lifecycle with parent and child blocks
In multi-block designs, each child builds itself, then parent composes submaps, then top-level lock occurs once composition is complete.
class top_block extends uvm_reg_block;
`uvm_object_utils(top_block)
rand cfg_block cfg;
rand dma_block dma;
uvm_reg_map default_map;
function new(string name = "top_block");
super.new(name, UVM_NO_COVERAGE);
endfunction
virtual function void build();
cfg = cfg_block::type_id::create("cfg");
cfg.configure(this);
cfg.build();
dma = dma_block::type_id::create("dma");
dma.configure(this);
dma.build();
default_map = create_map("default_map", 'h0, 4, UVM_LITTLE_ENDIAN);
default_map.add_submap(cfg.default_map, 'h0000);
default_map.add_submap(dma.default_map, 'h8000);
endfunction
endclass[UVM][RAL] execution walkthrough
1) top_block.build()
2) cfg child build complete
3) dma child build complete
4) top map adds cfg and dma submaps
5) env calls top_block.lock_model()
6) connect adapter/predictor
7) tests run accesses against frozen tree[BUS] integration readiness checkpoint
before first frontdoor access, verify:
- model locked
- default_map has sequencer + adapter
- predictor.map and adapter set
- monitor connected to predictor.bus_in
- no pending structural mutationsKey takeaways
RAL lifecycle is deterministic: create -> configure -> build -> compose maps -> lock -> connect.
lock_model is the transition from construction to safe operational use.
Parent-child composition should complete before top-level lock.
Early lifecycle assertions prevent deep runtime debug sessions.
Common pitfalls
Calling lock_model before add_submap/add_reg completes.
Forgetting lock_model and assuming model is production-ready.
Injecting structure changes in run-phase based on test conditions.
Separating configure and build too far apart, causing missing ownership links.