Part 9 · Register Model (RAL) · Intermediate
set, update, write, and read Semantics
Method-level semantics table and code examples for batching intent, committing writes, reading back values, and avoiding accidental mirror pollution.
Method semantics table
These four methods are frequently mixed in review code. The table below is the fastest way to choose the right one and predict side effects on desired/mirrored values.
[RAL] method semantics quick reference
Method Primary purpose Typical side effects
────────────────────────────────────────────────────────────────────────────────────────────────────
set(v) Stage value in model desired <- v, no bus activity
update(status, path, map...) Commit staged deltas writes only when desired!=mirrored
write(status, v, path...) Immediate bus write desired/mirror converge to written effect
read(status, v_out, path...) Observe DUT now mirror <- observed value
Decision heuristic:
- configuring multiple fields/registers first? use set() + one update()
- forcing known value now? use write()
- validating observed hardware state? use read() or mirror()
Anti-pattern:
set(); read(); expecting set value in DUT without update/write.set() is a planning API; by itself it never touches the DUT.
update() is delta-based and can skip writes if mirror already matches intent.
write() is explicit and immediate; ideal for one-shot control operations.
read() updates mirror and can influence later checks if used carelessly.
Batching with set() and update()
For multi-field programming, stage all fields first, then update once. This reduces traffic and keeps intent readable.
task configure_ctrl_block(my_reg_block ral, uvm_component parent);
uvm_status_e status;
// Stage all intent in software space.
ral.ctrl.enable.set(1'b1);
ral.ctrl.mode.set(2'b10);
ral.ctrl.threshold.set('h3F);
// Commit only differences from mirror.
ral.ctrl.update(status, UVM_FRONTDOOR, .parent(parent));
if (status != UVM_IS_OK)
`uvm_error("RAL_CFG", "ctrl.update failed")
endtask[UVM] batched commit timeline
set(enable=1) desired changes, mirror unchanged
set(mode=2) desired changes, mirror unchanged
set(thr=0x3F) desired changes, mirror unchanged
update() one frontdoor register write (if delta exists)
mirror catches up to committed valueWhen batching helps most
Programming many fields in one register before enabling operation.
Applying reset override tables in initialization.
Avoiding bus noise in constrained-random scenarios where intent evolves before commit.
Immediate control with write(), observation with read()
Use write() when a value must hit hardware immediately and unambiguously. Use read() when you need observed DUT state, not staged intent.
task force_and_verify(my_reg_block ral, uvm_component parent);
uvm_status_e status;
uvm_reg_data_t rd;
// Immediate command write.
ral.ctrl.write(status, 'hA5, UVM_FRONTDOOR, .parent(parent));
if (status != UVM_IS_OK)
`uvm_fatal("RAL_WR", "ctrl.write failed")
// Independent observation.
ral.ctrl.read(status, rd, UVM_FRONTDOOR, .parent(parent));
if (status != UVM_IS_OK)
`uvm_fatal("RAL_RD", "ctrl.read failed")
if (rd != 'hA5)
`uvm_error("RAL_RD", $sformatf("readback mismatch exp=0x%0h got=0x%0h", 'hA5, rd))
endtask[CHECK] read side-effect reminder
read() does two things:
1) returns observed value to caller
2) updates mirror internally
Consequence:
calling read() right before mirror(UVM_CHECK) can mask stale-mirror bugs,
because mirror has already been refreshed by the read.Use mirror() when intent is compare, not data fetch
For explicit compare semantics, prefer mirror(status, UVM_CHECK, ...) over manual read+if in places where you want UVM's standardized mismatch reporting.
Method selection matrix and examples
[RAL] practical method matrix
Goal Best method pattern
───────────────────────────────────────────────────────────────────────────────
Stage many config updates, commit once set() for fields -> update()
Force value now write()
Observe live hardware value read()
Validate hardware against model expectation mirror(UVM_CHECK)
Synchronize mirror from external monitor event predict() (via predictor)
Common recipe:
initialize:
set...set...update
operate:
write/read as needed
verify:
mirror(UVM_CHECK) at checkpointstask smoke_register_sequence(my_reg_block ral, uvm_component parent);
uvm_status_e status;
// 1) stage + commit
ral.mode.cfg.set(3'h4);
ral.mode.gain.set(5'h12);
ral.mode.update(status, UVM_FRONTDOOR, .parent(parent));
// 2) immediate control pulse
ral.cmd.write(status, 32'h1, UVM_FRONTDOOR, .parent(parent));
// 3) compare checkpoint
ral.mode.mirror(status, UVM_CHECK, UVM_FRONTDOOR, .parent(parent));
endtaskKey takeaways
Choose method by intent: stage, commit, observe, or compare.
set()+update() is for batched intent; write() is for immediate action.
read() refreshes mirror and can change later check behavior.
Keep method semantics explicit in code reviews to prevent drift bugs.
Common pitfalls
Calling set() and assuming DUT changed without update/write.
Using read() in pre-check path and unintentionally hiding stale mirror.
Replacing mirror(UVM_CHECK) everywhere with ad hoc read compares.