Skip to content

UVM

report_phase

The third cleanup phase — announcing results and the PASS/FAIL summary, the report server that holds the verdict, and why a printed verdict must derive from the error count, not invent one.

UVM Phasing · Module 5 · Page 5.9

The Engineering Problem

extract_phase gathered the results, check_phase judged them — and report_phase is where they're announced. It is the third cleanup phase: each component reports its own results, and the final PASS/FAIL summary is communicated. It's the phase that produces the human-readable conclusion of the run — the per-component statistics and the overall verdict in the log.

The crucial thing to understand — and the source of a subtle, dangerous bug — is that report_phase does not decide the verdict; it announces it. The pass/fail was already determined by the UVM_ERROR/UVM_FATAL count accumulated during the run and check_phase (Module 5.8). report_phase communicates that result; it doesn't compute it. This distinction matters because UVM's reporting infrastructure already knows the verdict (the report server has been counting messages all along), and a custom report that re-derives or, worse, invents the verdict can contradict the real one — a hand-printed "TEST PASSED" banner sitting above an automatic summary that shows errors. This chapter is about the announcement step: what report_phase communicates, the report server that holds the real verdict, and why a reported verdict must reflect the error count rather than ignore it.

What is report_phase, what does it communicate, why does it announce the verdict rather than decide it, and how does the report server's error count — not a hand-printed banner — define the real pass/fail?

Motivation — why announcing is separate from deciding

report_phase being purely the announcement, distinct from the decision, is what keeps the verdict trustworthy:

  • The verdict is already decided by the error count. UVM's report server has been counting UVM_ERROR/UVM_FATAL messages throughout the run and check_phase; the pass/fail is the result of that count. report_phase doesn't recompute it — it communicates it. Keeping decision (severity/count) and announcement (reporting) separate means the verdict has one source of truth.
  • Per-component reporting belongs to each component. Bottom-up, each component reports its results — a scoreboard its match counts, a coverage component its numbers, an agent its statistics. report_phase is where each piece announces its contribution to the run's conclusion, and the framework aggregates the overall summary.
  • The report server already holds the authoritative summary. UVM automatically prints a report summary (the UVM_INFO/UVM_WARNING/UVM_ERROR/UVM_FATAL counts) at the end — and that is the authoritative pass/fail signal. Custom reporting adds readable detail; it must not contradict the server's counts.
  • A reported verdict that ignores the count lies. If a custom report prints "PASS" without consulting the error count, it can say pass while errors occurred — a log that lies about the result. The fix is to derive any reported verdict from the actual count (or just rely on the automatic summary).

The motivation, in one line: the pass/fail is decided by the error count and held by the report server, so report_phase exists to communicate that result and per-component detail — and the discipline is to report the verdict the count defines, never to invent one that contradicts it.

Mental Model

Hold report_phase as the announcer reading out the official scoreboard — not keeping score:

