UVM
start_of_simulation_phase
The last zero-time phase before the run — announcing the run with banners and the final resolved configuration snapshot, and why config is only authoritative here.
UVM Phasing · Module 5 · Page 5.5
The Engineering Problem
The structure is built, wired, and validated. start_of_simulation_phase is the very last zero-time phase before the run — the final breath before time advances. It runs after end_of_elaboration_phase, and like every phase before the run, it's a zero-time function: no waiting, no driving. So a fair question is: if end_of_elaboration_phase already gave us a checkpoint over the finished structure, why does UVM provide another near-identical phase right after it?
The answer is separation of intent, and one concrete, valuable property: start_of_simulation_phase is the first point at which all configuration is fully resolved. By now every component has built (setting config as it went), every connection is made, and end_of_elaboration has run — so the configuration the simulation will actually start with is settled. That makes it the authoritative place to do the things that should happen as the run begins: announce the test, print banners, and — most usefully — log the final, resolved configuration, the definitive "here is exactly what's about to run" snapshot. Conventionally, end_of_elaboration is for final structural validation and adjustment; start_of_simulation is for announcing. Misunderstanding the phase — printing config too early, where it isn't resolved — produces logs that lie about what ran. This chapter is about that last pre-run moment and why it's the right place for the announce-and-snapshot.
What is
start_of_simulation_phase, why does it exist as a separate phase afterend_of_elaboration_phase, and why is it the authoritative place to announce the run and log the final resolved configuration?
Motivation — why a separate announce phase
A dedicated phase for announcing the run, distinct from the structural checkpoint before it, earns its place:
- Configuration is fully resolved by now. Configuration is set by many components as they build (top-down) and possibly adjusted at
end_of_elaboration. Only after all of that — atstart_of_simulation_phase— is the configuration the run will start with final. So this is the first point a configuration printout is authoritative; printed earlier (in build), it may capture defaults or half-set values. - It separates "validate the structure" from "announce the run".
end_of_elaborationis conventionally where you assert the structure is correct and make any last structural adjustment;start_of_simulationis where you say "this is the test, this is the configuration, here we go." Keeping these in separate phases means a base class can validate in one while a derived test announces in the other, without colliding. - It's the natural home for banners and run-start logging. The "Starting test X" banner, the resolved config dump, a final topology print for the log — these belong as the simulation starts, which is exactly what this phase is. They produce the header of the run's log.
- It's the last hook before commitment. After
start_of_simulation_phase, the run begins and time advances. Anything that should happen "just before we run" — and that doesn't take time — has this phase as its last opportunity.
The motivation, in one line: start_of_simulation_phase exists to cleanly separate announcing the run from validating the structure, and its position — after everything is built, wired, and resolved — makes it the authoritative place to log the final configuration and print the banners that head the simulation.
Mental Model
Hold start_of_simulation_phase as the "this is your captain speaking" announcement before takeoff:
start_of_simulation_phaseis the captain's pre-takeoff announcement. The aircraft is assembled, all systems connected, and the pre-flight walk-around (end_of_elaboration) is done — everything is checked and finalised. Now, right before the engines spin up, comes the announcement: "This is flight 287 to such-and-such, today's configuration is this, cabin crew prepare for departure." It doesn't change anything — the plane is already built and checked — it states the final, settled situation to everyone, on the record, as the journey begins. Because it happens after everything is finalised, the announcement is accurate: it reflects the actual aircraft and route, not a draft. Then the engines start (the run), and you're moving.
So in a start_of_simulation_phase, you don't build, wire, or validate-and-fix (that's done) — you announce: print the test name, dump the final resolved configuration, log a banner. It's accurate precisely because it runs after everything is settled, and it's the on-the-record header of the run that follows. Validate at the walk-around (end_of_elaboration); announce at the captain's address (start_of_simulation); then take off (run).
Visual Explanation — start_of_simulation in the phase sequence
start_of_simulation_phase is the final phase of the zero-time pre-run group — after end_of_elaboration, immediately before run. Its position makes it the authoritative announce-and-snapshot moment.
The position is the purpose. By the time start_of_simulation_phase runs, the entire zero-time pre-run sequence is essentially complete: the tree is built, the connections made, the structure validated at end_of_elaboration, and — crucially — every piece of configuration that components set during their builds (and any adjustment at elaboration) is now resolved. So this phase sits at the one point where the configuration is both final and not yet changed by the run — the authoritative snapshot of "what the simulation will start with." That's why it's the conventional home for the announce-and-log work: print the test name and a banner so the log has a clear header, and dump the resolved configuration so the run's record states exactly what it ran with. Then run_phase begins, time advances, and the announced setup is in effect. It is, by design, the last zero-time word before the run.
RTL / Simulation Perspective — announcing the run
A start_of_simulation_phase typically prints banners and the final configuration — no construction, no wiring, no validation-and-fix. It produces the run log's header from a now-settled configuration.
class my_test extends uvm_test;
`uvm_component_utils(my_test)
env_cfg cfg;
function void start_of_simulation_phase(uvm_phase phase);
super.start_of_simulation_phase(phase);
// ANNOUNCE: a banner / the test name — the run log's header
`uvm_info("RUN", $sformatf("=== Starting test '%s' ===", get_type_name()), UVM_LOW)
// SNAPSHOT: dump the FINAL, RESOLVED configuration — authoritative now (all builds done)
cfg.print();
// (optionally) print the finished topology for the log, if not done at end_of_elaboration
// uvm_top.print_topology();
endfunction
endclassThe work here is announcement, and its accuracy depends on the timing. The `uvm_info banner and get_type_name() give the log a clear "this is the test" header. The cfg.print() is the valuable part: it dumps the configuration as resolved, and it's trustworthy here because, by start_of_simulation_phase, every component has finished building (and thus finished setting configuration) — so what's printed is exactly what the run will use, not a half-set draft. Notice the absence of create, connect, or structural assertions: construction is build's job, wiring is connect's, validation is end_of_elaboration's. This phase states the final picture. Like all pre-run phases it's a zero-time function, so the announcement is instantaneous, and (as always) call super.start_of_simulation_phase(phase) to preserve base-class behaviour. The result is a clean, accurate header at the top of the run log.
Verification Perspective — end_of_elaboration vs start_of_simulation
The two adjacent pre-run phases are functionally similar (both zero-time, both after the structure is complete), and the distinction is intent. Knowing which phase is for what keeps the right work in the right place.
The two phases are like two pages in the same notebook with different headings. Mechanically they're nearly identical — both zero-time functions, both bottom-up, both running over the finished, connected structure — so UVM could have offered just one. It offers two so you can separate concerns: put "is the environment built correctly?" (validation, structural fixes) in end_of_elaboration_phase, and "what is about to run?" (announcing, config snapshot) in start_of_simulation_phase. This separation is practically useful: a reusable base test can do structural validation in end_of_elaboration, while each derived test announces its specific configuration in start_of_simulation, without the two overriding the same method and colliding. It also encodes a natural reading order in the log — first the structure is finalised, then the run is announced. You could do everything in one phase; the convention of splitting validation from announcement keeps each phase's purpose clear and composable.
Runtime / Execution Flow — configuration is resolved here
The property that makes start_of_simulation_phase the right place for the config snapshot is that configuration is fully resolved by the time it runs — set by components during their builds, adjusted at elaboration, and now final, just before the run uses it.
This resolution timeline is why where you print the configuration matters. Configuration in UVM is set incrementally: as each component builds (top-down), it may set config for its children, so during build_phase the configuration is a moving target — print it mid-build and you might capture a field before the component that sets it has run. By start_of_simulation_phase, the entire build cascade and end_of_elaboration are complete, so the configuration is final and stable — exactly what the run will start with. That makes this the authoritative point to log it: the snapshot is guaranteed to match the run's starting state. (Values that change during the run are a different matter — those you log during run_phase, because start_of_simulation only sees the pre-run state.) The lesson is that the resolved configuration exists at a specific moment in the schedule, and start_of_simulation_phase is that moment — which is why it, not an earlier phase, is where the config snapshot belongs.
Waveform Perspective — the final pre-run announcement
On a timeline, start_of_simulation_phase is the very last zero-time sliver of the pre-run sequence — the announcement, immediately before the run region where time advances and pins move.
start_of_simulation is the last zero-time announcement before the run
10 cyclesThe phase track shows SOS as the final cell of the zero-time pre-run region, right before RUN where the pins move. This placement captures the phase exactly: start_of_simulation_phase is the last static instant — everything built, wired, validated, and now announced — immediately before the run commits the environment to motion. No pin moves during the pre-run region because all of it (build, connect, end_of_elaboration, start_of_simulation) is zero-time function phases; the announcement happens in the same instant as the rest of elaboration, and then the run advances time. The waveform shows the complete pre-run sequence collapsing to time zero with SOS at its end — the on-the-record header just before behaviour begins. It's the bookend of the zero-time setup: build opens it, start_of_simulation closes it, and the run follows.
DebugLab — the config log that showed defaults
A configuration dump that didn't match the run — printed too early, before config resolved
To document each run, an engineer printed the environment's configuration to the log — but the printed values were wrong: several fields showed their default values, even though the test had clearly configured them differently and the run behaved according to the configured values. The log claimed one configuration; the simulation ran with another. Debugging from the log was actively misleading.
The configuration was printed in build_phase, before it was fully resolved. Configuration is set incrementally as components build (top-down), so at the moment this component printed, some fields hadn't been set yet:
config is set: incrementally, as components build top-down (throughout build_phase)
printed in: this component's build_phase ← BEFORE later components set their fields
result: fields set later still show DEFAULTS in the printout
→ log says 'default', run uses the real (later-set) value → mismatch
fix: print config in start_of_simulation_phase — all builds done, config RESOLVEDThe print captured a moving target: during build_phase, the configuration is still being assembled, so a printout taken mid-build reflects only what's been set so far, not the final picture. The values weren't wrong in the simulation — they were set after the (too-early) print, so only the log was wrong. Moving the print to start_of_simulation_phase, where the entire build cascade is complete and configuration is fully resolved, makes the log match the run.
The tell is a config log that disagrees with how the simulation actually behaved — almost always a snapshot taken before configuration resolved. Diagnose timing-of-snapshot bugs:
- Check where the config is printed. If it's in
build_phase, it may have been captured before later components set their fields. The authoritative point isstart_of_simulation_phase, after all builds complete. - Compare the log to the run's behaviour. If the simulation behaves per a value the log shows as default, the log was taken too early — the run used a value set after the print.
- Confirm the snapshot moment. Configuration is final at
start_of_simulation_phase; that's where a "what we ran with" dump is accurate. Earlier is a moving target.
Snapshot the configuration where it's resolved:
- Print the config in
start_of_simulation_phase. By then all builds are done and configuration is final, so the logged snapshot matches the run's starting state. This is the phase's signature use. - Don't trust mid-build snapshots. During
build_phase, configuration is still being set top-down; any printout is partial. Reserve the authoritative dump for after resolution. - Log run-time changes separately. If a config value changes during the run, the
start_of_simulationsnapshot won't show that — log such changes inrun_phase, becausestart_of_simulationonly captures the pre-run state.
The one-sentence lesson: configuration is fully resolved only by start_of_simulation_phase, so print the authoritative config snapshot there — printing in build_phase captures a half-set moving target, producing a log that misrepresents what the run actually used.
Common Mistakes
- Printing the config snapshot too early. During
build_phase, configuration is still being set top-down, so a printout is partial and may show defaults. Print the authoritative config instart_of_simulation_phase, after all builds resolve. - Doing construction, wiring, or validation here. Build constructs, connect wires, end_of_elaboration validates;
start_of_simulation_phaseis for announcing. Don't create, connect, or fix the structure in this phase. - Time-consuming work in start_of_simulation. It's a zero-time function phase; waiting or driving belongs in
run_phase. - Confusing it with end_of_elaboration. They're functionally similar but separated by intent: end_of_elaboration validates/adjusts the structure; start_of_simulation announces the run. Keep validation in the former and banners/snapshots in the latter.
- Expecting the snapshot to reflect run-time state.
start_of_simulationcaptures the pre-run configuration; values that change during the run aren't shown here — log those inrun_phase. - Forgetting
super.start_of_simulation_phase. Callsuperto preserve base-class behaviour, as with other phase overrides.
Senior Design Review Notes
Interview Insights
start_of_simulation_phase is the last zero-time phase before the run, and it's conventionally used to announce the run: print banners and the test name, and log the final, resolved configuration — the authoritative "here is what's about to run" snapshot. It runs after end_of_elaboration_phase, by which point the structure is final and, importantly, all configuration is fully resolved (every component has finished building and thus finished setting config). That makes it the right place to dump the configuration, because what you print is exactly what the run will start with, not a half-set draft. It's a zero-time function phase, so it does no construction (that's build), no wiring (that's connect), no structural validation (that's end_of_elaboration), and nothing time-consuming (that's the run) — it just states the final picture. The practical result is a clean, accurate header at the top of the run's log: the test name, a banner, and the resolved configuration, which makes each run self-documenting.
Exercises
- Write the announcement. Write a
start_of_simulation_phasefor a test that prints a banner with the test name and dumps the resolved configuration, with the correctsupercall. - Fix the log. A config dump in
build_phaseshows default values that don't match the run. State the cause in terms of configuration resolution and the phase the dump belongs in. - Split the intent. For each, name the phase (end_of_elaboration or start_of_simulation): (a) assert the scoreboard's input connection exists; (b) print the test name banner; (c) dump the final resolved config; (d) make a last structural adjustment that needs the connected tree.
- Snapshot vs run-time. Explain why a config value that changes during the run won't appear correctly in the
start_of_simulationsnapshot, and where you'd log such a change instead.
Summary
start_of_simulation_phaseis the last zero-time, bottom-up function phase beforerun_phase— the moment to announce the run, after the structure is built, wired, and validated.- Its key property: by now all configuration is fully resolved (set during the top-down build cascade, settled by
end_of_elaboration), so it's the authoritative place to log the final configuration snapshot — the definitive "what's about to run" record — plus banners and the test name for the log's header. - It is functionally similar to
end_of_elaboration_phasebut separated by intent:end_of_elaborationvalidates and adjusts the structure;start_of_simulationannounces it. The split keeps validation and announcement in distinct, composable hooks. - It does not construct, wire, validate-and-fix, or consume time — it states the final picture, instantaneously, just before the run commits the environment to motion.
- The durable rule of thumb: treat
start_of_simulation_phaseas the captain's pre-takeoff announcement — print the banner and the final resolved configuration here, where it's accurate, not inbuild_phasewhere config is still a moving target — so the run log's header matches exactly what the simulation runs.
Next — run_phase: the announcing is done; time begins. run_phase is the one time-consuming phase — where stimulus is driven, the DUT operates, monitors observe, and the whole verification actually happens. The next chapter covers the phase where simulation time lives.