Skip to content

UVM Messaging Infrastructure

Four severity levels, uvm_report_server, action tables, per-ID actions, throttling.

UVM Fundamentals · Module 10

Why UVM Has Its Own Messaging System

$display() prints to the console. That is all it does. It cannot count errors, it cannot determine pass or fail, it cannot be filtered, and it cannot be routed to a file without OS redirection. In a testbench that runs thousands of tests in a regression farm, these limitations are fatal to productivity.

UVM messaging solves each limitation:

Limitation of $displayHow UVM Messaging Solves It
No severity — all messages look the sameFour severity levels: INFO, WARNING, ERROR, FATAL — visually distinct, filterable
No error count — cannot determine pass/failuvm_report_server counts by severity — final summary printed automatically
No verbosity control — debug spam in all runsVerbosity levels (LOW, MEDIUM, HIGH, FULL, DEBUG) — filter at runtime
No component context — "who sent this?" unknownEvery message is tagged with the component's full path automatically
Cannot route to file without external toolsUVM report handlers route to any combination of console + file
Cannot silence per-message-typePer-ID, per-severity, per-component action overrides available

The Four Severity Levels

UVM_INFO

`uvm_info(ID, MSG, VERB) Normal progress messages, debug traces, status reports. Filtered by verbosity level. Never counted as failures. The most common message type.

UVM_WARNING

`uvm_warning(ID, MSG) Something unexpected happened but simulation can continue. Protocol deviations, unexpected values, non-fatal configuration issues. Counted and summarised.

UVM_ERROR

`uvm_error(ID, MSG) A test failure. Simulation continues by default (allowing further errors to surface). Error count drives CI pass/fail. Scoreboard mismatches, protocol violations.

UVM_FATAL

`uvm_fatal(ID, MSG) Unrecoverable condition. Simulation stops immediately after the message. Null handles, missing config_db entries, impossible state. No recovery possible.

SystemVerilog — all four macros with arguments explained
// ── uvm_info: three arguments ─────────────────────────────────────────
`uvm_info(
"DRV",                                  // ID — string label, used for filtering
$sformatf("Driving addr=0x%0h", req.addr), // MSG — the message text
UVM_MEDIUM                               // VERBOSITY — UVM_NONE/LOW/MEDIUM/HIGH/FULL/DEBUG
)
// Output: UVM_INFO apb_driver.sv(42) @ 100ns: drv [DRV] Driving addr=0x4000
 
// ── uvm_warning: two arguments (no verbosity — always displayed) ──────
`uvm_warning("PROT", "APB PSLVERR asserted — response error")
// Output: UVM_WARNING apb_monitor.sv(67) @ 200ns: mon [PROT] APB PSLVERR ...
 
// ── uvm_error: two arguments — simulation continues ───────────────────
`uvm_error("SCB", $sformatf(
"Data mismatch: expected 0x%0h, got 0x%0h", expected, actual))
// Output: UVM_ERROR my_scoreboard.sv(89) @ 300ns: scb [SCB] Data mismatch ...
// Counted as failure. Simulation continues.
 
// ── uvm_fatal: two arguments — simulation stops ───────────────────────
`uvm_fatal("CFG", $sformatf(
"vif not found. Component: %s", get_full_name()))
// Output: UVM_FATAL apb_driver.sv(31) @ 0ns: drv [CFG] vif not found ...
// Simulation stops immediately after this line.

The uvm_report_server — Singleton Counter and Summariser

The uvm_report_server is a global singleton — one instance for the entire simulation. Every message that passes through the pipeline is counted by the server by severity. At the end of simulation, the server prints an automatic summary that CI systems use to determine pass or fail. uvm_componentuvm_info(ID, MSG, V)uvm_warning(ID, MSG)uvm_error(ID, MSG)uvm_fatal(ID, MSG)Auto-tagged withcomponent path + file + lineReport Handler(one per component)Verbosity filterID filterSeverity overridePasses message toserver if not filtereduvm_report_server(global singleton)INFO: 1,042WARNING: 3ERROR: 0FATAL: 0→ PASS (0 errors)printed at report_phaseACTIONS (per severity)DISPLAYPrint to console transcriptCOUNTIncrement server counterLOGWrite to log file (if handler set)CALL_HOOKUser-defined callbackEXITTerminate simulation Figure 1 — UVM messaging pipeline. Every macro call flows through the component's report handler (verbosity and ID filters) into the global report server (counting), then triggers the configured action set for that severity level.

