Global training solutions for engineers creating the world's electronics

Easier UVM FAQ

How To Get Started
How To Get an Example Template File that I can Copy?
How To Insert User-Defined Code in an Automatically Generated Class, Module, or Interface
How To Add User-Defined Declarations Made Visible to Multiple Classes
How To Extend Automatically Generated Driver, Monitor, Agent, ... (and so forth) Classes
How To Substitute Your Own Code in Place of Automatically Generated Code
How to Override end_of_elaboration_phase, start_of_simulation_phase, and so forth
How to Add Extra Methods to a Transaction (Sequence Item) Class
How to Modify the Behavior of the Built-in Methods of a Transaction (Sequence Item) Class
How to Add User-Defined Sequences for an Agent
How to Add User-Defined Virtual Sequences for a Specific Agent (as opposed to a top-level virtual sequence)
How to Add User-Defined Variables to Configuration Objects
How to Modify the Values of Configuration Object Variables from a Test
How To Add a Scoreboard (or anything else) to the Top-level Env Class
How To Add a Scoreboard (or anything else) to an Agent Env Class
How To Add a User-Defined Arbitration Algorithm to a Sequencer
How To Generate an Interface that has Several Clocks or Resets
How To Generate an Interface that has No Clock
How To Generate Clock and Reset Signals that Don't Belong to any One Interface
How To Suppress Info Reports from Automatically Generated Sequences
How To Import an Existing Env into the Generated Code

