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_regthis field belongs to (this, from the register'sbuild()). - 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).
1for hardware-owned and shared fields. - is_rand — whether the field participates when the register is randomized.
1for software-writable config you want randomized;0for 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.
1only 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:
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:
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
endclassThe field methods then read and set the desired and mirrored values directly:
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
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// 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.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.
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.
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 = 1as 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 = 1on reserved or read-only fields. They pollute register randomization; reserved and status fields should beis_rand = 0. - Setting
is_rand = 0on config you meant to randomize. The field then never varies underrandomize(), quietly narrowing coverage. - Confusing
is_volatilewithindividually_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_accessiblefrom the bus's byte-enable capability;is_randfrom 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/getfor desired,get_mirrored_valuefor the mirror,predictto 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_fieldis the atom — the smallest unit with a value, an access policy, and a mirror — and its whole definition is oneconfigure()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_randgoverns randomization participation (set from intent —0for reserved/status);individually_accessibleis a promise about the bus, true only when the bus can write the field alone.- Declaring
individually_accessible = 1on 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.