Accessing the Report Server

SystemVerilog — uvm_report_server methods
// Get the singleton
uvm_report_server server = uvm_report_server::get_server();
 
// Query counts at any phase
int err_count  = server.get_severity_count(UVM_ERROR);
int warn_count = server.get_severity_count(UVM_WARNING);
int info_count = server.get_severity_count(UVM_INFO);
 
// Explicit pass/fail check in check_phase or report_phase
function void check_phase(uvm_phase phase);
if (server.get_severity_count(UVM_ERROR) == 0)
`uvm_info("RESULT", "*** TEST PASSED ***", UVM_NONE)
else
`uvm_error("RESULT", $sformatf(
"*** TEST FAILED with %0d error(s) ***",
server.get_severity_count(UVM_ERROR)))
endfunction
 
// Automatic summary printed at end of simulation (report_phase):
//
// --- UVM Report Summary ---
// ** Report counts by severity
// UVM_INFO :    1042
// UVM_WARNING :    3
// UVM_ERROR :    0
// UVM_FATAL :    0
// ** Report counts by id
// [DRV]    800   [MON]   240   [SCB]     2

Action Tables — What Happens After Each Message

When a message reaches the report server, the server consults the action table to determine what to do with it. Actions are bit flags — multiple can be combined with bitwise OR. Every severity has a default action set. You can override these globally, per-component, per-severity, or per-message-ID.

SeverityDISPLAYCOUNTLOGCALL_HOOKEXITEffect
UVM_INFOPrinted to console only. Not counted.
UVM_WARNINGPrinted and counted. Simulation continues.
UVM_ERRORPrinted, counted, hook called. Simulation continues.
UVM_FATALPrinted, counted, hook called, simulation exits.

The action constants and their bit values:

SystemVerilog — action constants (from uvm_pkg)
// Action bit flags — combine with | to set multiple actions
UVM_NO_ACTION  = 0    // suppress the message entirely
UVM_DISPLAY    = 1    // print to console (transcript)
UVM_LOG        = 2    // write to log file (requires report handler with file)
UVM_COUNT      = 4    // increment server severity counter
UVM_EXIT       = 8    // terminate simulation after message
UVM_CALL_HOOK  = 16   // invoke user-defined report hook
UVM_STOP       = 32   // break into interactive debugger
 
// Example: default UVM_ERROR action
UVM_DISPLAY | UVM_COUNT | UVM_CALL_HOOK   // = 1 | 4 | 16 = 21
 
// Example: suppress a message (no output, no count)
UVM_NO_ACTION   // = 0
 
// Example: count but don't display (accumulate silently)
UVM_COUNT   // = 4

Report Handlers — Per-Component Message Routing

Every uvm_component has its own uvm_report_handler. This is the local gate before the global server. The handler applies:

  • Verbosity filtering — messages below the component's verbosity threshold are dropped before reaching the server
  • Action table lookups — component-specific action overrides
  • File routing — if a log file is configured, the handler writes to it
  • Severity remapping — demote a known noisy error to a warning for specific components
SystemVerilog — anatomy of a uvm_info message in full
// What `uvm_info("DRV", "message", UVM_MEDIUM) expands to (simplified):
begin
if (uvm_report_enabled(UVM_MEDIUM, UVM_INFO, "DRV"))
uvm_report_info("DRV", "message", UVM_MEDIUM, `__FILE__, `__LINE__);
end
 
// uvm_report_enabled() checks the component's verbosity threshold
// If verbosity >= UVM_MEDIUM AND message verbosity UVM_MEDIUM <= threshold → enabled
// This check is fast — zero overhead when disabled
 
// Full console output format:
// UVM_INFO apb_driver.sv(42) @ 1200ns: uvm_test_top.env.apb_agent.drv [DRV] Driving addr=0x4000
// │         │                │          │                                │     │
// │     File+line          Time      Full path of component            ID    Message
// Severity

Modifying Default Actions — Customising the Pipeline

