Part 2 · Phases & Lifecycle · Intermediate
Function Phase Rules: Zero Time and Structural Work
Rules governing function phases — no time control, allowed actions per phase, and why structural work must finish before simulation time advances.
The zero-time contract
Function phases execute in zero simulation time . The entire build-time and cleanup strips complete before the first clock edge of run-time activity (unless you explicitly advance time inside a task phase later).
No #delay, @event, wait(), or fork inside function phases.
No blocking TLM calls that consume time.
No raise_objection/drop_objection — objections are for task phases.
[UVM][PHASE] function phase constraints
compiler/runtime enforcement:
time-consuming statements in functions -> error or undefined
methodology enforcement:
even 'quick' #1ns in build hides ordering bugs
allowed:
create, config get, connect, asserts, prints
non-blocking checks, pure functionsKey takeaways
Function phases are instantaneous scheduler barriers.
If it needs to wait for hardware, it belongs in a task phase.
Zero-time discipline is what makes build/connect ordering reliable.
Common pitfalls
Calling a task from a function phase and waiting for it.
Using @(posedge clk) in start_of_simulation to 'align' to clock.
Forking background threads in build_phase — races run_phase.
Allowed actions by function phase
build / connect / end_of_elaboration
function void build_phase(uvm_phase phase);
super.build_phase(phase);
void'(uvm_config_db#(my_cfg)::get(this, "", "cfg", cfg));
mon = my_monitor::type_id::create("mon", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
mon.ap.connect(scb.imp);
endfunction
function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
if (drv == null && cfg.is_active)
`uvm_fatal("ELAB", "active agent missing driver")
endfunctionextract / check / report / final
function void extract_phase(uvm_phase phase);
super.extract_phase(phase);
txn_count = scb.get_checked();
cov_snapshot = cov.get_inst_coverage();
endfunction
function void check_phase(uvm_phase phase);
super.check_phase(phase);
if (txn_count == 0) `uvm_error("CHK", "no activity")
if (scb.get_errors() > 0) `uvm_error("CHK", "scoreboard errors")
endfunction[PHASE] function phase forbidden list
#N delay
@(event)
wait()
fork ... join
blocking TLM put/get
drive physical pins
start sequences on sequencers
raise_objection / drop_objectionKey takeaways
Each function phase has a narrow job — respect the strip on the timeline map.
end_of_elaboration is ideal for structural fatal checks.
check_phase is for uvm_error, not long formatted reports.
Common pitfalls
Starting a sequence in connect_phase — sequencer may not be ready.
Heavy file I/O in check_phase — move harvesting to extract.
Using final_phase for functional pass/fail — too late.