Most of the time spent on a failing test goes into finding out what the testbench actually did. UVM has built-in switches that print the phases, objections, configuration lookups and factory decisions as they happen, and others that change verbosity, overrides or configuration without recompiling. Knowing them turns an hour of adding print statements into one rerun.
UVM command line options are plusargs, arguments starting with +, that the UVM library reads when the simulation starts. Some select the test or change message verbosity, some override factory types or configuration values, and the trace options such as +UVM_CONFIG_DB_TRACE and +UVM_OBJECTION_TRACE make UVM report its own internal activity. Your own code can read custom plusargs through uvm_cmdline_processor.
8 min read | Intermediate
What You'll Learn
- Select tests and control verbosity from the command line
- Turn on UVM's built-in tracing for phases, objections and the configuration database
- Override factory types and configuration values without recompiling
- Read your own plusargs with
uvm_cmdline_processor
Quick Reference
All options are passed to the simulator on the run command, after the usual tool options. The tool-specific part varies, the UVM part does not.
| Option | What it does |
|---|---|
+UVM_TESTNAME=<test> | Creates and runs the named test class |
+UVM_VERBOSITY=<level> | Sets the starting verbosity for all components |
+uvm_set_verbosity=<comp>,<id>,<level>,<phase> | Changes verbosity for some components, from a given phase or time |
+uvm_set_action=<comp>,<id>,<severity>,<action> | Changes what happens when a message is issued |
+uvm_set_severity=<comp>,<id>,<old>,<new> | Changes the severity of a message |
+UVM_TIMEOUT=<time>,<YES|NO> | Sets the global timeout, and whether code may change it |
+UVM_MAX_QUIT_COUNT=<n>,<YES|NO> | Stops the test after n errors |
+UVM_PHASE_TRACE | Reports phase execution |
+UVM_OBJECTION_TRACE | Reports every objection raise and drop |
+UVM_CONFIG_DB_TRACE | Reports every uvm_config_db set and get |
+UVM_RESOURCE_DB_TRACE | Reports every uvm_resource_db access |
+uvm_set_type_override=<req>,<override>[,<replace>] | Factory type override by name |
+uvm_set_inst_override=<req>,<override>,<path> | Factory instance override by name |
+uvm_set_config_int=<comp>,<field>,<value> | Sets an integer configuration value |
+uvm_set_config_string=<comp>,<field>,<value> | Sets a string configuration value |
+uvm_set_default_sequence=<seqr>,<phase>,<type> | Sets a sequencer's default sequence for a phase |
+UVM_DUMP_CMDLINE_ARGS | Prints all command line arguments UVM received |
The upper-case options are switches or single settings. The lower-case +uvm_set_* options can be given more than once, and each occurrence adds one more setting.
Choosing the Test and the Verbosity
+UVM_TESTNAME tells run_test() which test class to build through the factory. It takes priority over a name passed to run_test() in the top module, so one compiled image can run every test in a regression:
+UVM_TESTNAME=reg_rw_test +UVM_VERBOSITY=UVM_HIGH
+UVM_VERBOSITY raises or lowers the verbosity of every component at once. For a big testbench that is usually too much output. +uvm_set_verbosity targets part of the hierarchy and can switch on at a given point in the run, which keeps logs small:
// Full detail from the agent's components, but only from 800 ns into the run phase
+uvm_set_verbosity=uvm_test_top.m_env.m_agent.*,_ALL_,UVM_FULL,time,800
The second field is a message ID, or _ALL_ for every ID. Wildcards work in the component path but not in the ID. How verbosity levels filter messages is explained in Reporting Functions.
Changing Message Severity and Actions
Sometimes a known issue produces errors that hide the problem you are actually chasing. Rather than editing code, demote them for one run:
// Treat BAD_CRC errors from the env as warnings in this run
+uvm_set_severity=uvm_test_top.m_env.*,BAD_CRC,UVM_ERROR,UVM_WARNING
// Stop after the first 5 errors instead of running to the end
+UVM_MAX_QUIT_COUNT=5,NO
+uvm_set_action changes what UVM does when a message is issued, for example UVM_NO_ACTION to silence it completely, or adding UVM_STOP to stop the simulation at a particular message so you can inspect waveforms right where it happened.
The Trace Switches
The four trace switches make UVM report its own bookkeeping. They need no code changes; just add them to the command line.
+UVM_CONFIG_DB_TRACE
Every uvm_config_db::set() and get() is reported with its scope, field name and whether it matched. This is the fastest way to solve the classic "config_db get failed" fatal: the trace shows the exact path the get() looked for and every set() that was made, so a misspelt field name or a scope that doesn't reach the component is easy to spot. uvm_config_db Examples walks through reading this trace.
+UVM_OBJECTION_TRACE
Every raise and drop of an objection is reported, with the object that did it and the running count. Use it when a test ends too early, which means somebody dropped an objection, or never ends, which means somebody never dropped one. See UVM Objection.
+UVM_PHASE_TRACE
Reports each phase as it starts and ends. It is useful for custom phases, phase jumps, and for confirming that a test really reached the phase you think it did.
+UVM_RESOURCE_DB_TRACE
The same idea as the config_db trace, for uvm_resource_db accesses. Register test exclusions such as NO_REG_TESTS go through the resource database, so this is the switch for checking why a register was or wasn't tested.
Trace output is large. Combine a trace switch with +UVM_MAX_QUIT_COUNT=1,NO so the simulation stops at the first error, and the relevant trace lines are at the end of the log rather than buried in it.
Overrides and Configuration Without Recompiling
The factory and configuration database can both be driven from the command line. This is handy for trying a variant of a component or a different setting without writing a new test class:
// Use err_driver wherever bus_driver is created
+uvm_set_type_override=bus_driver,err_driver
// Only in one agent
+uvm_set_inst_override=bus_driver,err_driver,uvm_test_top.m_env.m_agent1.m_drv
// Set an int field called num_pkts in the env
+uvm_set_config_int=uvm_test_top.m_env,num_pkts,500
The override options work like set_type_override_by_name() and set_inst_override_by_name(), so both classes must be registered with the factory under those names. Integer values accept a base prefix such as 'h or 0x.
A value set with +uvm_set_config_int is stored as a uvm_bitstream_t. Read it with uvm_config_int::get(), which uses that type. A uvm_config_db #(int)::get() looks for a different type and never sees it. There is no command line option for objects, because an object cannot be described on a command line.
Quick Check
Q: A test fails with "virtual interface must be set for vif". Which single option shows you why?
Show Answer
+UVM_CONFIG_DB_TRACE. It lists the failed get() with the full path and field name it searched for, next to every set() that was made. Comparing the two usually reveals a wrong scope string, a misspelt field name or a mismatched type parameter.
Debug Calls Worth Keeping in Code
Two calls complement the command line options and are cheap enough to leave in a base test:
virtual function void end_of_elaboration_phase(uvm_phase phase);
uvm_coreservice_t cs = uvm_coreservice_t::get();
uvm_factory f = cs.get_factory();
uvm_top.print_topology(); // component tree, as actually built
f.print(); // registered types and all active overrides
endfunction
print_topology() shows the component hierarchy after build and connect, which is the quickest way to see whether an override really replaced a component. print() on the factory lists every override in place. Called with 0 it shows only the overrides, and with 2 it also lists UVM's own types.
Reading Your Own Plusargs
Your own options, such as a packet count or a seed for a specific generator, can be read through the uvm_cmdline_processor singleton:
virtual function void build_phase(uvm_phase phase);
uvm_cmdline_processor clp = uvm_cmdline_processor::get_inst();
string val;
super.build_phase(phase);
if (clp.get_arg_value("+NUM_PKTS=", val)) // returns how many matched
num_pkts = val.atoi(); // num_pkts: an int member of this class
endfunction
get_arg_value() returns the text after the first match, and get_arg_values() returns every match when an option is given several times. Unlike $value$plusargs, they need no format string. Plain SystemVerilog plusargs still work too, as described in SystemVerilog Command Line Input.
Common Mistakes
Mistake #1: Wildcards in the message ID
+uvm_set_verbosity and its relatives accept wildcards in the component path only. For every ID, use _ALL_, not *.
Mistake #2: Reading a command-line int with the wrong type
+uvm_set_config_int stores a uvm_bitstream_t. A get() with any other type parameter, such as int, fails to find it.
Mistake #3: Forgetting that only the first +UVM_TESTNAME counts
If a regression script adds a second +UVM_TESTNAME, the first one is used and the rest only produce a warning. Check the start of the log for which test actually ran.
Mistake #4: Leaving trace switches in regressions
Trace output can multiply log size many times over and slows the simulation. Use traces to debug a single failing test, not on every run.
Summary
In this article, you learned:
+UVM_TESTNAMEand+UVM_VERBOSITYselect the test and global verbosity;+uvm_set_verbositytargets part of the hierarchy+UVM_CONFIG_DB_TRACE,+UVM_OBJECTION_TRACE,+UVM_PHASE_TRACEand+UVM_RESOURCE_DB_TRACEreport UVM's internal activity+uvm_set_type_override,+uvm_set_inst_overrideand+uvm_set_config_intchange behaviour without recompilingprint_topology()and the factory'sprint()show what was actually builtuvm_cmdline_processorreads your own plusargs