Three methods control action overrides. They can be called on any component (affecting only that component's messages) or on the global server (affecting all messages of that severity).

SystemVerilog — action modification patterns
// ── Scenario 1: Silence a known false-alarm warning globally ──────────
// Called in test build_phase or start_of_simulation
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
 
// Suppress ALL messages with ID "PROT" globally
set_report_id_action("PROT", UVM_NO_ACTION);
 
// Suppress only UVM_WARNING with ID "PROT" — keep errors with same ID
set_report_severity_id_action(UVM_WARNING, "PROT", UVM_NO_ACTION);
endfunction
 
// ── Scenario 2: Demote UVM_ERROR to UVM_WARNING for a specific component
// Inside the component itself, or set from outside using a handle:
set_report_severity_action(UVM_ERROR, UVM_DISPLAY | UVM_COUNT);
// Same action as UVM_WARNING default — errors still counted but not fatal-escalated
 
// ── Scenario 3: Make UVM_WARNING exit the simulation ─────────────────
// For strict protocol checking — any warning stops the run
set_report_severity_action(UVM_WARNING,
UVM_DISPLAY | UVM_COUNT | UVM_EXIT);
 
// ── Scenario 4: Suppress ALL info below UVM_LOW (clean regression logs)
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
set_report_verbosity_level(UVM_LOW);   // only UVM_LOW info and above shown
endfunction
 
// ── Scenario 5: Per-component override (called from outside the component)
// In the test, after env is built:
env.apb_agent.drv.set_report_id_action("PROTO_CHK", UVM_NO_ACTION);
// Only the driver's PROTO_CHK messages are suppressed — monitor keeps them

Throttling — Error Limits and Message Counting

In a regression, a single bug can produce thousands of identical error messages before the simulation ends. Reading a 50,000-line log to find a single bug is unproductive. UVM provides two mechanisms to throttle this: the quit count and per-ID verbosity.

SystemVerilog — error count limit and throttling patterns
// ── set_max_quit_count: stop after N errors ────────────────────────────
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
 
// Stop simulation after 10 errors (avoids 50K-line logs)
uvm_report_server::get_server().set_max_quit_count(10);
endfunction
// After the 10th UVM_ERROR, the server calls EXIT automatically
// set_max_quit_count(0) = unlimited (default)
 
// ── Limiting repeated messages from the same ID ───────────────────────
// UVM does not have built-in per-ID count limiting, but you can implement it:
int scb_err_count = 0;
always begin
if (scb_err_count < 5)
`uvm_error("SCB", "Mismatch detected")
else if (scb_err_count == 5)
`uvm_error("SCB", "Further mismatches suppressed (count limit reached)")
scb_err_count++;
end
 
// ── Checking quit count in CI scripts ─────────────────────────────────
// Simulators return non-zero exit code when uvm_fatal fires or max_quit hit
// Check this in your Makefile or regression script:
// vsim ... && echo "PASS" || echo "FAIL: non-zero exit"
 
// ── Reading final counts from the server ──────────────────────────────
function void report_phase(uvm_phase phase);
uvm_report_server svr = uvm_report_server::get_server();
int errors   = svr.get_severity_count(UVM_ERROR);
int warnings = svr.get_severity_count(UVM_WARNING);
`uvm_info("SUMMARY",
$sformatf("Errors: %0d  Warnings: %0d  → %s",
errors, warnings,
(errors == 0) ? "PASS" : "FAIL"),
UVM_NONE)
endfunction

Quick Reference

TaskMethod / Call
Print info message``uvm_info("ID", msg, UVM_MEDIUM)`
Print warning``uvm_warning("ID", msg)`
Record test failure``uvm_error("ID", msg)`
Stop simulation immediately``uvm_fatal("ID", msg)`
Get error countuvm_report_server::get_server().get_severity_count(UVM_ERROR)
Stop after N errorsuvm_report_server::get_server().set_max_quit_count(N)
Silence a specific IDset_report_id_action("ID", UVM_NO_ACTION)
Suppress a severity for one componentset_report_severity_action(UVM_WARNING, UVM_NO_ACTION)
Set verbosity thresholdset_report_verbosity_level(UVM_LOW)
Precise per-severity-per-IDset_report_severity_id_action(UVM_WARNING, "ID", UVM_NO_ACTION)
Simulator console — end-of-simulation UVM report summary
--- UVM Report Summary ---
 
** Report counts by severity
UVM_INFO :   1204
UVM_WARNING :    3
UVM_ERROR :    0     ← 0 = PASS for CI
UVM_FATAL :    0
 
** Report counts by id
[DRV]    800   [MON]   400   [SCB]     4   [PROT]    3
 
## CI check: grep for "UVM_ERROR :    0" to determine PASS
## Or use simulator exit code: vsim exits non-zero on fatal/max_quit_count

§9 — Code Examples

Example 1 — Beginner: All Four Macros in a Minimal Driver

The cleanest way to understand the four macros is to see them fire in the right order inside a real driver. This example shows the natural decision points where each severity belongs.

SystemVerilog — four macros in their natural habitat
class apb_driver extends uvm_driver#(apb_seq_item);
`uvm_component_utils(apb_driver)
virtual apb_if vif;
 
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
// Cannot continue without VIF — simulation must stop NOW
`uvm_fatal("CFG", $sformatf(
"apb_vif not found in config_db. Path: %s", get_full_name()))
endfunction
 
task run_phase(uvm_phase phase);
apb_seq_item req;
forever begin
seq_item_port.get_next_item(req);
 
`uvm_info("DRV", $sformatf(
"Driving: addr=0x%0h data=0x%0h write=%0b",
req.addr, req.data, req.write), UVM_HIGH)
 
if (req.addr[1:0] != 2'b00)
// APB protocol requires word-aligned addresses — warn but continue
`uvm_warning("ALIGN", $sformatf(
"Unaligned APB address 0x%0h — protocol violation", req.addr))
 
@(posedge vif.clk);
vif.paddr  <= req.addr;
vif.pwdata <= req.data;
vif.pwrite <= req.write;
vif.psel   <= 1;
vif.penable <= 0;
 
@(posedge vif.clk);
vif.penable <= 1;
 
@(posedge vif.clk);
if (vif.pslverr)
// DUT reported an error response — this is a test failure
`uvm_error("SLVERR", $sformatf(
"APB slave error on addr=0x%0h", req.addr))
 
vif.psel   <= 0;
vif.penable <= 0;
 
seq_item_port.item_done();
end
endtask
endclass
 
// Expected output (UVM_HIGH verbosity):
// UVM_FATAL apb_driver.sv(8)  @ 0ns:   drv [CFG]    apb_vif not found ... → STOPS
// UVM_INFO  apb_driver.sv(19) @ 100ns: drv [DRV]    Driving: addr=0x4 data=0xAA write=1
// UVM_WARNING apb_driver.sv(24)@ 100ns: drv [ALIGN]  Unaligned APB address 0x5
// UVM_ERROR apb_driver.sv(37) @ 200ns: drv [SLVERR]  APB slave error on addr=0x4

Example 2 — Intermediate: Scoreboard with Controlled Error Counting

A scoreboard that counts mismatches and reports a clean summary at the end. Shows the right place to query the server and how to prevent a single runaway bug from filling the log with 10,000 identical error messages.

SystemVerilog — scoreboard with throttled error reporting
class apb_scoreboard extends uvm_scoreboard;
`uvm_component_utils(apb_scoreboard)
 
int mismatch_count = 0;
int unsigned MAX_REPORTED = 10;   // throttle — print only first 10
 
function void write(apb_seq_item actual);
apb_seq_item expected;
// ... compute expected ...
 
if (actual.data !== expected.data) begin
mismatch_count++;
if (mismatch_count <= MAX_REPORTED)
`uvm_error("SCB", $sformatf(
"[%0d] Mismatch addr=0x%0h exp=0x%0h got=0x%0h",
mismatch_count, actual.addr, expected.data, actual.data))
else if (mismatch_count == MAX_REPORTED + 1)
`uvm_warning("SCB", "Further mismatches suppressed (throttle limit reached)")
// Beyond limit: count continues silently in the background
end else begin
`uvm_info("SCB", $sformatf(
"MATCH addr=0x%0h data=0x%0h",
actual.addr, actual.data), UVM_HIGH)
end
endfunction
 
function void check_phase(uvm_phase phase);
if (mismatch_count > 0)
`uvm_error("SCB", $sformatf(
"Total mismatches: %0d (printed: %0d, suppressed: %0d)",
mismatch_count,
(mismatch_count < MAX_REPORTED) ? mismatch_count : MAX_REPORTED,
(mismatch_count > MAX_REPORTED) ? mismatch_count - MAX_REPORTED : 0))
else
`uvm_info("SCB", "All transactions matched. Scoreboard PASS.", UVM_NONE)
endfunction
endclass

Example 3 — Verification: Per-Component Action Customisation

A production test that silences a known-noisy protocol warning from the monitor (already tracked separately), promotes a specific ID to fatal, and sets the global error limit to 5 for regression efficiency.

SystemVerilog — production test with action table customization
class apb_regression_test extends base_test;
`uvm_component_utils(apb_regression_test)
 
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
 
// 1. Cap error count — stop after 5 failures (clean regression logs)
uvm_report_server::get_server().set_max_quit_count(5);
 
// 2. Suppress PSLVERR warnings from monitor — already counted by scoreboard
env.apb_agent.mon.set_report_severity_id_action(
UVM_WARNING, "PSLVERR", UVM_NO_ACTION);
 
// 3. Make null pointer errors in driver immediately fatal — stop on first
env.apb_agent.drv.set_report_id_action("NULL_PTR",
UVM_DISPLAY | UVM_COUNT | UVM_EXIT);
 
// 4. Set scoreboard verbosity low — only high-priority messages in regression
env.sb.set_report_verbosity_level(UVM_LOW);
 
`uvm_info("TEST", "Regression messaging config applied", UVM_LOW)
endfunction
 
function void report_phase(uvm_phase phase);
uvm_report_server svr = uvm_report_server::get_server();
int errors = svr.get_severity_count(UVM_ERROR);
`uvm_info("RESULT",
$sformatf("Test %s: %0d error(s)",
(errors == 0) ? "PASSED" : "FAILED", errors),
UVM_NONE)
endfunction
endclass

Example 4 — Tricky: Suppress Display but Still Count (Silent Accumulation)

Sometimes you want a message to count toward the error total without printing anything — useful for known-noisy errors that you track in bulk and report only in summary. The action flag combination makes this precise.

SystemVerilog — count without display, then summary report
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
 
// "ECC_CORR" warnings: count them but don't flood the log
// We only care about the total — individual prints are noise
set_report_severity_id_action(
UVM_WARNING, "ECC_CORR",
UVM_COUNT);  // COUNT without DISPLAY — silent accumulation
endfunction
 
function void report_phase(uvm_phase phase);
uvm_report_server svr = uvm_report_server::get_server();
int warn_count = svr.get_severity_count(UVM_WARNING);
 
// Now report the aggregate — one clean line instead of thousands
`uvm_info("ECC", $sformatf(
"Total ECC correctable events: %0d (threshold: 100)", warn_count),
UVM_NONE)
if (warn_count > 100)
`uvm_error("ECC", "ECC error rate exceeded acceptable threshold")
endfunction
 
// The trick: UVM_COUNT alone increments the server counter.
// DISPLAY is a separate flag — removing it silences the per-event print.
// You still see the total in report_phase via get_severity_count().
// UVM_NO_ACTION would suppress both DISPLAY and COUNT — avoid that here.

§10 — Bugs & Debugging

Bug 1 — Using uvm_error for Unrecoverable Conditions

⚠️ Production Bug — Null Handle Crash 500 Lines After the Real Error

An engineer uses uvm_error` for a null handle check — thinking it's "less severe" than uvm_fatal. The error message prints at time 0. The driver proceeds with a null VIF. 500 lines of run_phase later, the driver writes vif.paddr = req.addr and the simulator crashes with a confusing null-pointer error. The real cause is buried 500 log lines earlier. The fix is one word: change ``uvm_error to ``uvm_fatal`.

SystemVerilog — error vs fatal: choosing the right severity
// ❌ WRONG — uses uvm_error for an unrecoverable state ────────────────
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
`uvm_error("CFG", "vif not found")  // ← simulation continues with null vif!
endfunction
 
// What happens:
// @ 0ns  UVM_ERROR drv [CFG] vif not found
// ... 500 lines of run_phase output ...
// @ 1200ns  RUNTIME ERROR: null handle dereference at apb_driver.sv:52
//           Expression: vif.paddr
// The original cause is invisible without reading the log carefully.
 
// ✓ CORRECT — uses uvm_fatal for unrecoverable state ─────────────────
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
`uvm_fatal("CFG", $sformatf(
"apb_vif not found in config_db. Path: %s", get_full_name()))
endfunction
// @ 0ns  UVM_FATAL drv [CFG] apb_vif not found... → simulation stops HERE
// Clear root cause. Immediate stop. No confusion.
 
// ── Decision rule ────────────────────────────────────────────────────
// `uvm_fatal  → component cannot function; continuing would corrupt results
// `uvm_error  → testbench can continue running; failure is noted for summary
// `uvm_warning → unexpected but recoverable; simulation definitely continues

Bug 2 — Setting max_quit_count Too Late

⚠️ Production Bug — Error Limit Doesn't Take Effect

An engineer calls set_max_quit_count(5) in run_phase — thinking it will cap errors during the test. By then, the first 50 errors have already been counted and printed by the scoreboard (which also runs in run_phase). The quit count is set correctly but only kicks in for subsequent errors, not the ones that already fired. If the scoreboard runs before the test's run_phase task reaches that line, the limit never throttled anything.

SystemVerilog — quit count timing: wrong vs correct phase
// ❌ WRONG — setting max_quit_count in run_phase ──────────────────────
task run_phase(uvm_phase phase);
phase.raise_objection(this);
uvm_report_server::get_server().set_max_quit_count(5); // too late!
// Scoreboard's run_phase (parallel) may have already fired 50 errors
my_seq.start(sequencer);
phase.drop_objection(this);
endtask
 
// ✓ CORRECT — set in start_of_simulation_phase ─────────────────────────
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
// start_of_simulation runs before run_phase and before any component
// fires errors — guaranteed to apply from the very first message
uvm_report_server::get_server().set_max_quit_count(5);
endfunction
 
// Also valid: set in build_phase (even earlier, before any component builds)
// But start_of_simulation_phase is the canonical location for this.
 
// Alternative: command-line override (works in Questa and VCS)
// +UVM_MAX_QUIT_COUNT=5
// vsim +UVM_MAX_QUIT_COUNT=5 work.tb_top +UVM_TESTNAME=apb_test

Bug 3 — Using $display Instead of uvm_info in a Reusable VIP

SystemVerilog — $display vs uvm_info in VIP context
// ❌ WRONG — $display in a reusable driver ────────────────────────────
task drive_item(apb_seq_item req);
$display("Driving addr=0x%0h", req.addr);  // always prints, no tag, not filterable
endtask
 
// ✓ CORRECT — `uvm_info with appropriate verbosity ────────────────────
task drive_item(apb_seq_item req);
`uvm_info("DRV", $sformatf("Driving addr=0x%0h", req.addr), UVM_HIGH)
// ↑ Tagged, filterable, associated with component, goes through server
// With +UVM_VERBOSITY=UVM_LOW (regression mode): this line prints nothing
// With +UVM_VERBOSITY=UVM_HIGH (debug mode): this prints with full context
endtask
 
// $display IS acceptable in one place only:
// Non-UVM top-level modules (tb_top) where no UVM component exists.
// Everywhere inside a uvm_component: always use `uvm_info.

§11 — Ready-to-Run Code

A complete, self-contained UVM messaging demo. All four severity levels fire, the server is queried in report_phase, max_quit_count is set, and a custom pass/fail summary is printed. Run this and read the output against the inline comments to internalise the whole pipeline.

messaging_demo.sv — compile and run
// messaging_demo.sv
// Tests all four severity levels, server queries, max_quit_count, and action overrides.
// Compile: vlog -sv messaging_demo.sv
// Run:     vsim -c work.tb_msg_top +UVM_TESTNAME=msg_demo_test -do "run -all; quit"
 
`include "uvm_macros.svh"
import uvm_pkg::*;
 
// ── Leaf component: fires all four severity types ─────────────────────
class noisy_component extends uvm_component;
`uvm_component_utils(noisy_component)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
 
task run_phase(uvm_phase phase);
phase.raise_objection(this);
// UVM_INFO at different verbosity levels
`uvm_info("DEMO", "Starting run — UVM_LOW info",   UVM_LOW)
`uvm_info("DEMO", "Debug trace — UVM_HIGH info",  UVM_HIGH)
 
// UVM_WARNING — simulation continues
`uvm_warning("PROT", "Unexpected bus state detected")
 
// Suppress this warning ID from this point (demonstrate action override)
set_report_id_action("PROT", UVM_NO_ACTION);
`uvm_warning("PROT", "This warning is suppressed — you won't see it")
 
// UVM_ERROR — test failure, simulation continues
`uvm_error("SCB", "Data mismatch: expected 0xAA, got 0xBB")
 
// UVM_INFO at UVM_NONE — always prints (used for important summaries)
`uvm_info("DEMO", "End of run_phase", UVM_NONE)
 
phase.drop_objection(this);
endtask
endclass
 
// ── Test: configures messaging, then checks result ─────────────────────
class msg_demo_test extends uvm_test;
`uvm_component_utils(msg_demo_test)
noisy_component nc;
 
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
 
function void build_phase(uvm_phase phase);
nc = noisy_component::type_id::create("nc", this);
endfunction
 
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
// Cap at 3 errors — any more will exit automatically
uvm_report_server::get_server().set_max_quit_count(3);
// Set verbosity to UVM_HIGH — all messages visible in this demo
set_report_verbosity_level_hier(UVM_HIGH);
endfunction
 
function void report_phase(uvm_phase phase);
uvm_report_server svr = uvm_report_server::get_server();
int errors   = svr.get_severity_count(UVM_ERROR);
int warnings = svr.get_severity_count(UVM_WARNING);
`uvm_info("RESULT", $sformatf(
"=== Test %s | Errors: %0d | Warnings: %0d ===",
(errors == 0) ? "PASSED" : "FAILED", errors, warnings),
UVM_NONE)
endfunction
endclass
 
module tb_msg_top;
initial run_test();
endmodule
 
// Expected output:
// UVM_INFO  messaging_demo.sv @ 0: nc [DEMO] Starting run — UVM_LOW info
// UVM_INFO  messaging_demo.sv @ 0: nc [DEMO] Debug trace — UVM_HIGH info
// UVM_WARNING messaging_demo.sv @ 0: nc [PROT] Unexpected bus state detected
// (second PROT warning suppressed — no output)
// UVM_ERROR messaging_demo.sv @ 0: nc [SCB]  Data mismatch: expected 0xAA, got 0xBB
// UVM_INFO  messaging_demo.sv @ 0: nc [DEMO] End of run_phase
// --- UVM Report Summary ---
// UVM_INFO :    3     UVM_WARNING :    1     UVM_ERROR :    1     UVM_FATAL :    0
// UVM_INFO  @ 0: uvm_test_top [RESULT] === Test FAILED | Errors: 1 | Warnings: 1 ===

§12 — Interview Questions

Beginner Level

Intermediate Level

Senior / Architect Level

§13 — Best Practices

RulePracticeWhy It Matters
BP-1Use uvm_fatal` for unrecoverable states — never uvm_error`Stops at the right place with a clear message; prevents confusing downstream crashes
BP-2Never use $display inside any uvm_component$display is unfiltered, untagged, uncounted — defeats the entire messaging infrastructure
BP-3Use meaningful IDs — not "MSG", "DEBUG", or "ERR"IDs appear in the server's per-ID count summary; useful IDs tell you which component had issues at a glance
BP-4Set max_quit_count in start_of_simulation_phaseGuarantees the limit applies from the first message; run_phase is too late
BP-5Use UVM_NONE verbosity for test pass/fail and summary messagesUVM_NONE always prints regardless of verbosity setting — your final result line is never silenced
BP-6Call action overrides in start_of_simulation_phase, not build_phaseAll components are fully constructed by start_of_simulation — hierarchy handles are valid for per-component targeting
BP-7Check get_severity_count(UVM_ERROR) == 0 in check_phaseGives you an explicit, readable pass/fail line even if the auto-summary is buried in a long log
BP-8Suppress with set_report_severity_id_action — not set_report_severity_actionThe most targeted suppression avoids accidentally silencing unrelated messages from the same component

§14 — Summary

SeverityDefault ActionsSimulation Continues?Typical Use
UVM_INFODISPLAY onlyYesProgress, debug traces, status; filtered by verbosity
UVM_WARNINGDISPLAY + COUNTYesUnexpected but recoverable; protocol deviations
UVM_ERRORDISPLAY + COUNT + CALL_HOOKYes (until max_quit_count)Test failures; scoreboard mismatches; CI pass/fail driver
UVM_FATALDISPLAY + COUNT + CALL_HOOK + EXITNo — exits immediatelyNull handles, missing config, unrecoverable state