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.

SeverityMeaningDefault action
UVM_INFONormal progress or debug informationPrint if verbosity allows
UVM_WARNINGSomething suspicious but not fatalPrint and count
UVM_ERRORA real failure; the test should failPrint, count, continue (until the error limit)
UVM_FATALCannot continue safelyPrint 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
endclass

Notice 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.

LevelValueTypical use
UVM_NONE0Always print (banners, final status)
UVM_LOW100Important progress, few messages
UVM_MEDIUM200Default level; normal detail
UVM_HIGH300Detailed debug
UVM_FULL400Very detailed tracing
UVM_DEBUG500Everything

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=1

You 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);
endfunction

Message 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);
endfunction

Changing 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);
endfunction

Catching 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);
end

Use 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

AspectSeverityVerbosity
What it expressesHow serious a message isHow much detail you want to see
Applies toAll four message typesOnly uvm_info messages
ValuesINFO, WARNING, ERROR, FATALNONE, LOW, MEDIUM, HIGH, FULL, DEBUG
Affects pass or fail?Yes: errors and fatals fail the testNo: 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.

Similar Posts