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.

diagram
[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.

systemverilog
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
diagram
[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 value

When 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.

systemverilog
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
diagram
[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

diagram
[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 checkpoints
systemverilog
task 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));
endtask

Key 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.