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
  • Advanced UVM in Practice13
    • Callbacks, Command Line, Seeds and Versions
    • Synchronisation and Reporting
    • Testbenches at Scale
      • Testbench Code Quality
      • Stimulus at Scale
      • VIP and IP Reuse at Scale
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Testbenches at Scale
  1. Topics
  2. UVM Verification
  3. Advanced UVM in Practice
  4. Testbenches at Scale
  5. VIP and IP Reuse at Scale

Part 10 · Advanced Topics · Intermediate

VIP and IP Reuse at Scale

Packaging agents and envs as VIP for many projects: configuration contracts, versions, the VIP's own regression, documentation, and the works-at-block, breaks-at-SoC failure modes.

On this page

    An APB agent written for one block in 2019 was copied into nine projects over four years. Each copy was edited a little: a timeout here, a wider data bus there, a fix for a protocol corner case in one project that the other eight never received. When a tenth project asked which copy to take, nobody could say which one was correct, and three of them reported the same protocol error with three different message IDs.

    This lesson is about the step from "an agent that works in my bench" to verification IP: a packaged, versioned, regressed and documented product that many projects adopt without forking. Integrating someone else's VIP into your env is covered in Integrating Third-Party VIP, and semantic versions and deprecation shims in Versioning, Delivery and Release Readiness; this lesson is the producer's side at the scale of many projects.

    The idea: a library on a package manager, not a file you copy

    Software teams stopped copying library source between projects long ago. They depend on a named, versioned package, they read its documented interface rather than its code, and they upgrade on purpose. VIP deserves the same treatment, because a testbench agent is a library: its configuration object is its API, its message IDs are its error codes, and its behaviour is a contract that every consumer's tests depend on.

    Four things make that work: a configuration contract that says what can be set and checks it, a version that says what changed, a regression of the VIP itself that runs before every release, and documentation good enough that a new project integrates it without reading the source.

    The picture: one VIP, many consumers

    Flowchart: a VIP repository is regressed by its own CI across supported tools, released as a tagged version, and pinned by several projects whose issues flow back to the repository.
    The VIP is developed and regressed in one place, released with a version, and pinned by each consumer. Fixes flow back through issues, never through edited copies.

    Build it

    Packaging: one package, nothing global

    • One SystemVerilog package per VIP, including every class file, plus the interfaces outside it (interfaces cannot live in packages). Ship a file list, so consumers compile it the same way you do.

    • Prefix everything: classes, macros, typedefs, event-pool names, message IDs. Two VIPs that both define cfg_t, DATA_W or an event called "irq" collide only at SoC level, where both meet for the first time.

    • No global side effects. The VIP never installs a report server, sets the global timeout, changes verbosity or actions on uvm_root, or registers type overrides. Those are the test's decisions, and at SoC level several VIPs making them means the last one silently wins.

    • Declare the version in code, for example a localparam string APB_VIP_VERSION = "2.4.0"; in the package, printed at UVM_LOW in the agent's build_phase, so every log says which VIP produced it.

    The configuration contract

    The config object is the VIP's API. Every field is documented, has a safe default, and is validated in one place that the agent calls before it builds anything. The bench's agents keep their knobs simple (is_active through config_db, a job object for the test), which is right for one block; a VIP shared by many projects writes the contract down:

    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: a VIP configuration object with a validated contract
    class apb_vip_cfg extends uvm_object;
      `uvm_object_utils(apb_vip_cfg)
      uvm_active_passive_enum is_active     = UVM_ACTIVE;
      int unsigned            addr_width    = 32;   // 8..32
      int unsigned            data_width    = 32;   // 8, 16 or 32
      int unsigned            max_wait      = 16;   // PREADY wait states before PROTO_TIMEOUT
      bit                     has_checker   = 1;    // protocol assertions in the interface
      bit                     has_coverage  = 0;    // off by default: costs time at SoC scale
      function new(string name = "apb_vip_cfg");
        super.new(name);
      endfunction
      // Called by the agent in build_phase; every violation is a uvm_error with one ID.
      function bit validate(uvm_component ctx);
        validate = 1;
        if (!(data_width inside {8, 16, 32})) begin
          ctx.uvm_report_error("APB_CFG", $sformatf("data_width=%0d: must be 8, 16 or 32", data_width));
          validate = 0;
        end
        if (addr_width < 8 || addr_width > 32) begin
          ctx.uvm_report_error("APB_CFG", $sformatf("addr_width=%0d: must be 8..32", addr_width));
          validate = 0;
        end
        if (is_active == UVM_PASSIVE && max_wait == 0)
          ctx.uvm_report_warning("APB_CFG", "max_wait=0 in passive mode disables the PREADY timeout check");
      endfunction
    endclass

    Compile-time shape (bus widths) and run-time behaviour (timeouts, checkers) are different kinds of contract; which belongs where is the subject of VIP Parameterization. Whatever the split, a consumer must never need to set an undocumented config_db key to make the VIP work.

    The VIP's own regression

    A VIP is regressed against itself before any project sees a release. The standard shape is a loopback bench: an active instance of the agent drives an interface, a passive instance of the same agent monitors it, and a small responder or reference model plays the other side. That checks the driver against the monitor, which is the pairing every consumer relies on.

    Test class

    What it proves

    Loopback, all modes

    Driver and monitor agree on every transfer type, width and wait-state count the contract allows.

    Error injection with expected errors

    The protocol checker fires on each violation, with the documented ID. Each test expects its error and fails if it does not appear (checking expected faults).

    Configuration sweep

    Every documented config value builds and runs; every invalid value is rejected by validate().

    Library and tool matrix

    The same tests on each supported simulator and on UVM 1.2 and IEEE 1800.2, since consumers differ.

    Coverage of the VIP's features

    Every transfer type, response and mode in the contract was exercised, so a release is not "tested" in name only.

    Documentation consumers actually use

    • A ten-line quick start: compile, instantiate the interface, set the config, create the agent, connect the analysis port.

    • A configuration reference with every field, its default, its legal range and the version that added it.

    • A sequence catalogue: what each provided sequence does and which fields it randomizes.

    • A message ID catalogue, with severity and meaning. Consumers write waivers and regression buckets against IDs; renaming one is a breaking change.

    • Known issues with ticket numbers, and release notes per version that say what a consumer must change.

    Watch it fail: works at block, breaks at SoC

    Most VIP failures at scale are not bugs in the protocol logic. They are assumptions that held in the first bench and do not hold in someone else's:

    Failure at SoC level

    Assumption that broke

    Where it is covered

    The agent drives a bus that the SoC's real master drives

    Active by default, never tested passive

    Passive, monitor-only agents

    config_db lookups find nothing one level deeper

    Absolute paths such as uvm_test_top.env.apb

    Instance paths and wildcards

    Elaboration error: no such scope

    Hierarchical references into the DUT

    Lesson 1 of this topic

    Two VIPs' classes or events collide

    Unprefixed names and global pool keys

    Packaging, above

    The SoC test's verdict line disappears

    The VIP installed its own report server

    Custom report servers

    Tests end with traffic still in flight

    End of test owned by the block test

    End-of-test strategy

    Hangs or false errors after a mid-test reset

    Single reset at time zero

    Reset on the fly

    Regression time triples with 40 instances

    Field macros, recording and per-beat strings on by default

    Performance discipline

    The integration side of the same list, adapting a vendor's transactions, mapping its configuration and fencing its messages, is in Integrating Third-Party VIP. The bench's own proof that reuse can work is Vertical reuse: dma_env used twice, unchanged, inside dma_subsys_env, with one input agent switched to passive.

    What breaks: the VIP that replaced the referee

    A team packaged their AXI VIP with a convenience: its agent's build_phase installed a report server that printed a coloured summary. In block benches nobody noticed. At SoC level, the integration test installed its own verdict server in its constructor, as in the bench's install_verdict_server(), and then the AXI agents were built and installed theirs. The last call to set_server() won. The SoC regression, which keyed off the TEST VERDICT line, saw no line on any test and reported the whole nightly run as failed.

    The run was not wrong, and the fix was not in the SoC bench. The VIP removed the server install and documented a rule that it now checks in its own regression: the VIP touches no global state. Its coloured summary became an optional function the test can call. The general lesson is the one this topic keeps returning to: in reusable code, every global setting is a decision taken on behalf of a bench you have never seen.

    Interview

    "How would you make an agent reusable across projects?"

    A weak answer: "Make it configurable and put it in a shared folder." Configurable how, versioned how, tested by whom?

    A strong answer: "Treat it as a product. One prefixed package, interfaces outside, a file list, no hierarchical references and no global side effects: no report server, timeout or verbosity changes. A config object as the API, documented and validated in one function the agent calls in build. A semantic version printed in every log, a changelog, and projects pinning a version rather than copying source. A loopback regression with active driving and passive monitoring, expected-error tests for every protocol check, a config sweep, and runs on every simulator and UVM version we support. And a message ID catalogue, because consumers waive and bucket by ID, so renaming an ID is a breaking change."

    Expect the follow-ups: What breaks first at SoC level? (an active agent on a bus the SoC drives, absolute config paths, global side effects) and How do you test a VIP without a DUT? (loopback: active instance drives, passive instance of the same VIP monitors, a responder plays the slave).

    Check yourself A VIP release renames the message ID APB_TIMEOUT to APB_PREADY_TIMEOUT to be clearer. Why is that a major version change for consumers?

    Consumers' waivers, report catchers, verbosity settings and regression buckets match the old ID. After the rename, waived issues fail again and failure triage groups break. A message ID is part of the VIP's interface; renaming it is a breaking change, or needs a deprecation period.

    Check yourself Why is a loopback bench, an active and a passive instance of the same VIP, a useful self-test even though both sides come from the same team?

    It checks that the driver produces exactly what the monitor reconstructs, for every mode the contract allows, which is the pairing every consumer relies on (consumers drive with one and check with the other, or use only the passive monitor at SoC level). It needs no DUT, so it runs on every release and every supported tool.

    Check yourself At SoC level, two VIPs both use uvm_event_pool::get_global("irq"). What happens, and what packaging rule prevents it?

    They share one event: each VIP's waiters wake on the other's interrupts, which shows up as spurious or missed interrupt handling. Prefixing every global name with the VIP's prefix (apb_vip_irq), or keeping events in the VIP's config object instead of the global pool, prevents it.

    Next: this completes Testbenches at Scale. The Advanced UVM Topics hub lists the other advanced topics.

    Related topics

    • Up: Testbenches at Scale

    • Reusable VIP: active, passive and protocol agents

    • Integrating third-party VIP

    • Configuration strategy

    Previous

    181 / 280

    Next: Debug & Regression

    Up next

    Up nextDebug & Regression

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Testbench Code Quality