Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here4
  • UVM Foundations14
  • UVM Phases & Simulation Lifecycle26
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
  • UVM Sequences & Stimulus21
  • UVM Agents & Protocol VIP21
  • Environment & Test Classes11
  • Checking & Coverage in UVM21
  • UVM Register Abstraction Layer (RAL)22
    • RAL Overview
      • Why a Register Model: Desired, Mirrored and the DUT
      • Blocks, Registers, Fields and Memories
      • Maps, Offsets and Endianness
      • Nested Blocks and Submaps
      • Build, Lock and Generated Models
      • The Frontdoor Path, End to End
      • The Register Layer: Full Source
    • Register Access
    • Registers in Verification
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹RAL Overview
  1. Topics
  2. UVM Verification
  3. UVM Register Abstraction Layer (RAL)
  4. RAL Overview
  5. Why a Register Model: Desired, Mirrored and the DUT

Part 9 · Register Model (RAL) · Intermediate

Why a Register Model: Desired, Mirrored and the DUT

The problem a register model solves, the three values it keeps (desired, mirrored and the DUT's own), which call moves which, and why the mirror is a belief rather than a fact.

On this page

    A test writes LEN=4, starts a DMA job, and then, while the DMA is busy, writes LEN=9. The APB transfer completes cleanly. The predictor sees it, and the register model now says LEN is 9. A moment later the test reads LEN back over the bus and gets 4. The register model reports an error. Is the DUT broken?

    No. The DMA's spec says writes to LEN and KEY are ignored while the block is busy, and the DUT did exactly that. The model was confidently wrong. This lesson explains what a register model is for, the three values it juggles, and why that error is the most useful thing it can tell you.

    The idea: one description of the registers, used by everyone

    Without a model, a test programs registers with bus writes it assembles itself. The DMA's block tests do this through helpers: reg_write(KEY, key); reg_write(LEN, 6); reg_write(CTRL, 32'h3);. Every test has to know that CTRL is at 0x00, that IRQ_EN is bit 1, that DONE clears when you write 1 to it, and what each register should read back. With five registers that is fine. A chip has thousands, they move when the floor plan changes, the block's APB port sits behind an AXI interconnect at chip level, and the same test should run on both.

    A register model is one description of every register: its name, its address in each map (an address space seen from one bus), its fields (named bit ranges) and each field's access policy (RW, RO, write-1-to-clear and so on). Tests access registers by name, rm.len.write(st, 6); the map turns the name into an address, and an adapter turns the access into a transaction for whatever bus is underneath. Move the block, change the bus, and the tests stay the same.

    The model also keeps a copy of what it believes each register holds, so it can predict what a read should return. That copy is where the subtlety lives. Think of a bank: the deposit slip you have filled in but not handed over, the ledger the bank keeps, and the vault itself. The ledger is updated when a teller sees a transaction. If something in the vault changes without passing a teller, the ledger does not know until someone counts.

    Value

    Bank analogy

    What it is

    Read it with

    Desired

    The deposit slip

    The value you intend the register to hold. set() and randomize() change it; nothing goes on the bus.

    get()

    Mirrored

    The ledger

    What the model believes the DUT holds, updated by prediction after each access it knows about.

    get_mirrored_value()

    DUT value

    The vault

    What the RTL flip-flops really hold. Only the hardware changes it, and only a bus read or a backdoor peek shows it.

    read(), mirror(), peek()

    The picture: which call moves which value

    Flowchart: test code changes the desired value with set or randomize; update writes desired to the DUT; write and poke change the DUT; read, mirror and peek observe it. Every observed or written value goes through prediction, which sets the mirrored value and the desired value. predict sets the mirror directly. Hardware events change only the DUT.
    Desired moves on set() and randomize(); the DUT moves on any write, or on its own; the mirror moves only when a prediction runs. Nothing moves the mirror when the hardware changes a register by itself.

    Three things in this picture matter more than the rest. First, every prediction sets the mirrored and the desired value, so after a completed access they agree. Second, update() writes only when desired differs from mirrored, which makes it cheap to call often on configuration registers, and dangerous for a trigger bit that the mirror remembers and for registers with volatile W1C fields such as STATUS, which it writes back every time (the interrupts lesson shows a block-level update() clearing a pending DONE) (the access API lesson has the details). Third, the arrow from hardware events stops at the DUT. When the DMA finishes a job and sets DONE, no prediction runs; the mirror finds out only on the next read.

    Build it: the DMA test, by name

    Here is the test that produced the opening scene. It uses dma_ral_env (the block env plus the model, the adapter and a predictor), turns on auto-prediction so the mirror updates the instant an access returns, and does everything by register name. Compare the LEN write with reg_write(LEN, 9): the address and the bit packing are gone, and the model now has an opinion about the result.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    // snippet: from the course DMA bench
    // The model meets a DUT rule it does not know: the DMA ignores writes to LEN
    // while BUSY. The write goes out on the bus, the predictor updates the mirror,
    // and a mirror(UVM_CHECK) read then reports the DUT's real value as a mismatch.
    // This test is expected to end with one UVM_ERROR from the register model.
    class dma_ral_busy_len_test extends dma_ral_base_test;
      `uvm_component_utils(dma_ral_busy_len_test)
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
      function void build_phase(uvm_phase phase);
        uvm_config_db#(bit)::set(this, "env", "auto_predict", 1);  // mirror() takes its expected value at once
        super.build_phase(phase);
      endfunction
      task run_phase(uvm_phase phase);
        uvm_status_e   st;
        uvm_reg_data_t peeked;
        dma_reg_block  rm  = renv.regmodel;
        axis_pkt_seq   pkt = axis_pkt_seq::type_id::create("pkt");
        phase.raise_objection(this);
        rm.key.write(st, 32'h0000_00FF);
        rm.len.write(st, 4);
        rm.ctrl.write(st, 32'h3);                 // the DMA is now BUSY
        rm.len.write(st, 9);                      // ignored by the DMA, believed by the mirror
        `uvm_info("RAL", $sformatf("LEN mirror after the write: %0d", rm.len.get_mirrored_value()), UVM_LOW)
        rm.len.mirror(st, UVM_CHECK);             // reads 4, expects 9: RegModel error
        // peek() after the check, not before: a peek predicts the value it reads,
        // so peeking first would quietly set the mirror to 4 and hide the mismatch.
        rm.len.peek(st, peeked);
        `uvm_info("RAL", $sformatf("DUT LEN (backdoor peek)=%0d, mirror now %0d", peeked, rm.len.get_mirrored_value()), UVM_LOW)
        if (!pkt.randomize() with { n == 4; }) `uvm_fatal("RAND", "axis_pkt_seq randomize failed")
        pkt.start(env.in_agt.sqr);
        wait (irq_vif.irq === 1'b1);
        rm.status.write(st, 32'h2);
        #100ns;
        phase.drop_objection(this);
      endtask
    endclass

    mirror(st, UVM_CHECK) reads the register over the bus, compares what came back with the mirror, reports any difference, and then sets the mirror to the value read. It is the model's way of asking was I right?

    Watch it run

    Diagram
    UVM_INFO dma_ral_tb.sv(385) @ 195000: uvm_test_top [RAL] LEN mirror after the write: 9
    UVM_ERROR reg/uvm_reg.svh(2812) @ 235000: reporter [RegModel] Register "regmodel.len" value read from DUT (0x0000000000000004) does not match mirrored value (0x0000000000000009) (valid bit mask = 0x000000000000ffff)
    UVM_INFO reg/uvm_reg.svh(2833) @ 235000: reporter [RegModel] Field value (regmodel.len[15:0]) mismatch read=16'h4 mirrored=16'h9
    UVM_INFO dma_ral_tb.sv(390) @ 235000: uvm_test_top [RAL] DUT LEN (backdoor peek)=4, mirror now 4
    UVM_INFO dma_tb.sv(411) @ 515000: uvm_test_top.env.sb [SB] 4 words matched, 0 mismatched
    UVM_ERROR :    1
    UVM_FATAL :    0
    • LEN mirror after the write: 9. The write completed on the bus, so the model predicted it. Desired and mirrored are both 9; the DUT still holds 4.

    • The RegModel error is mirror(UVM_CHECK) at work: read 4, expected 9. The valid bit mask 0xffff shows only the 16 modelled bits of LEN were compared.

    • mirror now 4. After the check, the mirror takes the value it read, so the model is back in step with the DUT. The backdoor peek confirms the DUT's value without a bus access.

    • 4 words matched. The job ran with LEN=4, as the DUT's spec says it should. The single UVM_ERROR is this test's expected result: it exists to show the mismatch.

    What breaks: the mirror is a belief, not a fact

    The model knows each field's static policy: LEN is RW, DONE is write-1-to-clear, COUNT is read-only. It does not know dynamic rules such as "LEN is ignored while BUSY", unless someone models them. The same blind spot bit the bench before: the DMA's scoreboard and predictor take a new KEY at once, and a scratch run writing KEY mid-job gave five mismatches in both checkers. A model that copies the spec's register table but not its prose has the same hole, in a new place.

    While building this test there was a second trap. The first version peeked LEN through the backdoor before the mirror check, to print the DUT's value. The check then passed. A peek is an access too: it predicts the value it reads, so it quietly set the mirror to 4, and mirror(UVM_CHECK) compared 4 with 4. Any access that observes the DUT corrects the belief, so the check has to come first. That is why the test above peeks after the check, with a comment saying so.

    The third limit is by design. STATUS and COUNT change without a bus write, so their fields are configured volatile. The library turns off the mirror compare for volatile fields, so mirror(UVM_CHECK) on STATUS never reports a mismatch, and reading a volatile field's mirror draws a warning from the IEEE 1800.2 library. dma_ral_job_test prints one for each STATUS bit:

    Diagram
    UVM_WARNING reg/uvm_reg_field.svh(1327) @ 425000: reporter [UVM/FLD/GET_MIRRORED_VAL/VOL] Mirrored value returned for volatile field "regmodel.status.done" may not reflect the actual current value.

    For a volatile register, check the value a read() returns against your own expectation, not against the mirror. For a rule like busy-LEN, either keep the error as a known spec gap (as this bench does) or teach the model the rule with a callback or a predictor extension; Register Access shows how.

    Interview

    "What is the difference between the desired and the mirrored value?"

    A weak answer: "desired is what you write and mirrored is what is in the DUT." The mirror is not what is in the DUT; it is what the model believes is there.

    A strong answer: "Desired is what I intend: set() and randomize() change it with no bus traffic, and update() writes it if it differs from the mirror. Mirrored is the model's belief about the DUT, updated only by prediction, either automatically when an access returns or by a predictor fed from the bus monitor. Every prediction sets both. The DUT can disagree with the mirror whenever it changes a register by itself, ignores a write, or something the model cannot see accesses it, so I treat the mirror as a hypothesis and check it with mirror(UVM_CHECK). Volatile fields are excluded from that check."

    Follow-ups: How can the mirror be wrong after a clean write? (the DUT ignored it by a rule the model does not know, like LEN while busy) and Why can a peek hide a bug? (it predicts what it reads, so a check after it compares the DUT with itself).

    Check yourself After rm.len.set(9) and no other call, what do get(), get_mirrored_value() and the DUT's LEN hold, if LEN was 4 before?

    get() returns 9 (desired), get_mirrored_value() returns 4 and the DUT holds 4. set() touches only the desired value. A later update() would see desired 9 and mirrored 4, write 9 over the bus, and the prediction from that write would bring the mirror to 9.

    Check yourself The DMA has finished a job and DONE is 1 in the RTL, but the mirror says 0. A test calls rm.status.mirror(st, UVM_CHECK). Does it report a mismatch?

    No. DONE is configured volatile, and the library turns the compare off for volatile fields. The read still happens and the mirror takes the value read, so DONE's mirror becomes 1 silently. To check STATUS, compare the value read() returns with what you expect.

    Check yourself In dma_ral_busy_len_test, someone moves the backdoor peek() above mirror(UVM_CHECK). What changes in the log, and why?

    The RegModel error disappears and the test reports 0 errors. The peek reads 4 from the DUT and predicts it, setting the mirror to 4, so the check compares 4 with 4. The DUT's behaviour is identical; only the evidence is gone.

    Next: the model can only be as right as its description of each bit. Blocks, Registers, Fields and Memories builds the DMA's model field by field.

    Related topics

    • Blocks, Registers, Fields and Memories

    • The access API

    • The frontdoor path, end to end

    • Reference models

    Previous

    147 / 280

    Next: Blocks, Registers, Fields and Memories

    Up next

    Up nextBlocks, Registers, Fields and Memories

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Maps, Offsets and Endianness
    • Nested Blocks and Submaps
    • Build, Lock and Generated Models