UVM RAL · Chapter 1 · Register Specification & CSR Thinking
Reserved Bits
Almost every real register has gaps between its functional fields that the spec marks reserved, and treating them as unimportant filler is exactly what gets a register model wrong. Reserved bits are not undefined, they are constrained. The spec usually promises they read as zero and that software must not disturb them, and that promise exists for forward compatibility, since a bit reserved today may become a real feature in the next revision. This page explains what reserved means, why you model these bits read-only rather than read-write, why well-behaved software does read-modify-write instead of blasting a whole word, and the common bug where modelling reserved as read-write lets a random-value test write into bits the spec forbids and verify behaviour that does not exist.
Foundation11 min readUVM RALReserved BitsRAZ/WIForward Compatibilityuvm_reg_fieldRO
Chapter 1 · Section 1.4 · Register Specification & CSR Thinking
1. Why Should I Learn This?
Reserved bits look like nothing and cause real bugs in both directions. Model them too permissively — as read-write — and your tests write values the spec forbids, exercising and 'verifying' bit behaviour that is supposed to have no effect, which hides the day a reserved bit becomes a real field and is never checked. Model them wrong the other way, or ignore the read-as-zero rule, and a reserved bit that comes up as one in silicon slides through because nothing was watching. And on the software side, code that ignores reserved bits — writing a whole register instead of read-modify-write — silently clobbers a feature the moment a future chip lights those bits up.
Learning the reserved-bit contract is what lets you model these ranges honestly, write the check that catches a stuck reserved bit, and reason about forward compatibility the way a real programmer's guide expects. It is a small, unglamorous part of a register that separates a model that merely compiles from one that is actually faithful.
2. Industry Story — the reserved bit that grew up
A block ships with a CFG register whose bits [7:4] are reserved — read as zero, writes ignored. The verification engineer, under time pressure, models the whole register as one 32-bit RW field 'to keep it simple,' and a regression test writes randomized 32-bit values across it. Every write completes, the reserved bits read back as zero (the RTL ties them off), and because the model is RW it expects... whatever it wrote. To make the test pass, someone masks the reserved bits out of the comparison. Green suite, shipped.
Two revisions later the architecture team turns [7:4] into a real prescale field — a genuine functional control. The old software, which had been writing full 32-bit words with garbage in [7:4], now sets a random prescale on every configuration write, and the block misbehaves intermittently in the field. On the verification side, nobody had a reserved-bits test, so when prescale arrived there was no baseline proving those bits used to read zero and ignore writes — the forward-compatibility contract was never verified, so its violation was invisible. The post-mortem lesson: reserved bits are a contract, not filler — model them as the read-only, read-as-zero range the spec promises, verify that promise, and never let a test write values into them.
3. Concept — reserved is constrained, not undefined
A reserved bit is a bit with no current function but a definite rule. The common conventions:
- RAZ/WI — Read-As-Zero, Write-Ignored. The most common: the bit always reads
0, and writes to it have no effect. This is what lets old software write whole words safely-ish and new software rely on a zero when the feature is absent. - RES0 / RES1 — reserved, should-be-zero / should-be-one. The spec requires software to write the bit as
0(or1) and may leave the read value defined or not. Common in architected register spaces (ARM-style), where the burden is on software to preserve the value. - Reserved, preserve on write. Some specs say software must read-modify-write: read the register, keep the reserved bits as read, change only the fields it owns, write back. This is the strongest forward-compatibility guarantee — software never disturbs bits it does not understand.
In every case the modelling answer is the same: a reserved field is read-only from the testbench's point of view and reset to its specified value (usually 0) — you do not model it as RW, because RW invites tests to write it and asserts a behaviour (write sticks) that the spec explicitly denies. Here is a register with reserved gaps between its functional fields, drawn as a bit map:
4. Mental Model — reserved bits are roped off, not empty
5. Working Example — modelling reserved honestly
Model each reserved range as its own read-only field with the specified reset, exactly like a functional field but with the access that matches its contract:
class cfg_reg extends uvm_reg;
`uvm_object_utils(cfg_reg)
rand uvm_reg_field enable; // [0] RW, reset 0
uvm_reg_field rsvd_lo; // [3:1] RO, reset 0 (reserved, RAZ/WI)
rand uvm_reg_field mode; // [7:4] RW, reset 0
uvm_reg_field rsvd_hi; // [31:8] RO, reset 0 (reserved, RAZ/WI)
function new(string name = "cfg_reg");
super.new(name, .n_bits(32), .has_coverage(UVM_NO_COVERAGE));
endfunction
virtual function void build();
enable = uvm_reg_field::type_id::create("enable");
rsvd_lo = uvm_reg_field::type_id::create("rsvd_lo");
mode = uvm_reg_field::type_id::create("mode");
rsvd_hi = uvm_reg_field::type_id::create("rsvd_hi");
// parent size lsb access reset volatile rand indiv
enable.configure (this, 1, 0, "RW", 0, 0, 1, 0); // functional
rsvd_lo.configure(this, 3, 1, "RO", 0, 0, 0, 0); // reserved — RO, not rand
mode.configure (this, 4, 4, "RW", 0, 0, 1, 0); // functional
rsvd_hi.configure(this, 24, 8, "RO", 0, 0, 0, 0); // reserved — RO, not rand
endfunction
endclassTwo details carry the intent. The reserved fields are RO, so RAL never drives a write into them and asserts they read their reset. And they are not rand (the is_rand argument is 0), so a randomized register write leaves them alone — randomization only touches the fields software actually owns. A reserved-bits check then confirms the contract:
// Reserved ranges must read as their specified value (0 here) out of reset and
// after arbitrary functional writes. A focused check makes the promise explicit.
uvm_status_e s; logic [31:0] got;
cfg.write(s, 32'hFFFF_FFFF); // try to set everything, including reserved
cfg.read (s, got);
if (got[31:8] != 0 || got[3:1] != 0)
`uvm_error("RSVD", $sformatf("reserved bits not RAZ/WI: got %0h", got))6. Debugging Session — reserved modelled as read-write
Reserved bits modelled RW let a randomized write assert non-zero values the spec forbids, then hide behind a masked comparison
RESERVED AS RW// The whole register modelled as one RW field 'to keep it simple':
data.configure(this, 32, 0, "RW", 0, 0, 1, 0); // BUG: reserved bits are now RW + rand
// A randomized write drives non-zero into reserved [31:8] and [3:1]:
cfg.randomize(); cfg.update(s); // writes e.g. 0xA5A5_00A5 — junk in reserved bits
cfg.read(s, got); // DUT ties reserved to 0, returns 0x0000_00A5-ish
// UVM_ERROR unless someone masks reserved out of the compare to 'make it pass'Randomized register tests mismatch on the reserved ranges — the model expected the junk it wrote, the DUT returned zero — and the usual 'fix' is to mask the reserved bits out of the comparison so the suite goes green. The register now appears verified, but two things are quietly broken: tests are writing forbidden values into reserved bits (so any software-visible side effect of doing so is being exercised as if it were legal), and there is no assertion that the reserved bits actually read zero and ignore writes. When those bits later become a real field, nothing was watching the space and nothing flags the change.
The reserved bits were modelled as writable and randomizable, which is a false statement of their contract. The spec says RAZ/WI — read zero, ignore writes — which is precisely RO with reset 0 and is_rand = 0. Modelling them RW and rand tells RAL two untruths: that software may write them (so tests do) and that their written value should stick (so the mirror predicts junk the DUT will not hold). The masked comparison then papers over the resulting mismatch, converting a modelling error into a silent hole. The DUT is behaving correctly the whole time — it is the model that misdescribes the register.
Model each reserved range as its own RO, non-rand field reset to its specified value, as in Section 5. Now randomization never touches them, RAL never writes them, and the mirror expects the specified read value — so no mask is needed and the comparison is honest. Add the reserved-bits check that asserts the range reads its specified value after an all-ones write, giving you the forward-compatibility baseline. The rule the bug teaches: reserved is a read-only, do-not-write contract; model it as such and verify it, never as RW masked out of the compare — the mask deletes exactly the check you needed.
7. Common Mistakes
- Modelling reserved ranges as RW. It asserts writability the spec denies and drags reserved bits into randomized writes.
- Leaving reserved fields
rand. Even asRO, arandreserved field can confuse register randomization; setis_rand = 0. - Masking reserved bits out of comparisons to force a pass. The mask deletes the reserved-bits check — the one thing that would catch a stuck bit or a future feature.
- Assuming reserved always reads zero. Some specs say RES1 (should read/write one) or leave the read undefined; transcribe the actual rule.
- Writing whole registers in sequences instead of read-modify-write. Blasting a word disturbs reserved bits software is meant to preserve.
8. Industry Best Practices
- Model reserved as read-only, non-rand, reset to its specified value. One field per reserved range, transcribed from the spec's rule.
- Write a reserved-bits test. Confirm each reserved range reads its specified value out of reset and after an all-ones write — the forward-compatibility baseline.
- Prefer read-modify-write in register sequences. Change only the fields you own; preserve reserved bits, mirroring correct software behaviour.
- Re-audit reserved ranges on every spec revision. A reserved range becoming a field is a spec event your model and tests must track.
- Never mask a mismatch away. A reserved-bit mismatch is information; fix the model, do not hide the compare.
9. Interview / Review Questions
10. Key Takeaways
- Reserved bits are constrained, not undefined — a forward-compatibility reservation with a definite rule (commonly RAZ/WI: read-as-zero, write-ignored).
- Model reserved ranges read-only, non-
rand, reset to their specified value — never RW, which asserts writability the spec denies and drags them into randomized writes. - Verify the reservation with a reserved-bits test; never mask a reserved mismatch out of the comparison — the mask deletes the check you need.
- Reserved bits underpin safe evolution: software preserves them via read-modify-write so a future revision can turn them into real fields.
- Re-audit reserved ranges on every spec revision — a reserved range becoming a field is a spec event the model and tests must follow.