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
⚪ There are example template files containing all the settings in ./examples/example_templates
⚪ 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
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).
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
⚪ 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.
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.
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
⚪ 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
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:
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
⚪ 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
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
⚪ 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:
... config_var = int number; config_var = string text; agent_copy_config_vars = myagent_copy_config_vars.sv inline
m_myagent_config.number = m_config.number_myagent; m_myagent_config.text = m_config.text_myagent;
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.
(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:
⚪ 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:
⚪ 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 );
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>
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
⚪ 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
⚪ 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
⚪ 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.
⚪ 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.
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.
Easier UVM Coding Guidelines
Easier UVM - Deeper Explanations
Easier UVM Code Generator
Easier UVM Video Tutorial
Easier UVM Paper and Poster
Easier UVM Examples Ready-to-Run on EDA Playground