Desired values, mirrors and reset
Describe a register in a UVM register model and predict its desired and mirrored values after set, predict and reset, none of which touches the bus.
- Lesson
- U13.1
- Time
- 25 min
- Runs on
- Verilator with UVM
- Check
- 4 questions
A test calls regs.ctrl.set('h22) to choose a new mode, then a helper calls regs.ctrl.predict('h44) to record what it believes the hardware holds. The test's update() then sends nothing, so the design never receives mode 0x22. predict overwrote the pending 0x22, because it sets both values the model keeps.
| Call | Desired | Mirrored | Bus transfer |
|---|---|---|---|
reset() | 01 | 01 | None |
set('h22) | 22 | 01 | None |
predict('h44) | 44 | 44 | None |
update() | 44 | 44 | None: the values already agree |
The CTRL register's two values after each call. The 0x22 that the test chose is gone after predict.
A model of the registers, in classes
The incrementer now has a small register slice. CTRL, at address 0, holds an 8-bit mode field that software reads and writes; it resets to 0x01. STATUS, at address 4, holds an 8-bit level that only the hardware changes.
A register model describes those registers as objects, so tests can say regs.ctrl.write(...) instead of building bus items by hand. UVM supplies the base classes: uvm_reg_field for a field, uvm_reg for a register and uvm_reg_block for a group of registers with their addresses.
Each field gets its size, position, access policy and reset value in one configure call. The access policy is a string such as "RW" (read-write), "RO" (read-only) or "W1C" (write 1 to clear). The model uses it to work out what a read or write should do to the field.
// parent size lsb access volatile reset has_reset is_rand indiv
mode.configure(this, 8, 0, "RW", 0, 8'h01, 1, 1, 0);volatile marks a field the hardware may change by itself, like STATUS. has_reset says whether the reset value is real. is_rand lets randomize() pick a value for the field, and indiv says whether the field can be accessed on its own.
What each configure argument changes later
| Argument | When set | Where it shows up |
|---|---|---|
| volatile = 1 | Mirror checks skip the field; a writable volatile field always reports needs_update() | mirror(UVM_CHECK), update (U13.2, U14.3) |
| has_reset = 0 | reset() leaves the field's values alone | The spot item below |
| is_rand = 1 | randomize() may pick a value; UVM turns randomization off for read-only policies (RO, RC) and for writable side-effect policies such as W1C | Register-driven configuration (U14.3) |
| individually_accessible = 1 | A field write may go out alone, if the bus also has byte enables | Writing one field (U13.2) |
The last line of the example prints the size, position, access, volatility and rand mode the model stored for both fields.
A register wider than the bus word takes one transfer per word: on this 4-byte map a 64-bit register would cost two. Values travel as uvm_reg_data_t, which is UVM_REG_DATA_WIDTH bits wide: 64 here, and larger only if the bench defines the macro before compiling the library.
The block and its address map
The block creates each register, calls its configure and build, and then creates an address map. The map records where each register sits on a bus. Here create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN) describes a bus with 4-byte words, and add_reg places CTRL at 0 and STATUS at 4.
The map does not know about any bus yet. It only records offsets and the width of a bus word. Lesson U13.2 connects it to a real sequencer, and from then on the same map turns register calls into bus transfers. A block can hold several maps, one per bus that reaches the registers, such as a processor bus and a debug port.
Finally lock_model() freezes the structure and computes the address tables. The model cannot be used for accesses until it is locked, and nothing can be added after. A block is a uvm_object, not a component, so the test creates it and calls build() itself.
Real projects rarely type these classes. A generator writes them from the register specification (IP-XACT, SystemRDL or a spreadsheet), together with the RTL decoder and the software headers, so the model cannot drift from the design. U14.4 shows a generated model and why you never edit it by hand.
Two values per field
Every field keeps two values. The mirrored value is the model's best belief about what the hardware holds right now. The desired value is what the test wants the hardware to hold next. Most of the time they are equal.
- reset()
Both values become 01, the configured reset value.
More context
The example prints "after reset() ctrl desired=1 mirrored=1 update=0". Before reset() both values were 0, the value a field has after build.
Follow the calls, values and waits
- data reset value 01: reset() → Desired value
- data reset value 01: reset() → Mirrored value
- set(5a)
Desired becomes 5a, mirror stays 01, so
needs_update()returns 1.More context
A request the model holds until an update() or write() sends it. Nothing has reached the design.
Follow the calls, values and waits
- data v = 5a: set(v) → Desired value
- Waiting: Desired value — 5a requested, mirror 01: needs_update() = 1
- predict(5a)
Both become 5a; the values agree again and
needs_update()returns 0.More context
predict records a belief about the design. Here it closes the request without any transfer, so the model now claims a 5a the design never received.
Follow the calls, values and waits
- data v = 5a: predict(v) → Desired value
- data v = 5a: predict(v) → Mirrored value
- Released: Desired value
- set(22)
A new request: desired becomes 22 while the mirror stays 5a.
More context
For a moment
needs_update()would return 1 again.Follow the calls, values and waits
- data v = 22: set(v) → Desired value
- Waiting: Desired value — 22 requested, not yet sent
- predict(44)
predict overwrites the pending 22 in the desired value: the hook's lost request.
More context
The example prints "set(22), predict(44) ctrl desired=44 mirrored=44 update=0": no trace of the 22 remains.
Follow the calls, values and waits
- data v = 44: predict(v) → Desired value
- data v = 44: predict(v) → Mirrored value
- Released: Desired value
- The design
Nothing above touched it. Changing it needs a bus access; in the state just shown, desired and mirror are both 44, so
update()would send nothing.
Read the diagram as text
- set(v): A request: what the test wants next
- predict(v): A record: what the design is believed to hold
- reset(): The model's side of a design reset
- Desired value: Read with get()
- Mirrored value: Read with get_mirrored_value()
- incr_csr.ctrl: Unchanged by every call on the left
- set(v) → Desired value: desired = v
- predict(v) → Desired value: desired = v
- predict(v) → Mirrored value: mirror = v
- reset() → Desired value: reset value
- reset() → Mirrored value: reset value
- Desired value → incr_csr.ctrl: update(), only if values differ (U13.2)
The register ledger without a bus: two model values per field, and a design that only bus accesses change.
| Method | Desired | Mirrored | Uses the bus |
|---|---|---|---|
set(v) | Becomes v (for RW) | Unchanged | No |
get() | Returned | - | No |
get_mirrored_value() | - | Returned | No |
predict(v) | Becomes v | Becomes v | No |
reset() | Reset value | Reset value | No |
needs_update() | Compared | Compared | No: for CTRL, returns 1 if they differ |
The bookkeeping methods, for a writable field that is not volatile, such as CTRL. A volatile writable field always reports needs_update() = 1, and a read-only one always 0. Lesson U13.2 adds write, read, update and mirror, which do use the bus.
set changes only the desired value, so after set('h5a) the model says "I want 5a, I believe 01". needs_update() then returns 1, and a later update() writes the difference to the hardware. Several set calls before one update cost a single bus write.
set follows the access policy. On the read-only STATUS field it changes nothing, because software cannot change that field: status.set('h33) leaves the desired value at 00.
How the access policy shapes the values
The access string is not only documentation. The model uses it each time it works out a new mirrored value after an access. A few common access types show the idea.
For "RW", a write stores the written value, and a read returns it. For "RO", a write has no effect on the field, so the mirror keeps its old value. For "W1C", each 1 in the written value clears that bit and each 0 leaves it alone: writing 0x01 to a W1C field that holds 0x03 leaves 0x02.
Read side effects are modelled too. A "RC" field clears itself when read, so after a predicted read the mirror becomes 0. That is how a status register that clears on read stays correct in the model.
Take each field's access type from the register specification. A wrong access string does not break the model at once. The mirror drifts from the hardware after the first access, and the next mirror check reports an error that looks like a design bug.
predict changes both values
predict(v) tells the model "the hardware now holds v". It sets the mirrored value, and it sets the desired value too. Any pending set is lost, which is the hook's bug: after set('h22) and predict('h44), both values are 44, needs_update() returns 0 and update() sends nothing.
So keep predict for what it is meant for: recording something that really happened on the hardware. Lesson U13.3 lets a predictor component call it for every transfer a monitor sees. A test that wants a new value should use set and update, or write.
Records a guess, loses the request
regs.ctrl.set('h22);
void'(regs.ctrl.predict('h44));
regs.ctrl.update(status); // sends nothing
predict overwrites the desired value, so the requested 0x22 never reaches the design.
Requests, then sends
regs.ctrl.set('h22);
regs.ctrl.update(status); // one bus write of 0x22
The desired value differs from the mirror, so update writes it and the mirror follows.
Resetting the model
regs.reset() sets every field's desired and mirrored values to its reset value. It is pure bookkeeping: it does not drive the design's reset pin. Call it when the design really is reset, so the model starts from the same values as the hardware.
Reset values matter beyond the first access. Many tests start with a quick check that every register reads back its reset value. That check compares the design with the model's reset values, so a typo in one configure call shows up as a design failure on the first day.
The model's idea of reset comes only from configure. A field configured with has_reset = 0 has no reset value, and reset() leaves it alone. Its desired and mirrored values then keep whatever they held before.
No step uses a bus; the model changes only its own two values.
Try it
Each line shows CTRL's desired value, its mirror and needs_update(), plus STATUS's desired value. Compare them with the trace above: only set makes the two CTRL values differ, and predict and reset bring them together again.
Change STATUS's access from "RO" to "RW" and run again: status.set('h33) now changes its desired value. Then set CTRL's has_reset argument to 0 and see what the second reset() does.
after build ctrl desired=0 mirrored=0 update=0 | status desired=0 after reset() ctrl desired=1 mirrored=1 update=0 | status desired=0 after set(5a) ctrl desired=5a mirrored=1 update=1 | status desired=0 after predict(5a) ctrl desired=5a mirrored=5a update=0 | status desired=0 set(22), predict(44) ctrl desired=44 mirrored=44 update=0 | status desired=0 after reset() again ctrl desired=1 mirrored=1 update=0 | status desired=0 mode: 8 bits at bit 0, RW, volatile=0, rand_mode=1 | level: RO, volatile=1, rand_mode=0 | data width 64 UVM_ERROR : 0Check yourself
Remember
- A register model describes fields, registers and their addresses as objects:
uvm_reg_field,uvm_reg,uvm_reg_blockand an address map. - Each field keeps a desired value (what the test wants) and a mirrored value (what the model believes the hardware holds).
setchanges the desired value only,predictchanges both, andresetsets both to the configured reset value; none uses the bus.setfollows the access policy, so it does nothing on a read-only field.- A field with
has_reset= 0 is skipped byreset();volatile,is_randand individual access shape mirror checks, randomization and field writes later.