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 $display | How UVM Messaging Solves It |
|---|---|
| No severity — all messages look the same | Four severity levels: INFO, WARNING, ERROR, FATAL — visually distinct, filterable |
| No error count — cannot determine pass/fail | uvm_report_server counts by severity — final summary printed automatically |
| No verbosity control — debug spam in all runs | Verbosity levels (LOW, MEDIUM, HIGH, FULL, DEBUG) — filter at runtime |
| No component context — "who sent this?" unknown | Every message is tagged with the component's full path automatically |
| Cannot route to file without external tools | UVM report handlers route to any combination of console + file |
| Cannot silence per-message-type | Per-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.
// ── 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
// 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] 2Action 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.
| Severity | DISPLAY | COUNT | LOG | CALL_HOOK | EXIT | Effect |
|---|---|---|---|---|---|---|
| UVM_INFO | ✓ | — | — | — | — | Printed to console only. Not counted. |
| UVM_WARNING | ✓ | ✓ | — | — | — | Printed and counted. Simulation continues. |
| UVM_ERROR | ✓ | ✓ | — | ✓ | — | Printed, counted, hook called. Simulation continues. |
| UVM_FATAL | ✓ | ✓ | — | ✓ | ✓ | Printed, counted, hook called, simulation exits. |
The action constants and their bit values:
// 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 // = 4Report 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
// 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
// SeverityModifying 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).
// ── 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 themThrottling — 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.
// ── 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)
endfunctionQuick Reference
| Task | Method / 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 count | uvm_report_server::get_server().get_severity_count(UVM_ERROR) |
| Stop after N errors | uvm_report_server::get_server().set_max_quit_count(N) |
| Silence a specific ID | set_report_id_action("ID", UVM_NO_ACTION) |
| Suppress a severity for one component | set_report_severity_action(UVM_WARNING, UVM_NO_ACTION) |
| Set verbosity threshold | set_report_verbosity_level(UVM_LOW) |
| Precise per-severity-per-ID | set_report_severity_id_action(UVM_WARNING, "ID", UVM_NO_ACTION) |
--- 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.
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=0x4Example 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.
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
endclassExample 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.
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
endclassExample 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.
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`.
// ❌ 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 continuesBug 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.
// ❌ 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_testBug 3 — Using $display Instead of uvm_info in a Reusable VIP
// ❌ 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
// 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
| Rule | Practice | Why It Matters |
|---|---|---|
| BP-1 | Use uvm_fatal` for unrecoverable states — never uvm_error` | Stops at the right place with a clear message; prevents confusing downstream crashes |
| BP-2 | Never use $display inside any uvm_component | $display is unfiltered, untagged, uncounted — defeats the entire messaging infrastructure |
| BP-3 | Use 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-4 | Set max_quit_count in start_of_simulation_phase | Guarantees the limit applies from the first message; run_phase is too late |
| BP-5 | Use UVM_NONE verbosity for test pass/fail and summary messages | UVM_NONE always prints regardless of verbosity setting — your final result line is never silenced |
| BP-6 | Call action overrides in start_of_simulation_phase, not build_phase | All components are fully constructed by start_of_simulation — hierarchy handles are valid for per-component targeting |
| BP-7 | Check get_severity_count(UVM_ERROR) == 0 in check_phase | Gives you an explicit, readable pass/fail line even if the auto-summary is buried in a long log |
| BP-8 | Suppress with set_report_severity_id_action — not set_report_severity_action | The most targeted suppression avoids accidentally silencing unrelated messages from the same component |
§14 — Summary
| Severity | Default Actions | Simulation Continues? | Typical Use |
|---|---|---|---|
| UVM_INFO | DISPLAY only | Yes | Progress, debug traces, status; filtered by verbosity |
| UVM_WARNING | DISPLAY + COUNT | Yes | Unexpected but recoverable; protocol deviations |
| UVM_ERROR | DISPLAY + COUNT + CALL_HOOK | Yes (until max_quit_count) | Test failures; scoreboard mismatches; CI pass/fail driver |
| UVM_FATAL | DISPLAY + COUNT + CALL_HOOK + EXIT | No — exits immediately | Null handles, missing config, unrecoverable state |