Skip to content
VLSI Mentor

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.

Standards & specifications

Governing standard
IEEE Std 1800.2 (UVM)(opens IEEE in a new tab)

Defines the UVM class library's API and semantics. Testbench structure, naming conventions and project methodology around that API are engineering practice, not requirements of the standard.

Reference implementation
Accellera UVM reference implementation(opens Accellera Systems Initiative in a new tab)

The open-source class library and user guide that most simulators ship. Useful alongside IEEE 1800.2 when a page discusses the implementation rather than the standardised API.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of UVM RAL Register Model — Fields, Registers & Blocks.