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
    • How to Use This Course
    • The DMA You Will Verify
    • Running UVM on EDA Playground
    • UVM 1.2 vs IEEE 1800.2 at a Glance
  • 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
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Start Here
  1. Topics
  2. UVM Verification
  3. Start Here
  4. UVM 1.2 vs IEEE 1800.2 at a Glance

Start here · Intermediate

UVM 1.2 vs IEEE 1800.2 at a Glance

The UVM 1.2 to IEEE 1800.2 differences that change code and interview answers, and the common subset this course writes to.

On this page

    A sequence from a ten-year-old VIP has worked in every project since. The team moves to a new simulator release that ships an IEEE 1800.2 library, and the compile stops on one line: starting_phase.raise_objection(this);. Someone suggests adding a define to bring the old name back. In the interview a week later, the question is almost the same: your sequence uses `starting_phase`. What happens on 1800.2, and what would you write instead?

    This page answers that question and the dozen like it. It lists the differences between UVM 1.2 and IEEE 1800.2 that change code you write or answers you give, explains why the course writes to the subset both accept, and shows how to tell which library you are actually running.

    The idea: a standard and the libraries that implement it

    "UVM" names two different kinds of thing, and most confusion comes from mixing them up. UVM 1.2 (2014) is an Accellera library release: a package of SystemVerilog code that was, in practice, the standard. IEEE 1800.2 (first 2017, revised 2020) is a standard document: it defines the API but ships no code. Libraries then implement it, including Accellera's reference releases (the 2020.3.x line) and the libraries simulator vendors ship.

    The analogy is a language standard and its compilers. C++17 says what a conforming compiler must accept; each compiler also accepts some extras. In the same way, a 1800.2 library may keep some UVM 1.2 names for compatibility even though the standard removed them. Code that leans on those extras compiles on one library and fails on the next, which is the hook's story. Code written to the common subset, the API that both UVM 1.2 and the 1800.2 standard define, compiles everywhere.

    The picture: how the versions relate

    Timeline from OVM and VMM through UVM 1.0, 1.1d and 1.2 to IEEE 1800.2-2017 and 2020, with dotted links to the Accellera 2017 and 2020.3.x libraries and vendor libraries.
    Solid arrows: the API's lineage. Dotted arrows: libraries that implement a standard. UVM 1.2 is both a library and the last Accellera-only definition of the API.

    The history of how OVM and VMM became UVM is in Why UVM exists. This page is about the last step, 1.2 to 1800.2, because that is the one that breaks code.

    The differences that change your code

    Topic

    UVM 1.2

    IEEE 1800.2

    Portable code writes

    A sequence's phase

    starting_phase member, deprecated; get_starting_phase() / set_starting_phase() added

    starting_phase removed from the standard

    get_starting_phase(), or set_automatic_phase_objection(1)

    Old config API

    set_config_int/string/object, get_config_*, deprecated

    Removed

    uvm_config_db#(T)::set and get

    Global end of test

    uvm_test_done, global_stop_request(), deprecated

    Removed

    phase.raise_objection / drop_objection

    Factory handle

    Global factory variable, deprecated; uvm_factory::get() added

    Global variable removed

    uvm_factory::get()

    The root

    uvm_top variable, documented

    The standard documents uvm_root::get(); libraries still provide uvm_top

    uvm_root::get() in new code

    Automatic config

    build() always calls apply_config_settings()

    Only if use_automatic_config() returns 1 (the default)

    Call super.build_phase(); override use_automatic_config only on purpose

    Sequence macros

    uvm_do, uvm_do_with, uvm_do_on, ... a family

    One uvm_do(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS) with defaults

    start_item, randomize, finish_item

    Printer settings

    Public knobs fields

    set_* methods, such as set_max_depth()

    Keep printer tweaks in one helper

    Register predict on a bus error

    do_predict updates the mirror even for UVM_NOT_OK

    do_predict skips the update and warns PREDICT_NOK

    Filter errored transfers yourself if the mirror matters

    The register row deserves a sentence of its own because it changes behaviour, not just names. The stock uvm_reg_predictor passes every observed transfer to the register's do_predict, and skips only the read check when the status is UVM_NOT_OK. In UVM 1.2 the mirror is then updated anyway, so a write that the slave rejected with an error still changes the model. In 1800.2 the mirror stays put. A register test that passes on one and fails on the other is often this. The register layer chapters build the predictor that makes the difference visible.

    Note

    Several things did not change and are safe to rely on: uvm_config_db, the factory's type_id::create and override calls, objections on uvm_phase, uvm_analysis_port, the uvm_*_utils registration macros, and the command-line plusargs (+UVM_TESTNAME, +UVM_VERBOSITY, +UVM_TIMEOUT, +UVM_PHASE_TRACE, +UVM_OBJECTION_TRACE, +UVM_CONFIG_DB_TRACE, +uvm_set_type_override, +uvm_set_inst_override, +uvm_set_config_int). There is no +UVM_FACTORY_TRACE in either; print the factory with uvm_factory::get().print().

    Build it: the sequence from the hook, both ways

    The legacy form

    This is the UVM 1.1d idiom the hook's VIP used. A sequence raised an objection on the phase it was started in, so the test ended only when the sequence did. It compiles on UVM 1.2 with a deprecation warning, and it is not part of the 1800.2 standard.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    // bad-example: UVM 1.1d style; starting_phase is not in IEEE 1800.2
    task pre_body();
      if (starting_phase != null) starting_phase.raise_objection(this);
    endtask
    
    task post_body();
      if (starting_phase != null) starting_phase.drop_objection(this);
    endtask

    The portable form

    UVM 1.2 added two calls that 1800.2 kept. set_automatic_phase_objection(1) tells the sequence to raise an objection on its starting phase before pre_start and drop it after post_start, so the sequence no longer needs pre_body and post_body at all. set_starting_phase() tells it which phase that is, because start() from a test does not set it.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // snippet: compiles on UVM 1.2 and IEEE 1800.2
    class dma_job_vseq extends uvm_sequence;
      `uvm_object_utils(dma_job_vseq)
      function new(string name = "dma_job_vseq");
        super.new(name);
        set_automatic_phase_objection(1);   // object to the starting phase for the whole run
      endfunction
      task body();
        // program a DMA job and stream its data (see the sequences chapter)
      endtask
    endclass
    
    // In the test:
    task run_phase(uvm_phase phase);
      dma_job_vseq vseq = dma_job_vseq::type_id::create("vseq");
      vseq.set_starting_phase(phase);       // which phase to object to
      vseq.start(null);                     // a virtual sequence needs no sequencer
    endtask

    The config call follows the same pattern. The left form is gone from the standard; the right form is what the DMA bench's env already uses to make the output agent passive:

    design.sv
    // snippet
    // set_config_int("out_agt", "is_active", UVM_PASSIVE);           // UVM 1.2 only, removed in 1800.2
    uvm_config_db#(uvm_active_passive_enum)::set(this, "out_agt", "is_active", UVM_PASSIVE);

    Watch it run: which library is this?

    Before you debug a version difference, confirm which library you are running. Every UVM library prints a release banner at time 0 unless you pass +UVM_NO_RELNOTES. This is the start of every course run of the DMA bench:

    Diagram
    UVM_INFO .../base/uvm_root.svh(457) @ 0: reporter [UVM/RELNOTES]
      ***********       IMPORTANT RELEASE NOTES         ************
    
      This implementation of the UVM Library deviates from the 1800.2-2020
      standard.  See the DEVIATIONS.md file contained in the release
      for more details.
    
    ----------------------------------------------------------------
    Accellera:1800.2:UVM:2020.3.2
    • `Accellera:1800.2:UVM:2020.3.2` is the library: Accellera's implementation of the 2020 standard, release 3.2. On EDA Playground with UVM 1.2 selected, the banner names UVM 1.2 instead.

    • "deviates from the 1800.2-2020 standard" is honest: even the reference library does not match the document exactly, which is one more reason to write to the common subset.

    • In code, Accellera 1800.2 libraries define UVM_VERSION_POST_2017, so VIP that must support both can guard the few calls that differ with an ifdef UVM_VERSION_POST_2017 block.

    Why this course writes to the common subset

    Interview code has to compile in the interviewer's head, whatever library they use at work. Most teams in the industry still run UVM 1.2 or a vendor 1800.2 library with compatibility switches; a few have moved fully to 1800.2. The course therefore writes every listing so that it compiles on UVM 1.2 and under the 1800.2 standard, checks the 1800.2 side with its own Verilator runs, and relies on EDA Playground with UVM 1.2 for the other side. Where the two give different behaviour, as in the predictor row, the lesson says so in a note. Compile switches exist to blur the line (UVM 1.2's UVM_NO_DEPRECATED hides deprecated API; Accellera's 2017 library had UVM_ENABLE_DEPRECATED_API to bring some back), but code that needs a switch is code that will break on the next project.

    What breaks: the define that hid the problem

    Back to the hook, and to how that kind of quick fix usually plays out. The team adds a compatibility define to the build script, which brings starting_phase back on that library. The VIP compiles, the regression goes green, and everyone moves on. Months later the same VIP is integrated by a partner whose library has no such switch. Their compile fails in every sequence that used the old idiom, close to a milestone, and they end up patching a supplier's code under pressure.

    The define did not fix anything; it moved the failure to someone else's schedule. The real fix was a few lines per sequence: delete pre_body and post_body, set set_automatic_phase_objection(1) in the constructor, and have the starting code call set_starting_phase(phase). A useful habit when you own VIP is to compile it once against UVM 1.2 with UVM_NO_DEPRECATED defined: whatever fails there will fail on 1800.2 too.

    Interview

    "What changed between UVM 1.2 and IEEE 1800.2?"

    A weak answer: "1800.2 is the IEEE version of UVM, it's basically the same." True in spirit, but it shows you have never ported code.

    A strong answer names the breaking changes and the replacements: "1800.2 removed the APIs 1.2 had deprecated: starting_phase, set_config_*, uvm_test_done and global_stop_request, and the global factory. The replacements, get_starting_phase or automatic phase objections, uvm_config_db, phase objections and uvm_factory::get(), already exist in 1.2, so you can write code that runs on both. It also added use_automatic_config() so a component can skip field auto-configuration, and it changed do_predict so a bus error no longer updates the register mirror."

    Expect the follow-ups: How do you make a sequence end-of-test aware without starting_phase? (set_automatic_phase_objection plus set_starting_phase, or the test holds the objection) and Why would a component return 0 from use_automatic_config? (to save build time on components that read config explicitly, accepting that field-macro auto-config no longer happens).

    Check yourself A VIP's agent overrides use_automatic_config() to return 0 for speed. Does is_active still reach the agent from config_db?

    Yes, as long as the agent's build_phase calls super.build_phase(): uvm_agent::build_phase looks up is_active in config_db itself, separately from automatic configuration. What stops working are fields that relied on field macros being applied automatically; those now have to be read with explicit uvm_config_db::get calls.

    Check yourself A register test writes a field the model declares RW, but the hardware has it locked and the slave answers with an error. The next mirror() check fails on UVM 1.2 but passes on a 1800.2 library. Why?

    With the stock predictor, the errored write still reaches do_predict. UVM 1.2's do_predict updates the mirror regardless of status, so the model now holds the rejected value and the next mirror check disagrees with the hardware. The 1800.2 do_predict skips the update on UVM_NOT_OK. To get the same behaviour on 1.2, filter errored transfers in pre_predict or in a custom predictor. Note that the field must be one whose policy accepts writes: a field modelled RO keeps its mirror on a write in both libraries, error or not, so it cannot show the difference.

    Check yourself Legacy code calls uvm_top.print_topology(); the course's bench calls uvm_root::get().print_topology(). Which is right?

    Both work on every current library, because libraries still provide the uvm_top variable. The IEEE standard documents uvm_root::get() (and the core service's get_root()), so new code should use it, which is what the bench does. Recognise uvm_top when you read older code, and do not rewrite working code just to change it.

    Next: you have the map, the design, the tools and the dialect. Start the course proper with UVM Foundations, and its first full testbench.

    Related topics

    • Up: Start here

    • UVM Foundations

    • Compatibility layer

    • Objections and end of test

    Previous

    4 / 280

    Next: UVM Foundations

    Up next

    Up nextUVM Foundations

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • How to Use This Course
    • The DMA You Will Verify