Part 2 · Phases & Lifecycle · Intermediate
final_phase: Last Teardown and Resource Cleanup
Top-down final_phase traversal, closing file handles, flushing coverage databases, and safe shutdown ordering.
final_phase role
The final_phase callback is the very last phase in the UVM schedule. Unlike extract/check/report, traversal is top-down : parents finalize before children. Use it to close files, flush coverage databases, release PLI handles, and perform any cleanup that must happen after all reporting is done.
[PHASE][UVM] final_phase traversal (top-down)
uvm_test (final first)
│
├── tb_env
│ ├── axi_agent
│ └── scoreboard
│
└── closes shared resources, then children finalizeNo UVM phase runs after final_phase — this is truly the end.
Do not raise objections or consume simulation time.
Idempotent close logic handles multiple final_phase calls safely.
Reference implementation
class my_env extends uvm_env;
int log_fd;
bit log_opened;
function void final_phase(uvm_phase phase);
super.final_phase(phase); // children finalize first (top-down parent calls super, children run)
if (log_opened && log_fd != 0) begin
$fwrite(log_fd, "=== simulation complete ===
");
$fclose(log_fd);
log_fd = 0;
log_opened = 0;
`uvm_info("FINAL", "closed transaction log", UVM_MEDIUM)
end
endfunction
endclass// Coverage DB flush (vendor-specific example pattern)
function void final_phase(uvm_phase phase);
super.final_phase(phase);
`uvm_info("FINAL", "flushing coverage database", UVM_MEDIUM)
// cov_db.flush(); // tool-specific API
endfunctionKey takeaways
final_phase is top-down — opposite of extract/check/report.
Close files and flush coverage DBs here, not in report_phase.
Guard close operations with handle-valid checks for robustness.
Common pitfalls
Opening new files in final_phase for primary reporting — too late.
Closing shared handles in a child before parent finishes writing.
Skipping super.final_phase and leaving child resources open.
Cleanup checklist
[PHASE][UVM] final_phase audit
□ transaction log file handles closed
□ waveform dump controls released (if TB-owned)
□ coverage database flushed / saved
□ PLI/VPI callbacks unregistered
□ no dangling fork from run_phase (should have joined before extract)