report_phase is the announcer at the end of the match, reading out the result from the official scoreboard. The score was decided by the play (the errors logged during the run and check_phase) and kept by the official scorekeeper (UVM's report server, counting every error and fatal). The announcer's job is to read it out clearly — "final score, here are the team statistics, the result is X" — making the official result audible to everyone. The announcer does not decide who won; they report what the scoreboard says. The danger is an announcer who makes up the result instead of reading the board — confidently declaring "home team wins!" while the scoreboard plainly shows they lost. That announcement is worthless, because the official result is on the board, not in the announcer's mouth. A good announcer reads the board; a bad one invents a score.

So in a report_phase, you announce: print your component's statistics, and communicate the result — but the result you communicate must be the one the report server holds (the error count), not a verdict you hand-invented. Read the official scoreboard; don't make up the score.

Visual Explanation — report in the cleanup sequence

report_phase is the third cleanup phase — after check_phase decided the verdict, before final_phase cleans up. It announces; it doesn't decide.

report_phase announces the results and verdict, after check decided it, before final cleanupextract → check → report → finalextract → check → report → final1extract_phase — gatheredthe final results were collected.2check_phase — decidedend-of-test conditions judged; pass/fail set by the error/fatalcount.3report_phase — announceeach component reports its results; the PASS/FAIL summary iscommunicated (not decided here).4final_phase — clean uplast teardown after the result is announced.
Figure 1 — report_phase, the announcement step, in the cleanup sequence. extract_phase gathered the results; check_phase judged them (deciding pass/fail via error severity); report_phase announces — each component reports its results and the final PASS/FAIL summary is communicated; final_phase cleans up. report communicates the verdict that check (and run-time checking) already decided via the error count.

report_phase is the communication step of the cleanup. By the time it runs, the verdict is already determined: the error/fatal count from run-time checking and check_phase is settled, and UVM's report server holds it. report_phase's job is to make that result and its supporting detail readable — each component, bottom-up, announces its own results (a scoreboard its match/mismatch tallies, coverage its numbers), and the framework produces the overall summary. It does not recompute pass/fail; that was check_phase's job (via severity) and the report server's tally. This separation — extract gathered, check decided, report announces — is what keeps the verdict's source single and authoritative. The report communicates; it does not adjudicate. And the authoritative number it communicates lives in the report server, which the next sections make precise.

RTL / Simulation Perspective — announcing results from the report server

A report_phase prints a component's statistics and, where it summarises pass/fail, derives that from the report server's error count — never inventing it. UVM also prints an automatic summary regardless.

report_phase — announce results; derive any verdict from the error count
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class my_test extends uvm_test;
  `uvm_component_utils(my_test)
 
  function void report_phase(uvm_phase phase);
    super.report_phase(phase);
    uvm_report_server svr = uvm_report_server::get_server();
 
    // ANNOUNCE the verdict DERIVED FROM the authoritative error count (don't invent it):
    int errors = svr.get_severity_count(UVM_ERROR) + svr.get_severity_count(UVM_FATAL);
    if (errors == 0)
      `uvm_info("RESULT", "=== TEST PASSED ===", UVM_NONE)
    else
      `uvm_error("RESULT", $sformatf("=== TEST FAILED: %0d error(s) ===", errors))
  endfunction
endclass
 
class my_scoreboard extends uvm_scoreboard;
  function void report_phase(uvm_phase phase);
    super.report_phase(phase);
    // each component announces ITS results (statistics, tallies):
    `uvm_info("SB", $sformatf("matched=%0d mismatched=%0d", num_matched, num_mismatched), UVM_LOW)
  endfunction
endclass

The test's report_phase shows the correct pattern: it queries the report server for the authoritative error count (get_severity_count(UVM_ERROR) plus fatals) and announces the verdict derived from that count — so the printed "PASSED"/"FAILED" cannot contradict reality. The scoreboard's report_phase shows the other role: each component, bottom-up, announces its own results (the match/mismatch tallies it accumulated). Crucially, UVM also prints an automatic report summary at the end — the counts of UVM_INFO/UVM_WARNING/UVM_ERROR/UVM_FATAL — which is the framework's own authoritative pass/fail signal, present whether or not you write a custom banner. So report_phase adds readable per-component detail and (optionally) a derived verdict banner, but the truth is the report server's count. The anti-pattern — printing "PASSED" unconditionally — is exactly what the DebugLab is about.

Verification Perspective — the report server holds the real verdict

UVM's report server is the central tally of all messages by severity, and it — not any printed banner — defines the pass/fail. Understanding that the verdict lives in the count, communicated by the report, is what keeps reporting honest.

All messages go to the report server which tallies by severity; the error count is the verdict; report_phase communicates itall messageserror/fatal count = verdicterror/fatalcount =…report_phasereads countuvm_info / warning / error /fatalfrom run-time + check_phaseReport servertallies messages by severityVerdict = error/fatalcount0 errors → PASS (the truth)report_phaseannounces; derives banner fromcount12
Figure 2 — the report server holds the verdict; report_phase communicates it. Every uvm_info/warning/error/fatal message (from run-time checking and check_phase) goes to the report server, which tallies them by severity. The pass/fail verdict IS this tally: zero errors/fatals means pass. report_phase announces that result — and any custom verdict it prints must be derived from the server's count. UVM also auto-prints a summary of the counts. The count is the truth; the report communicates it.

The report server is the single source of truth for the verdict. Every message — every uvm_info, uvm_warning, uvm_error, uvm_fatal issued anywhere in the environment, during run-time checking or check_phase — is routed to the report server, which counts them by severity. The pass/fail verdict is that count: zero errors and zero fatals means pass; otherwise fail. This is why severity matters so much (Module 5.8) — it's what increments the count the server holds. report_phase's role relative to the server is to communicate: it can query the server's count (get_severity_count) to print a derived verdict, and each component announces its detail; UVM additionally prints the server's automatic summary. The key discipline: any pass/fail the report prints must come from the server's count, because the count is the verdict and a printed banner is just an announcement. A banner that doesn't consult the count isn't a verdict — it's a guess that can be wrong, which is the bug to avoid.

Runtime / Execution Flow — announce, don't decide

The flow makes the boundary explicit: the verdict is decided upstream (by error severity, during run and check) and held by the report server; report_phase announces it. Deciding and announcing are different steps, and conflating them is where reports go wrong.

Verdict decided by severity during run and check, tallied by report server, announced in report_phaseThe verdict is decided upstream; report announces itThe verdict is decided upstream; report announces it1Decided: run + check (severity)every uvm_error/uvm_fatal during the run and check_phase sets theverdict.2Tallied: report serverthe server counts messages by severity; the error/fatal count isthe verdict.3Announced: report_phasecommunicate the result — derive any printed verdict from theserver's count; report detail.4Auto-summaryUVM prints the report summary (the counts) — the framework'sauthoritative signal.
Figure 3 — decided upstream, announced in report. The verdict is decided by error/fatal severity during run-time checking and check_phase, and tallied by the report server. report_phase then announces it — querying the server's count for any printed verdict and reporting each component's detail. The decision is upstream (severity → count); the announcement is in report. A report that re-decides (prints a verdict not derived from the count) can contradict the truth.

This flow draws the line between deciding and announcing. The verdict is decided upstream — every uvm_error issued during run-time checking or check_phase is a decision that the test has failed, and those decisions accumulate in the report server's count. By the time report_phase runs, the decision is done; the count is final. report_phase's job is purely to announce — to make the decided result readable, querying the server's count if it wants to print a derived verdict, and having each component report its detail. The framework's automatic summary (the printed counts at the end) is the authoritative communication of the same result. The failure mode is a report_phase that re-decides — that prints a verdict it computed independently (or hard-coded) rather than reading the count — because that printed verdict can disagree with the real one. Decide upstream via severity; announce downstream via the count. Keeping those separate is what makes the report trustworthy.

Waveform Perspective — announcing after the verdict is set

On a timeline, report_phase is a zero-time cleanup sliver after check_phase, where the already-decided verdict is announced — the conclusion printed after the judgement.

report_phase announces the verdict at zero time, after check decided it

10 cycles
report_phase announces the verdict at zero time, after check decided itextract (EXT) gathered; check (CHK) decided pass/fail via error severityextract (EXT) gathered…report_phase (RPT): announce the verdict from the report server's countreport_phase (RPT): an…the PASS/FAIL printed must reflect the error count — it is reported, not decided herethe PASS/FAIL printed …clkphaseRUNRUNRUNRUNEXTCHKRPTRPTFINFINvaliddata00A0A100B0B100000000t0t1t2t3t4t5t6t7t8t9
Figure 4 — during run_phase, transactions flow and errors (if any) accumulate in the report server. After the run, the zero-time cleanup phases run: extract (EXT) gathered, check (CHK) decided the verdict via error severity, and report (RPT) announces it — each component reports its results and the PASS/FAIL summary is communicated from the report server's count. The phase track shows report after check; it communicates the decided result, with no pin activity. The verdict was set upstream; report reads it out.

The phase track shows RPT after CHK — the announcement after the decision. Through the RUN and CHK regions, the verdict was being decided: any uvm_error from run-time checking or check_phase incremented the report server's count. By RPT, that count is final, and report_phase reads it out — each component announcing its results, the summary communicating the pass/fail from the server's tally. No pins move during RPT because it's zero-time announcement, not behaviour. The waveform reinforces the boundary: the verdict's decision happened earlier (severity, during run and check), and report_phase is purely its communication. The PASS/FAIL printed in this sliver is a readout of the count set upstream — which is exactly why it must be derived from that count, not invented, as the DebugLab shows.

DebugLab — the "TEST PASSED" banner that lied

A custom report printed PASSED above an automatic summary showing errors

Symptom

A test's log ended with a clear, confident custom banner: === TEST PASSED ===. But just below it, UVM's automatic report summary showed UVM_ERROR : 7. The test had, in fact, failed — seven errors were logged during the run — yet the custom banner declared a pass. Two contradictory verdicts sat in the same log, and anyone scanning for the banner (or a script grepping for "PASSED") was misled.

Root cause

The custom report printed PASSED unconditionally — it never consulted the actual error count, so it announced a verdict it had invented rather than the one the report server held:

why the banner contradicted the real verdict
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
report_phase:   `uvm_info("RESULT", "=== TEST PASSED ===", UVM_NONE)   // printed ALWAYS ✗
report server:  UVM_ERROR count = 7   (the REAL verdict: FAILED)
UVM auto-summary: UVM_ERROR : 7        (authoritative — the test failed)
result:         custom banner says PASSED, server count says FAILED → contradiction
fix:            derive the banner from the count:
                int e = svr.get_severity_count(UVM_ERROR)+svr.get_severity_count(UVM_FATAL);
                if (e==0) ...PASSED... else `uvm_error("RESULT", "FAILED: %0d errors")

The verdict was decided upstream — seven uvm_errors during the run and check_phase set it to FAILED, and the report server counted them. But the custom report_phase printed "PASSED" without ever reading that count, so it re-decided the verdict (incorrectly) instead of announcing the real one. The automatic summary below it was correct; the hand-printed banner was a guess that happened to be wrong. The report announced a verdict it should only have communicated.

Diagnosis

The tell is a printed verdict that contradicts the automatic report summary — a report that decides instead of reads. Diagnose verdict-reporting bugs:

  1. Compare the custom banner to the report server's count. If a printed "PASSED" sits above a non-zero UVM_ERROR count, the banner ignored the count. The summary's count is the truth; the banner lied.
  2. Check whether the banner consults the count. A correct verdict banner derives from get_severity_count(UVM_ERROR) (and fatals); an unconditional print, or one based on ad-hoc local flags, can disagree with the real verdict.
  3. Trust the automatic summary over custom prints. UVM's auto-printed counts are authoritative; a custom message is only as trustworthy as its derivation. When they disagree, the count wins.
Prevention

Announce the verdict the report server holds — never invent one:

  1. Derive any printed verdict from the error count. Query uvm_report_server::get_severity_count(UVM_ERROR) (plus fatals) and print PASSED only when it's zero. The banner must reflect the count, not a hand-set flag.
  2. Let report_phase announce, not decide. The verdict is decided upstream by error severity; report_phase communicates it. Don't compute pass/fail independently in the report — read it from the server.
  3. Rely on (and don't contradict) the automatic summary. UVM's report summary is the authoritative signal; a custom banner should agree with it. If you grep logs for pass/fail, key on the server's counts, not a hand-printed string.

The one-sentence lesson: the verdict is the report server's error/fatal count, and report_phase announces it — so a custom "PASSED" banner that ignores the count can contradict the real (failed) result; derive any printed verdict from the count, or rely on the automatic summary.

Common Mistakes

  • Printing a verdict that ignores the error count. An unconditional or hand-flag "PASSED" can contradict the report server's count. Derive any printed verdict from get_severity_count(UVM_ERROR) (and fatals), or rely on the automatic summary.
  • Thinking report_phase decides pass/fail. It announces; the verdict was decided upstream by error severity (run-time + check_phase) and held by the report server. Don't re-decide in the report.
  • Doing checking in report_phase. Judgement belongs in check_phase (at error severity); report_phase communicates the result. (If you must issue a failing condition late, it still needs error severity — but the natural place is check_phase.)
  • Contradicting the automatic summary. UVM auto-prints the message counts — the authoritative signal. A custom report must agree with it, not override it.
  • Time-consuming work in report. It's a zero-time function phase; nothing time-consuming belongs here.
  • Grepping logs for a hand-printed string. Keying pass/fail detection on a custom banner (which can lie) rather than the server's counts makes your CI trust an unreliable signal. Key on the counts.

Senior Design Review Notes

Interview Insights

report_phase is the third cleanup phase (after extract and check), and its job is to announce the results: each component, bottom-up, reports its own results — a scoreboard its match/mismatch tallies, a coverage component its numbers — and the final PASS/FAIL summary is communicated. It's a zero-time, bottom-up function phase. The most important thing to understand is that report_phase announces the verdict; it does not decide it. The pass/fail was already determined upstream by the UVM_ERROR/UVM_FATAL count accumulated during run-time checking and check_phase, and that count is held by UVM's report server. So report_phase communicates the decided result and per-component detail; if it prints a verdict banner, that banner must be derived from the report server's error count, not invented. UVM also automatically prints a report summary of the message counts at the end, which is the framework's authoritative pass/fail signal. The classic bug is a custom report that prints "TEST PASSED" unconditionally, ignoring the actual error count, so it can contradict the real (failed) verdict — which is why the rule is to announce the result the report server holds, never to compute an independent one.

Exercises

  1. Derive the banner. Write a report_phase for a test that prints PASSED or FAILED derived from the report server's error and fatal counts, at the correct severity for each.
  2. Find the lie. A log shows "TEST PASSED" above "UVM_ERROR : 3". State the cause and the change that makes the banner truthful.
  3. Decide vs announce. For each, name the phase: (a) assert the expected queue is empty; (b) print a scoreboard's match/mismatch tally; (c) issue a uvm_error for a leftover transaction; (d) print the final PASS/FAIL summary.
  4. Trust the right signal. Explain why CI should key pass/fail on the report server's counts rather than a custom banner string, using the lying-log scenario.

Summary

  • report_phase is the third cleanup phase — zero-time, bottom-up — where each component announces its results and the final PASS/FAIL summary is communicated. It reports the verdict; it does not decide it.
  • The verdict was decided upstream by UVM_ERROR/UVM_FATAL severity (run-time checking + check_phase) and is held by the report server, which tallies messages by severity — the pass/fail is that count (zero errors → pass). UVM also auto-prints a summary of the counts.
  • Any verdict report_phase prints must be derived from the report server's count (get_severity_count), not invented — and each component announces its own detail (statistics, tallies) bottom-up.
  • The classic bug is a custom "TEST PASSED" banner that ignores the error count, contradicting the automatic summary that shows errors — a log that lies. Derive printed verdicts from the count, and key CI on the counts, not a hand-printed string.
  • The durable rule of thumb: report_phase announces the verdict the report server holds — derive any printed PASS/FAIL from the error/fatal count and report per-component detail, but never compute a verdict here, because the count decided upstream is the truth and the report only reads it out.

Next — final_phase: the results are announced; one phase remains. final_phase is the last cleanup phase — for any final teardown after the verdict is reported, closing out the simulation. The next chapter ends the phase schedule.