UVM RAL · Chapter 1 · Register Specification & CSR Thinking
What a CSR Specification Is
Before you model a single register you have to read the specification like an engineer, because a RAL model is only ever as correct as the spec you built it from. A control and status register spec is the programmer's contract for a block: for every register it says where it lives, what its bits mean, how each bit answers a read and a write, what it holds out of reset, and what hardware does to it behind software's back. This page teaches how to read a spec row and extract the five facts every register must pin down: its offset, its fields with their bit positions and widths, each field's access policy, each field's reset value, and any hardware side effects. It maps each fact to the field-configure argument it becomes, and ends with the most common transcription bug, a field width read off by one that ships a truncated register.
Foundation11 min readUVM RALCSRSpecificationuvm_reg_fieldAccess PolicyReset
Chapter 1 · Section 1.1 · Register Specification & CSR Thinking
1. Why Should I Learn This?
Every RAL bug you will ever chase has one of two homes: the model or the DUT. A huge fraction of the model bugs are not clever mistakes in SystemVerilog — they are misreadings of the spec. A field modelled one bit too narrow, an access policy copied from the wrong row, a reset value taken from a draft that changed: none of these are coding errors, and none of them will be caught by a compiler. They are reading errors, and they turn your golden reference into a confident liar.
So the first real skill in register verification is not writing uvm_reg; it is reading the CSR spec so precisely that the model writes itself. Learn to see the five load-bearing facts in every register row and you eliminate an entire class of bugs before you type a line of code — and you gain the ability to review someone else's model against the spec in minutes, which is most of what a register-verification review actually is.
2. Industry Story — the draft that never got re-read
A team starts verification against version 0.7 of a programmer's guide. An engineer transcribes the TIMER_LOAD register, reads its counter field as 16 bits wide, and builds the model. Weeks later the architecture team widens the timer to 24 bits for a longer timeout range and ships version 0.9 of the spec — but the model is never re-read against the new draft, because 'the register already exists in RAL.'
The regression keeps passing. The tests write values that fit in 16 bits, the 16-bit model happily mirrors them, and the top 8 bits of the real 24-bit counter are never exercised by RAL at all. The gap surfaces in silicon: a long-timeout use case programs a value above 16 bits, the software driver writes it through the real register, and the timer fires far too early because — as far as anyone verified — those upper bits did not exist. The root cause in the post-mortem is not RTL and not the testbench harness; it is a stale reading of the spec frozen into the model. The lesson the team writes down: the spec is the source of truth, the model is a transcription of it, and a transcription must be re-verified against the source every time the source moves.
3. Concept — the five facts in every register row
A CSR spec entry for one register pins down five things. Miss any one and the model is wrong in a way no compiler will tell you about:
- Offset — the register's byte address relative to the block's base (
0x00,0x04, …). This is what places the register in the address map; get it wrong and every access lands on the neighbour. - Fields — the named sub-ranges of the register, each with a bit position (LSB) and a width. A 32-bit register might be one 32-bit field, or a dozen 1-bit flags, or anything between. The bit layout is the meaning of the register.
- Access policy — for each field, how it answers a read and a write:
RW,RO,W1C,RC,WO, and so on (Chapter 0.2). This is the field's behaviour, and it is what keeps the mirror honest. - Reset value — what each field holds after reset. This is the mirror's starting point and the thing a reset test checks against.
- Hardware side effects — what hardware does to the field independent of software: a status bit it sets, a counter it increments, a field another register's write clears. These are the events the model must predict or the mirror drifts.
The quiet truth of RAL is that each of these five facts becomes a specific argument to uvm_reg_field.configure(). Read the row, and the arguments are already chosen for you. Here is one register-spec row broken into exactly those five facts:
4. Mental Model — the model is a transcription, the spec is the score
5. Working Example — a spec row becomes a field, argument by argument
Take one register from a programmer's guide, CTRL @ 0x08, with the fields the spec defines. The transcription is mechanical once you can read the row:
CTRL @ offset 0x08, 32-bit register
[0] enable RW reset 0 — 1 starts the block
[2:1] mode RW reset 0 — operating mode (00..11)
[3] irq_en RW reset 0 — 1 enables the done interrupt
[31:4] reserved RO reset 0 — must read 0Each row becomes one configure() call, and every argument is read, not chosen:
class ctrl_reg extends uvm_reg;
`uvm_object_utils(ctrl_reg)
rand uvm_reg_field enable; // [0] RW
rand uvm_reg_field mode; // [2:1] RW
rand uvm_reg_field irq_en; // [3] RW
uvm_reg_field reserved; // [31:4] RO
function new(string name = "ctrl_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");
mode = uvm_reg_field::type_id::create("mode");
irq_en = uvm_reg_field::type_id::create("irq_en");
reserved = uvm_reg_field::type_id::create("reserved");
// parent size lsb access reset volatile rand indiv
enable.configure (this, 1, 0, "RW", 0, 0, 1, 0); // [0]
mode.configure (this, 2, 1, "RW", 0, 0, 1, 0); // [2:1] width 2, lsb 1
irq_en.configure (this, 1, 3, "RW", 0, 0, 1, 0); // [3]
reserved.configure(this, 28, 4, "RO", 0, 0, 0, 0); // [31:4] width 28, lsb 4
endfunction
endclassRead the pairing directly: mode is spec bits [2:1], so size = 2 and lsb_pos = 1; reserved is [31:4], so size = 28 and lsb_pos = 4. The access strings and reset values are copied cell-for-cell. There is no design decision here — only faithful reading. That is the whole point of the chapter.
6. Debugging Session — the field read one bit too narrow
A field transcribed one bit too narrow silently truncates the register, and the missing bits are never verified
SPEC MISREAD// Spec says: mode occupies bits [2:1] — a 2-bit field. It was read as 1 bit.
mode.configure(this, 1, 1, "RW", 0, 0, 1, 0); // BUG: size=1, should be 2
// Now the model only knows about bit [1]; bit [2] of the register is unmodelled.
// A test writes mode = 2'b11 through RAL; the model writes/mirrors only bit [1].Tests that use the full range of mode behave oddly and inconsistently. Writing mode = 3 through RAL only drives one bit, so the DUT's mode[2] is left at reset while mode[1] is set — the block runs in the wrong mode, and a read-back may or may not mismatch depending on how the reserved/adjacent bits are modelled. Worst of all, mode[2] is never exercised by any register test, so a bug in that bit's decode ships clean. The failure is subtle precisely because the narrow field still 'works' for small values.
The field's width was read off the spec incorrectly: [2:1] is two bits (positions 1 and 2), but it was transcribed as size = 1. This is the classic off-by-one of register work — confusing the high bit index with the width. Width is high - low + 1, so [2:1] is 2 - 1 + 1 = 2. Because the model compiles and passes for values that happen to fit in the truncated field, nothing flags it until a full-range value or a targeted mode[2] behaviour is needed. The DUT is fine; the transcription dropped a bit.
Transcribe the width correctly: mode.configure(this, 2, 1, "RW", 0, 0, 1, 0); — size = 2, lsb_pos = 1. Now RAL knows the whole field, drives both bits, and every value of mode is reachable and checkable. The durable habit: compute every field width as high - low + 1 straight from the spec's [high:low] notation, and in review confirm that a field's size + lsb_pos never exceeds the register width and that adjacent fields tile the register with no gaps or overlaps. A quick sanity pass — do the field widths add up to the register width? — catches most transcription slips before they reach a test.
7. Common Mistakes
- Confusing the top bit index with the width.
[7:0]is 8 bits,[2:1]is 2 bits — alwayshigh - low + 1. - Copying an access policy from the wrong row. Fields on adjacent rows often differ (
RWcontrol next toROstatus); transcribe each row's policy from its own cell. - Modelling reserved fields as
RW. Reserved bits are usuallyROand 'must read 0' or 'must be written 0'; modelling them writable lets tests scribble on bits the spec forbids. - Freezing the model at a stale spec version. A spec revision that widens a field or moves an offset invalidates the transcription until you re-read it.
- Leaving a spec ambiguity as a guess. If a row does not state a field's reset or access clearly, get it clarified — do not pick a plausible value and move on.
8. Industry Best Practices
- Trace every configure() argument to a spec cell. Make traceability the review standard: no argument without a source.
- Sanity-check the bit budget. Field widths plus positions must tile the register exactly — a mismatch means a misread field or a missing one.
- Prefer a machine-readable spec (IP-XACT / RALF / a CSV) as the single source. Generating the model from it (Chapter 13) removes transcription error entirely; where you must hand-model, treat the spec table as law.
- Re-transcribe on every spec revision, and diff. Keep the spec version the model was built from recorded, and re-read on each bump.
- Write reset and reserved-bit checks explicitly. A reset test and a reserved-bits test catch the exact misreads this page is about.
9. Interview / Review Questions
10. Key Takeaways
- The spec comes before the model, and the model is only ever a transcription of it — no more correct than your reading.
- Every register row pins down five facts: offset, fields (bit position + width), access policy, reset value, and hardware side effects.
- Each fact becomes a
configure()argument (or, for side effects, a modelled behaviour) — read the row and the model writes itself; do not choose what you should read. - Field width is
high - low + 1, andsize + lsb_posmust never exceed the register width — the fastest transcription check is whether the fields tile the register. - Re-transcribe on every spec revision. A model frozen at a stale spec version silently verifies the wrong contract.