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
    • Register Access
      • The Access API: write, read, set, get, update, mirror, predict, peek, poke
      • The Adapter Contract: reg2bus, bus2reg and provides_responses
      • Prediction: Auto, Explicit and Passive
      • Field Access Policies and Callbacks
      • Volatile Fields and Mirror Checks
      • Backdoor Access: HDL Paths and User Backdoors
      • User-Defined Frontdoors: Indirect and Paged Registers
    • Registers in Verification
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Register Access
  1. Topics
  2. UVM Verification
  3. UVM Register Abstraction Layer (RAL)
  4. Register Access
  5. Prediction: Auto, Explicit and Passive

Part 9 · Register Model (RAL) · Intermediate

Prediction: Auto, Explicit and Passive

Who updates the mirror and when: auto prediction in the map, explicit prediction from the monitor, passive tracking, where each breaks, and the prediction lag that failed a bit-bash on 94 bits.

On this page

    The DMA's LEN and KEY registers are plain read-write storage, and the designers have tested them for months. The first register bit-bash on the new register layer reports 94 errors: every bit of LEN and KEY, written and read back through the bus, "yielded" a value that differs from what the model expected. The read values are correct; the expectations are wrong. Nothing was mis-modelled. The model simply heard about each write a moment too late.

    The idea: who tells the mirror, and when

    The mirror is only as good as the reports it gets. Prediction is the act of updating it after an access, and UVM gives you three ways to organise it.

    • Auto prediction (map.set_auto_predict(1)): the map updates the mirror inside write()/read() itself, right after the bus access completes, from what the register call asked for and the data the adapter returned. It sees only accesses made through this register model.

    • Explicit prediction: a uvm_reg_predictor listens to the bus monitor, decodes every observed transfer with the adapter's bus2reg(), finds the register by address and predicts it. It sees every transfer on that bus, whoever started it. Auto prediction is off by default, so explicit prediction only means wiring the predictor.

    • Passive prediction: explicit prediction with no register traffic of your own. The model is never connected to a sequencer for accesses; firmware on a CPU model, another master or a legacy sequence drives the bus, and the predictor keeps the mirror in step so checks and coverage still work.

    An analogy: auto prediction is keeping a diary of what you said in a meeting, written the moment you finish speaking. Explicit prediction is a stenographer transcribing everything said in the room, including by people you did not invite, typing a beat behind the speaker. The diary is instant but deaf to others; the transcript is complete but always slightly behind.

    The picture: the moment between the access and the prediction

    Sequence diagram: the test calls write, the map sends the item to the driver, the transfer completes on clock edge t, the driver returns, auto prediction updates the mirror before write returns, the test reads the mirror at time t and sees the old value with explicit prediction, and only afterwards the monitor publishes and the predictor updates the mirror.
    One write with each prediction mode. With auto prediction the mirror is updated before write() returns. With explicit prediction the update arrives later in the same time step, after the test has already moved on.

    The driver and the monitor both wake on the same clock edge. The driver returns the item, finish_item() returns, and write() returns to the test, all in that time step. The monitor's ap.write() also runs in that time step, but nothing orders it before the test's next statement. In this bench it lands after, so for a few delta cycles the mirror is stale.

    Build it: the bench's explicit predictor

    dma_ral_env builds a uvm_reg_predictor #(apb_item), gives it the map and the adapter, and connects the APB monitor to its bus_in export. Auto prediction is a config_db knob that defaults to 0:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // snippet: from dma_ral_env in the course DMA bench (dma_ral_tb.sv)
    function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      regmodel.map.set_sequencer(apb.sqr, adapter);
      // The explicit predictor updates the mirror from what the APB monitor sees.
      // That happens when the monitor publishes the transfer - in this bench a
      // moment after write()/read() return, in the same time step. Code that
      // reads the mirror the instant an access returns (uvm_reg_bit_bash_seq's
      // rg.get(), mirror()'s expected value) then sees the old value. Tests
      // that need it turn on auto_predict as well.
      regmodel.map.set_auto_predict(auto_predict);
      predictor.map     = regmodel.map;
      predictor.adapter = adapter;
      apb.ap.connect(predictor.bus_in);
    endfunction

    Three lines make it work: predictor.map (to look up the register by address), predictor.adapter (to decode the item) and apb.ap.connect(predictor.bus_in). Forget the connection and nothing fails: the mirror simply never changes, and the first mirror(UVM_CHECK) reports a confusing mismatch. A predictor also does more than update the mirror. If the map has check_on_read set, it compares each observed read against the mirror (lesson 5); it samples register coverage; and it publishes each predicted uvm_reg_item on its reg_ap port for anyone who wants register-level transactions.

    That port is the clean feed for a register-aware scoreboard. Each item names the register (element), the access kind, the value and the status of one observed bus transfer, already decoded from address to register, so the scoreboard reasons about regmodel.key rather than address 0x0C. It fires only for traffic the predictor sees on the bus; auto prediction inside write()/read() publishes nothing there.

    Mode

    Updates the mirror from

    Sees other masters?

    When

    Breaks when

    Auto

    The register call and the adapter's returned data

    No

    Inside write()/read()

    Anything else writes the register: a second master, firmware, a sequence that bypasses the model

    Explicit

    Monitored bus transfers

    Yes, on the monitored bus

    A moment after the access returns

    Code reads the mirror the instant an access returns; a register has no address on that bus (lesson 7)

    Passive

    Monitored transfers only

    Yes

    As explicit

    As explicit, plus nothing in the model initiates accesses, so checks must be scheduled

    Auto and explicit

    Both, one after the other

    Yes

    Twice per access

    A side-effect callback runs twice (lesson 4 shows it); multi-beat transfers can collide

    No mode sees what the hardware does to itself. When the DMA sets DONE, counts COUNT up or a second internal engine writes a CSR, no bus transfer exists to predict. That is what volatile fields and backdoor reads are for (lessons 5 and 6). The DMA itself teaches the other limit: it ignores LEN and KEY writes while BUSY, the write still appears on the bus, and every prediction mode believes it.

    Watch it run

    The bench's own job test shows the lag in one line. It reads STATUS after the interrupt and immediately prints the mirror's DONE bit:

    Diagram
    +UVM_TESTNAME=dma_ral_job_test
    UVM_INFO dma_ral_tb.sv(346) @ 425000: uvm_test_top [RAL] STATUS=2: done=0 err=0
    UVM_INFO dma_ral_tb.sv(355) @ 545000: uvm_test_top [RAL] KEY mirror=cafef00d, backdoor peek=cafef00d, status=UVM_IS_OK
    UVM_INFO dma_tb.sv(411) @ 645000: uvm_test_top.env.sb [SB] 6 words matched, 0 mismatched
    UVM_ERROR :    0
    UVM_FATAL :    0

    STATUS=2 is the value read() returned, so DONE is 1 in the DUT. done=0 is the mirror, which the predictor has not updated yet. The data was right; the test was asking the wrong copy. To see the bigger effect, two scratch tests rerun the bench's bit-bash and busy-LEN tests with the auto-prediction knob forced back to 0. Each is three lines on top of the bench test:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // snippet: verified in a scratch run with the DMA bench
    class s_bitbash_explicit_test extends dma_ral_bitbash_test;
      `uvm_component_utils(s_bitbash_explicit_test)
      function new(string name, uvm_component parent); super.new(name, parent); endfunction
      function void build_phase(uvm_phase phase);
        super.build_phase(phase);                                  // sets auto_predict = 1
        uvm_config_db#(bit)::set(this, "env", "auto_predict", 0);  // same setter, later: this one wins
      endfunction
    endclass
    Diagram
    scratch run: +UVM_TESTNAME=s_bitbash_explicit_test (dma_ral_bitbash_test with auto_predict forced to 0)
    UVM_INFO reg/sequences/uvm_reg_bit_bash_seq.svh(158) @ 0: reporter@@seq.reg_single_bit_bash_seq [uvm_reg_bit_bash_seq] Verifying bits in register regmodel.len in map "regmodel.map"...
    UVM_ERROR reg/sequences/uvm_reg_bit_bash_seq.svh(211) @ 195000: reporter@@seq.reg_single_bit_bash_seq [uvm_reg_bit_bash_seq] Writing a 0 in bit #0 of register "regmodel.len" with initial value 'h0000000000000001 yielded 'h0000000000000000 instead of 'h0000000000000001
    UVM_ERROR reg/sequences/uvm_reg_bit_bash_seq.svh(211) @ 275000: reporter@@seq.reg_single_bit_bash_seq [uvm_reg_bit_bash_seq] Writing a 1 in bit #1 of register "regmodel.len" with initial value 'h0000000000000000 yielded 'h0000000000000002 instead of 'h0000000000000000
       ... 92 more, one per bit write except the very first of each register: 31 on LEN, 63 on KEY
    UVM_INFO dma_ral_tb.sv(316) @ 12835000: uvm_test_top [RAL] bit-bash finished for LEN, KEY and COUNT (errors, if any, are reported above)
    UVM_ERROR :   94
    UVM_FATAL :    0

    Read one error closely. Writing a 0 in bit #0 with initial value 1 yielded 0 instead of 1: the DUT returned 0, exactly what was written. The sequence takes its expected value from rg.get() right after write() returns. But the predictor's update for the previous read landed after the sequence had called write(0), and every prediction also overwrites the desired value (lesson 1), so rg.get() returned the stale 1. Only the very first write of each register escapes, because no late prediction is pending yet. Notice too that the test's own summary line is printed whatever happened. It used to say "bit-bash passed" and was reworded once this run showed it printing after 94 errors. The error count is the verdict, never a friendly info line.

    Diagram
    scratch run: +UVM_TESTNAME=s_busy_len_explicit_test (dma_ral_busy_len_test with auto_predict forced to 0)
    UVM_INFO dma_ral_tb.sv(385) @ 195000: uvm_test_top [RAL] LEN mirror after the write: 4
    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 :    0
    UVM_FATAL :    0

    This one is worse, because it is silent. The DMA ignores the LEN=9 write while BUSY, so a correct model check must fail (the bench's version, with auto prediction on, reports exactly one RegModel error). Here the mirror still said 4 when mirror(UVM_CHECK) captured its expected value, the DUT also said 4, and the check passed. A real model-versus-DUT disagreement was missed.

    What breaks: a correct design, 94 errors, and a missed bug

    This is war story 11 from building the bench. The register layer was wired with explicit prediction only, the robust textbook setup. dma_ral_job_test printed done=0 after a STATUS read of 2, the bit-bash failed on every bit, and the busy-LEN check passed when it should have failed. All three had one cause: code that reads the mirror (or the desired value) the instant an access returns, in a bench where the predictor updates it a moment later.

    There are three honest fixes, and the bench uses the first two:

    • Use the value the access returned, not the mirror. read(st, v) hands you v; compare that. The job test's COUNT check does this and is immune to the lag.

    • Turn on auto prediction for tests whose sequences read the mirror right away. The bench's bit-bash and busy-LEN tests set auto_predict to 1. The explicit predictor stays connected, so other masters are still seen; each access is simply predicted twice, which is harmless when both predictions agree.

    • Wait for the predictor. A sequence can wait a clock, or wait on the predictor's reg_ap, before reading the mirror. This keeps one prediction path but costs simulation time on every access.

    Tip

    With both modes on, every frontdoor access is predicted twice: once by the map inside the call, once by the predictor from the monitor. Most field policies give the same answer both times (RW, RO, W1C, W1S, RC), but a toggle policy (W1T, W0T) applies its toggle to the current mirror, so the second prediction undoes the first; the DMA has no toggle fields, which is the only reason this bench gets away with it. A side-effect callback also runs twice, and with multi-beat registers the predictor can report REG_PREDICT_COLLISION if two accesses to the same register overlap on the bus.

    Interview

    "Auto prediction or explicit prediction: which would you use, and why?"

    A weak answer: "Explicit, because it is more accurate."

    A strong answer: "Explicit as the base, because the predictor sees every transfer on the bus, including other masters and sequences that bypass the model. But explicit prediction lags: the mirror updates when the monitor publishes, which can be after write() has returned. Anything that reads the mirror right after an access, such as uvm_reg_bit_bash_seq taking its expected value from get() or mirror(UVM_CHECK) capturing its expectation, can see the old value. In our DMA bench that made the bit-bash fail on 94 bits and let a real mismatch pass. So I either compare returned values, or enable auto prediction too for those tests and accept double prediction. Neither mode sees hardware-side updates; those are volatile fields."

    Follow-ups: What is passive mode for? (A model that only tracks traffic from firmware or another master, for checks and coverage.) What does the predictor do on a read when `check_on_read` is set? (It compares the observed data with the mirror before predicting.)

    Check yourself A chip has a CPU model running firmware on the same APB bus as your register sequences. Which prediction mode keeps the mirror right, and why does the other one fail?

    Explicit (with or without auto prediction on top). The predictor decodes every transfer the monitor sees, including the firmware's. Auto prediction alone updates the mirror only for accesses made through the register model, so every firmware write leaves the mirror stale.

    Check yourself With explicit prediction only, a test calls rm.key.write(st, v) and then rm.key.mirror(st, UVM_CHECK) on the next line. The DUT works. What can go wrong, and in which direction?

    mirror() captures its expected value from the mirror before its read. If the write's prediction has not landed yet, the expected value is the old KEY: the check fails on a correct DUT. If a DUT bug made the write not stick, the stale expectation can match the DUT's old value, and the check passes on a buggy DUT. Either way the check is testing timing, not the design.

    Check yourself Why does the bit-bash's very first write of each register pass, even with explicit prediction only?

    The failures come from a late prediction of the previous access overwriting the desired value after the next write() has started. Before the first write of a register, no prediction for that register is pending, so rg.get() after the write returns the value just written.

    Next: both prediction paths apply each field's access policy to decide the new mirror value. Field Access Policies and Callbacks shows the policies, and how to teach the model the DUT rule that broke the busy-LEN test.

    Related topics

    • Up: Register Access

    • The frontdoor path, end to end

    • Monitor sampling and races

    Previous

    156 / 280

    Next: Field Access Policies and Callbacks

    Up next

    Up nextField Access Policies and Callbacks

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • The Access API: write, read, set, get, update, mirror, predict, peek, poke
    • Volatile Fields and Mirror Checks
    • Backdoor Access: HDL Paths and User Backdoors