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.
Try it
Section titled “Try it”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.
-
Open
uvm/my_if.sv, put the caret onmy_ifininterface my_if(input logic clk);and press ⌥F7 (Alt+F7). The Find tool window (a panel at the bottom) lists five usages: the end labelendinterface : my_if,virtual my_if vif;and theuvm_config_db#(virtual my_if)::getcall inuvm/my_pkg.sv, andmy_if vif_inst(.clk(clk));and theuvm_config_db#(virtual my_if)::setcall inuvm/tb_top.sv. The window groups them by file (my_if.sv1,my_pkg.sv2,tb_top.sv2); click the arrows to expand a file.
-
In
uvm/my_pkg.sv, findagt.drv.vif = null;in classmy_envand ⌘-click (Ctrl-click)vif. You land onvirtual my_if vif;in classmy_driver: the editor jumps there. To get there the plugin resolvedagttomy_agent,drvtomy_driver, thenvif; a plain text search forvifalso findsmy_env’sagt.drv.vifandtb_top.sv’svif_inst; only the plugin knows which declarationagt.drv.vifmeans. -
In class
my_driver, put the caret onvifinvirtual my_if vif;and rename it tom_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;andagt.drv.m_vif = null;in classmy_env. The string"vif"stays, because it is a text key, not a name.uvm/tb_top.svis unchanged. Undo. -
In
uvm/my_pkg.sv, put the caret onmy_iteminclass my_item extends uvm_sequence_item;and rename it tobus_txn. The class, its end labelendclass : my_item,uvm_sequence #(bus_txn),uvm_driver #(bus_txn),bus_txn it;andbus_txn::type_id::create("it")change; the default argument ofnew(string name = "my_item", a few lines below the class declaration) stays, because it is a string. Undo. -
In
uvm/my_if.sv, put the caret onmy_ifininterface my_if(input logic clk);and rename the interface tobus_if. The end labelendinterface : my_if,virtual bus_if vif;anduvm_config_db#(virtual bus_if)::getinuvm/my_pkg.sv, and the instancebus_if vif_inst(.clk(clk));anduvm_config_db#(virtual bus_if)::setinuvm/tb_top.svall 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.
What works
Section titled “What works”| 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) |
Names the utility macros declare
Section titled “Names the utility macros declare”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_nameor another declared name opens the macro call in the class; oncreateinmy_driver::type_id::create(…), the registry’screate. - Code completion offers them after
my_driver::anddrv.; 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.
If it doesn’t work
Section titled “If it doesn’t work”| 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 |