Part 9 · Register Model (RAL) · Intermediate

uvm_reg_field::configure Walkthrough

Parameter-by-parameter explanation of configure(), with field layout examples, mutability choices, and practical modeling guidelines.

The configure() contract

Most field behavior originates from one call: uvm_reg_field::configure. Teams often copy-paste this call without understanding each argument, then spend days debugging behavior that was accidentally encoded in metadata.

The method signature is compact but dense. Understanding each parameter gives you deterministic control over layout, side effects, reset semantics, randomization, and access granularity.

systemverilog
function void configure(
  uvm_reg        parent,
  int unsigned   size,
  int unsigned   lsb_pos,
  string         access,
  bit            volatile,
  uvm_reg_data_t reset,
  bit            has_reset,
  bit            is_rand,
  bit            individually_accessible
);
diagram
[RAL] configure() argument map

  parent                  -> which register owns this field
  size                    -> number of bits in field
  lsb_pos                 -> starting bit position in register
  access                  -> RW/RO/W1C/... policy string
  volatile                -> can DUT change without software access?
  reset                   -> reset value bits for this field
  has_reset               -> whether reset value is defined/checked
  is_rand                 -> include field in randomization flows
  individually_accessible -> legal partial-lane direct access
  • Every argument should be traceable to explicit specification text.

  • Wrong size/lsb_pos silently corrupts mapping and check interpretation.

  • volatile and has_reset are behavior flags, not convenience defaults.


Parameter deep dive

parent, size, lsb_pos

These three parameters define bit placement. Incorrect placement yields the most dangerous class of bug: accesses appear to work, but they target wrong bits.

systemverilog
err_code.configure(this, 4, 8, "RO", 1, 4'h0, 1, 0, 0);
// means err_code occupies reg[11:8]
  • Ensure fields do not overlap unless explicitly intended by spec aliases.

  • Validate total field coverage against register width.

  • Keep a bit-map comment near build() for reviewer clarity.

access, volatile

access encodes software operation semantics. volatile indicates the DUT may update this field outside explicit software writes. Volatile fields are common in counters, FIFO levels, and asynchronous status.

systemverilog
pkt_count.configure(this, 8, 16, "RO", 1, 8'h00, 1, 0, 0);
irq_stat.configure(this, 1, 7, "W1C", 0, 1'b1, 1, 0, 1);
  • Volatile does not disable checking; it changes expectation strategy.

  • Pair volatile fields with deliberate read timing and predictor setup.

  • Do not use volatile as a blanket fix for mirror mismatches.

reset, has_reset

reset is the modeled reset value for this field. has_reset tells RAL whether reset is defined and therefore checkable. If has_reset is 0, reset comparisons for that field are intentionally skipped.

systemverilog
version_id.configure(this, 8, 24, "RO", 0, 8'h00, 0, 0, 0);
// has_reset=0: version register may not have deterministic reset value
  • Set has_reset=1 only when spec guarantees deterministic reset state.

  • For unknown-on-reset fields, use has_reset=0 and document rationale.

  • Keep reset value width consistent with field size.

is_rand, individually_accessible

is_rand controls whether field participates in randomization helpers. individually_accessible indicates whether software can target field independently (often relevant for bus byte-lane semantics and tool-generated behavior).

systemverilog
cfg_sel.configure(this, 3, 0, "RW", 0, 3'h0, 1, 1, 1);
// randomizable and individually accessible
  • Disable is_rand for status fields to avoid unrealistic sequence randomization.

  • Mark individually_accessible conservatively unless architecture guarantees it.

  • Consistency here improves built-in sequence usefulness.


Full register walkthrough with annotations

Example: a mixed control/status register where each field uses different configure choices. Comments explain why each parameter is selected.

systemverilog
class ctrl_status_reg extends uvm_reg;
  `uvm_object_utils(ctrl_status_reg)

  uvm_reg_field enable;       // bit0, RW
  uvm_reg_field mode;         // bits[2:1], RW
  uvm_reg_field soft_reset;   // bit3, WO pulse
  uvm_reg_field irq_status;   // bit4, W1C
  uvm_reg_field fifo_level;   // bits[12:8], RO volatile
  uvm_reg_field rev_id;       // bits[31:24], RO constant

  function new(string name = "ctrl_status_reg");
    super.new(name, 32, UVM_NO_COVERAGE);
  endfunction

  virtual function void build();
    enable = uvm_reg_field::type_id::create("enable");
    enable.configure(
      this,    // parent
      1,       // size
      0,       // lsb_pos
      "RW",    // access
      0,       // volatile
      1'b0,    // reset
      1,       // has_reset
      1,       // is_rand
      1        // individually_accessible
    );

    mode = uvm_reg_field::type_id::create("mode");
    mode.configure(this, 2, 1, "RW", 0, 2'b00, 1, 1, 1);

    soft_reset = uvm_reg_field::type_id::create("soft_reset");
    soft_reset.configure(this, 1, 3, "WO", 0, 1'b0, 1, 0, 1);

    irq_status = uvm_reg_field::type_id::create("irq_status");
    irq_status.configure(this, 1, 4, "W1C", 0, 1'b1, 1, 0, 1);

    fifo_level = uvm_reg_field::type_id::create("fifo_level");
    fifo_level.configure(this, 5, 8, "RO", 1, 5'h00, 1, 0, 0);

    rev_id = uvm_reg_field::type_id::create("rev_id");
    rev_id.configure(this, 8, 24, "RO", 0, 8'h3A, 1, 0, 0);
  endfunction
endclass
diagram
[REG][FIELD] annotated layout

  31:24 rev_id       RO reset=0x3A
  23:13 reserved     (separate reserved handling)
  12:8  fifo_level   RO volatile
  7:5   reserved
  4     irq_status   W1C reset=1
  3     soft_reset   WO
  2:1   mode         RW
  0     enable       RW
  • Mixed fields in one register are normal; each field carries unique semantics.

  • Use comments sparingly but deliberately for non-obvious choices.

  • Keep build() deterministic and easy to diff during spec changes.


Debugging configure() mistakes

When behavior is wrong, isolate configure metadata before blaming adapters, predictors, or RTL. A quick field-dump utility often surfaces model issues immediately.

systemverilog
function void dump_field_meta(uvm_reg rg);
  uvm_reg_field fields[$];
  rg.get_fields(fields);
  foreach (fields[i]) begin
    `uvm_info(
      "FIELD_META",
      $sformatf("name=%s access=%s n_bits=%0d lsb=%0d volatile=%0d",
                fields[i].get_name(),
                fields[i].get_access(),
                fields[i].get_n_bits(),
                fields[i].get_lsb_pos(),
                fields[i].is_volatile()),
      UVM_LOW
    )
  end
endfunction
diagram
[RAL] configure triage flow

  unexpected read/write behavior?
    ├─ dump access policy + lsb/size
    ├─ confirm reset/has_reset values
    ├─ confirm volatile flag intent
    ├─ check map address/adapter only after metadata passes
    └─ re-run directed per-field check

Key takeaways

  • configure() arguments encode most field runtime semantics.

  • Treat each argument as spec-bound data, not template filler.

  • Use metadata dump helpers to localize model issues quickly.

  • Correct configure choices reduce downstream debug complexity across the stack.

Common pitfalls

  • Copying configure lines across fields without revisiting each argument.

  • Using has_reset=1 for unknown-reset fields and forcing false failures.

  • Setting is_rand on status fields and generating nonsensical stimulus.

  • Ignoring bit placement validation until integration phase.