Skip to content

UVM RAL · Chapter 2 · Register Model Basics

uvm_reg_field — Modeling Fields

The register field is the atom of the register model, the smallest thing that has a value, an access policy, and a mirror. Everything else in RAL is built out of fields, and prediction, checking, coverage, and randomization all ultimately happen per field. A field's entire definition is one configure call with eight arguments: the parent register, size, LSB position, access policy, reset value, volatile flag, a randomization flag, and an individual-access flag. This lesson walks all eight and gives the last two the attention they deserve, since they are the ones people set on autopilot and get wrong. The individual-access flag is a promise about the bus, not the field, and declaring it wrongly makes RAL believe a field write is isolated when it is really a read-modify-write of the whole register. It ends with the bug where that false promise makes a routine field write clear an interrupt that hardware had just raised.

Foundation12 min readUVM RALuvm_reg_fieldconfigureindividually_accessibleis_randMirror

Chapter 2 · Section 2.3 · Register Model Basics

1. Why Should I Learn This?

The field is where all the per-bit truth of a register lives — its value, its policy, its mirror, its coverage — so a field configured wrong is a register modelled wrong at the most fundamental level. And unlike a wrong offset or a missing lock, a wrong configure() argument fails quietly and specifically: a field that should not randomize but does, a field the model thinks it can write alone but cannot, a field marked non-volatile that hardware changes. Each is a single argument, and each produces a distinct, confusing bug.

Learning the eight configure() arguments — especially is_rand and individually_accessible, the two most often set on autopilot — lets you define a field so it behaves exactly as the spec and the bus dictate, and lets you read a field definition in review and immediately see whether it is honest. The field is the atom; getting the atom right is what makes the whole model trustworthy.

2. Industry Story — the interrupt that a config write kept clearing

A register holds a software threshold field and, in the same register, a hardware-set overflow interrupt bit specified W1C. The verification engineer, wanting threshold writes to be efficient, declares threshold as individually_accessible = 1 — reasoning 'it's its own field, so it can be written on its own.' The bus, however, is a plain 32-bit APB with no byte enables: it cannot write a sub-word, so there is no way to write threshold alone.

Now a test that periodically adjusts threshold starts losing overflow interrupts. Each threshold.write() becomes a read-modify-write of the whole register — RAL reads the register, splices in the new threshold, writes it all back — and if overflow was set by hardware between the read and the write-back, the write-back drives the read-time value of overflow (which, depending on timing and the RMW data, can be a 1) into the W1C bit, clearing the interrupt software never saw. The overflow is silently acknowledged by a threshold adjustment. The team burns two days on 'missing interrupts' before finding that individually_accessible was a false promise about the bus: the field is not independently writable on APB, so RAL should have known the write was a full-register RMW and the register layout should never have mixed a volatile W1C with an RMW-accessed field. The lesson: individually_accessible describes what the bus can do, not what the field would like — set it true only when the bus can actually write the field alone.

3. Concept — the eight arguments and the field methods

