UVM Macros, Messaging and UVM Reporting:
In short: UVM reporting is how your testbench prints messages, and it comes with four severities: uvm_info, uvm_warning, uvm_error, and uvm_fatal. Each message carries an ID and, for info, a verbosity level so you can filter noise. The `uvm_info style macros wrap these calls and add file and line info automatically. You control what prints at run time with verbosity and severity settings, without recompiling.
Every UVM testbench talks to you through its reporting system. Get it right and a failing run tells you exactly what went wrong and where; get it wrong and you either drown in noise or miss the one message that mattered. This guide covers the four severities, the reporting macros, verbosity levels and how to filter by them, message IDs, how to change behavior at run time from the command line, and how to catch or reshape messages with a report catcher. Every idea has runnable code and a table, and we use one running example: a scoreboard reporting matches, mismatches, and a fatal setup error.
A simple way to picture it
Think of messages the way a hospital sorts patients at the door. Most arrivals are routine check-ins (info): useful to log, but you would not want an alarm for each one. Some need attention soon (warning). Some are real emergencies that must be handled (error). And a few are so serious the whole ward stops until they are dealt with (fatal). On a quiet day you record everything; on a busy day you turn down the routine logging and keep only the serious cases visible. UVM reporting is that triage system: severities sort messages by importance, and verbosity is the dial that decides how much routine detail you see.
The four severities
Every UVM message has one of four severities, and each behaves differently by default.
| Severity | Meaning | Default action |
|---|---|---|
| UVM_INFO | Normal progress or debug information | Print if verbosity allows |
| UVM_WARNING | Something suspicious but not fatal | Print and count |
| UVM_ERROR | A real failure; the test should fail | Print, count, continue (until the error limit) |
| UVM_FATAL | Cannot continue safely | Print and end the simulation at once |
The important behavioral split: only UVM_FATAL stops the run immediately. UVM_ERROR marks the test as failed and keeps going, so one run can report many errors, up to a configurable limit. That lets you see several failures in a single run instead of fixing them one at a time.
The reporting macros
You almost always use the macros rather than the raw function calls, because the macros add the file name and line number automatically and skip building the message string when verbosity would hide it. Here is our scoreboard using all four.
class bus_scoreboard extends uvm_scoreboard;
`uvm_component_utils(bus_scoreboard)
function new(string n, uvm_component p); super.new(n,p); endfunction
function void check(bus_item tr, bit [31:0] expected);
// INFO with a verbosity level (only prints if verbosity is high enough)
`uvm_info("SCB", $sformatf("checking addr %0h", tr.addr), UVM_HIGH)
if (tr.data === expected)
`uvm_info("SCB", $sformatf("match @%0h = %0h", tr.addr, tr.data), UVM_MEDIUM)
else
`uvm_error("SCB", $sformatf("mismatch @%0h: got %0h exp %0h", tr.addr, tr.data, expected))
endfunction
function void build_phase(uvm_phase phase);
if (cfg == null)
`uvm_fatal("SCB", "no config object: cannot run") // stops the sim
endfunction
endclassNotice the info macro takes a fourth argument, the verbosity level, while warning, error, and fatal do not. Severity says how serious a message is; verbosity says how much detail you want to see, and it only applies to info.
Verbosity levels
Verbosity is the volume dial for info messages. Each uvm_info call is tagged with a level, and the message prints only if the current verbosity setting is at least that level. The standard levels, from quietest to loudest, are below.
| Level | Value | Typical use |
|---|---|---|
| UVM_NONE | 0 | Always print (banners, final status) |
| UVM_LOW | 100 | Important progress, few messages |
| UVM_MEDIUM | 200 | Default level; normal detail |
| UVM_HIGH | 300 | Detailed debug |
| UVM_FULL | 400 | Very detailed tracing |
| UVM_DEBUG | 500 | Everything |
The rule is simple: a message prints when the run verbosity is greater than or equal to the message level. At the default UVM_MEDIUM, a UVM_HIGH message stays hidden; raise the verbosity to UVM_HIGH and it appears. So you tag rare, important messages UVM_LOW and chatty debug messages UVM_HIGH, then turn the dial to match how much you want to see.
Controlling messages at run time
The best part is that you change all of this from the command line, with no recompile. These plusargs are read by UVM at start-up.
# show everything down to UVM_HIGH for the whole run
+UVM_VERBOSITY=UVM_HIGH
# raise verbosity for just one component and its children
+uvm_set_verbosity=uvm_test_top.env.agt.*,_ALL_,UVM_FULL,time,0
# stop the run after the first error instead of the default limit
+UVM_MAX_QUIT_COUNT=1You can also do the same from code with set_report_verbosity_level_hier, which is handy when a specific test always needs more detail from one block.
function void end_of_elaboration_phase(uvm_phase phase);
// turn up detail only inside the scoreboard
env.scb.set_report_verbosity_level_hier(UVM_HIGH);
endfunctionMessage IDs and why they matter
The first argument to every macro is an ID string, like SCB in our examples. The ID is not decoration; it is a filter key. You can change the behavior of every message sharing an ID at once, for example demoting a noisy error to a warning, or raising the verbosity of just one ID. Pick short, consistent IDs per component and you gain a powerful control surface for free.
// promote or demote by ID from code
function void start_of_simulation_phase(uvm_phase phase);
// treat all "SCB" errors as warnings for this run only
set_report_severity_id_override(UVM_ERROR, "SCB", UVM_WARNING);
endfunctionChanging what a severity does
Beyond filtering, you can change the action attached to a severity. For example, you might want every error to also stop the simulation during early bring-up, or you might want to send a copy of all messages to a log file.
function void start_of_simulation_phase(uvm_phase phase);
// during bring-up, make errors fatal so you stop at the first one
set_report_severity_action_hier(UVM_ERROR, UVM_DISPLAY | UVM_COUNT | UVM_EXIT);
// also mirror everything to a file
set_report_default_file_hier(logfile);
set_report_severity_action_hier(UVM_INFO, UVM_DISPLAY | UVM_LOG);
endfunctionCatching and reshaping messages
When you need finer control than IDs and severities allow, a uvm_report_catcher intercepts every message before it prints, letting you rewrite, demote, or drop it. A common use is silencing a known, harmless error from a third-party VIP without editing that code.
class demote_known extends uvm_report_catcher;
virtual function action_e catch();
// turn one specific known error into an informational note
if (get_severity() == UVM_ERROR && get_id() == "VIP_KNOWN") begin
set_severity(UVM_INFO);
set_verbosity(UVM_LOW);
end
return THROW; // let the (now-modified) message continue
endfunction
endclass
// install it once, early
initial begin
demote_known c = new();
uvm_report_cb::add(null, c);
endUse catchers sparingly and document why, because silencing errors can hide real bugs. The right use is a known, understood, harmless message, never a failure you have not explained.
Severity versus verbosity, side by side
| Aspect | Severity | Verbosity |
|---|---|---|
| What it expresses | How serious a message is | How much detail you want to see |
| Applies to | All four message types | Only uvm_info messages |
| Values | INFO, WARNING, ERROR, FATAL | NONE, LOW, MEDIUM, HIGH, FULL, DEBUG |
| Affects pass or fail? | Yes: errors and fatals fail the test | No: it only filters what prints |
Common mistakes to avoid
- Using $display instead of the macros, so messages lose their file, line, ID, and cannot be filtered or counted.
- Putting a verbosity level on warning, error, or fatal. Only uvm_info takes a verbosity level; the others always print.
- Tagging everything UVM_NONE or UVM_LOW, which floods the log and defeats the purpose of verbosity.
- Using a report catcher to silence a real error you have not explained, which hides bugs.
- Expecting UVM_ERROR to stop the run. It counts and continues; only UVM_FATAL stops immediately.
- Reusing the same generic ID everywhere, so you lose the ability to filter one component from another.
Keep learning
Reporting is used in every component, so see it in context in the UVM testbench architecture guide, and learn how fatal messages relate to ending a run in how to terminate a UVM test with objections. More topics are on the UVM category page, the language basics on the SystemVerilog page, and reporting comes up in our interview questions.
Frequently asked questions
What is the difference between severity and verbosity?
Severity says how serious a message is, from info to fatal, and it decides whether a test passes or fails. Verbosity says how much info-level detail you want to see and only filters uvm_info messages. Severity is about importance; verbosity is about volume.
Why does my UVM_HIGH message not print?
Because the default run verbosity is UVM_MEDIUM, and a message only prints when the run verbosity is at least as high as the message level. Raise the verbosity to UVM_HIGH, for example with +UVM_VERBOSITY=UVM_HIGH, and the message will appear.
Does UVM_ERROR stop the simulation?
No. An error marks the test as failed, prints, and counts, but the run continues until the error count reaches its limit. Only UVM_FATAL stops the simulation immediately. This lets one run surface several errors instead of only the first.
Why use the macros instead of $display?
The macros add the file name, line number, component name, and an ID automatically, and they let UVM count, filter, and route messages. A plain $display gives you none of that, so you lose the ability to filter by verbosity or fail the test on errors.
What is a message ID good for?
The ID groups messages so you can control them together. You can raise the verbosity of one ID, demote a noisy error by ID, or route one ID to a file. Consistent per-component IDs give you a precise filter without changing the code that prints.
When should I use a report catcher?
Use one when you need to reshape or drop specific messages that the ID and severity controls cannot handle, such as silencing a single known, harmless error from a third-party model. Avoid using it to hide real failures, since that can mask genuine bugs.


