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.