Skip to main content

Module 9 — Advanced UVM

VLSI Verification  •  SystemVerilog Series


Module 9 — Advanced UVM

Beyond the basics: callbacks for hook-based behavior injection, objections for run_phase lifetime control, virtual sequences for cross-agent coordination, the register model, sequence libraries, report servers, factory overrides, and the resource database.

🧩 Advanced ⏱ ~26 min read 💻 Code Examples Included

76Callbacks

The UVM callback mechanism (uvm_callback) lets you inject additional behavior into a component at predefined hook points, without modifying or subclassing the component itself — ideal for reusable VIP that end users need to extend without touching the VIP's source.

SystemVerilog / UVM
// 1. Define a callback base class with hook methods class axi_driver_cb extends uvm_callback; virtual task pre_drive(axi_driver drv, axi_txn tr); endtask endclass // 2. Driver declares itself callback-aware and invokes the hook class axi_driver extends uvm_driver #(axi_txn); `uvm_component_utils(axi_driver) `uvm_register_cb(axi_driver, axi_driver_cb) task automatic drive_transfer(axi_txn tr); `uvm_do_callbacks(axi_driver, axi_driver_cb, pre_drive(this, tr)) // ... normal pin-level driving continues ... endtask endclass // 3. End user extends the callback WITHOUT touching axi_driver at all class error_inject_cb extends axi_driver_cb; virtual task pre_drive(axi_driver drv, axi_txn tr); tr.addr = tr.addr ^ 8'h01; // flip a bit before driving -- error injection endtask endclass // 4. Register the callback instance onto a specific driver at runtime error_inject_cb cb = new(); uvm_callbacks #(axi_driver, axi_driver_cb)::add(env.agent.driver, cb);
💡 Callbacks vs Factory Overrides

Use a factory override when you want to replace a component's entire behavior with a different class. Use a callback when you want to inject small, targeted behavior at specific points while leaving the rest of the component untouched — callbacks can also be added/removed dynamically during a test.

77Objections

The objection mechanism controls when run_phase (and other task-based phases) is allowed to end — a component "raises" an objection to say "keep the phase alive, I'm still working," and "drops" it when done. The phase ends once all outstanding objections are dropped.

SystemVerilog / UVM
class axi_write_seq extends uvm_sequence #(axi_txn); virtual task body(); // raised in the TEST that starts this sequence, not usually here -- // shown here just to illustrate the raise/drop pairing endtask endclass class axi_test extends uvm_test; virtual task run_phase(uvm_phase phase); axi_write_seq seq = axi_write_seq::type_id::create("seq"); phase.raise_objection(this); // "don't end run_phase yet" seq.start(env.agent.sequencer); phase.drop_objection(this); // "I'm done -- OK to end if nothing else objects" endtask endclass
🚫 Two Classic Bugs

Forgetting to raise an objection: run_phase ends immediately at time 0 because nothing is holding it open, and the test finishes before any stimulus runs. Forgetting to drop an objection: the simulation hangs forever, since run_phase never sees the objection count reach zero. Always pair raise/drop carefully, ideally using try/finally-style patterns or drain time via phase.phase_done.set_drain_time().

78Virtual Sequence

A virtual sequence doesn't drive any single sequencer directly — instead it coordinates multiple sequences across multiple agents/sequencers, enabling cross-interface scenarios (e.g., "inject an error on the AXI bus while simultaneously toggling reset on the config interface").

