Skip to content

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:

Anatomy of a CSR spec row: offset, field position and width, access policy, reset value, and hardware side effects, each mapped to a configure() argumentOffset — 0x08byte address in the block; becomes the offset in default_map.add_reg(reg, .offset('h08))byte address in the block; becomes the offset in default_map.add_reg(reg, .offset('h08))Field — bits [3:0], width 4the field's LSB and width become configure() size=4 and lsb_pos=0 — the bit layout is the register's meaningthe field's LSB and width become configure() size=4 and lsb_pos=0 — the bit layout is the register's meaningAccess — RWhow the field answers a read/write; becomes the configure() access string ('RW', 'RO', 'W1C', ...)how the field answers a read/write; becomes the configure() access string ('RW', 'RO', 'W1C', ...)Reset — 0x0value held after reset; becomes the configure() reset argument and the mirror's starting pointvalue held after reset; becomes the configure() reset argument and the mirror's starting pointSide effects — none / HW-updatedwhat hardware does behind software's back; modelled via the access policy + prediction, not a configure() argumentwhat hardware does behind software's back; modelled via the access policy + prediction, not a configure() argument
Figure 1 — the anatomy of a single CSR spec row, top to bottom, and the configure() argument each fact becomes. Offset places the register in the map (add_reg). The field's LSB and width become the 'size' and 'lsb_pos' arguments. The access policy becomes the 'access' string. The reset value becomes the 'reset' argument. Side effects are not a configure() argument — they are behaviour you model with the right access policy and, where needed, prediction. Read the row and the model writes itself.

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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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 0

Each row becomes one configure() call, and every argument is read, not chosen:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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
endclass

Read 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

1

A field transcribed one bit too narrow silently truncates the register, and the missing bits are never verified

SPEC MISREAD
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// 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].
Symptom

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.

Root Cause

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.

Fix

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 — always high - low + 1.
  • Copying an access policy from the wrong row. Fields on adjacent rows often differ (RW control next to RO status); transcribe each row's policy from its own cell.
  • Modelling reserved fields as RW. Reserved bits are usually RO and '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, and size + lsb_pos must 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.