uvm_reg_field::configure(parent, size, lsb_pos, access, reset, is_volatile, is_rand, individually_accessible) is the field's complete definition:

  • parent — the uvm_reg this field belongs to (this, from the register's build()).
  • size — the field width in bits (high - low + 1, from 1.1).
  • lsb_pos — the field's low bit position in the register (from 1.1).
  • access — the access-policy string (RW, RO, W1C, …, from 1.5).
  • reset — the reset value (from 1.3).
  • is_volatile — whether a writer other than RAL can change the field (from 1.6). 1 for hardware-owned and shared fields.
  • is_rand — whether the field participates when the register is randomized. 1 for software-writable config you want randomized; 0 for reserved, read-only status, and fields you want to hold fixed.
  • individually_accessible — whether the field can be written by itself on the bus without a read-modify-write of its siblings. This is a promise about the bus's sub-word (byte-enable) capability, not about the field. 1 only when the bus can actually write just this field.

And the field-level methods you operate with: set(v) / get() for the desired value, get_mirrored_value() for the mirror, predict(v) to set the mirror explicitly, get_reset() for the reset, plus write/read (from 2.2). Here are the eight arguments as the field's definition:

The eight uvm_reg_field configure arguments: parent, size, lsb_pos, access, reset, is_volatile, is_rand, individually_accessibleparent · size · lsb_poswhich register owns it, its width (high-low+1), and its low bit position — where the field liveswhich register owns it, its width (high-low+1), and its low bit position — where the field livesaccess · resetthe access-policy string (RW/RO/W1C/RC/WO) and the reset value — how it behaves and where it startsthe access-policy string (RW/RO/W1C/RC/WO) and the reset value — how it behaves and where it startsis_volatile1 if a writer other than RAL (hardware) can change the field — hardware-owned and shared fields (section 1.6)1 if a writer other than RAL (hardware) can change the field — hardware-owned and shared fields (section 1.6)is_rand1 if the field participates in register randomization; 0 for reserved, read-only status, and fixed fields1 if the field participates in register randomization; 0 for reserved, read-only status, and fixed fieldsindividually_accessible1 only if the BUS can write this field alone (byte enables / sub-word); otherwise a field write is a read-modify-write1 only if the BUS can write this field alone (byte enables / sub-word); otherwise a field write is a read-modify-write
Figure 1 — the eight configure() arguments that completely define a uvm_reg_field. The first five (parent, size, lsb, access, reset) place the field and describe its behaviour and starting value. The last three are flags: is_volatile (a writer other than RAL exists), is_rand (participate in register randomization), and individually_accessible (the BUS can write this field alone without a read-modify-write). is_rand and individually_accessible are the two set most often on autopilot — and individually_accessible is a promise about the bus, not the field.

4. Mental Model — the field is one object, and two of its flags describe the world, not the field

5. Working Example — the eight arguments, deliberately set

A register whose fields exercise all eight arguments with intent — note is_rand and individually_accessible chosen from the field's role and the bus's capability, not by habit:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class sensor_reg extends uvm_reg;
  `uvm_object_utils(sensor_reg)
  rand uvm_reg_field threshold;  // [7:0]   RW  config, randomize it
       uvm_reg_field overflow;   // [8]     W1C hardware-set interrupt (volatile)
       uvm_reg_field rsvd;       // [30:9]  RO  reserved
       uvm_reg_field ready;      // [31]    RO  hardware status (volatile)
 
  function new(string name = "sensor_reg");
    super.new(name, .n_bits(32), .has_coverage(UVM_NO_COVERAGE));
  endfunction
 
  virtual function void build();
    threshold = uvm_reg_field::type_id::create("threshold");
    overflow  = uvm_reg_field::type_id::create("overflow");
    rsvd      = uvm_reg_field::type_id::create("rsvd");
    ready     = uvm_reg_field::type_id::create("ready");
    //  configure(parent, size, lsb, access, reset, is_volatile, is_rand, individually_accessible)
    threshold.configure(this,  8,  0, "RW",  0, 0, 1, 0);  // config: rand=1; APB has no byte enables -> indiv=0
    overflow.configure (this,  1,  8, "W1C", 0, 1, 0, 0);  // HW-set: volatile=1; not rand
    rsvd.configure     (this, 22,  9, "RO",  0, 0, 0, 0);  // reserved: not rand, not volatile
    ready.configure    (this,  1, 31, "RO",  0, 1, 0, 0);  // HW status: volatile=1; not rand
  endfunction
endclass

The field methods then read and set the desired and mirrored values directly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sensor.threshold.set(8'h40);                 // desired(threshold) = 0x40 (nothing driven yet)
$display("desired=%0h", sensor.threshold.get());          // 0x40
sensor.update(s);                            // push the changed field to the DUT
$display("mirror=%0h", sensor.threshold.get_mirrored_value());  // now 0x40
if (sensor.ready.get_mirrored_value() == 1) ...            // hardware status via the mirror
void'(sensor.overflow.predict(1));           // model that hardware raised overflow (Chapter 6 does this live)

Every argument is chosen for a reason: threshold is randomizable config, so is_rand = 1; the APB bus has no byte enables, so individually_accessible = 0 even for threshold; overflow and ready are hardware-touched, so is_volatile = 1; rsvd is fixed and out of randomization. That last choice — individually_accessible = 0 — is the one the next section shows you must get right.

6. Debugging Session — the field that was not as independent as it claimed

1

A field wrongly declared individually_accessible on a bus without byte enables turns a field write into a read-modify-write that clears a W1C sibling

FALSE INDEPENDENCE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// APB (no byte enables) cannot write a sub-word, but threshold is declared independent:
threshold.configure(this, 8, 0, "RW", 0, 0, 1, 1);   // BUG: individually_accessible = 1 on APB
// overflow (W1C, HW-set) shares the register. A periodic config write:
sensor.threshold.write(s, 8'h50);
//   RAL, believing threshold is independent, still cannot issue a sub-word write on APB,
//   so the field write becomes a READ-MODIFY-WRITE of the whole register. If overflow was
//   set by hardware around that window, the write-back drives a 1 into the W1C bit -> CLEARS it.
Symptom

Overflow interrupts go missing, and the losses correlate with periodic threshold adjustments — a config write and a vanished interrupt, over and over. The interrupt logic tests clean in isolation; the RTL sets overflow exactly when it should. The failure only appears in the full test where threshold is being tuned, and it looks like an interrupt-generation or race bug in hardware, when in fact a software field write is quietly acknowledging the interrupt through a read-modify-write nobody realized was happening.

Root Cause

individually_accessible = 1 is a promise that the bus can write the field alone, and on a byte-enable-less APB that promise is false: there is no sub-word write, so a field write must be a read-modify-write of the whole register regardless of the flag. The RMW reads the register (capturing overflow), splices in the new threshold, and writes the whole word back — and because the write-back includes overflow's bit, a W1C bit that was set can be cleared by the write-back. The flag did not change what the bus can do; it only misled whoever read the model into thinking the write was isolated. The deeper fault is a register-layout one: a volatile, hardware-set W1C field should not share a register with a field that is updated by read-modify-write, because the RMW cannot be atomic with respect to the hardware set.

Fix

Set the flag to the truth of the bus: individually_accessible = 0 on APB, so the model correctly represents that a field write is a full-register access and no one is misled. But the real fix is at the layout level — do not mix a volatile W1C interrupt with an RMW-accessed config field in one register; put overflow in its own status/interrupt register so a threshold write never has to read-and-rewrite it. Where a bus does have byte enables and can write the field alone, then individually_accessible = 1 is honest and the field write is a clean single access. The rule: individually_accessible states what the bus can do; set it true only when the bus can genuinely write the field alone, and never rely on it to make an RMW atomic against hardware.

7. Common Mistakes

  • Setting individually_accessible = 1 as a wish. It is a promise about the bus's sub-word capability; false on a bus without byte enables, where a field write is always an RMW.
  • Leaving is_rand = 1 on reserved or read-only fields. They pollute register randomization; reserved and status fields should be is_rand = 0.
  • Setting is_rand = 0 on config you meant to randomize. The field then never varies under randomize(), quietly narrowing coverage.
  • Confusing is_volatile with individually_accessible. One is about hardware writing the field; the other is about the bus writing it alone — different worlds.
  • Miscomputing size/lsb_pos. The 1.1 off-by-one; size = high - low + 1, lsb_pos = low.

8. Industry Best Practices

  • Set the last two flags deliberately, from the bus and the intent. individually_accessible from the bus's byte-enable capability; is_rand from whether the field should vary.
  • Do not co-locate a volatile W1C field with an RMW-accessed field. A register-layout rule that prevents the field-write-clears-interrupt hazard entirely.
  • Use the field methods for desired/mirrored access. set/get for desired, get_mirrored_value for the mirror, predict to model a hardware change — clearer than reaching through the register.
  • Review each field's eight arguments against its spec row and the bus. A field definition is dense; a per-argument check catches the quiet ones.
  • Prefer generated models for large maps. Generation sets is_rand, is_volatile, and access consistently from a machine-readable spec (Chapter 13).

9. Interview / Review Questions

10. Key Takeaways

  • The uvm_reg_field is the atom — the smallest unit with a value, an access policy, and a mirror — and its whole definition is one configure() call of eight arguments.
  • The eight are parent, size, lsb_pos, access, reset, is_volatile, is_rand, individually_accessible; the field methods (set/get, get_mirrored_value, predict, get_reset) read and set the desired and mirrored values.
  • is_rand governs randomization participation (set from intent — 0 for reserved/status); individually_accessible is a promise about the bus, true only when the bus can write the field alone.
  • Declaring individually_accessible = 1 on a bus without sub-word access is a false promise — a field write is still a read-modify-write, which can clear a volatile W1C sibling.
  • The deeper rule is a layout one: do not co-locate a volatile, hardware-set W1C field with a read-modify-write-accessed field, or a field write can lose a hardware interrupt.