SystemVerilog / UVM
class top_virtual_seq extends uvm_sequence; `uvm_object_utils(top_virtual_seq) // handles to the sequencers this virtual sequence will drive axi_sequencer axi_sqr; apb_sequencer apb_sqr; virtual task body(); axi_write_seq axi_seq = axi_write_seq::type_id::create("axi_seq"); apb_config_seq apb_seq = apb_config_seq::type_id::create("apb_seq"); // run both sequences concurrently on their own sequencers fork axi_seq.start(axi_sqr); apb_seq.start(apb_sqr); join endtask endclass // started from the test, with sequencer handles assigned from the env top_virtual_seq vseq = top_virtual_seq::type_id::create("vseq"); vseq.axi_sqr = env.axi_agent.sequencer; vseq.apb_sqr = env.apb_agent.sequencer; vseq.start(null); // virtual sequences often start with a null sequencer
📘 Why Not Just Start Both From the Test?

Wrapping cross-agent coordination in a virtual sequence (rather than forking sequences directly from the test) keeps the scenario itself reusable and composable — a virtual sequence can be started from different tests, or nested inside a larger virtual sequence, just like any other sequence.

79Register Model

The UVM Register Abstraction Layer (RAL) models a DUT's memory-mapped register space as reusable classes (uvm_reg, uvm_reg_block), letting sequences read/write registers by name through an abstract API instead of hand-coding bus transactions for every register access.

SystemVerilog / UVM
// Define a register with named, bit-field-aware fields class ctrl_reg extends uvm_reg; `uvm_object_utils(ctrl_reg) rand uvm_reg_field enable; rand uvm_reg_field mode; function new(string name = "ctrl_reg"); super.new(name, 32, UVM_NO_COVERAGE); endfunction virtual function void build(); enable = uvm_reg_field::type_id::create("enable"); enable.configure(this, 1, 0, "RW", 0, 1'b0, 1, 1, 0); mode = uvm_reg_field::type_id::create("mode"); mode.configure(this, 2, 1, "RW", 0, 2'b00, 1, 1, 0); endfunction endclass // Usage in a sequence -- abstracted from raw bus protocol details uvm_status_e status; regmodel.ctrl.write(status, 32'h0000_0003, UVM_FRONTDOOR); regmodel.ctrl.read(status, read_val, UVM_FRONTDOOR); // mirror-based checking -- compares DUT register value against // the model's shadow/mirror value without issuing a new bus read regmodel.ctrl.mirror(status, UVM_CHECK);
💡 Frontdoor vs Backdoor

UVM_FRONTDOOR access goes through the actual bus protocol (realistic, but slower); UVM_BACKDOOR access uses direct hierarchical DUT signal references (via DPI or hierarchical paths) to read/write register storage instantly, bypassing the bus entirely — useful for fast test setup/initialization that isn't the focus of the test itself.

80Sequence Library

A uvm_sequence_library is a container that groups related sequences and can randomly select and execute one or more of them — useful for building randomized regression scenarios out of a pool of directed and constrained-random sequences.

SystemVerilog / UVM
class axi_seq_lib extends uvm_sequence_library #(axi_txn); `uvm_object_utils(axi_seq_lib) `uvm_sequence_library_utils(axi_seq_lib) function new(string name = "axi_seq_lib"); super.new(name); init_sequence_library(); endfunction endclass // registering member sequences with the library, typically in a package initial begin axi_seq_lib::add_sequence(axi_write_seq::get_type()); axi_seq_lib::add_sequence(axi_read_seq::get_type()); axi_seq_lib::add_sequence(axi_burst_seq::get_type()); end // in a test -- randomly runs a mix of the registered sequences axi_seq_lib lib = axi_seq_lib::type_id::create("lib"); lib.selection_mode = UVM_SEQ_LIB_RAND; lib.min_random_count = 10; lib.max_random_count = 20; lib.start(env.agent.sequencer);

81Report Server

The uvm_report_server centralizes how uvm_info/uvm_warning/uvm_error/uvm_fatal messages are formatted, filtered, counted, and ultimately reported — it's also the mechanism behind final pass/fail determination based on error counts.

SystemVerilog / UVM
// Custom report server overriding message formatting class my_report_server extends uvm_report_server; virtual function string compose_report_message( uvm_report_message report_message, string report_object_name = ""); return $sformatf("[CUSTOM] %s: %s", report_message.get_severity().name(), report_message.get_message()); endfunction endclass // install it globally, typically in the test's build_phase my_report_server svr = new(); uvm_report_server::set_server(svr); // querying error/warning counts -- often used at the end of a test // to determine PASS/FAIL for the regression dashboard uvm_report_server default_svr = uvm_report_server::get_server(); if (default_svr.get_severity_count(UVM_ERROR) > 0) $display("TEST FAILED -- errors were reported");
📘 Why It's "Advanced"

Most engineers never touch the report server directly — it's used out-of-the-box. Customizing it becomes relevant when integrating with a company-wide regression dashboard that needs a specific message format, or when centrally controlling verbosity/severity actions (e.g., promoting specific warnings to errors) across an entire testbench.

82Factory Override

Building on the factory concept from Module 8, overrides come in two flavors — type overrides (replace every instance of a class globally) and instance overrides (replace only a specific instance path) — giving fine-grained control over test-specific component/object substitution.

SystemVerilog / UVM
// TYPE override -- affects EVERY axi_driver in the testbench axi_driver::type_id::set_type_override(error_injecting_driver::get_type()); // INSTANCE override -- affects ONLY the driver at this specific hierarchy path, // leaving other axi_driver instances (e.g. in a second agent) untouched axi_driver::type_id::set_inst_override( error_injecting_driver::get_type(), "env.agent2.driver"); // object-level override example -- swap the default transaction type axi_txn::type_id::set_type_override(axi_error_txn::get_type()); // checking what the factory would actually create (debug aid) uvm_factory::get().print(); // dumps all registered types and active overrides
Override TypeScopeUse Case
Type overrideEvery instance of the class, testbench-wideGlobally swap a component's implementation for this test
Instance overrideOnly the specified hierarchy pathModify behavior of one specific agent/component while leaving siblings unchanged
⚠️ Ordering Matters