How To Get Started

 

  1. Download the code generator from here
  2. Follow the instructions here to install the Perl File::Copy::Recursive and File::stat modules
  3. Go to the Getting Started tutorial here, find the directory named ./minimal, and run the code generator as described in the tutorial
  4. Open the full set of SystemVerilog files generated by the code generator. Depending which text editor you use, you can open the files using a command something like
    edit */*/*/*/*.sv
    
  5. Spend some time browsing through the files and get a sense of the range of classes, modules, and interfaces generated

Back to the top.

How To Get an Example Template File that I can Copy?

 

⚪ There are example template files containing all the settings in ./examples/example_templates

Back to the top.

How To Insert User-Defined Code in an Automatically Generated Class, Module, or Interface

 

⚪ Add the appropriate setting to the interface template or common template file. Each setting takes one of the following forms:

<whatever>_inc_before_class                                  = <filename> inline

<whatever>_inc_inside_class / module / interface             = <filename> inline

<whatever>_inc_after_class                                   = <filename> inline

<whatever>_prepend_to_build_phase                            = <filename> inline

<whatever>_append_to_build_phase / connect_phase / run_phase = <filename> inline

For example

agent_append_to_build_phase = <filename> inline

The optional argument inline means that the user-defined code will be inserted inline in the generated code (as opposed to inserting an `include directive).

Back to the top.

How To Add User-Defined Declarations Made Visible to Multiple Classes

 

There are three ways to add user-defined declarations that are made visible to multiple classes in the generated code. The difference between the three methods is the scope over which the declarations are visible:

⚪ To make a declaration visible throughout the DUT and the entire verification environment, add the declaration to a common package and use the setting common_pkg in the common template file to identify that package.

⚪ To make a declaration visible throughout the entire verification environment (but not the DUT), add the declaration to a common package and use the setting common_env_pkg in the common template file to identify that package.

⚪ To make a declaration visible throughout the classes and the interface assocated with one specific agent, add the declaration to an include file and use the setting trans_inc_before_class in the interface template file. This works because the transaction class (i.e. the uvm_sequence_item class) is the first class included inside the package assocated with any agent after the common package and common env package, for example:

package bus_pkg;
  `include "uvm_macros.svh"
  import uvm_pkg::*;

  import common_pkg::*;      // From the setting common_pkg
  import common_env_pkg::*;  // From the setting common_env_pkg
  
  `include "bus_bus_tx.sv"   // trans_inc_before_class inserts code at the top of this file
  `include "bus_config.sv"
  `include "bus_driver.sv"
  `include "bus_monitor.sv"
  `include "bus_sequencer.sv"
  `include "bus_coverage.sv"
  `include "bus_agent.sv"
  `include "bus_seq_lib.sv"
  `include "bus_env.sv"
  `include "bus_env_seq_lib.sv"
endpackage : bus_pkg

Back to the top.

 

How To Extend Automatically Generated Driver, Monitor, Agent, ... (and so forth) Classes

 

⚪ Define the extended class in the file given by the setting driver / monitor / agent /..._inc_after_class in the interface template file, picking the appropriate setting according to which class you wish to extend.

⚪ Add the setting agent_factory_set = <generated_class> <extended_class> to the interface template file. Such a factory override can replace any kind of class - a transaction, a sequence, or a component.

⚪ Do not try to use common_pkg or common_env_pkg to extend generated classes because these two packages are always imported first before any generated classes.

 

Back to the top.

How To Substitute Your Own Code in Place of Automatically Generated Code

 

There are two different approaches. Either extend an automatically generated class and use a factory type override to replace the generated class with your own class, as described above, or have the code generator generate different code in the first place, as follows:

⚪ Find the appropriate setting to suppress the generation of the code you want to replace, and set its value to no (the default always being yes). You will find these settings highlighted with comments in the generated code itself, for example:

// You can remove build_phase by setting test_generate_methods_inside_class = no in common.tpl

There are many such settings, most of which contain the word _generate_ in their names.

⚪ Insert a fragment of user-defined code as described above. Again, you will find these settings highlighted with comments, for example:

// You can insert code here by setting test_inc_inside_class in file common.tpl

_generate_ and _inc_ settings are often provided in pairs so they can be used in this way, _generate_ controlling the automatic code generation and _inc_ the insertion of user-defined code.

 

Back to the top.

How to Override end_of_elaboration_phase, start_of_simulation_phase, and so forth

 

The code generator does not generate the start_of_simulation_phase, extract_phase, check_phase, or final_phase methods at all, only generates <top>_env::end_of_elaboration_phase, only generates report_phase in the case of the coverage (subscriber) component, and only generates run_phase in the case of the driver, monitor and top-level env components. Hence it is possible to insert most of these phase methods into components simply by using the appropriate ..._inc_inside_class or ..._inc_after_class settings.

The generation of end_of_elaboration_phase in the top-level env can be suppressed by adding the following setting to the common template file:

top_env_generate_end_of_elaboration = no

Back to the top.

How to Add Extra Methods to a Transaction (Sequence Item) Class

 

⚪ Add the setting trans_inc_inside_class = <filename> to the interface template file, pointing to a file that contains the extra methods, for example:

function void dump;
  ...
endfunction

⚪ Alternatively, use the setting trans_inc_inside_class = <filename> to insert just the prototypes of any new methods inside the class:

extern function void dump;

and add the setting trans_inc_after_class = <filename> to insert the bodies of any new methods after the class:

function void my_class::dump;
  ...
endfunction

Back to the top.

How to Modify the Behavior of the Built-in Methods of a Transaction (Sequence Item) Class

 

You can either replace the automatically generated code for the methods entirely (do_copy, do_compare and so forth) or you can override individual methods.

To replace the automatically generated methods entirely:

⚪ Add the following settings to the interface template file:

trans_generate_methods_after_class = no
trans_inc_after_class = <filename>  inline

⚪ The inserted code should define bodies for all the predefined methods, that is:

function void <transaction_class>::do_copy(uvm_object rhs);
  ...
endfunction

function void <transaction_class>::do_compare(uvm_object rhs, uvm_comparer comparer);
  ...
endfunction

function void <transaction_class>::do_print(uvm_printer printer);
  ...
endfunction

function void <transaction_class>::do_record(uvm_recorder recorder);
  ...
endfunction

function void <transaction_class>::do_pack(uvm_packer packer);
  ...
endfunction

function void <transaction_class>::do_unpack(uvm_packer packer);
  ...
endfunction

function string <transaction_class>::convert2string();
  ...
endfunction

Alternatively, you could define a new class that extends the automatically generated transaction class and then set a factory override to use your new class instead of the original:

⚪ Add the following settings to the interface template file:

trans_inc_after_class = <filename>  inline
agent_factory_set     = <transaction_class> <extended_class>

⚪ The inserted code should define the new class and override any methods you want to extend, for example:

Back to the top.

class extended_tx extends my_tx;
  `uvm_object_utils(extended_tx)

  function new(string name = "");
    super.new(name);
  endfunction

  function void do_print(uvm_printer printer);
    super.do_print();
    ...
  endfunction
endclass

Back to the top.

How to Add User-Defined Sequences for an Agent

 

⚪ Extend the automatically generated class <agent>_default_seq to create the parent agent sequence (the one started from the virtual sequence)

⚪ Put this and any other user-defined sequences that are to run on this agent (such as child sequences) in a single file in the include (inc_path) directory (you could nest `include directives if you need to)

⚪ Add the setting agent_seq_inc = <filename> to the interface template file, pointing to this include file

⚪ Add the setting agent_factory_set = <agent>_default_seq <extended-sequence> to the interface template file

⚪ You can add as many agent_factory_sets as you need

 

Back to the top.

How to Add User-Defined Virtual Sequences for a Specific Agent (as opposed to a top-level virtual sequence)

 

Add the following settings to the interface template file:

agent_has_env = yes
agent_env_seq_inc = <filename>
agent_factory_set = <agent>_env_default_seq  <my-virtual-sequence>

agent_has_env = yes means that the one-and-only top-level virtual sequence starts a virtual sequence associated with the env for the agent. You can then override this virtual sequence.

⚪ Extend the automatically generated class <agent>_env_default_seq to define your virtual sequence

⚪ Put this and any other associated user-defined sequences in a single file in the include (inc_path) directory. agent_env_seq_inc should point to this include file. (Note that reg_seq_inc is now just an alias for agent_env_seq_inc.)

⚪ If you want to run your new sequence in parallel with the automatically generated default sequence, you can fork off the original body method. If your virtual sequence needs to grab the sequencer, it can find the sequencer from the m_env variable of class <agent>_env_default_seq, for example:

class my_virtual_seq extends <agent>_env_default_seq;
  ...
  fork
    super.body(); // <agent>_env_default_seq::body
    begin
      // super.m_env.m_sequencer holds a reference to the sequencer of the agent,
      // which the virtual sequence could grab
      this.lock(super.m_env.m_sequencer);
      ...
      this.unlock(super.m_env.m_sequencer);
    end

Back to the top.

How to Add User-Defined Variables to Configuration Objects

 

⚪ Add the setting config_var to the interface template file to add variables to the agent configuration object. You can have as many config_var settings as you need, for example:

config_var = int number;
config_var = string text;

⚪ Simple configuration variables can be initialized as they are specified in the template file, for example:

config_var = int number = 4;
config_var = string text = "text";

⚪ If nested_config_objects = yes (recommended), there is no need to add config_var to common.tpl or agent_copy_config_vars to the interface template file.

⚪ If nested_config_objects = no (the default), also add the setting config_var to the common.tpl file to add corresponding variables to the top-level configuration object. In the case of multiple agent configuration objects using the same variable names, each variable name will need to be made unique in the top-level configuration object. The recommendation is to use the agent name as a suffix.

⚪ If nested_config_objects = no (the default), also add the setting agent_copy_config_vars = <filename> to the interface template file, pointing to a file that contains code to copy variables from the top-level configuration object to the agent configuration object. Do this for each interface template file that declares config_vars. The user-defined code will be inserted at the appropriate point just before the agent configuration object set in the configuration database. For example:

myagent.tpl

...
config_var = int number;
config_var = string text;
agent_copy_config_vars = myagent_copy_config_vars.sv  inline


myagent_copy_config_vars.sv

m_myagent_config.number = m_config.number_myagent;
m_myagent_config.text   = m_config.text_myagent;

 

Back to the top.

How to Modify the Values of Configuration Object Variables from a Test

 

Although the top-level configuration object is created by the top-level module, you may very well want to modify the values of variables within the configuration object from the test. The exact details will depend on whether nested_config_objects = yes or no, because if and only if nested_config_objects = yes the agent configuration objects can be accessed directly from the test class.

⚪ Add the setting test_prepend_to_build_phase = <filename> inline to common.tpl

 

⚪ Put code such as the following in the included file

top_config m_config;

if (!uvm_config_db #(top_config)::get(this, "", "config", m_config))
  `uvm_error(get_type_name(), "Unable to get top_config")

If nested_config_objects = yes (recommended), assign variables in any configuration object:

m_config.m_myagent_config.coverage_enable = 0;  // Automatically generated variable
m_config.m_myagent_config.extra_var       = 1;  // User-defined variable

If nested_config_objects = no (the default), assign variables in the top-level configuration object:

m_config.coverage_enable = 0;  // Automatically generated variable
m_config.extra_var       = 1;  // User-defined variable

Note that the above code is getting the configuration object(s) that was created from the top-level module and is modifying its members before they are read by the top-level env.

 

Back to the top.

How To Add a Scoreboard (or anything else) to the Top-level Env Class

 

(This FAQ refers to user-defined scoreboards. You can find instructions on how to add the Syosil UVM Scoreboard in the Reference Guide.)

The following instructions would apply for adding any kind of component to the top-level env class, not just a scoreboard.

⚪ Add the setting top_env_inc_before_class = <filename> to the common.tpl file, pointing to a file that includes the source code for your user-defined scoreboard class, for example:

`include "scoreboard/my_scoreboard.sv"

⚪ Add the setting top_env_inc_inside_class = <filename> to the common.tpl file, pointing to a file that contains the declaration of your scoreboard variable, for example:

my_scoreboard m_scoreboard;

⚪ Add the setting top_env_append_to_build_phase = <filename> to the common.tpl file, pointing to a file that contains the code to instantiate your scoreboard, for example:

m_scoreboard = my_scoreboard::type_id::create("m_scoreboard", this);

⚪ Add the setting top_env_append_to_connect_phase = <filename> to the common.tpl file, pointing to a file that contains the code to connect the analysis ports of the appropriate agents to the analysis exports of your scoreboard, for example:

m_bus1_agent.analysis_port.connect( m_scoreboard.bus1_export );
m_bus2_agent.analysis_port.connect( m_scoreboard.bus2_export );

Back to the top.

How To Add a Scoreboard (or anything else) to an Agent Env Class

 

As above, except use the following settings in the interface template file as opposed to the common template file common.tpl:

agent_has_env = yes
agent_env_inc_before_class = <filename>
agent_env_inc_inside_class = <filename>
agent_env_append_to_build_phase = <filename>
agent_env_append_to_connect_phase = <filename>

 

Back to the top.

How To Add a User-Defined Arbitration Algorithm to a Sequencer

 

A user-defined arbitration algorithm is a rather esoteric case. This is really just another example of adding a new method to an existing class.

⚪ Add the setting uvm_seqr_class = yes to the interface template file so that a sequencer class is generated.

⚪ Add the setting sequencer_inc_inside_class = <filename> inline to the interface template file.

⚪ Define your arbitration algorithm in the included file, for example:

function integer user_priority_arbitration(integer avail_sequences[$]);
  ...
endfunction

 

Back to the top.

How To Generate an Interface that has Several Clocks or Resets

 

⚪ Add the clocks and resets to your interface template file as regular ports using if_port but without using if_clock or if_reset.

⚪ Add the setting th_generate_clock_and_reset = no to common.tpl to suppress the automatic generation of clock and reset code.

⚪ Add the setting th_inc_inside_module = <filename> to common.tpl to insert your own clock and reset declarations and assignments.

⚪ Use the settings driver_inc_before/inside/after_class and monitor_inc_before/inside/after_class rather than driver_inc/monitor_inc in the interface template file in order to insert code into the driver and monitor components to synchronize with the interface using multiple clocks.

 

Back to the top.

How To Generate an Interface that has No Clock

 

⚪ There is no need to define a clock (if_clock) in the interface template file. The only catch is that if you do not define a clock, you will be unable to use the setting driver_inc and you will probably want to set th_generate_clock_and_reset = no in common.tpl and use th_inc_inside_module = <filename> to insert your own clock and reset declarations and assignments.

 

Back to the top.

How To Generate Clock and Reset Signals that Don't Belong to any One Interface

 

⚪ You can connect DUT ports to signals that are not part of any interface or that are common to multiple interfaces using an unnamed section within the pinlist file, for example:

!
dut_clock_port global_clock_var
dut_reset_port global_reset_var

You can then make these clocks and resets visible to individual UVM agents by adding them with the if_port setting to the corresponding interface template file, even if these particular if_ports are not themselves connected to the DUT. Any signal added as an if_port will be visible from the UVM side through a virtual interface. These signals can be driven either by using the if_clock and if_reset settings of the interface template file or by providing your own code in the test harnness module as described above.

 

Back to the top.

How To Suppress Info Reports from Automatically Generated Sequences

 

⚪ Add the setting test_inc_inside_class = <filename> to common.tpl to insert the following code:

function void start_of_simulation_phase(uvm_phase phase);
  uvm_top.set_report_id_action_hier("<classname>", UVM_NO_ACTION);
endfunction

replacing <classname> with the name of the sequence class that is creating the unwanted reports. Note that all reports from automatically generated code use get_type_name() (the class name) as the report id.

Back to the top.

How To Import an Existing Env into the Generated Code

 

If you want to import an env from a previous project and instantiate it in the automatically generated code, a good approach is to create a bare bones interface template file for the imported env as if you were going to generate the code from scratch, and then replace the automatically generated env class and everything beneath with the imported env class.

⚪ Create a bare bones interface template file for the imported env. You only need the agent name, transaction type, and the interface variables to be connected to the DUT. For example:

agent_name = <imported-agent>
trans_item = <transaction-type>

agent_has_env = yes

if_port = <input_or_output>
if_port = <input_or_output>
...

⚪ Edit the dut_pfile to add the connections for the new interface.

⚪ Replace the generated env and agent classes and everything beneath with your imported classes (you could write a script to overwrite the generated files with the imported files).

⚪ Replace the generated interface with your imported interface (again, you could use a script to do this).

⚪ Specify settings in the templates file to customize the configuration object, sequences, and factory overrides for the imported env as appropriate.

 

Back to the top.

 

Links

Easier UVM Coding Guidelines

Easier UVM - Deeper Explanations

Easier UVM Code Generator

Easier UVM Video Tutorial

Easier UVM Paper and Poster

Easier UVM Q&A Forum

Easier UVM Examples Ready-to-Run on EDA Playground

 

Back to the full list of UVM Resources