Skip to content

UVM testbenches

UVM (the standard SystemVerilog verification library) testbenches reach their classes through handles (variables that point to an object, like vif), factory calls (creating an object by class name, my_agent::type_id::create(…)) and string keys (like the config_db key "vif") that a text search or a find-and-replace gets wrong. Go to Declaration, Find Usages and Rename understand these patterns: you can jump from vif.req to the signal, find every use of an interface or class, and rename it everywhere, while strings such as the config_db key "vif" are left alone.

Open the sample project and wait until indexing has finished (the progress bar at the bottom right disappears). One ⌘Z (Ctrl+Z) undoes a whole rename in every file; undo after each rename step. A rename starts with the caret (the text cursor) on the name: press ⇧F6 (Shift+F6), type the new name and click Refactor (or press Enter if the IDE renames in place; see Rename). To check a rename, open each file a step names; click in the editor before you undo.

  1. Open uvm/my_if.sv, put the caret on my_if in interface my_if(input logic clk); and press ⌥F7 (Alt+F7). The Find tool window (a panel at the bottom) lists five usages: the end label endinterface : my_if, virtual my_if vif; and the uvm_config_db#(virtual my_if)::get call in uvm/my_pkg.sv, and my_if vif_inst(.clk(clk)); and the uvm_config_db#(virtual my_if)::set call in uvm/tb_top.sv. The window groups them by file (my_if.sv 1, my_pkg.sv 2, tb_top.sv 2); click the arrows to expand a file.

    Find Usages of the interface my_if listing usages in my_pkg.sv and tb_top.sv

  2. In uvm/my_pkg.sv, find agt.drv.vif = null; in class my_env and ⌘-click (Ctrl-click) vif. You land on virtual my_if vif; in class my_driver: the editor jumps there. To get there the plugin resolved agt to my_agent, drv to my_driver, then vif; a plain text search for vif also finds my_env’s agt.drv.vif and tb_top.sv’s vif_inst; only the plugin knows which declaration agt.drv.vif means.

  3. In class my_driver, put the caret on vif in virtual my_if vif; and rename it to m_vif. These change: the declaration, uvm_config_db#(virtual my_if)::get(this, "", "vif", m_vif), m_vif.req <= 1;, m_vif.data <= req.addr; and agt.drv.m_vif = null; in class my_env. The string "vif" stays, because it is a text key, not a name. uvm/tb_top.sv is unchanged. Undo.

  4. In uvm/my_pkg.sv, put the caret on my_item in class my_item extends uvm_sequence_item; and rename it to bus_txn. The class, its end label endclass : my_item, uvm_sequence #(bus_txn), uvm_driver #(bus_txn), bus_txn it; and bus_txn::type_id::create("it") change; the default argument of new (string name = "my_item", a few lines below the class declaration) stays, because it is a string. Undo.

  5. In uvm/my_if.sv, put the caret on my_if in interface my_if(input logic clk); and rename the interface to bus_if. The end label endinterface : my_if, virtual bus_if vif; and uvm_config_db#(virtual bus_if)::get in uvm/my_pkg.sv, and the instance bus_if vif_inst(.clk(clk)); and uvm_config_db#(virtual bus_if)::set in uvm/tb_top.sv all change. Undo.

The sample project does not include the UVM library, so the section Names the utility macros declare below cannot be tried in it. If a step does nothing, see If it doesn’t work.

Pattern Example What works
Virtual interfaces virtual my_if vif; … vif.req <= 1; The interface type, its signals through vif., clocking items through vif.cb. and modports
Configuration database uvm_config_db#(virtual my_if)::get(this, "", "vif", vif) The interface in the type argument and the vif handle; the string key "vif" is left alone
Factory my_agent::type_id::create("agt", this) The class name before ::type_id; type_id (declared by `uvm_component_utils, see below) and create of the registry
Parameterized base classes class my_driver extends uvm_driver #(my_item); The class in the type argument
Component hierarchy agt.drv.vif = null; Each step of the member chain, across classes
Sequence items rand bit [7:0] addr; with constraint c_addr { addr < 8'h10; } Fields in constraints and through handles (it.addr)
Phase methods virtual function void build_phase(uvm_phase phase); Overrides in your classes; super.build_phase(phase) refers to the library method and is not changed
Clocking blocks and modports clocking cb @(posedge clk); output req; endclocking The block (vif.cb), its items (vif.cb.req), default clocking cb;, modport mp(clocking cb)
Bind bind dut chk u_chk(.clk(clk)); The target module; port expressions resolve in the target module; the bound instance inside the target module (u_chk.flag) and through its instances (u_dut.u_chk.flag)

This section needs the UVM sources (or your own macro definitions) in the project; the sample project has neither.

A macro called among a class’s items, such as `uvm_component_utils(my_driver) or `uvm_object_utils(my_item), declares members of the class: type_id, get_type(), get_type_name(), …. The IDE expands such a call as the compiler’s preprocessor does (the `define in effect there, see Macros) and takes what it declares as members of the class, so these names work like written ones:

  • Go to Declaration on type_id, get_type_name or another declared name opens the macro call in the class; on create in my_driver::type_id::create(…), the registry’s create.
  • Code completion offers them after my_driver:: and drv.; in the class and its subclasses they resolve like written members.
  • Rename is not offered for them: they are not written anywhere.

Any project macro called that way counts, not only UVM’s. A call inside a method, or one whose expansion stops with an error, declares nothing. A class’s macro calls are expanded once, and again when its file, a header it includes or a file whose `define it used changes. While the IDE indexes, only the class’s written members are known.

A typedef or a type parameter naming a class can stand before :: like the class (reg_t::create(), T::type_id::create(…)); a type parameter is taken as its default type.

A handle typed by a class’s type parameter takes the type a subclass binds. With class base_vseq #(type CFG_T = base_cfg) declaring CFG_T cfg; and class my_vseq extends base_vseq #(.CFG_T(my_cfg)), cfg.my_knob in my_vseq (and in its subclasses) is the my_cfg member: Go to Declaration reaches it, code completion after cfg. offers it, and Find Usages and Rename of my_knob include the use. The binding is followed through every level (.CFG_T(CFG_T) passes it on; a class that passes on its own parameter without a binding below it uses that parameter’s default), by name or by position, through a typedef of a specialization (typedef base_vseq #(.CFG_T(my_cfg)) base_t;), in out-of-block methods (task my_vseq::body()), and for members a macro declares with the parameter as their type (a p_sequencer). Through another object (o.cfg.my_knob), the binding is that of o’s class when that class binds the parameter (o declared as my_vseq); a handle declared with the specialization itself (base_vseq #(.CFG_T(my_cfg)) o;) keeps the default. When the bound class is not in the project (a class generated at build time), the parameter’s default type is used.

You see Likely cause What to do
Nothing is found or renamed Indexing has not finished, or the file is outside the opened project folder (files in the project folder work even if no file list names them) Wait for the progress bar at the bottom right; open the folder that contains the file (Your first project)
uvm_driver, uvm_config_db or type_id do not resolve The UVM sources are not in the project: copy the folder that contains uvm_pkg.sv into the project folder, or list +incdir+<uvm>/src and <uvm>/src/uvm_pkg.sv in your compile file See the note “The UVM library” above and Known limitations
The string "vif" was renamed too Search in comments and strings is ticked in the Rename dialog Untick it and rename again
A usage inside greyed-out code is missing Its `ifdef branch is inactive See Conditional compilation