Factory overrides must be set before the component/object they affect is actually created (typically in the test's build_phase, before super.build_phase() propagates down, or even earlier). Setting an override after the target has already been constructed has no effect on that already-existing instance.

83Resource Database

uvm_resource_db is the lower-level mechanism that uvm_config_db is actually built on top of — a more general-purpose, precedence- and scope-based database for arbitrary named resources, not limited to configuration use.

SystemVerilog / UVM
// set/get look nearly identical to config_db (config_db is a thin // wrapper around resource_db with slightly different precedence rules) uvm_resource_db #(int)::set("env.agent.driver", "timeout_cycles", 500, this); int timeout; if (!uvm_resource_db #(int)::read_by_name( "env.agent.driver", "timeout_cycles", timeout)) timeout = 100; // fallback default if not found // dumping the whole resource database for debug uvm_resource_db#(int)::dump();
Aspectuvm_config_dbuvm_resource_db
RelationshipConvenience layer built on top of resource_dbLower-level, more general engine
Typical UseVirtual interfaces, per-component config objects/knobsRarely used directly — mostly config_db underneath
Precedence RulesSimpler, hierarchy-path basedMore flexible but more complex (read/write precedence, scope matching)
💡 Practical Guidance

In day-to-day UVM code, use uvm_config_db — it's simpler and covers the vast majority of configuration-passing needs. Understanding that it's built on uvm_resource_db underneath is mostly useful for debugging obscure precedence issues or for advanced VIP infrastructure work.

🎯 Module 9 Recap

  • Callbacks inject targeted behavior at defined hook points without subclassing — ideal for end-user VIP customization.
  • Objections keep run_phase alive until all raised objections are dropped; forgetting to raise ends the test too early, forgetting to drop hangs it forever.
  • Virtual sequences coordinate multiple sequences across multiple sequencers/agents for cross-interface scenarios.
  • The register model (RAL) abstracts memory-mapped register access away from raw bus transactions, with frontdoor (via the bus) and backdoor (direct signal access) options.
  • A sequence library groups related sequences for randomized selection and execution — good for regression-style scenario mixing.
  • The report server centralizes message formatting, filtering, and pass/fail determination via severity counts.
  • Factory overrides come as type (global) or instance (path-specific) — always set before the target component is constructed.
  • uvm_resource_db is the general-purpose engine underlying uvm_config_db — use config_db day-to-day, understand resource_db for debugging edge cases.

📚 You've completed the SystemVerilog / UVM Series! From basics through advanced UVM — Modules 1 through 9 now cover the full stack from data types to register models.

↑ Back to Table of Contents

Comments

Popular posts from this blog

Module 1 — SystemVerilog Basics

VLSI Verification  •  SystemVerilog Series Module 1 — SystemVerilog Basics A complete, example-driven walkthrough of SystemVerilog fundamentals: data types, variables vs nets, arrays of every flavor, strings, enums, structs, unions, and typedef — the building blocks every verification engineer needs before UVM. 🧩 Beginner → Intermediate ⏱ ~25 min read 💻 Code Examples Included 📑 Table of Contents Introduction to SystemVerilog Verilog vs SystemVerilog Data Types: 2-State vs 4-State Logic Variables and Nets Arrays: Packed vs Unpacked Dynamic Arrays Queues Associative Arrays Strings Enumerations Structures Unions Typedef 1 Introduction to SystemVerilog SystemVerilog (IEEE 1800) is a unified hardware description and verification language (HDVL). It extends Verilog (IEEE 1364) with powerful constructs for design (interfaces, always_comb/always_ff, enhanced d...

GPU Memory System & Interconnect: How Data Travels Inside a Modern GPU

GPU Memory System & Interconnect: How Data Travels Inside a Modern GPU Modern GPUs deliver incredible computing power by processing thousands of operations simultaneously. While compute cores often receive the spotlight, the real performance of a GPU depends heavily on how efficiently data moves between memory and compute units. A GPU memory system is designed to provide extremely high bandwidth while keeping latency as low as possible. This is achieved through a collection of specialized hardware components working together, including the Memory Controller, HBM/GDDR memory, Crossbar, Network-on-Chip (NoC), DMA Engine, PCIe Interface, and NVLink. In this article, we'll explore each component in detail and understand how they collectively enable high-performance parallel computing. Why Does GPU Memory Matter? Every GPU application—from gaming and graphics rendering to AI model training and scientific simulations—requires continuous movement of enormous amounts of data. For examp...

Clock Domain Crossing (CDC): A Complete Beginner's Guide to Metastability, Synchronizers & Safe Data Transfer

Clock Domain Crossing (CDC): A Complete Beginner's Guide to Metastability, Synchronizers & Safe Data Transfer Modern System-on-Chip (SoC) designs rarely operate with a single clock. Instead, different modules such as CPUs, GPUs, memory controllers, communication interfaces, and peripherals often run at different clock frequencies or even use completely independent clock sources . When data moves from one clock domain to another, timing uncertainty can cause serious problems such as metastability , data corruption , and unpredictable system behaviour . This challenge is known as Clock Domain Crossing (CDC) . CDC is one of the most important topics in ASIC Design , RTL Design , ASIC Verification , FPGA Design , and SoC Architecture . Every modern chip—from smartphones to automotive processors—uses CDC techniques to ensure reliable communication between multiple clock domains. In this article, we'll explore CDC fundamentals, metastability, synchronization techniques, asynchro...

AXI Verification Interview Questions and Real-World Debugging

AMBA Protocol Series · Part 10 of 10 AXI Verification Interview Questions and Real-World Debugging Common protocol violations, how to read them off a waveform, and a curated set of interview questions with concise, correct answers — the capstone of the core 10-day series. AMBA Protocol Series: From APB & AHB to AXI — A Complete Guide for VLSI Engineers This wraps up the core 10-day series. Everything here draws directly on Days 1–9 — if any answer below feels unfamiliar, that's a good signal for which earlier post to revisit. (If you want to go further, see the 20-day extended list at the end of this post.) In This Article Common Protocol Violations Debugging From a Waveform Interview Questions and Answers Series Recap Extending the Series 1. Common Protocol Violations VALID deasserted before handshake — a source drops VALID before READY ever went high (violates the Day 8 stabi...

Cache Basics and Cache Mapping Techniques

  Cache Basics and Cache Mapping Techniques  Modern processors execute billions of instructions every second. However, processor speed has increased much faster than main memory (RAM) speed. This difference, known as the memory wall , creates a performance bottleneck because the CPU often has to wait for data from slower memory. To solve this problem, computer systems use cache memory —a small, high-speed memory located between the CPU and the main memory. In this article, we will understand the fundamentals of cache memory, the principle of locality, cache organization, and the three major cache mapping techniques. What is Cache Memory? Cache memory is a small, high-speed memory that stores copies of frequently accessed data from the main memory. Since accessing cache is much faster than accessing RAM, the CPU first checks whether the required data is available in the cache. If the data is found in the cache, it is called a cache hit . Otherwise, the CPU fetches the dat...

Timing Basics in Static Timing Analysis (STA): Delay Models, Sequential Timing, and Wire Delays

Timing Basics in Static Timing Analysis (STA) Static Timing Analysis (STA) is one of the most important verification techniques used in modern VLSI design. Before a chip is manufactured, engineers must verify that every signal reaches its destination within the required clock period. Even if a design passes functional simulation, it may still fail after fabrication because of timing violations. Unlike simulation, Static Timing Analysis mathematically analyzes every timing path without applying input test vectors. This makes STA significantly faster and capable of analyzing millions of timing paths efficiently. Table of Contents What is Static Timing Analysis? Why STA is Required Propagation Delay Contamination Delay Cell Delay Models Combinational Gate Delay Sequential Timing Setup Time Hold Time Wire Delay RC Delay Model PVT Corners Worked Examples Interview Questions Key Takeaways What is Static Timing Analysis? Static Ti...

Computer Architecture Interview Question Part 1

Skip to content Computer Architecture Interview Series Part 1 · CPU Basics · RISC vs CISC · ISA 50 Most Asked Computer Architecture Interview Questions (Part 1) The foundational round every VLSI, ASIC, and CPU design interview opens with: instruction set architecture, RISC vs CISC trade-offs, Von Neumann vs Harvard design, registers, endianness, and the fetch-decode-execute cycle — explained the way interviewers actually expect you to answer.  40 Questions (Q1–Q40) ⌛ 22 min read  VLSI & Computer Architecture Interviews On this page Foundations of Computer Architecture RISC vs CISC — Core Differences Von Neumann, Harvard & the Semantic Gap Architecture Styles, Endianness & Registers ILP, VLIW, Superscalar & Amdahl's Law Register Files, Microcontrollers & the Fetch Cycle Tip...