UVM
final_phase
The terminal cleanup phase — true teardown (closing files, releasing resources) that must happen last, and why releasing resources earlier strands the phases that still need them.
UVM Phasing · Module 5 · Page 5.10
The Engineering Problem
The verdict is decided and announced; one phase remains. final_phase is the last phase in the entire schedule — the absolute final thing UVM runs before the simulation ends. After build, connect, the run, extract, check, and report, final_phase is the terminal step, and its job is true teardown: closing files, releasing external or DPI resources, flushing buffers — any cleanup that must happen after everything else is done.
The engineering point is precisely that position: final_phase is where teardown belongs because it's the only phase guaranteed to run after all the others. This matters because teardown done too early — releasing a resource in an earlier phase that a later phase still needs — breaks the later phase. Close a log file in report_phase and the automatic report summary (or another component's reporting) that runs afterward has nothing to write to; free a reference-model handle in check_phase and report_phase can't read it. The reason final_phase exists as a distinct, terminal phase is to give resource teardown a home where nothing else will need the resource afterward. This short chapter is about the last phase and the discipline of doing teardown last.
What is
final_phase, why is it the terminal phase, what teardown belongs there, and why does doing that teardown in an earlier phase break the phases that still need the resource?
Motivation — why teardown belongs in the last phase
final_phase being the terminal phase is exactly what makes it the right home for teardown:
- It's the only phase guaranteed to run after all others. Resource teardown must happen when nothing else will use the resource — and the only such moment is after the last phase that uses it, which is
final_phase. Anywhere earlier, some later phase might still need what you released. - Premature cleanup breaks later phases. Close a file, free a handle, or release a resource in extract, check, or report, and any subsequent phase (or the automatic report summary) that depends on it fails — lost output, a null handle, a crash. The bug is position: the cleanup was correct, just too early.
- Reporting and the auto-summary run before final. UVM's automatic report summary and each component's
report_phaseproduce output up to and includingreport_phase;final_phaseruns after. So resources those use (log files, result sinks) must survive untilfinal_phaseto release. - It's the last hook for anything. Beyond resource release,
final_phaseis the final opportunity for any action that should happen at the very end — a last flush, a closing message, post-processing that needs the whole run complete.
The motivation, in one line: resource teardown must happen after everything that uses the resource, and final_phase — the terminal phase — is the only place guaranteed to be after all the rest, so it's where cleanup belongs and why releasing early breaks the phases that still need the resource.
Mental Model
Hold final_phase as turning off the lights and locking up — only after everyone has left:
final_phaseis locking up the building at the very end — after the last person has gone. Throughout the day, rooms were used (the phases ran, each needing its resources — files open, the heating on, the doors unlocked). The performance ended, the audit finished, the results were announced. Now, last of all, you do the closing-up: switch off the lights, shut down the heating, lock the doors, take out the trash. The whole point is that this happens after everyone has left and everything is done — if you locked the doors while the audit was still running, you'd lock people in; if you cut the power before the announcement, no one would hear the result. Closing-up is correct only as the final act, because anything done earlier strands a later activity that still needed the lights on. So you tear down last, when nothing remains that depends on what you're shutting off.
So in a final_phase, you do the closing-up: release resources, close files, flush and shut down — knowing nothing runs after you, so nothing will miss what you released. And the corollary: never do this closing-up in an earlier phase, or you cut the power on something still working.
Visual Explanation — final, the terminal phase
final_phase is the last of the four cleanup phases and the last phase overall — the terminal teardown after the verdict is reported.
final_phase is the terminal step — there is no phase after it. By the time it runs, everything is done: the run is over, the results gathered, the verdict decided and announced, the automatic report summary printed. So final_phase is where teardown is safe, because nothing remains that could need the resources you're about to release. Its content is true cleanup: closing the log or results file you wrote to, releasing an external model or DPI handle, flushing any buffered output, freeing allocated resources. It is a zero-time, bottom-up function phase like the other cleanup phases, so it does no time-consuming work and no behaviour — just the final closing-up. The reason it exists as its own phase, separate from report_phase, is exactly so this teardown has a guaranteed after-everything moment, which the next sections make concrete.
RTL / Simulation Perspective — true teardown in code
A final_phase releases resources — closes files, frees handles, flushes — that the earlier phases used. It runs after report, so those resources were available throughout reporting and are released only now.
class my_env extends uvm_env;
`uvm_component_utils(my_env)
int results_file; // opened earlier, written to through report_phase
function void final_phase(uvm_phase phase);
super.final_phase(phase);
// TRUE TEARDOWN — last of all, after report and the auto-summary:
if (results_file) $fclose(results_file); // close the results file (report wrote to it)
// release external / DPI resources, flush buffers, free allocations ...
`uvm_info("ENV", "final cleanup complete", UVM_LOW)
endfunction
endclassThe teardown here is deliberately last. The results_file was opened earlier and written to throughout the run and the reporting (the scoreboard's report_phase may have written its tallies to it, and UVM's automatic summary prints around this time) — so it had to stay open until now. final_phase closes it, after all the writing is done, because closing it any earlier would strand a later write. The same logic applies to any resource: an external reference-model handle used through check_phase and report_phase, a DPI/C resource, a buffered output stream — they're released in final_phase precisely because that's the first moment nothing else needs them. The phase does only this closing-up: no checking (done in check_phase), no announcing (done in report_phase), no behaviour (the run is over) — just the terminal release of resources, and call super.final_phase as with any phase override.
Verification Perspective — why teardown must be last
Releasing a resource before the last phase that uses it breaks that later phase. final_phase is the terminal phase, so teardown there is safe; teardown earlier is a use-after-release waiting to happen.
The contrast is the whole lesson. A resource — say a results file or an external model handle — is used by phases up to and including report_phase (and the automatic report summary that prints around then). If you release it in report_phase, you've cut it off while later reporting still needs it: the auto-summary or another component's report_phase finds a closed file or a null handle, and you lose output or crash — a use-after-release. If you release it in final_phase, after every phase that uses it, the release is safe, because nothing runs afterward to need it. This is why final_phase is a distinct, terminal phase: it provides the guaranteed after-everything moment that resource teardown requires. The discipline is simple and absolute — resource cleanup goes in final_phase, not in an earlier phase, because only final_phase is certain to be after the last use.
Runtime / Execution Flow — the last thing that runs
final_phase is the terminal node of the entire phase schedule. Tracing the schedule to its end shows final_phase as the closing act, after which the simulation ends.
This is the end of the whole schedule (Module 5.1) — final_phase is its last node, and after it the simulation terminates. Everything you've learned across this module funnels here: the environment was built, wired, validated, announced, run, and its results gathered, judged, and reported — and final_phase closes it out. The single property that defines the phase is being last, and that property is what makes it the correct home for teardown: because nothing runs after final_phase, any resource you release in it cannot possibly be needed afterward. Conversely, this is why teardown in any earlier phase is risky — there's always a later phase that might need the resource. So the rule that falls out of the schedule's shape is: do final cleanup in the final phase. It's the closing act, and closing acts go last.
Waveform Perspective — the closing act
On a timeline, final_phase is the very last zero-time sliver — the terminal cleanup after report, after which the simulation ends.
final_phase is the last zero-time sliver — teardown, then the simulation ends
10 cyclesThe phase track ends with FIN — the last cell before the simulation terminates. Through the cleanup region, no pins move (all zero-time), and the phases run in their fixed order: gather (EXT), judge (CHK), announce (RPT), and finally tear down (FIN). final_phase's placement at the very end is the entire point: it's the last moment anything runs, so resources used through report_phase (a log file, an external handle) are safely released here because nothing follows to need them. The waveform closes the whole phase story from Module 5.1: a thin setup sliver, the wide run, and a thin cleanup sliver ending in final_phase — the run's closing act, after which the schedule, and the simulation, are done.
DebugLab — the results file closed one phase too early
Lost report output — a results file closed in report_phase, before the summary that still wrote to it
A test wrote its results to a custom file, and a component closed that file in its report_phase right after writing its own summary. But the final portion of the results — written by UVM's end-of-test summary and another component's later reporting — was missing from the file, and on some runs the simulation emitted a "write to closed file" error near the end. The file had everything up to one component's report, then nothing.
The file was closed too early — in report_phase — while later reporting still needed to write to it:
component A.report_phase: write summary to results_file; $fclose(results_file); // ✗ too early
later (still to come): component B.report_phase writes; UVM auto-summary writes
result: those writes hit a CLOSED file → lost output / "write to closed file"
fix: close the file in FINAL_PHASE — after ALL reporting is done:
function void final_phase(...); $fclose(results_file); endfunctionreport_phase is not the last phase — other components' report_phase methods and UVM's automatic summary run around and after it, and final_phase runs after all of them. So closing the file in report_phase released it while later writers still needed it, losing their output (and erroring on the write-to-closed-file). The teardown logic was correct; its phase was wrong. The file had to stay open until final_phase, the terminal phase, where closing it is safe because nothing writes afterward.
The tell is lost end-of-run output, or a use-after-release near simulation end — resource teardown done before the last user. Diagnose premature-cleanup bugs:
- Check the phase of resource release. Closing a file, freeing a handle, or releasing a resource belongs in
final_phase(the terminal phase). If it's inreport_phase(or earlier), a later phase that uses the resource will fail. - Identify who uses the resource after the release point. If anything — another component's report, the auto-summary,
final_phaseitself — uses the resource after where it's released, the release is too early. The resource must outlive its last user. - Look for "write to closed file" / null-handle at end-of-run. These errors near the end signal a resource released before the last phase that needed it; move the release to
final_phase.
Release resources in the terminal phase, after their last use:
- Do resource teardown in
final_phase. Close files, release external/DPI handles, and flush buffers infinal_phase— the last phase — so nothing runs afterward to need them. Never release them in an earlier phase. - Let the resource outlive its last user. Identify the latest phase that uses a resource (often
report_phaseor the auto-summary) and release it strictly after — which, being safe, meansfinal_phase. - Keep cleanup phases to their roles. Extract gathers, check judges, report announces, final tears down. Releasing resources during gather/judge/announce risks stranding the phases that follow; confine teardown to final.
The one-sentence lesson: final_phase is the only phase guaranteed to run after all others, so resource teardown (closing files, releasing handles) belongs there — releasing a resource in report_phase or earlier strands the later phases that still use it, losing output or crashing.
Common Mistakes
- Releasing resources before
final_phase. Closing a file or freeing a handle in extract/check/report strands the later phases (and the auto-summary) that still use it — lost output or a crash. Do teardown infinal_phase, the terminal phase. - Confusing report with the last phase.
report_phaseis not last — other components' reporting, the auto-summary, andfinal_phasefollow it. Resources used by reporting must survive untilfinal_phase. - Doing checking or announcing in
final_phase. Judgement ischeck_phase, announcement isreport_phase;final_phaseis teardown only. (Issuing a late error in final still counts toward the verdict, but the natural place to check ischeck_phase.) - Time-consuming work in final. It's a zero-time function phase; nothing time-consuming belongs here — just instantaneous cleanup.
- Forgetting
super.final_phase. Callsuperas with other phase overrides to preserve base-class behaviour. - Leaving resources unreleased. The flip side: failing to close files or free external resources at all can leak or corrupt output.
final_phaseis where you ensure clean release.
Senior Design Review Notes
Interview Insights
final_phase is the terminal cleanup phase — the absolute last phase UVM runs, after the verdict has been reported — and its job is true teardown: closing log and results files, releasing external or DPI resources, flushing buffers, and any final cleanup that must happen after everything else. It's a zero-time, bottom-up function phase like the other cleanup phases. Its defining property is simply that it's last: nothing runs after final_phase, which is exactly why it's the safe place to release resources — because nothing remains that could need them. This is the reason it exists as a distinct phase separate from report_phase: resource teardown must happen after the last phase that uses the resource, and final_phase provides that guaranteed after-everything moment. The classic mistake it prevents is premature cleanup — closing a file in report_phase, before another component's report or UVM's automatic summary writes to it — which loses output or crashes. So final_phase is where you put resource release, knowing it's the closing act of the entire run.
Exercises
- Tear it down. Write a
final_phasefor an env that closes a results file opened earlier, and explain why this belongs infinal_phaserather thanreport_phase. - Find the early release. A results file is closed in
report_phase; the auto-summary's output is missing from it. State the cause and the corrected phase. - Place the concern. For each cleanup action, name the phase: (a) gather the final outstanding count; (b) assert no transactions are outstanding; (c) print the PASS/FAIL summary; (d) close the log file and release the DPI handle.
- Resource lifetime. Explain the rule "a resource must outlive its last user," and how it determines that resource release belongs in
final_phase.
Summary
final_phaseis the terminal cleanup phase — zero-time, bottom-up — the absolute last thing UVM runs, after the verdict is reported. It is for true teardown: closing files, releasing external/DPI resources, flushing buffers, final cleanup.- Its value is precisely its position: being last means nothing runs afterward, so releasing a resource there cannot strand any later phase — it's the safe, guaranteed after-everything moment for cleanup.
- Teardown done earlier breaks later phases: closing a file in
report_phasestrands the auto-summary or another component's later reporting (lost output, write-to-closed-file); freeing a handle incheck_phaseleavesreport_phasewith a null. A resource must outlive its last user. - It is the end of the whole schedule — after
final_phase, the simulation terminates. Keep it to teardown only (no checking, no announcing, nothing time-consuming), and ensure resources are actually released (the flip side of premature cleanup is a leak). - The durable rule of thumb: do final cleanup in the final phase — release resources in
final_phase, after every phase that uses them, because it's the only phase guaranteed to be last, so teardown there strands nothing.
Next — Phase Ordering: you've seen all the phases individually; the last chapter of this module steps back to the ordering itself — how the framework sequences the whole schedule across the component tree, the top-down/bottom-up directions, and how the run-time sub-phases synchronise — consolidating phasing into one ordered picture.