Part 9 · Register Model (RAL) · Intermediate
Volatile Counters and Reserved Fields
How to model volatile hardware-updated fields and reserved bits so mirror/check behavior remains meaningful and robust.
Volatile fields: hardware moves, software observes
Volatile fields can change independently of software access. Typical examples: packet counters, FIFO occupancy, sticky error status, and asynchronous hardware state machines.
Marking volatility correctly prevents stale mirror assumptions. It does not mean 'ignore this field'; it means check strategy must account for time and side effects.
[DUT] counter increments on packet receive
│
▼
[RAL][FIELD volatile RO] value may differ from last software write/read
│
├─ mirror can drift between accesses (expected)
└─ explicit read/predict cadence required
▼
[TEST] compare with timing-aware expectationpkt_count = uvm_reg_field::type_id::create("pkt_count");
pkt_count.configure(this, 16, 0, "RO", 1, 16'h0000, 1, 0, 0);Volatile flag should reflect real hardware autonomy, not test convenience.
Pair volatile checks with explicit timing assumptions in test intent.
Use monitor+predictor to keep mirror useful under autonomous updates.
Reserved bits: explicit modeling strategy
Reserved bits are often under-modeled. Leaving them unaccounted can produce accidental write pollution, false mismatches, or hidden RTL escapes when reserved read behavior is constrained by spec.
A practical approach is to model reserved slices explicitly (usually RO with deterministic reset if defined) or ensure register-level masks in checks enforce expected behavior.
[SPEC] reserved bits policy examples
Policy A: reads as 0, writes ignored
Policy B: reads retain reset value
Policy C: undefined read, must not be checked
Modeling must match which policy your spec declares.class cfg_reg extends uvm_reg;
`uvm_object_utils(cfg_reg)
uvm_reg_field mode;
uvm_reg_field threshold;
uvm_reg_field rsvd_hi;
virtual function void build();
mode = uvm_reg_field::type_id::create("mode");
mode.configure(this, 3, 0, "RW", 0, 3'h0, 1, 1, 1);
threshold = uvm_reg_field::type_id::create("threshold");
threshold.configure(this, 5, 3, "RW", 0, 5'h10, 1, 1, 1);
// Reserved [31:8], reads as zero, writes ignored
rsvd_hi = uvm_reg_field::type_id::create("rsvd_hi");
rsvd_hi.configure(this, 24, 8, "RO", 0, 24'h000000, 1, 0, 0);
endfunction
endclassChoose a reserved-bit convention and enforce it consistently across model/tests.
Document reserved modeling rationale directly near build() definitions.
Avoid hidden assumptions about reserved readback values.
Volatile + reserved in one status register
Real registers combine active status with reserved regions. The example below demonstrates a realistic mixed register and a conservative check approach.
class status_mixed_reg extends uvm_reg;
`uvm_object_utils(status_mixed_reg)
uvm_reg_field fifo_fill; // volatile
uvm_reg_field err_sticky; // W1C
uvm_reg_field busy; // volatile
uvm_reg_field rsvd;
function new(string name = "status_mixed_reg");
super.new(name, 32, UVM_NO_COVERAGE);
endfunction
virtual function void build();
fifo_fill = uvm_reg_field::type_id::create("fifo_fill");
fifo_fill.configure(this, 8, 0, "RO", 1, 8'h00, 1, 0, 0);
err_sticky = uvm_reg_field::type_id::create("err_sticky");
err_sticky.configure(this, 1, 8, "W1C", 0, 1'b0, 1, 0, 1);
busy = uvm_reg_field::type_id::create("busy");
busy.configure(this, 1, 9, "RO", 1, 1'b0, 1, 0, 0);
rsvd = uvm_reg_field::type_id::create("rsvd");
rsvd.configure(this, 22, 10, "RO", 0, 22'h0, 1, 0, 0);
endfunction
endclass[REG] status_mixed_reg read-check guidance
volatile fields:
fifo_fill, busy
-> compare with tolerance/time-aware expectation
side-effect field:
err_sticky (W1C)
-> directed clear/ack checks
reserved:
rsvd
-> strict zero check if spec says reads-as-zeroMixed-field check plans must separate volatile and deterministic domains.
Do not fail volatile checks against stale snapshots without timing context.
Keep reserved checks strict where spec guarantees behavior.
Check patterns for volatile and reserved semantics
Two targeted patterns cover most use cases: bounded-delta checks for counters and masked checks for reserved slices.
task check_counter_monotonic(my_reg_block ral, int unsigned max_delta);
uvm_status_e status;
uvm_reg_data_t a, b;
ral.status_mixed.read(status, a, UVM_FRONTDOOR, .parent(this));
#100ns;
ral.status_mixed.read(status, b, UVM_FRONTDOOR, .parent(this));
if (b[7:0] < a[7:0])
`uvm_error("VOLATILE", "fifo_fill decreased unexpectedly")
if ((b[7:0] - a[7:0]) > max_delta)
`uvm_warning("VOLATILE", "Counter jump exceeds expected traffic window")
endtask
task check_reserved_zero(my_reg_block ral);
uvm_status_e status;
uvm_reg_data_t rd;
ral.status_mixed.read(status, rd, UVM_FRONTDOOR, .parent(this));
if (rd[31:10] !== '0)
`uvm_error("RSVD", $sformatf("Reserved bits non-zero: 0x%08x", rd))
endtask[RAL] practical check split
volatile signals -> trend/range checks
reserved fields -> deterministic mask checks
W1C/RC fields -> operation-triggered semantic checks
One uniform "read == expected constant" pattern is insufficient.Key takeaways
Volatile fields require timing-aware validation, not blind equality checks.
Reserved bits should be modeled and checked according to explicit spec policy.
Separate check strategies by field class to reduce false failures and misses.
Correct volatile/reserved modeling stabilizes mirror and reset diagnostics.
Common pitfalls
Marking non-volatile fields as volatile to silence mismatches.
Leaving reserved bits unmodeled and assuming they are harmless.
Applying strict deterministic checks to autonomous hardware counters.
Ignoring monitor/predict synchronization for rapidly changing status fields.