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.

CallDesiredMirroredBus transfer
reset()0101None
set('h22)2201None
predict('h44)4444None
update()4444None: 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.

The nine arguments of uvm_reg_field::configure, as the example uses them.
//             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
ArgumentWhen setWhere it shows up
volatile = 1Mirror checks skip the field; a writable volatile field always reports needs_update()mirror(UVM_CHECK), update (U13.2, U14.3)
has_reset = 0reset() leaves the field's values aloneThe spot item below
is_rand = 1randomize() may pick a value; UVM turns randomization off for read-only policies (RO, RC) and for writable side-effect policies such as W1CRegister-driven configuration (U14.3)
individually_accessible = 1A field write may go out alone, if the bus also has byte enablesWriting 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.

Which value each bookkeeping call changesArrows show which of CTRL.mode's two values a call assigns. None of these calls reaches the design; only a bus access, from U13.2 on, does that.
Which value each bookkeeping call changesArrows show which of CTRL.mode's two values a call assigns. None of these calls reaches the design; only a bus access, from U13.2 on, does that. call set(v) A request: what thetest wants next call predict(v) A record: what thedesign is believed tohold call reset() The model's side of adesign reset model Desired value Read with get() model Mirrored value Read withget_mirrored_value() design incr_csr.ctrl Unchanged by everycall on the left desired = vdesired = vmirror = vreset valuereset valueupdate(), only ifvalues differ(U13.2)
  1. 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
    1. data reset value 01: reset() → Desired value
    2. data reset value 01: reset() → Mirrored value
  2. 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
    1. data v = 5a: set(v) → Desired value
    2. Waiting: Desired value — 5a requested, mirror 01: needs_update() = 1
  3. 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
    1. data v = 5a: predict(v) → Desired value
    2. data v = 5a: predict(v) → Mirrored value
    3. Released: Desired value
  4. 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
    1. data v = 22: set(v) → Desired value
    2. Waiting: Desired value — 22 requested, not yet sent
  5. 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
    1. data v = 44: predict(v) → Desired value
    2. data v = 44: predict(v) → Mirrored value
    3. Released: Desired value
  6. 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.

MethodDesiredMirroredUses the bus
set(v)Becomes v (for RW)UnchangedNo
get()Returned-No
get_mirrored_value()-ReturnedNo
predict(v)Becomes vBecomes vNo
reset()Reset valueReset valueNo
needs_update()ComparedComparedNo: 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.

CTRL's two values through the example, one column per step
012345stepbuildresetset(5a)predict(5a)set(22)+predict(44)resetdesired015a441mirrored015a441needs_updatevalues differ22 is lost

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.

RUNVerilator + UVM 2020.3.1 · simulator service
Edit the code, then run it
Expected among the UVM messages: 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 : 0

Check yourself

Predict the outputCTRL resets to 0x01. What line does the $display print? UVM's own messages are not part of the answer.
`timescale 1ns/1ns
`include "uvm_macros.svh"
import uvm_pkg::*;
class ctrl_reg extends uvm_reg;
  `uvm_object_utils(ctrl_reg)
  uvm_reg_field mode;
  function new(string name = "ctrl_reg"); super.new(name, 32, UVM_NO_COVERAGE); endfunction
  function void build();
    mode = uvm_reg_field::type_id::create("mode");
    mode.configure(this, 8, 0, "RW", 0, 8'h01, 1, 0, 0);
  endfunction
endclass
class regs_c extends uvm_reg_block;
  `uvm_object_utils(regs_c)
  ctrl_reg ctrl;
  function new(string name = "regs_c"); super.new(name, UVM_NO_COVERAGE); endfunction
  function void build();
    ctrl = ctrl_reg::type_id::create("ctrl");
    ctrl.configure(this);
    ctrl.build();
    default_map = create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);
    default_map.add_reg(ctrl, 'h0, "RW");
    lock_model();
  endfunction
endclass
class my_test extends uvm_test;
  `uvm_component_utils(my_test)
  function new(string name, uvm_component parent); super.new(name, parent); endfunction
  function void end_of_elaboration_phase(uvm_phase phase);
    regs_c regs = regs_c::type_id::create("regs");
    regs.build();
    regs.reset();
    regs.ctrl.set('h33);
    void'(regs.ctrl.predict('h44));
    $display("desired=%0h mirrored=%0h needs_update=%0d",
             regs.ctrl.get(), regs.ctrl.get_mirrored_value(), regs.ctrl.needs_update());
  endfunction
endclass
module tb;
  initial run_test("my_test");
endmodule
Spot the bugCTRL should reset to 0x0f. The program sets 0xa0, predicts 0xa0, calls reset() and checks that both values are back to 0x0f. Each option is CTRL's configure call (arguments: parent, size, lsb, access, volatile, reset, has_reset, is_rand, individually accessible). Which one fails?
ConceptA test calls regs.reset() at time 0. What happens to the design?
ConceptAfter set('h5a) on CTRL, needs_update() returns 1. Which statement is true?

Remember

  • A register model describes fields, registers and their addresses as objects: uvm_reg_field, uvm_reg, uvm_reg_block and an address map.
  • Each field keeps a desired value (what the test wants) and a mirrored value (what the model believes the hardware holds).
  • set changes the desired value only, predict changes both, and reset sets both to the configured reset value; none uses the bus.
  • set follows the access policy, so it does nothing on a read-only field.
  • A field with has_reset = 0 is skipped by reset(); volatile, is_rand and individual access shape mirror checks, randomization and field writes later.