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
    • Registers in Verification
      • Built-in Register Sequences and NO_* Attributes
      • Register Coverage
      • Memories in the Register Model
      • Multiple Maps and Address Spaces
      • Shadow, Alias and Paged Registers
      • Interrupts with the Register Model
      • The Register Model at SoC Scale
      • RAL Interview Set
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Registers in Verification
  1. Topics
  2. UVM Verification
  3. UVM Register Abstraction Layer (RAL)
  4. Registers in Verification
  5. Built-in Register Sequences and NO_* Attributes

Part 9 · Register Model (RAL) · Intermediate

Built-in Register Sequences and NO_* Attributes

What uvm_reg_hw_reset_seq, bit-bash, access and memory sequences really check, how NO_* attributes exclude registers, and why bit-bash failed with explicit prediction.

On this page

    The DMA's reset test was green, and its last line used to be reassuring: hardware reset values checked for every register. In a design review someone read the three lines above it. The sequence verified CTRL, LEN and KEY. STATUS and COUNT, two of the five registers, were never read. Nobody excluded them, nothing warned, and the message that said "every register" was written by a person, not by the library. (The bench's message now says what really happened: registers whose fields are all volatile are skipped.)

    The built-in register sequences are the cheapest tests in a DV plan: a few lines of code each, generated from the register model, run on every block. This lesson shows what each one really does, which registers and bits it silently skips, how you exclude the ones it must not touch, and why one of them failed on every bit of the DMA until the prediction setup changed.

    The idea: a checklist generated from the floor plan

    A building inspector arrives with a standard checklist: every door opens, every switch turns its light on, every tap runs. The checklist is generated from the floor plan, so it is only as good as the plan, and the inspector never asks whether the building is any good as a hospital. The built-in sequences are that inspector. They read the register model (the floor plan) and check that the hardware agrees with it: reset values, read/write paths for each bit, frontdoor against backdoor. They know nothing about what the DMA does.

    Two facts decide what they check. A field whose get_compare() is UVM_NO_CHECK is skipped, and configuring a field as volatile sets exactly that: uvm_reg_field::configure stores m_check = volatile ? UVM_NO_CHECK : UVM_CHECK. STATUS (BUSY, DONE, ERR) and COUNT are volatile, so the reset sequence finds nothing to compare in them and moves on without a word. The second fact: bits the model does not describe are treated as read-only zeros by bit-bash, so they are checked, whether you meant it or not.

    Sequence

    What it does, per register (or memory) and map

    What it skips or needs

    uvm_reg_hw_reset_seq

    mirror(UVM_CHECK) through the frontdoor. It never drives reset itself: the test must hold the DUT in reset, release it and wait before start(null), or it checks whatever state the DUT is in

    Fields with no reset or with UVM_NO_CHECK (volatile); registers with no checkable field are skipped silently

    uvm_reg_bit_bash_seq

    For each bit: write it flipped, read back, compare; then flip it back

    Bits of WO, WOC, WOS, WO1, NOACCESS and UVM_NO_CHECK fields. Unmodelled bits are bashed as RO and must read 0

    uvm_reg_access_seq

    Frontdoor write, backdoor check; backdoor write, frontdoor check

    Needs a backdoor (uvm_reg_backdoor or HDL path); skips registers whose fields are all RO

    uvm_mem_walk_seq

    Walking-pattern write and read of every word

    RW memories only

    uvm_mem_access_seq

    Frontdoor against backdoor for memory words

    Needs a memory backdoor

    uvm_reg_mem_shared_access_seq

    A write through one map is visible through every other map

    Registers and memories in more than one map

    uvm_reg_mem_built_in_seq

    Runs the ones selected by its tests bit mask, in order

    Each sub-sequence keeps its own skips

    The picture: one wrapper, six tests, and the attributes that gate them

    Flowchart: uvm_reg_mem_built_in_seq fans out to the hw_reset, bit_bash, access and memory sequences; a dotted arrow shows NO_ attributes in uvm_resource_db skipping a block, register or memory.
    The wrapper runs the sequences its mask selects. Each attribute is a uvm_resource_db bit under the scope REG:: plus the full name of a block, register or memory; any value disables that test for that element.

    Read the dotted arrows as filters that the sequences consult, not switches you flip on the sequence. Each sequence calls uvm_resource_db#(bit)::get_by_name({"REG::", element.get_full_name()}, "NO_...", 0) and skips the element if a resource exists at all. The value is never read, so setting an attribute to 0 still disables the test. There are also narrower names: NO_REG_SHARED_ACCESS_TEST, NO_MEM_WALK_TEST, NO_MEM_ACCESS_TEST, and NO_REG_HW_RESET_TEST on a single field to drop just that field from the reset check.

    Build it: the bench's reset and bit-bash tests

    The reset test: five lines and one honest question

    The test hands the sequence the model and starts it with no sequencer: register sequences reach the bus through the map's sequencer, so start(null) is the normal way to run them. The test waives the scoreboard's empty-compare error because it moves no data.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // snippet: from the course DMA bench
    // Every register's reset value, read through the bus and checked against the model.
    class dma_ral_reset_test extends dma_ral_base_test;
      `uvm_component_utils(dma_ral_reset_test)
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
      function void end_of_elaboration_phase(uvm_phase phase);
        super.end_of_elaboration_phase(phase);
        waive_empty_scoreboard();
      endfunction
      task run_phase(uvm_phase phase);
        uvm_reg_hw_reset_seq seq = uvm_reg_hw_reset_seq::type_id::create("seq");
        phase.raise_objection(this);
        seq.model = renv.regmodel;
        seq.start(null);
        `uvm_info("RAL", "reset values checked (registers whose fields are all volatile are skipped)", UVM_LOW)
        phase.drop_objection(this);
      endtask
    endclass

    Its log is the one from the opening. CTRL is checked (IRQ_EN; GO is WO and is left out of the compare), and so are LEN and KEY. STATUS and COUNT are missing because every field in them is volatile. That is correct behaviour, not a bug: a volatile field may legally change between reset and the read, so there is no safe expected value. But it means the reset values of BUSY, DONE, ERR and COUNT need a directed check if your plan lists them, for example one read() right after reset compared with 0 in the test itself.

    The bit-bash test: leave out what the walk would break

    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
    // snippet: from the course DMA bench
    // Walk a 1 and a 0 through every bit of the RW registers. CTRL and STATUS are
    // excluded: writing CTRL.GO would start a job, and STATUS bits are W1C.
    class dma_ral_bitbash_test extends dma_ral_base_test;
      `uvm_component_utils(dma_ral_bitbash_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);  // bit-bash reads rg.get() right after write()
        super.build_phase(phase);
      endfunction
      function void end_of_elaboration_phase(uvm_phase phase);
        super.end_of_elaboration_phase(phase);
        waive_empty_scoreboard();
      endfunction
      task run_phase(uvm_phase phase);
        uvm_reg_bit_bash_seq seq = uvm_reg_bit_bash_seq::type_id::create("seq");
        phase.raise_objection(this);
        uvm_resource_db#(bit)::set({"REG::", renv.regmodel.ctrl.get_full_name()},   "NO_REG_TESTS", 1, this);
        uvm_resource_db#(bit)::set({"REG::", renv.regmodel.status.get_full_name()}, "NO_REG_TESTS", 1, this);
        seq.model = renv.regmodel;
        seq.start(null);
        `uvm_info("RAL", "bit-bash finished for LEN, KEY and COUNT (errors, if any, are reported above)", UVM_LOW)
        phase.drop_objection(this);
      endtask
    endclass

    Two registers are excluded, each for its own reason. CTRL: bit-bash writes every bit pattern, and a write with bit 0 set starts a DMA job. The GO bit is WO, so bit-bash would not even compare it; the harm is the side effect of the write, not the check. STATUS: DONE and ERR are write-1-to-clear and volatile. Bit-bash could not compare them anyway, and its writes would clear status bits other checks rely on. Excluding a register with NO_REG_TESTS says why in one line, and it also keeps the reset and access sequences off it.

    Common pitfall

    The attribute must be set before the sequence reaches that register, under the exact scope {"REG::", rg.get_full_name()}. A typo in the scope, such as "REG::ctrl" instead of "REG::regmodel.ctrl", creates a resource that nobody looks up, and bit-bash happily starts a DMA job.

    Watch it run

    Diagram
    +UVM_TESTNAME=dma_ral_reset_test   (run.sh dma_ral_tb.sv)
    UVM_INFO reg/sequences/uvm_reg_hw_reset_seq.svh(153) @ 0: reporter@@seq [uvm_reg_hw_reset_seq] Verifying reset value of register regmodel.ctrl in map "regmodel.map"...
    UVM_INFO reg/sequences/uvm_reg_hw_reset_seq.svh(153) @ 75000: reporter@@seq [uvm_reg_hw_reset_seq] Verifying reset value of register regmodel.len in map "regmodel.map"...
    UVM_INFO reg/sequences/uvm_reg_hw_reset_seq.svh(153) @ 115000: reporter@@seq [uvm_reg_hw_reset_seq] Verifying reset value of register regmodel.key in map "regmodel.map"...
    UVM_INFO dma_ral_tb.sv(289) @ 155000: uvm_test_top [RAL] reset values checked (registers whose fields are all volatile are skipped)
    UVM_INFO dma_tb.sv(411) @ 155000: uvm_test_top.env.sb [SB] 0 words matched, 0 mismatched
    UVM_ERROR :    0
    UVM_FATAL :    0
    Diagram
    +UVM_TESTNAME=dma_ral_bitbash_test   (run.sh dma_ral_tb.sv)
    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_INFO reg/sequences/uvm_reg_bit_bash_seq.svh(158) @ 5155000: reporter@@seq.reg_single_bit_bash_seq [uvm_reg_bit_bash_seq] Verifying bits in register regmodel.key in map "regmodel.map"...
    UVM_INFO reg/sequences/uvm_reg_bit_bash_seq.svh(158) @ 10275000: reporter@@seq.reg_single_bit_bash_seq [uvm_reg_bit_bash_seq] Verifying bits in register regmodel.count in map "regmodel.map"...
    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_INFO dma_tb.sv(411) @ 12835000: uvm_test_top.env.sb [SB] 0 words matched, 0 mismatched
    UVM_ERROR :    0
    UVM_FATAL :    0
    • The bit-bash timestamps tell you how many bits were bashed. Each bit costs two writes and two reads at 40 ns per APB access (the DMA adds one wait state), so 160 ns per bit. LEN took 5.12 µs: 32 bits, its 16 field bits and the 16 unmodelled upper bits, which must read 0.

    • COUNT took 2.56 µs: 16 bits. Its only field is RO and volatile, so all 16 field bits are skipped, and what bit-bash really checked on COUNT is that bits 31:16 read 0. Zero errors on COUNT is true, but it means less than it sounds.

    • Random values and timestamps differ by simulator. The bit counts do not.

    The same choices can be made in one place with the wrapper sequence. A scratch test (not part of the bench) runs reset, bit-bash and access from uvm_reg_mem_built_in_seq with three attributes: no tests at all on CTRL, no bit-bash on STATUS, and no access test anywhere in the block, set at block scope.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    // snippet: scratch test body
    uvm_resource_db#(bit)::set({"REG::", rm.ctrl.get_full_name()},   "NO_REG_TESTS",         1, this);
    uvm_resource_db#(bit)::set({"REG::", rm.status.get_full_name()}, "NO_REG_BIT_BASH_TEST", 1, this);
    uvm_resource_db#(bit)::set({"REG::", rm.get_full_name()},        "NO_REG_ACCESS_TEST",   1, this);
    seq.model = rm;
    seq.tests = UVM_DO_REG_HW_RESET | UVM_DO_REG_BIT_BASH | UVM_DO_REG_ACCESS;
    seq.start(null);
    Diagram
    scratch run, Verilator
    UVM_INFO reg/sequences/uvm_reg_hw_reset_seq.svh(153) @ 0: reporter@@seq.reg_hw_reset_seq [uvm_reg_hw_reset_seq] Verifying reset value of register regmodel.len in map "regmodel.map"...
    UVM_INFO reg/sequences/uvm_reg_hw_reset_seq.svh(153) @ 75000: reporter@@seq.reg_hw_reset_seq [uvm_reg_hw_reset_seq] Verifying reset value of register regmodel.key in map "regmodel.map"...
    UVM_INFO reg/sequences/uvm_reg_mem_built_in_seq.svh(92) @ 115000: reporter@@seq [FINISH_SEQ] Finished reg_hw_reset_seq sequence.
    UVM_INFO reg/sequences/uvm_reg_bit_bash_seq.svh(158) @ 115000: reporter@@seq.reg_bit_bash_seq.reg_single_bit_bash_seq [uvm_reg_bit_bash_seq] Verifying bits in register regmodel.len in map "regmodel.map"...
    UVM_INFO reg/sequences/uvm_reg_bit_bash_seq.svh(158) @ 5235000: reporter@@seq.reg_bit_bash_seq.reg_single_bit_bash_seq [uvm_reg_bit_bash_seq] Verifying bits in register regmodel.key in map "regmodel.map"...
    UVM_INFO reg/sequences/uvm_reg_bit_bash_seq.svh(158) @ 10355000: reporter@@seq.reg_bit_bash_seq.reg_single_bit_bash_seq [uvm_reg_bit_bash_seq] Verifying bits in register regmodel.count in map "regmodel.map"...
    UVM_INFO reg/sequences/uvm_reg_mem_built_in_seq.svh(103) @ 12915000: reporter@@seq [FINISH_SEQ] Finished reg_bit_bash_seq sequence.
    UVM_ERROR :    0

    CTRL disappeared from the reset check too: NO_REG_TESTS means every register test. The access sequence never started, because the block-level attribute is checked by the wrapper before it creates the sequence.

    The access sequence needs a backdoor that can write

    Without that block-level attribute, uvm_reg_access_seq runs, and it shows what the bench's backdoor can and cannot do. Its recipe per register is: write ~value through the frontdoor, mirror(UVM_CHECK) through the backdoor, write the old value through the backdoor, then mirror(UVM_CHECK) through the frontdoor. The bench's dma_reg_backdoor reads KEY, LEN and COUNT through a bound interface and refuses writes. Scratch run, Verilator:

    Diagram
    UVM_WARNING reg/sequences/uvm_reg_access_seq.svh(291) @ 0: reporter@@seq [uvm_reg_access_seq] Register 'regmodel.ctrl' does not have a backdoor mechanism available
    UVM_WARNING reg/sequences/uvm_reg_access_seq.svh(291) @ 0: reporter@@seq [uvm_reg_access_seq] Register 'regmodel.status' does not have a backdoor mechanism available
    UVM_INFO reg/sequences/uvm_reg_access_seq.svh(157) @ 0: reporter@@seq.single_reg_access_seq [uvm_reg_access_seq] Verifying access of register 'regmodel.len' in map 'regmodel.map' ...
    UVM_WARNING dma_ral_tb.sv(195) @ 76000: reporter [BD_RO] this backdoor is read-only
    UVM_WARNING reg/uvm_reg.svh(1664) @ 76000: reporter [PREDICT_NOK] status UVM_NOT_OK; skip prediction.
    UVM_ERROR reg/sequences/uvm_reg_access_seq.svh(181) @ 76000: reporter@@seq.single_reg_access_seq [uvm_reg_access_seq] Status was 'UVM_NOT_OK' when writing 'regmodel.len' through backdoor
      ... the same three lines for regmodel.key ...
    UVM_WARNING reg/sequences/uvm_reg_access_seq.svh(140) @ 195000: reporter@@seq.single_reg_access_seq [uvm_reg_access_seq] Register 'regmodel.count' has only RO fields in map regmodel.map, skipping
    UVM_ERROR :    2

    The frontdoor-write, backdoor-read half passed for LEN and KEY; the backdoor-write half failed because the backdoor is read-only by design. The fix is a decision, not a patch: give the backdoor a write path (an HDL path with the simulator's DPI backdoor, or an interface that can deposit), or exclude the block with NO_REG_ACCESS_TEST and say in the testplan that frontdoor/backdoor consistency is covered by the KEY peek() check in dma_ral_job_test. Memories get the same treatment from uvm_mem_access_seq and uvm_mem_walk_seq; the memories lesson runs the walk.

    What breaks: bit-bash failed on every bit

    When the bit-bash test was first written, the env had explicit prediction only: the APB monitor feeds uvm_reg_predictor, and auto prediction is off. That is the recommended setup, and it made bit-bash fail almost everywhere. A scratch run of the same test with auto_predict set back to 0 gives this (Verilator):

    Diagram
    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 ...
    UVM_ERROR :   94

    94 errors: every bash of LEN's and KEY's field bits except the very first one of each register. The DUT is fine: it yielded exactly the value that was written. The expected value is wrong, and it is always the value from one access earlier. Bit-bash takes its expectation from rg.get() immediately after write() returns. With explicit prediction the mirror is updated when the APB monitor publishes a transfer, a moment after the driver finishes it. So the prediction for the previous read lands after write() has staged the new desired value, and overwrites it with the old one. The first bash of each register passes because nothing was in flight before it.

    The bench fixes it by turning auto prediction on for this test (uvm_config_db#(bit)::set(this, "env", "auto_predict", 1)), so the map predicts inside write() before it returns. The explicit predictor stays connected, so traffic from anything else is still seen. The general rule: library sequences that compare against the mirror straight after an access need prediction that has finished by the time the access returns. The same lag, and the same fix, appears in dma_ral_busy_len_test for mirror(UVM_CHECK).

    Interview

    "How would you use the built-in register sequences on a new block, and what can go wrong?"

    A weak answer: "Run reset and bit-bash on the whole model; they check every register."

    A strong answer: "Reset and bit-bash come first, on the block model, through start(null). Before running them I read the model for side effects: trigger bits, W1C and clear-on-read fields, FIFOs, anything that starts the design. Those registers get NO_REG_TESTS or a narrower attribute through uvm_resource_db under REG:: plus the full name. I know what they skip: volatile fields are never compared, so status and counters need directed checks, and unmodelled bits are bashed as read-as-zero. The access sequence needs a backdoor that writes. And with explicit prediction I turn auto prediction on for these tests, because bit-bash compares against rg.get() the moment write() returns."

    Follow-ups: Your bit-bash fails on every bit but the DUT looks fine. Where do you look first? (prediction timing: compare 'yielded' with 'instead of'; the expected value is one access stale). How do you exclude one field from the reset check? (NO_REG_HW_RESET_TEST on the field's full name).

    Check yourself A colleague adds uvm_resource_db#(bit)::set({"REG::", rm.ctrl.get_full_name()}, "NO_REG_TESTS", 0, this) to *re-enable* tests on CTRL. What happens?

    CTRL stays excluded. The sequences only ask whether a resource with that name exists (get_by_name(...) != null); they never read its value. To re-enable the tests, do not set the attribute at all in that test.

    Check yourself The reset test is green. Your testplan says "COUNT resets to 0". Is that row covered?

    No. COUNT's field is volatile, so its compare is UVM_NO_CHECK and uvm_reg_hw_reset_seq skips the register without a message, as the log shows (only CTRL, LEN and KEY are verified). Cover the row with a directed read() right after reset and an explicit compare against 0.

    Check yourself Bit-bash of LEN passes, but a designer says LEN is only 16 bits wide and asks why bit-bash spent time on bits 31:16.

    Bits the model does not describe are bashed as read-only: bit-bash writes each one flipped and expects it to read back as the mirror value, which is 0. It is checking that the unused upper bits are tied to zero. That is useful, but if the spec allows them to read as anything, model them as a reserved field with UVM_NO_CHECK.

    Next: the sequences check that registers exist and behave as described. They say nothing about which values the tests used. Register Coverage asks what register coverage measures, and why it is so often switched off without anybody noticing.

    Related topics

    • Up: Registers in Verification

    • Next: Register Coverage

    • The frontdoor path, end to end

    Previous

    161 / 280

    Next: Register Coverage

    Up next

    Up nextRegister Coverage

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Memories in the Register Model
    • Multiple Maps and Address Spaces
    • Shadow, Alias and Paged Registers