Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here4
  • UVM Foundations14
  • UVM Phases & Simulation Lifecycle26
    • The Phase System
    • UVM Phases: Build, Connect, Elaborate and Run
    • Objections Deep Dive: Drain Time and Propagation
    • Phase Debugging and End-of-Test Signoff
      • End-of-Test Chain: Extract, Check, Report and Final
      • Phase Domains and Sync
      • Reset Phase and Phase Jump
      • Ordering Bugs and Silent Overrides
      • Objection Triage and Hang Forensics
      • Custom Phases
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
  • UVM Sequences & Stimulus21
  • UVM Agents & Protocol VIP21
  • Environment & Test Classes11
  • Checking & Coverage in UVM21
  • UVM Register Abstraction Layer (RAL)22
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Phase Debugging and End-of-Test Signoff
  1. Topics
  2. UVM Verification
  3. UVM Phases & Simulation Lifecycle
  4. Phase Debugging and End-of-Test Signoff
  5. End-of-Test Chain: Extract, Check, Report and Final

Part 2 · Phases & Lifecycle · Intermediate

End-of-Test Chain: Extract, Check, Report and Final

Extract, check, report and final: turning run-time results into one verdict, and the false-pass sources that slip past it.

On this page

    Run the bench's dma_timeout_test and look at the end of the log. There is a PH_TIMEOUT fatal and a report summary, and nothing from the scoreboard: no [SB] ... words matched line at all. Now imagine your regression script decides PASS by looking for a line the test prints in report_phase. What does it do with a log where that phase never ran?

    This lesson walks through the four phases that run after run time ends, what each one is for, and how to turn their output into one verdict that a fatal, a demoted error or a silent override cannot fake.

    The idea: closing the till

    At closing time, a shop does four things in order. It counts the cash in each drawer (extract: collect the final numbers). It reconciles the totals against the receipts (check: decide whether anything is wrong). It writes the day's summary for the owner (report: print the results). Then it locks the doors and turns off the lights (final: release files and handles). Mixing the steps, such as writing the summary before reconciling, is how a shop reports a good day with money missing.

    All four are function phases, so they take no simulated time: they run at the time the run phase ended. Extract, check and report run bottom-up, so a parent sees its children's results; final runs top-down.

    The picture

    Flow: run phase ends, extract, check, report, final, then the UVM report summary. A dotted arrow from run phase ends straight to the summary is labelled UVM_FATAL.
    The normal path visits every phase. A fatal jumps straight to the summary: check and report never run.

    Build it: what the bench does in each phase

    The bench's scoreboard compares every word live, during run time, and keeps two counts. Its check_phase asks the two aggregate questions that matter at the end: did any prediction go unmatched, and was anything compared at all? Its report_phase prints the counts.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    // snippet: from the course DMA bench
    // Predicts every output word from the input word and the KEY register the
    // DUT was programmed with, then compares in order. The write_* methods are
    // virtual so a derived scoreboard (a factory override) can extend them.
    class dma_scoreboard extends uvm_scoreboard;
      `uvm_component_utils(dma_scoreboard)
      uvm_analysis_imp_apb #(apb_item,  dma_scoreboard) apb_export;
      uvm_analysis_imp_in  #(axis_item, dma_scoreboard) in_export;
      uvm_analysis_imp_out #(axis_item, dma_scoreboard) out_export;
    
      bit [31:0]   key;
      bit [31:0]   expected[$];
      int unsigned n_match, n_mismatch;
    
      function new(string name, uvm_component parent);
        super.new(name, parent);
        apb_export = new("apb_export", this);
        in_export  = new("in_export", this);
        out_export = new("out_export", this);
      endfunction
    
      virtual function void write_apb(apb_item t);
        if (t.write && !t.slverr && t.addr == KEY) key = t.data;
      endfunction
    
      virtual function void write_in(axis_item t);
        expected.push_back(t.data ^ key);
      endfunction
    
      virtual function void write_out(axis_item t);
        bit [31:0] exp;
        if (expected.size() == 0) begin
          `uvm_error("SB_UNEXP", $sformatf("output word %08h with nothing expected", t.data))
          n_mismatch++;
          return;
        end
        exp = expected.pop_front();
        if (t.data !== exp) begin
          `uvm_error("SB_MISMATCH", $sformatf("expected %08h, got %08h", exp, t.data))
          n_mismatch++;
        end else begin
          n_match++;
        end
      endfunction
    
      function void check_phase(uvm_phase phase);
        if (expected.size() != 0)
          `uvm_error("SB_LEFTOVER", $sformatf("%0d expected word(s) never came out", expected.size()))
        if (n_match == 0)
          `uvm_error("SB_EMPTY", "no words were compared: the test checked nothing")
      endfunction
    
      function void report_phase(uvm_phase phase);
        `uvm_info("SB", $sformatf("%0d words matched, %0d mismatched", n_match, n_mismatch), UVM_LOW)
      endfunction
    endclass

    Check does not re-compare anything. Per-item comparison belongs in run time, where the scoreboard has the context (time, ordering) to give a useful message. Check answers questions about the whole run.

    Extract: read through handles, at the end

    When an env or test needs totals from several components, it copies them in extract_phase, reading the components directly through their handles. config_db is for configuration at build time, not for moving results around afterwards.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // snippet
    class dma_env_signoff extends dma_env;
      `uvm_component_utils(dma_env_signoff)
      int unsigned words_checked, words_bad;
    
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
    
      function void extract_phase(uvm_phase phase);
        words_checked = sb.n_match + sb.n_mismatch;   // the scoreboard extracted first: bottom-up
        words_bad     = sb.n_mismatch;
      endfunction
    endclass

    One verdict, from the report server

    UVM already prints a verdict you can trust: the report summary at the end of every run, with a count per severity. The report server counts every severity, warnings included. If a test prints its own PASS or FAIL banner, derive it from those counts, never from a local flag:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    // snippet
    function void report_phase(uvm_phase phase);
      uvm_report_server svr = uvm_report_server::get_server();
      int errs = svr.get_severity_count(UVM_ERROR) + svr.get_severity_count(UVM_FATAL);
      if (errs == 0) `uvm_info("RESULT", "TEST PASSED", UVM_NONE)
      else           `uvm_info("RESULT", $sformatf("TEST FAILED (%0d errors)", errs), UVM_NONE)
    endfunction

    Remember the opening log: a UVM_FATAL ends the simulation at once, so report_phase does not run and this banner never appears. Regression scripts should therefore decide from the summary lines (UVM_ERROR : 0 and UVM_FATAL : 0), and treat a log with no summary as a failure, because it means the simulator crashed or something called $finish early.

    Watch it run

    The passing smoke test, end of log:

    Diagram
    UVM_INFO dma_tb.sv(411) @ 655000: uvm_test_top.env.sb [SB] 8 words matched, 0 mismatched
    
    --- UVM Report Summary ---
    ** Report counts by severity
    UVM_ERROR :    0
    UVM_FATAL :    0

    The timeout test, end of log. The scoreboard's report line is missing because report_phase never ran; the summary is still printed:

    Diagram
    UVM_FATAL uvm_phase_hopper.svh(598) @ 20000000: reporter [PH_TIMEOUT] Explicit timeout of 20000000 hit, indicating a probable testbench issue
    
    --- UVM Report Summary ---
    ** Report counts by severity
    UVM_ERROR :    0
    UVM_FATAL :    1

    Final: top-down, and the shared-file trap

    final_phase is the place to close files, flush databases and release handles. It runs top-down, so a parent's final_phase runs before its children's. A test that closes a log file in final_phase while a child still writes to it in its own final_phase will lose those writes. Either the owner of a resource closes it, and children finish writing by report_phase, or the shared file is closed by whatever finalizes last.

    What breaks: the catcher that ate the mismatches

    A team had one known-benign error from a vendor VIP that fired on every reset. Someone added a report catcher to demote it to a warning, matching on an ID pattern that looked specific enough. Weeks later, a DUT change broke the XOR path for one key value. Tests that hit it stayed green. The catcher's pattern also matched SB_MISMATCH, so every mismatch became a warning, the error count stayed at 0, and the summary said pass.

    They found it only because a designer asked why the scoreboard's report line said 3 mismatched in a passing test. The fixes became their standing rules: catchers match an exact ID and message text, every catcher counts what it demotes and reports that count in report_phase, and the regression script cross-checks the scoreboard's mismatch count against the summary.

    False-pass source

    How it hides an error

    Guard

    Report catcher demotes or catches an error

    The error is counted as a warning or not at all

    Exact matches; report the number demoted

    set_report_severity_action(UVM_ERROR, UVM_NO_ACTION)

    Messages with no action are not displayed and not counted

    Grep the code base for UVM_NO_ACTION

    PASS banner from a local flag

    Prints PASS while the server holds errors

    Derive the verdict from server counts

    $finish called from a component

    Phases after run never execute; no summary

    Treat a missing summary as a failure

    Scoreboard compared nothing

    No mismatches because no compares

    An SB_EMPTY-style check in every scoreboard

    Interview

    "Which phases run after run_phase, in which order, and where does PASS or FAIL come from?"

    A weak answer: "extract, check, report, final; the test prints PASS in report_phase." Correct names, and a verdict that disappears on the first fatal.

    A strong answer: "Extract, check and report run bottom-up, final top-down, all in zero time at the end of run. Extract copies totals through handles; check asks aggregate questions such as leftovers and zero compares; report prints. The verdict comes from the report server's counts: a fatal skips check and report entirely, so CI decides from the UVM summary lines and treats a missing summary as a fail. I also guard against catchers or UVM_NO_ACTION settings that hide errors."

    Follow-ups: Why not fail a single test on a coverage goal in check_phase? (coverage is a merged regression metric; a single test rarely closes a goal on its own). What does report_phase see after a fatal? (nothing: it does not run).

    Check yourself A test's report_phase prints TEST PASSED from a bit failed that the scoreboard sets on mismatch. The scoreboard calls uvm_error too. What can go wrong?

    Two sources of truth. If any other component reports an error (a protocol checker, a timeout in a sequence), the banner still says PASSED because only the scoreboard sets the flag. And after a fatal the banner never prints at all. Derive the banner from the report server's error and fatal counts.

    Check yourself Why does dma_scoreboard fail on zero compares in check_phase rather than in run_phase?

    Only at the end of run time is it certain that no more words will arrive. In run_phase, zero compares is the normal state at the start of every test. check_phase runs once, after run time, which is exactly when the aggregate question has a final answer.

    Check yourself The env's final_phase closes a transaction log; an agent below it writes its last summary line to the same file in its own final_phase. What happens?

    The agent's write goes to a closed handle and is lost, because final_phase runs top-down: the env finalizes first. Have the agent write by report_phase, or let the component that finalizes last own the close.

    Next: everything so far assumed one schedule. Phase domains and sync shows what changes when parts of the bench run their own.

    Related topics

    • Phase domains and sync

    • check_phase debug

    • Reporting and verbosity

    Previous

    39 / 280

    Next: Phase Domains and Sync

    Up next

    Up nextPhase Domains and Sync

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Reset Phase and Phase Jump
    • Ordering Bugs and Silent Overrides
    • Objection Triage and Hang Forensics