UVM
UVM Verbosity Control
The six levels, +UVM_VERBOSITY plusarg, per-component and per-ID control, regression patterns.
UVM Fundamentals · Module 11
The Verbosity Hierarchy — Six Levels, One Rule
UVM defines six verbosity constants. Every ``uvm_info` call declares its verbosity level. Every component has a verbosity threshold. The rule is simple:
Figure 1 — The threshold gate. With default threshold UVM_MEDIUM (200): UVM_NONE/LOW/MEDIUM messages pass, UVM_HIGH/FULL/DEBUG are filtered. Raising the threshold to UVM_HIGH shows more detail without showing everything.
| Constant | Value | Typical Use | Always Visible At Threshold |
|---|---|---|---|
UVM_NONE | 0 | Critical status — test PASS/FAIL, simulation milestones. Must always be visible. | Every threshold setting |
UVM_LOW | 100 | Important events — transaction starts/ends, phase transitions, key state changes. | UVM_LOW and above |
UVM_MEDIUM | 200 | Normal operation debug — transaction content, scoreboard checks, driver activity. Default threshold. | UVM_MEDIUM and above |
UVM_HIGH | 300 | Detailed signal-level debug — cycle-by-cycle bus activity, intermediate values. | UVM_HIGH and above |
UVM_FULL | 400 | Extremely verbose — internal state dumps, every clock edge logged. | UVM_FULL and above |
UVM_DEBUG | 500 | UVM framework internals. Very rarely needed by testbench engineers. | UVM_DEBUG only |
Runtime Verbosity Control — The +UVM_VERBOSITY Plusarg
The production-standard approach: control verbosity entirely from the command line without touching source code. One plusarg sets the threshold for every component in the simulation. This is the method used in regression farms.
## Standard regression run — minimal output (fast logs, CI-friendly)
vsim +UVM_VERBOSITY=UVM_LOW work.tb_top
## Default development run
vsim +UVM_VERBOSITY=UVM_MEDIUM work.tb_top
## Debug run — detailed transaction-level output
vsim +UVM_VERBOSITY=UVM_HIGH work.tb_top
## Numeric also accepted — useful when scripting
vsim +UVM_VERBOSITY=300 work.tb_top ## same as UVM_HIGH
vsim +UVM_VERBOSITY=400 work.tb_top ## same as UVM_FULL
## VCS syntax
vcs +UVM_VERBOSITY=UVM_HIGH ... && simv +UVM_VERBOSITY=UVM_HIGH
## Xcelium syntax
xrun -uvmverbosity UVM_HIGH work.tb_top
## No plusarg = UVM_MEDIUM (the compiled-in default)
## Combine with other UVM plusargs:
vsim +UVM_TESTNAME=smoke_test \
+UVM_VERBOSITY=UVM_LOW \
+UVM_TIMEOUT=1ms \
work.tb_topProgrammatic Verbosity Control — In-Simulation Adjustment
For interactive debug sessions where you want to raise verbosity on one specific component without rebuilding the command line — or for tests that dynamically increase verbosity just before a known complex region — use the programmatic API.
// ── Set verbosity on this component only ─────────────────────────────
// Called inside the component (this.set_report_verbosity_level)
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
set_report_verbosity_level(UVM_HIGH); // only THIS component
endfunction
// ── Set verbosity on this component AND all its children ─────────────
set_report_verbosity_level_hier(UVM_HIGH);
// If called on env: all agents, drivers, monitors also get UVM_HIGH
// ── Set from outside a component (via handle) ─────────────────────────
// In the test, after env is built:
env.apb_agent.drv.set_report_verbosity_level(UVM_FULL);
// Only apb_agent's driver gets UVM_FULL — all others stay at default
// ── Dynamic verbosity change during run_phase ─────────────────────────
task run_phase(uvm_phase phase);
// Normal operation — minimal output
set_report_verbosity_level(UVM_LOW);
// Before a complex handshake sequence — bump verbosity
@(vif.START_CONDITION);
set_report_verbosity_level(UVM_HIGH);
execute_handshake();
set_report_verbosity_level(UVM_LOW); // restore after
endtask
// ── Read the current verbosity level ─────────────────────────────────
int current = get_report_verbosity_level();
`uvm_info("VRB", $sformatf("Current verbosity: %0d", current), UVM_NONE)Per-Component and Per-ID Verbosity — Precision Targeting
When a specific module is noisy and you want to silence just its detailed messages without changing global verbosity, or when a critical flow needs detailed logging while everything else stays quiet:
// ── Silence a noisy message ID (even if global verbosity is high) ─────
// Context: "CYCLE_TRACE" fires on every clock edge — 50,000 messages per test
// Global verbosity is UVM_HIGH — but we don't want CYCLE_TRACE messages
set_report_id_verbosity("CYCLE_TRACE", UVM_DEBUG);
// CYCLE_TRACE messages now only shown if global verbosity = UVM_DEBUG (500)
// At UVM_HIGH (300), they are filtered — other HIGH messages still appear
// ── Always show a specific ID (override lower global verbosity) ───────
// Context: global verbosity is UVM_LOW for regression
// But "HANDSHAKE" messages are critical debug info — always show them
set_report_id_verbosity("HANDSHAKE", UVM_NONE);
// HANDSHAKE messages now appear regardless of global verbosity setting
// ── Combining per-component + per-ID ─────────────────────────────────
// In start_of_simulation_phase of apb_driver:
set_report_verbosity_level(UVM_LOW); // driver: default LOW
set_report_id_verbosity("DRV_DETAIL", UVM_NONE); // always show DRV_DETAIL
set_report_id_verbosity("BUS_IDLE", UVM_DEBUG); // BUS_IDLE: only at DEBUG level
// ── The override priority chain ───────────────────────────────────────
// Per-ID verbosity overrides the component threshold for that specific ID.
// Component threshold applies to all other IDs.
// Global +UVM_VERBOSITY sets the initial component threshold for all components.
// Programmatic set_report_verbosity_level() overrides the plusarg for that component.Priority Order — From Highest to Lowest
| Priority | Mechanism | Scope |
|---|---|---|
| 1 (highest) | set_report_id_verbosity("ID", level) | Specific message ID in this component |
| 2 | set_report_verbosity_level(level) | All messages in this component |
| 3 | +UVM_VERBOSITY=LEVEL plusarg | All components globally |
| 4 (lowest) | Compiled-in default: UVM_MEDIUM | All components globally |
Compile-Time Verbosity — Performance and Message Removal
Runtime filtering is powerful but has a small cost: even filtered messages still evaluate the $sformatf() string before the check discards the result. For production-speed simulation, compile-time removal eliminates this overhead entirely.
// ── Why compile-time control matters: the sformatf overhead ──────────
`uvm_info("MON", $sformatf(
"Observed addr=0x%0h data=0x%0h resp=%0b at %0t",
txn.addr, txn.data, txn.resp, $time), UVM_HIGH)
// Even if verbosity is UVM_LOW and this message is filtered:
// 1. $sformatf() is evaluated — string formatting takes time
// 2. uvm_report_enabled() is called — returns 0 and discards the string
// In a monitor that fires every 2ns for 1ms: 500,000 sformatf calls
// ── UVM compile-time define: set default verbosity ───────────────────
// vlog +define+UVM_DEFAULT_VERBOSITY=UVM_LOW
// Sets the initial threshold for all components at compile time
// Still overridable at runtime with +UVM_VERBOSITY plusarg
// ── Performance trick: check verbosity BEFORE the expensive sformatf ─
if (uvm_report_enabled(UVM_HIGH, UVM_INFO, "MON")) begin
// sformatf only called if the message will actually be shown
`uvm_info("MON", $sformatf("..."), UVM_HIGH)
end
// Use this pattern for messages in tight loops or with expensive formatting
// ── UVM_NO_* defines: compile-time message removal ───────────────────
// These are preprocessor defines that eliminate macro calls at compile time:
//
// +define+UVM_NO_INFO → removes ALL `uvm_info calls from compilation
// Warnings, errors, fatals still present
//
// Use case: gate-level simulation for timing — no testbench overhead
// Warning: you lose ALL diagnostic output — debug is impossible
// Recommendation: never use in regular development; only for specific GLS flowsVerbosity in Production Regression Environments
A well-designed regression infrastructure never requires log files to be examined manually for pass/fail. Verbosity is set low enough to keep logs small but high enough to diagnose failures when they occur.
## Makefile — tiered verbosity targets
## Default: regression mode — minimal log, CI-friendly
regression:
vsim +UVM_TESTNAME=$(TEST) \
+UVM_VERBOSITY=UVM_NONE \
+UVM_TIMEOUT=10ms \
work.tb_top
## Development: medium output for daily runs
run:
vsim +UVM_TESTNAME=$(TEST) \
+UVM_VERBOSITY=UVM_MEDIUM \
work.tb_top
## Debug: high verbosity — use when a test is failing
debug:
vsim +UVM_TESTNAME=$(TEST) \
+UVM_VERBOSITY=UVM_HIGH \
work.tb_top
## Deep debug: full verbosity — last resort
trace:
vsim +UVM_TESTNAME=$(TEST) \
+UVM_VERBOSITY=UVM_FULL \
work.tb_top
## Usage: make regression TEST=smoke_test
## make debug TEST=failing_testLogging to File for Post-Processing
// In regression: errors and warnings always appear in the transcript
// CI systems grep for specific patterns:
// grep "UVM_ERROR\|UVM_FATAL" sim.log | wc -l → 0 = PASS
// ── Set up file logging for HIGH verbosity (selective per-component) ──
function void start_of_simulation_phase(uvm_phase phase);
integer fd;
super.start_of_simulation_phase(phase);
if ($test$plusargs("LOG_DRIVER")) begin
fd = $fopen("driver_log.txt", "w");
set_report_default_file(fd); // all messages from this comp → file
set_report_severity_action(UVM_INFO,
UVM_DISPLAY | UVM_LOG); // console AND file
set_report_verbosity_level(UVM_FULL); // full detail in log
end
endfunction
// Simulate with: vsim +UVM_VERBOSITY=UVM_LOW +LOG_DRIVER work.tb_top
// Result: console has LOW verbosity; driver_log.txt has FULL verbosityVerbosity Anti-Patterns — What to Avoid
Anti-Pattern 1 — Using $display instead of uvm_info $display("addr=%0h", req.addr)`
Bypasses verbosity filtering completely. Always prints regardless of +UVM_VERBOSITY. Not tagged with component path. Cannot be routed to file. Cannot be silenced per-ID.
Use uvm_info("ID", msg, UVM_HIGH)` instead. Correct — `uvm_info with appropriate verbosity uvm_info("DRV", $sformatf("addr=%0h", req.addr), UVM_HIGH)`
Filtered by verbosity. Tagged with component path. Routable to file. Silenceable per-ID. Counted separately from warnings/errors.
Anti-Pattern 2 — Hardcoding UVM_FULL in components
set_report_verbosity_level(UVM_FULL) in every component's build_phase.
Overrides the plusarg. Every regression run generates gigabyte logs regardless of +UVM_VERBOSITY. Defeats the entire verbosity control system.
Correct — Respect the hierarchy
Let plusarg control global threshold. Use set_report_verbosity_level() only for targeted debug in specific circumstances. Remove it before commit.
Anti-Pattern 3 — All messages at UVM_LOW or UVM_NONE
Every ``uvm_infousesUVM_NONEorUVM_LOW. In regression with +UVM_VERBOSITY=UVM_NONE, 10,000 messages still appear — they're all "critical". Correct — Use verbosity to express message importance UVM_NONE: test result only. UVM_LOW: major events. UVM_MEDIUM: transaction-level. UVM_HIGH`: signal-level. Fewer UVM_LOW messages = cleaner regression logs.
Quick Reference
| Task | Method / Plusarg |
|---|---|
| Set global verbosity (runtime) | +UVM_VERBOSITY=UVM_HIGH (or numeric 300) |
| Set this component's verbosity | set_report_verbosity_level(UVM_HIGH) |
| Set this component + all children | set_report_verbosity_level_hier(UVM_HIGH) |
| Read current verbosity | get_report_verbosity_level() |
| Silence specific message ID | set_report_id_verbosity("ID", UVM_DEBUG) |
| Always show specific message ID | set_report_id_verbosity("ID", UVM_NONE) |
| Check enabled before expensive format | if (uvm_report_enabled(UVM_HIGH, UVM_INFO, "ID")) |
| Set verbosity from outside component | env.agent.drv.set_report_verbosity_level(UVM_FULL) |
| Compile-time default | +define+UVM_DEFAULT_VERBOSITY=UVM_LOW |
// ── UVM_NONE (0): always visible — test PASS/FAIL only ────────────────
`uvm_info("RESULT", "*** TEST PASSED ***", UVM_NONE)
// ── UVM_LOW (100): important milestones ───────────────────────────────
`uvm_info("TEST", "Starting directed register test sequence", UVM_LOW)
// ── UVM_MEDIUM (200): transaction-level — default visibility ─────────
`uvm_info("DRV", $sformatf("Driving txn: addr=0x%0h write=%0b",
req.addr, req.write), UVM_MEDIUM)
// ── UVM_HIGH (300): signal/cycle-level — debug runs only ─────────────
`uvm_info("MON", $sformatf("psel=%0b penable=%0b pready=%0b",
vif.psel, vif.penable, vif.pready), UVM_HIGH)
// ── UVM_FULL (400): internal state — last resort ──────────────────────
`uvm_info("SEQR", $sformatf("Arbitration state: %s", arb_state), UVM_FULL)
// ── Production verbosity table ────────────────────────────────────────
// Regression CI : +UVM_VERBOSITY=UVM_NONE (0) — only PASS/FAIL
// Development run : +UVM_VERBOSITY=UVM_MEDIUM (200) — default
// Debug run : +UVM_VERBOSITY=UVM_HIGH (300) — detailed
// Full trace : +UVM_VERBOSITY=UVM_FULL (400) — everything§9 — Code Examples
Example 1 — Beginner: Verbosity Threshold in Action
The best way to understand the threshold rule is to see the same five messages filtered differently across different verbosity settings. This example makes the gate visible.
class apb_monitor extends uvm_monitor;
`uvm_component_utils(apb_monitor)
task run_phase(uvm_phase phase);
apb_seq_item txn;
forever begin
@(posedge vif.clk);
if (vif.psel) begin
txn = apb_seq_item::type_id::create("txn");
txn.addr = vif.paddr;
txn.data = vif.pwdata;
txn.write = vif.pwrite;
// Level 0 — always visible, even at UVM_NONE threshold
`uvm_info("MON", "APB transaction captured", UVM_NONE)
// Level 100 — visible at UVM_LOW and above
`uvm_info("MON", $sformatf("TXN: addr=0x%0h write=%0b",
txn.addr, txn.write), UVM_LOW)
// Level 200 — visible at UVM_MEDIUM and above (DEFAULT)
`uvm_info("MON", $sformatf("TXN: data=0x%0h", txn.data), UVM_MEDIUM)
// Level 300 — only at UVM_HIGH and above
`uvm_info("MON", $sformatf("BUS: psel=%0b penable=%0b pready=%0b",
vif.psel, vif.penable, vif.pready), UVM_HIGH)
// Level 400 — only at UVM_FULL (internal state, rarely needed)
`uvm_info("MON", $sformatf("CLK cycle %0d, internal fsm=%0d",
cycle_count, fsm_state), UVM_FULL)
end
end
endtask
endclass
// What prints at different thresholds per transaction:
//
// +UVM_VERBOSITY=UVM_NONE (threshold=0): 1 message — "APB transaction captured"
// +UVM_VERBOSITY=UVM_LOW (threshold=100): 2 messages — + "TXN: addr=0x... write=..."
// +UVM_VERBOSITY=UVM_MEDIUM (threshold=200): 3 messages — + "TXN: data=0x..."
// +UVM_VERBOSITY=UVM_HIGH (threshold=300): 4 messages — + "BUS: psel=... penable=..."
// +UVM_VERBOSITY=UVM_FULL (threshold=400): 5 messages — + "CLK cycle N, fsm=..."
//
// In regression: +UVM_VERBOSITY=UVM_NONE → minimal log
// In debug mode: +UVM_VERBOSITY=UVM_HIGH → full transaction + bus signal viewExample 2 — Intermediate: Precision Targeting with Per-Component and Per-ID Control
A test that runs globally at UVM_NONE verbosity for a clean regression log, but specifically raises verbosity on the scoreboard for detailed mismatch analysis and permanently silences a high-frequency bus-idle message.
class precision_test extends base_test;
`uvm_component_utils(precision_test)
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
// Step 1: Start everyone at UVM_LOW (set globally, then override specific ones)
// Global default from +UVM_VERBOSITY=UVM_LOW on command line
// Step 2: Scoreboard gets full verbosity — mismatches need all context
env.sb.set_report_verbosity_level(UVM_FULL);
// Step 3: APB monitor gets reduced verbosity — already verified
env.apb_agent.mon.set_report_verbosity_level(UVM_NONE);
// Step 4: Silence BUS_IDLE from driver — fires every cycle, already known good
env.apb_agent.drv.set_report_id_verbosity("BUS_IDLE", UVM_DEBUG);
// BUS_IDLE: only shows if global verbosity is UVM_DEBUG — effectively silenced
// Step 5: HANDSHAKE must always appear — critical protocol event
env.apb_agent.drv.set_report_id_verbosity("HANDSHAKE", UVM_NONE);
// HANDSHAKE: visible at threshold 0 — always shown regardless of global setting
`uvm_info("TEST", "Precision verbosity config applied", UVM_LOW)
endfunction
// Result at +UVM_VERBOSITY=UVM_LOW:
// env.sb: FULL verbosity — all scoreboard detail printed
// env.apb_agent.mon: NONE verbosity — silent (only errors/warnings from it)
// env.apb_agent.drv: LOW verbosity — BUS_IDLE silenced, HANDSHAKE always visible
// Everything else: LOW verbosity — from the global plusarg
endclassExample 3 — Verification: Dynamic Verbosity During Run-Phase
A production pattern for complex protocol flows: run at low verbosity until a specific signal condition is detected, then temporarily raise verbosity to capture full detail through the critical handshake window, then drop back.
class protocol_monitor extends uvm_monitor;
`uvm_component_utils(protocol_monitor)
virtual apb_if vif;
task run_phase(uvm_phase phase);
int saved_verbosity;
forever begin
// Wait for an error response — rare event worth detailed capture
@(posedge vif.clk iff (vif.psel && vif.pslverr));
// Save current verbosity before bumping
saved_verbosity = get_report_verbosity_level();
`uvm_info("MON", "PSLVERR detected — capturing detailed context", UVM_LOW)
// Temporarily raise verbosity for detailed analysis window
set_report_verbosity_level(UVM_FULL);
// Capture 10 cycles of detailed signal state
repeat(10) begin
@(posedge vif.clk);
`uvm_info("MON", $sformatf(
"[POST-ERR] paddr=0x%0h pwdata=0x%0h pslverr=%0b pready=%0b",
vif.paddr, vif.pwdata, vif.pslverr, vif.pready), UVM_FULL)
end
// Restore previous verbosity — don't leave it elevated
set_report_verbosity_level(saved_verbosity);
`uvm_info("MON", "Verbosity restored", UVM_LOW)
end
endtask
endclass
// Normal operation: LOW verbosity — minimal output
// On PSLVERR event: FULL verbosity for 10 cycles — complete bus capture
// After window: verbosity restored to original level
// No code changes, no recompile, no waveform needed for most failuresExample 4 — Tricky: The sformatf Performance Trap
The most common performance mistake in high-frequency monitors: expensive string formatting runs on every cycle even when the message will be filtered. At 500 MHz simulation with a cycle-by-cycle monitor, this matters.
// ❌ SLOW — sformatf runs every cycle regardless of verbosity ─────────
task run_phase(uvm_phase phase);
forever begin
@(posedge vif.clk);
// At UVM_LOW threshold: this line fires uvm_report_enabled → returns 0
// BUT $sformatf was already evaluated before the call! Zero benefit from filter.
`uvm_info("MON", $sformatf(
"CYCLE %0d: paddr=0x%0h pwdata=0x%0h psel=%0b penable=%0b pready=%0b @%0t",
cycle_cnt, vif.paddr, vif.pwdata,
vif.psel, vif.penable, vif.pready, $time), UVM_HIGH)
cycle_cnt++;
end
endtask
// At 1ns clock for 1ms simulation: 1,000,000 $sformatf calls
// Each formats 6 signals + timestamp — significant string allocation overhead
// ✓ FAST — guard with uvm_report_enabled() ─────────────────────────────
task run_phase(uvm_phase phase);
forever begin
@(posedge vif.clk);
// Check FIRST — only format the string if it will actually be printed
if (uvm_report_enabled(UVM_HIGH, UVM_INFO, "MON")) begin
`uvm_info("MON", $sformatf(
"CYCLE %0d: paddr=0x%0h pwdata=0x%0h psel=%0b penable=%0b pready=%0b @%0t",
cycle_cnt, vif.paddr, vif.pwdata,
vif.psel, vif.penable, vif.pready, $time), UVM_HIGH)
end
cycle_cnt++;
end
endtask
// At UVM_LOW threshold, UVM_HIGH guard returns 0:
// sformatf() never called → zero string allocation → measurable speedup
// At UVM_HIGH threshold: guard returns 1 → sformatf runs normally
// Use this pattern in any monitor that fires on every clock cycle§10 — Bugs and Debugging
Bug 1 — Hardcoded UVM_FULL Left from a Debug Session
// ❌ COMMITTED BUG — debug verbosity left in build_phase ─────────────
class apb_driver extends uvm_driver#(apb_seq_item);
function void build_phase(uvm_phase phase);
super.build_phase(phase);
set_report_verbosity_level(UVM_FULL); // ← FORGOT TO REMOVE
endfunction
endclass
// What happens:
// Command line: vsim +UVM_VERBOSITY=UVM_NONE ← engineers expect minimal log
// Reality: driver overrides at build_phase → UVM_FULL → massive output
// UVM applies plusarg BEFORE build_phase fires, then set_report_verbosity_level() wins
// ✓ CORRECT — no hardcoded verbosity in committed code ────────────────
class apb_driver extends uvm_driver#(apb_seq_item);
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// No set_report_verbosity_level here — let the plusarg control it
// Per-component overrides belong in the TEST's start_of_simulation_phase
endfunction
endclass
// If you need per-component verbosity in tests, set it from the test:
// env.apb_agent.drv.set_report_verbosity_level(UVM_HIGH);
// This way it's scoped to specific tests, not baked into the reusable component.Bug 2 — All Messages Tagged UVM_LOW: Verbosity Control Becomes Useless
// ❌ WRONG — verbosity inflation: everything is UVM_LOW ───────────────
task run_phase(uvm_phase phase);
forever begin
@(posedge vif.clk);
`uvm_info("DRV", "Clock edge", UVM_LOW) // 1M times/ms
`uvm_info("DRV", $sformatf("paddr=0x%0h", vif.paddr), UVM_LOW) // cycle-level
`uvm_info("DRV", $sformatf("pwdata=0x%0h", vif.pwdata), UVM_LOW) // cycle-level
`uvm_info("DRV", $sformatf("pready=%0b", vif.pready), UVM_LOW) // cycle-level
end
endtask
// Result: +UVM_VERBOSITY=UVM_NONE does nothing useful
// Regression logs are still massive — verbosity system is defeated
// ✓ CORRECT — verbosity reflects actual importance ─────────────────────
task run_phase(uvm_phase phase);
apb_seq_item req;
forever begin
seq_item_port.get_next_item(req);
`uvm_info("DRV", $sformatf("Driving txn: addr=0x%0h", req.addr), UVM_MEDIUM)
// ↑ Transaction start: medium importance — one per transaction
@(posedge vif.clk);
vif.paddr <= req.addr;
`uvm_info("DRV", $sformatf("Bus cycle: psel=%0b pready=%0b",
vif.psel, vif.pready), UVM_HIGH)
// ↑ Cycle-level: high verbosity — only in debug mode
seq_item_port.item_done();
end
endtask
// Result: +UVM_VERBOSITY=UVM_LOW → no driver messages (all MEDIUM/HIGH)
// Result: +UVM_VERBOSITY=UVM_MEDIUM → transaction-level only (clean)
// Result: +UVM_VERBOSITY=UVM_HIGH → full cycle detail for debuggingBug 3 — set_report_id_verbosity on the Wrong Component
// ❌ WRONG — called on env, but BUS_IDLE is from drv ──────────────────
class apb_env extends uvm_env;
function void start_of_simulation_phase(uvm_phase phase);
set_report_id_verbosity("BUS_IDLE", UVM_DEBUG); // sets on env — not driver!
// env doesn't emit BUS_IDLE — driver does. This call does nothing useful.
endfunction
endclass
// ✓ CORRECT option A — set on driver directly from test ───────────────
class my_test extends base_test;
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
env.apb_agent.drv.set_report_id_verbosity("BUS_IDLE", UVM_DEBUG);
// Explicitly targeting the component that emits the ID
endfunction
endclass
// ✓ CORRECT option B — set inside the driver itself ───────────────────
class apb_driver extends uvm_driver#(apb_seq_item);
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
set_report_id_verbosity("BUS_IDLE", UVM_DEBUG);
// 'this' IS the driver — correct component
endfunction
endclass§10b — The Message That Was Never Printed
Every other bug on this page produces wrong output. This one produces no output, which is harder, because absence of evidence looks exactly like absence of the event.
A driver appeared to stop driving, and the transactions were fine
VERBOSITY-FILTERED-EVIDENCEA scoreboard reported that a burst of transactions never reached the DUT. The
driver's own trace confirmed it: uvm_info messages logging each drive stopped
appearing partway through the test, and the log went quiet for thousands of
cycles before resuming.
A day went into the driver and the sequencer handshake — get_next_item,
item_done, the sequence's own loop — on the reasonable assumption that a driver
which stops logging has stopped driving. It had not. The waveform showed the bus
active throughout the silent window.
// In a test, to quieten one noisy agent during a long soak phase:
function void my_test::configure_phase(uvm_phase phase);
super.configure_phase(phase);
// Intent: reduce output from the memory agent only.
uvm_top.set_report_verbosity_level_hier(UVM_NONE); // ✗ applies to EVERYTHING
endfunction
// The driver's trace, written at the usual level:
`uvm_info("DRV", $sformatf("driving %s", req.convert2string()), UVM_MEDIUM)The decisive check took one line and should have been the first thing tried:
`uvm_info("DBG", $sformatf("my verbosity threshold = %0d",
get_report_verbosity_level()), UVM_NONE)
// -> my verbosity threshold = 0 (UVM_NONE)The driver's threshold was 0, so its UVM_MEDIUM (200) trace could not print.
Printing the query at UVM_NONE is what makes this work — a diagnostic written
at the level you are debugging is filtered by the same setting you are trying to
observe.
+UVM_VERBOSITY was not on the command line, which is why the run looked like a
default one. The threshold had been changed from inside the testbench, and
set_report_verbosity_level_hier on uvm_top reaches the entire component tree
rather than the one agent the author intended.
The confirming evidence was that the bus was active in the waveform for the same window the log was silent. A quiet log and a busy bus is a reporting problem, not a stimulus problem — and that comparison costs nothing.
Two mistakes compounding.
set_report_verbosity_level_hier applied to uvm_top sets the threshold for
every component in the hierarchy, not for a subtree. Targeting one agent requires
calling it on that agent's handle — env.mem_agent.set_report_verbosity_level_hier(UVM_NONE)
— or, better, leaving the threshold alone and using set_report_id_verbosity to
quieten one message ID.
And the driver's own trace was written at UVM_MEDIUM, which is exactly the
default threshold. Anything written at the default disappears the moment the
threshold moves down, which makes the most useful traces the most fragile.
What makes this class expensive is the inference it invites. A missing log line is read as a missing event, and every subsequent hypothesis is built on that. The verification engineer was debugging a driver that was working, using evidence that had been deleted rather than never produced — and there is nothing in a log to distinguish those two cases.
// 1. Target the subtree you mean, not the whole tree.
env.mem_agent.set_report_verbosity_level_hier(UVM_NONE);
// 2. Better still: leave thresholds alone and quieten one message ID, so
// every other message from that component survives.
env.mem_agent.set_report_id_verbosity_hier("MEM_TRACE", UVM_HIGH);
// 3. Write structural traces one level ABOVE the noise, so a routine
// threshold change cannot erase them.
`uvm_info("DRV", $sformatf("driving %s", req.convert2string()), UVM_HIGH) // detail
`uvm_info("DRV", "driver entered run_phase", UVM_LOW) // structureThe habit that closes the whole class is to make the reporting configuration observable rather than assumed:
function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
// Printed at UVM_NONE so it survives any threshold.
`uvm_info("CFG", $sformatf("%s verbosity = %0d",
get_full_name(), get_report_verbosity_level()), UVM_NONE)
endfunctionOne line per component, printed at a level nothing can filter, and every future "my message vanished" question is answered by the top of the log.
The diagnostic rule worth internalising: before debugging a missing event,
prove the event is missing. Compare the log against a waveform, a coverage
count, or a UVM_NONE heartbeat. A quiet log and a busy bus is a reporting
problem, and the two investigations have nothing in common.
See messaging infrastructure for the report path this sits on and report catchers for changing a message's severity rather than its visibility.
§11 — Ready-to-Run Code
A complete, self-contained verbosity demo. Run it three times with different +UVM_VERBOSITY arguments and observe how the output changes without touching a single line of code. That's the whole point.
// verbosity_demo.sv
// Compile: vlog -sv verbosity_demo.sv
// Run A: vsim -c work.tb_verb_top +UVM_TESTNAME=verb_test +UVM_VERBOSITY=UVM_NONE -do "run -all; quit"
// Run B: vsim -c work.tb_verb_top +UVM_TESTNAME=verb_test +UVM_VERBOSITY=UVM_MEDIUM -do "run -all; quit"
// Run C: vsim -c work.tb_verb_top +UVM_TESTNAME=verb_test +UVM_VERBOSITY=UVM_HIGH -do "run -all; quit"
`include "uvm_macros.svh"
import uvm_pkg::*;
class verbose_component extends uvm_component;
`uvm_component_utils(verbose_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("DEMO", "[NONE=0] Always visible — test milestone", UVM_NONE)
`uvm_info("DEMO", "[LOW=100] Important event — visible at LOW+", UVM_LOW)
`uvm_info("DEMO", "[MED=200] Transaction — visible at MEDIUM+", UVM_MEDIUM)
`uvm_info("DEMO", "[HIGH=300] Signal detail — visible at HIGH+", UVM_HIGH)
`uvm_info("DEMO", "[FULL=400] Internal state — visible at FULL+", UVM_FULL)
// Per-ID override demonstration
set_report_id_verbosity("ALWAYS_ON", UVM_NONE);
set_report_id_verbosity("ALWAYS_OFF", UVM_DEBUG);
`uvm_info("ALWAYS_ON", "[ID override UVM_NONE] Always shown", UVM_FULL)
`uvm_info("ALWAYS_OFF", "[ID override UVM_DEBUG] Never shown", UVM_NONE)
// ALWAYS_ON fires despite UVM_FULL tag — per-ID override wins
// ALWAYS_OFF silent despite UVM_NONE tag — per-ID DEBUG threshold applied
phase.drop_objection(this);
endtask
endclass
class verb_test extends uvm_test;
`uvm_component_utils(verb_test)
verbose_component vc;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
vc = verbose_component::type_id::create("vc", this);
endfunction
function void report_phase(uvm_phase phase);
`uvm_info("RESULT", $sformatf("Verbosity threshold was: %0d",
vc.get_report_verbosity_level()), UVM_NONE)
endfunction
endclass
module tb_verb_top;
initial run_test();
endmodule
// Expected output:
//
// Run A: +UVM_VERBOSITY=UVM_NONE (threshold=0)
// [NONE=0] Always visible — test milestone
// [ID override UVM_NONE] Always shown ← per-ID override fires despite FULL tag
// Verbosity threshold was: 0
//
// Run B: +UVM_VERBOSITY=UVM_MEDIUM (threshold=200)
// [NONE=0] Always visible — test milestone
// [LOW=100] Important event — visible at LOW+
// [MED=200] Transaction — visible at MEDIUM+
// [ID override UVM_NONE] Always shown ← still fires
// Verbosity threshold was: 200
//
// Run C: +UVM_VERBOSITY=UVM_HIGH (threshold=300)
// [NONE=0] Always visible — test milestone
// [LOW=100] Important event — visible at LOW+
// [MED=200] Transaction — visible at MEDIUM+
// [HIGH=300] Signal detail — visible at HIGH+
// [ID override UVM_NONE] Always shown ← still fires
// Verbosity threshold was: 300
//
// Note: "ALWAYS_OFF" never appears in any run (per-ID threshold = 500 = UVM_DEBUG)§12 — Interview Questions
A message prints when its declared verbosity is less than or equal to the component's threshold. The default threshold is UVM_MEDIUM (200).
The numbering runs the opposite way from importance, which is the part that confuses people: UVM_NONE is 0 and always prints, UVM_DEBUG is 500 and prints only when the threshold is raised that far. So a lower number means a more important message, and raising the threshold shows more output rather than less.
At the default of 200, UVM_NONE, UVM_LOW and UVM_MEDIUM messages appear while UVM_HIGH, UVM_FULL and UVM_DEBUG are filtered.
The practical consequence for authoring: anything written at UVM_MEDIUM sits exactly at the default and disappears the moment anyone lowers the threshold. Structural traces — a component entering run_phase, a phase objection being raised — belong at UVM_LOW or UVM_NONE, and per-transaction detail belongs at UVM_HIGH or above.
No, and conflating them is the most common misconception about UVM reporting.
Severity is what the message is: UVM_INFO, UVM_WARNING, UVM_ERROR, UVM_FATAL. It determines the count that decides pass/fail and whether the simulation stops — the counting itself lives in the report server, and report catchers can change a severity without touching verbosity.
Verbosity is how much detail the message represents, and it only gates visibility. Changing verbosity never changes a verdict — filtering an UVM_INFO does not make a test pass, and no verbosity setting can turn an error into a non-error.
The precise mechanism is worth knowing because the usual shorthand is slightly wrong. Warnings and errors are not exempt from the verbosity check — they pass through the same filter. What protects them is that the `uvm_warning, `uvm_error and `uvm_fatal macros hard-code UVM_NONE, which is 0 and therefore passes every threshold.
Call the function form directly and that protection is gone: uvm_report_warning("ID", "msg", UVM_HIGH) is genuinely filterable at the default threshold. Prefer the macros — they also capture file and line for you.
Target the narrowest scope that solves the problem, in this order of preference.
By message ID, which is the least destructive: agent.set_report_id_verbosity_hier("MEM_TRACE", UVM_HIGH) silences one stream while every other message from that component survives.
By component subtree: env.mem_agent.set_report_verbosity_level_hier(UVM_NONE). Note the handle — calling the same method on uvm_top applies it to the entire hierarchy, which is the mistake in this page's Debug Lab.
Globally, from the command line: +UVM_VERBOSITY=UVM_LOW for a regression run. Reporting debug techniques covers the wider set of controls. This is the right tool for controlling log volume across a whole regression and the wrong one for isolating a single agent.
What to avoid is deleting the messages, or writing them at a level so high that nobody ever sees them. A trace that exists only in a comment is not a trace, and the cost of a filtered message is zero at run time — UVM checks the threshold before formatting the string.
Four checks, cheapest first.
Print the component's threshold at UVM_NONE — get_report_verbosity_level(). Writing that query at the level you are debugging is self-defeating, since the same filter hides it.
Check whether anything set it from inside the testbench. An absent +UVM_VERBOSITY on the command line does not mean the threshold is default; set_report_verbosity_level_hier on uvm_top reaches everything, and a call meant for one agent commonly lands on all of them.
Check for an ID-specific override. set_report_id_verbosity beats the component threshold for that ID, so one message can be invisible while its neighbours print.
Check the code actually executes. Only after the first three — put a UVM_NONE message immediately before the missing one, and if that also fails to appear, you have a control-flow problem rather than a reporting one.
The framing that saves the most time: before debugging a missing event, prove the event is missing. Compare the log against a waveform or a coverage count. A quiet log and a busy bus is a reporting problem, and it shares nothing with the stimulus investigation it superficially resembles.
It removes the message from the compiled code entirely, so there is no run-time check, no string formatting and no branch.
The run-time cost of a filtered message is already small — UVM tests the threshold before evaluating the message, so a $sformatf inside a filtered uvm_info is never executed. What remains is the call and the comparison, which matters only in the hottest paths: a per-cycle monitor trace in a long regression can measurably slow a run.
The cost is that the message no longer exists. You cannot raise verbosity to see it, which defeats the purpose of having tiered logging at all — the message you most want during a failure is the one you compiled out to make the passing runs faster.
The reasonable compromise is to compile out only the highest tiers (UVM_FULL, UVM_DEBUG) in regression builds while keeping a debug build where nothing is removed, and to keep every structural message at UVM_LOW or UVM_NONE so it survives both. Measure before doing any of it: the cost is usually not where people assume.
The useful split is by what the message tells you, not by how interesting it seemed when written.
UVM_NONE — things that must survive any setting: test pass/fail markers, the configuration dump at end_of_elaboration, a heartbeat proving a long-running process is alive.
UVM_LOW — structural events, one per phase or per major milestone: components entering run_phase, objections raised and dropped, sequences starting and finishing, end-of-test summaries.
UVM_MEDIUM — per-transaction summaries at the granularity you would want in a routine failing run. Note that this sits exactly at the default threshold, so it is the first thing to vanish if anyone lowers it.
UVM_HIGH — per-transaction detail: full field dumps, protocol phase transitions, scoreboard match reporting.
UVM_FULL / UVM_DEBUG — per-cycle traces and internal state, useful for a single hand-run debug session and never in a regression.
The test to apply when choosing: at what point in a debug would I want this? A message you would want in the first five minutes belongs at LOW; one you would want only after narrowing to a single transaction belongs at HIGH.
Where This Is Specified
- IEEE 1800.2-2020 (UVM) — reporting. The
uvm_verbosityenumeration (UVM_NONE=0,UVM_LOW=100,UVM_MEDIUM=200,UVM_HIGH=300,UVM_FULL=400,UVM_DEBUG=500), and the rule that a report is issued when its verbosity is less than or equal to the configured threshold. - IEEE 1800.2-2020 —
uvm_report_object.set_report_verbosity_level,set_report_verbosity_level_hier,get_report_verbosity_level,set_report_id_verbosityandset_report_id_verbosity_hier, and the precedence of an ID-specific setting over the component-wide threshold. - IEEE 1800.2-2020 — severity versus verbosity.
uvm_severity(UVM_INFO,UVM_WARNING,UVM_ERROR,UVM_FATAL) determines counting and simulation control; verbosity gates visibility only. The`uvm_warning/`uvm_error/`uvm_fatalmacros supplyUVM_NONEas the verbosity, while the correspondinguvm_report_*functions accept a verbosity argument and are therefore filterable. - IEEE 1800.2-2020 — command-line processing. The
+UVM_VERBOSITYplusarg and its component-scoped+uvm_set_verbosityform. - Accellera UVM User Guide — message tiering. The convention of reserving low verbosities for structural events and high verbosities for per-transaction and per-cycle detail.
§13 — Best Practices
| Rule | Practice | Why It Matters |
|---|---|---|
| BP-1 | Never hardcode set_report_verbosity_level() in a reusable component's build_phase | Overrides the plusarg; breaks regression log sizing for everyone using the VIP |
| BP-2 | Assign verbosity based on actual message importance: UVM_NONE for pass/fail only, UVM_HIGH for cycle-level signals | Verbosity inflation defeats the hierarchy — +UVM_VERBOSITY=UVM_NONE becomes meaningless |
| BP-3 | Place per-component verbosity overrides in the test's start_of_simulation_phase, not in the component itself | Keeps reusable components verbosity-neutral; test controls debug granularity without modifying VIP code |
| BP-4 | Use if (uvm_report_enabled(...)) guard before expensive $sformatf() in tight loops | Eliminates string formatting overhead in filtered messages; critical for cycle-by-cycle monitors |
| BP-5 | Use set_report_id_verbosity("ID", UVM_DEBUG) to silence noisy IDs, UVM_NONE to force critical ones visible | Precision targeting — silences one noisy ID without affecting other messages from the same component |
| BP-6 | Set regression Makefile target to +UVM_VERBOSITY=UVM_NONE, debug target to +UVM_HIGH | No code changes between regression and debug — only command-line argument changes |
| BP-7 | Save and restore verbosity when dynamically raising it in run_phase | A temporarily raised verbosity that isn't restored pollutes the rest of the simulation log |
| BP-8 | Use UVM_NONE verbosity only for the final test PASS/FAIL line — never for routine messages | At UVM_NONE threshold, only UVM_NONE messages appear alongside errors/warnings — this is the CI-grade minimal log |
§14 — Summary
| Level | Value | Typical Message Type | Regression Visibility |
|---|---|---|---|
| UVM_NONE | 0 | Test PASS/FAIL, critical simulation milestones | Always — at every threshold setting |
| UVM_LOW | 100 | Phase transitions, sequence start/end, key state changes | At UVM_LOW threshold and above |
| UVM_MEDIUM | 200 | Transaction-level: addr, data, write flag — one per transaction | Default — shown unless threshold below MEDIUM |
| UVM_HIGH | 300 | Signal-level: bus signals, handshake cycles, interrupt states | Only with +UVM_VERBOSITY=UVM_HIGH or above |
| UVM_FULL | 400 | Internal state dumps, arbitration state, every clock edge | Only with +UVM_VERBOSITY=UVM_FULL or above |
| UVM_DEBUG | 500 | UVM framework internals — rarely needed by testbench engineers | Only with +UVM_VERBOSITY=UVM_DEBUG |
Continue learning
Related tutorials
- Recommended next
Report Server
The central engine that processes every report — the uvm_report_server that receives reports after the catchers, applies actions per severity (display, log, count, exit), tallies the error and fatal counts that decide pass/fail, bounds a flood with max_quit_count, formats and routes messages, and is customizable to tailor the whole testbench's reporting — and why the count, not the log, is the verdict.
- Recommended next
Transaction Lifecycle
The temporal journey of one transaction — created and randomized in zero time, sent through a blocking handshake while the driver consumes time on the pins, then completed and answered — and what blocks when.