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 Here6
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive36
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
    • Testbench Anatomy & Layering
    • Stimulus: Generators, Transactions & Drivers
    • Monitors, Scoreboards & Reference Models
    • Self-Checking Tests & Regression
    • From Hand-Built TB to UVM
      • Concept Mapping: TB to UVM
      • What UVM Automates (and What It Costs)
      • Migrating a TB to UVM
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹From Hand-Built TB to UVM
  1. Topics
  2. SystemVerilog
  3. Testbench Architecture in SystemVerilog
  4. From Hand-Built TB to UVM
  5. Concept Mapping: TB to UVM

Part 6 · Testbench Architecture · Intermediate

Concept Mapping: TB to UVM

Side-by-side mapping: env to uvm_env, generator to sequences, mailboxes to TLM, objections, and test selection.

On this page

    Every piece has a twin

    Nothing in a UVM environment is conceptually new after a hand-built class testbench. Read each row as the hand-written version versus the standardized one.

    Hand-built

    UVM equivalent

    Env class with new() wiring

    uvm_env with build_phase and connect_phase

    Generator class pushing into a mailbox

    uvm_sequence holding stimulus, running on a uvm_sequencer

    mailbox#(txn) handoff with put / get

    seq_item_port with get_next_item / item_done

    Monitor fanning out into one mailbox per consumer

    uvm_analysis_port broadcast with write()

    Done counter or event ending the test

    uvm_objection raised at start, dropped when done

    +TESTNAME plusarg with a case statement

    +UVM_TESTNAME with factory creation by name

    Logger class with levels and scopes

    uvm_report: uvm_info / uvm_error with verbosity

    Config object threaded through constructors

    uvm_config_db set and get by hierarchical path

    Two mappings that change shape

    A hand-built generator does two jobs: it decides what stimulus to create and it delivers that stimulus to the driver. UVM splits the jobs: the uvm_sequence holds the stimulus code while the uvm_sequencer delivers items and arbitrates between competing sequences.

    Arbitration is the genuine addition. Several sequences can share one sequencer and interleave, which is painful to hand-roll with mailboxes. Sequencer detail lives in the UVM course under sequencer arbitration.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // snippet: hand-built push handoff, fire and forget
    mailbox#(bus_txn) out_mb = new(8);
    task automatic gen_run(int count);
      for (int i = 0; i < count; i++) begin
        bus_txn t = new();
        void'(t.randomize());
        out_mb.put(t);   // returns as soon as the mailbox accepts
      end
    endtask

    A mailbox handoff is producer-push: the generator calls put() and moves on. The UVM handshake is consumer-pull with completion: the driver calls get_next_item(), drives the pins, then calls item_done().

    The sequence blocks in finish_item() until then, so it knows when each item finished. That completion signal is what makes reactive stimulus natural in UVM and clumsy with a plain mailbox.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // snippet: UVM pull handshake with completion feedback
    task automatic seq_body(int count);
      for (int i = 0; i < count; i++) begin
        req = bus_txn::type_id::create("req");   // a fresh item each time
        start_item(req);        // wait until the driver is ready
        void'(req.randomize());
        finish_item(req);       // unblocks only after item_done()
      end
    endtask
    // driver side: get_next_item(req); drive_pins(req); item_done();

    The two stacks, aligned

    The same architecture appears twice, once hand-wired and once standardized. Trace one transaction from test to verdict on either side.

    Diagram
    HAND-BUILT STACK                 UVM STACK
      ────────────────                 ─────────
      test_lib.sv                      my_test (uvm_test)
       │ case(+TESTNAME)                │ +UVM_TESTNAME → factory
       ▼                                ▼
      env ───────────────────────────  uvm_env
       ├─ generator                     ├─ sequence ─► sequencer
       │     │ mailbox.put              │                │ seq_item_port
       ├─ driver ◄──┘                   ├─ driver ◄──────┘ (pull + done)
       │     │ virtual if               │     │ virtual if (config db)
       ├─ monitor                       ├─ monitor
       │     │ mailbox per consumer     │     │ analysis_port.write
       ├─ scoreboard ◄┘                 ├─ scoreboard (analysis_imp)
       └─ done event / counter          └─ objections raised/dropped
            │                                │
            ▼                                ▼
      verdict banner + $finish         run_phase ends → report_phase

    Common mistakes

    Common pitfall

    The classic mix-up is calling the sequencer the generator: stimulus content lives in the sequence, while the sequencer only delivers and arbitrates. A second trap is reading write() as blocking: analysis-port writes are non-blocking broadcasts, unlike puts into a full mailbox.

    Interview

    Likely depth question: “Is a mailbox of depth 1 plus an ack event the same as the handshake?” Close but not equal: the pair approximates pull-with-completion, while UVM adds arbitration among sequences, which is the part that hurts to hand-roll.

    Check yourself A generator pushes three items into a mailbox of depth 8 while the driver is slow. How far ahead can the generator run, and what does a sequence learn from finish_item that the generator never learns?

    The generator can run all three items ahead because depth 8 holds them, and it learns nothing about completion. A sequence blocked in finish_item learns that each item completed, and it can read back a response before choosing the next item.

    Check yourself One monitor feeds a scoreboard and a coverage collector. Compare the mailbox version with the analysis-port version.

    The mailbox version needs two boxes and two puts per transaction, and the monitor knows its consumers. The analysis-port version calls write() once, and subscribers connect themselves without the monitor changing.

    Related topics

    • Driver-sequencer handshake

    • Analysis ports and subscribers

    Previous

    183 / 259

    Next: What UVM Automates (and What It Costs)

    Up next

    Up nextWhat UVM Automates (and What It Costs)

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Migrating a TB to UVM