When we created the jelly_bean_driver
in Agent, we coded the build_phase
function and the run_phase
task, but who actually calls them? The answer is uvm_phase
class.
UVM Phases
UVM has nine common phase classes (shown in yellow) and twelve run-time phase classes (shown in pink). These phase classes are derived from the uvm_topdown_phase
, uvm_bottomup_phase
, or uvm_task_phase
classes, which in turn are derived from the uvm_phase
class. The uvm_phase
class has a virtual function called exec_func
and a virtual task called exec_task
. The phase classes derived from the uvm_topdown_phase
and the uvm_bottomup_phase
implement the exec_func
, while the phase classes derived from the uvm_task_phase
implement the exec_task
. As shown in the diagram, each phase class calls the corresponding phase function or task of the uvm_component
. For example, the exec_func
of the uvm_build_phase
class calls the build_phase
function of the uvm_component
, and the exec_task
of the uvm_run_phase
class calls the run_phase
task of the uvm_component
, etc.
Simplified Flow
The way UVM phases are implemented is rather complicated, but here is a simplified flow.
- We call
run_test
(if you don’t remember, see the line 17 of thetop
module in Tasting), which in turn calls therun_test
task of theuvm_root
class. - The
uvm_root
calls them_run_phases
task of theuvm_phase
class. - For each phase, the
execute_phase
task is called. - If the phase is a top-down or bottom-up phase,
exec_func
is called for each component. - For example, the
exec_func
calls thebuild_phase
function of each component. - If the phase is a task phase,
exec_task
is called for each component. - For example, the
exec_task
calls themain_phase
task of each component. - The
uvm_phase
checks if any objections are raised by the components. Thephase_done
is theuvm_objection
object that theuvm_phase
keeps track of the number of objections with. When we calledphase.raise_objection()
from inside therun_phase
of thejelly_bean_test
class (see the line 27 of thejelly_bean_test
class in Tasting),phase_done.raise_objection()
is called in theuvm_phase
under the hood. - If no objection is raised, all the processes started by the
exec_task
are killed. In other words, unless an objection is raised, the phase is immediately killed! - The steps 3 to 9 repeat until all phases are executed.
I hope this article helped in your understanding of the UVM phasing.
Hi Keisuke
Thanks for this amazing blog. I was wondering that when you pass the argument (uvm_phase phase) during a phase call is it that you are passing a singleton instance of phase to synchronize all the various components across the testbench? I am just trying to understand the need for the phase handle as an argument while calling any of the phase methods. Thanks again. This blog is very helpful.
Regards
Dhaval
Hi Dhaval,
Thank you for your comment. Typical usage of the (singleton)
phase
argument is to raise/drop objection. Please see therun_phase
task of thejelly_bean_test
class in Tasting, for example. Phase synchronization is done in theuvm_phase
class, not in theuvm_component
.Hi Keisuke,
Can I know that final phase is top down or bottom up and can you please give the reason also why it is so?
The
final_phase
is a top-down phase. The reason is explained well here.Hi Shimizu,
My task is to use the VIP to verify the DUT. I have legacy tests that are written with run_phase. However, the VIP components have reset_phase and main_phase. I am confused as how to synchronize them to work properly. I’ll be gals if you can help me with this!!
The
run_phase
and the run-time phases (such as themain_phase
) are running in parallel. They are synchronized at theextract_phase
only. You can think as if your VIP has therun_phase
which is broken down into smaller tasks (thereset_phase
and themain_phase
). Most of the time, you would have no problem with mixing therun_phase
and themain_phase
.If utilizing the config_/reset_/main_phase’s as opposed to just managing all in the run_phase, is there a built-in event that the run_phase can use to know when in each phase? I dont think my brief review of the code shows any (although, UVM does know when its in each sub-phase).
I know I can create an event as needed if there is a need for the run_phase to know. Example: run_phase does some things across all phases in the background, but would like to do something *after* the reset_phase. The reset_phase can evt.trigger() and the run_phase can wait for that at some point.
I understand some schools of thought are to only use the run_phase, and to just use separate sequences (config, reset, etc). Anyway, want to know if its bad form to create a uvm_event and trigger it in the reset_phase and ‘wait’ for it somewhere in the run_phase.
As far as I know, there is no built-in event that is triggered at the end of each phase. I think using the
uvm_event
is a proper workaround.Hi Keisuke,
I am a beginner in the ASIC verification field. Thank you very much for this blog for UVM phases. Can I know why the build phase is executed in top-down manner while other phases are executed in down-top manner?
Regards,
Vishal
The
build_phase
has to be top-down because the parent component creates the child components (not the other way around). It also allows the parent component to configure the child components. On the other hand, theconnect_phase
, for example, is bottom-up. It allows the parent component to verify that the child components are connected properly.Hi Keisuke,
We already have the components created during build phase. So would it matter if it was top-down or bottom-up for connect_phase.
For example: The connection between the sequencer and driver is done in the agent, and normally this connection would be present in the connect_phase. What would happen in this scenario if connect phase was top-down?
Thanks,
Subash
One benefit of the
connect_phase
being bottom-up is that the upper component can verify the connection of the child components.Hi,
Once a phase of a particular component completes(say main_phase), will it wait for that phase of all the components to complete, to go to the next phase? Assuming I don’t raise and drop objection in that phase in any of the components. Could u also tell me how mixing run_phase with its sub phases work? For eg, I have run_phase in one component and main_phase in another. Which one will run first?
run_phase
and the run-time sub-phases (reset_phase
,main_phase
, etc.) are executed in parallel. They are synchronized at theextract_phase
at the end.Hi Keisuke,
Are the run_phase and run-time sub-phases in some ways equivalent?
I mean, if I want to control run time simulation roughly just use run_phase, otherwise I can use run-time sub-phases to tune.
Another question is can you give an example to start other run-time sub-phases?
Eg, if I want to add some control in uvm_configure_phase, can I add following code in a uvm component?
task configure_phase( uvm_phase phase );
phase.raise_objection( .obj( this ) );
/*
specific code needed
*/
phase.drop_objection( .obj( this ) );
endtask
Thanks in advance!
Best Regards,
Rui
Your understanding is correct for both of the questions.
run_phase
.configure_phase
, it will be used and synchronized with theconfigure_phase
of other components (other components will wait until yourconfigure_phase
finishes).Thanks Keisuke for your answer!
Hi Keisuke,
Can you explain use of final phase and why it is top down ?
Please see this comment.
Hi Keisuke san,
Thanks for my article. I can understand why the driver with forever loop can be terminate m_phase_proc.kill, because it did not raise objection. Is the sequencer associated with that driver to raise objection to keep it loop running? Is it true that when all the sequences are finished, the sequencer will drop the objection and we can reach the m_phase_proc.kill? Many thanks.
Correction. It is supposed to be “Thanks for your article to help to understand…” It is a terrible typo. I have learned a lot from you, I have followed your articles for a year. Thanks so much for your work.
Thanks for the comment. The sequencer does not automatically raise/drop the objection. You have to manually do it if you want to.
Hi Keisuke,
Thank you for the pictures and the post.
However, I am still not clear on what the difference between “new”, “create” and “build” is.
What happens when a “new” is executed? is it only memory allocation for the object that is “yet” to be created or is the object actually created at this step as well? When is the “new” command executed?
What happens in a “build” phase? The parent class creates the object and “remembers” the object handle for all further references to that object or something else?
What happens when “create” is executed?
Thank you.
Shamanth Huddar
Let’s look at the
jelly_bean_agent
in Agent as an example.new
function creates an object ofjelly_bean_agent
. It also creates the handles foruvm_analysis_port
,jelly_bean_sequencer
,jelly_bean_driver
, andjelly_bean_monitor
. However, these handles arenull
because we have not created the objects for them yet.build_phase
function creates the components mentioned above. We callednew
to create an object ofuvm_analysis_port
. However, we calledcreate
to create the other components in case we want to override the component type later.create
function asks the UVM factory to create an object. The UVM factory knows which component to create even if the component type is overridden. The factory (or to be precise,uvm_component_registry
) will callnew
on behalf of you.Hi Keisuke,
Thank you for the reply. I think I understand the phasing much better now.
Here’s my understanding, could you please confirm if it’s correct:
1. build_phase is top-down:
You must create the top-level object first to further create the objects inside it. That’s why build_phase is top-down.
First, “new” of the top object, say “env”, creates the actual object of type “env” + only handles to all objects (not the objects themselves) that were declared in the same class.
Second, “build_phase” function of the top level object, i.e. “env”, is executed. This means “creating” the actual “objects” for the above mentioned “handles” – Build phase of a class literally builds the object by creating it’s low-level objects using the “new” of those low-level objects.
Third, “build_phase” function of the low-level objects is executed, ex: agent, and the above steps repeat for the agent and it’s low-level components like driver and monitor.
This way all OBJECTS and their respective handles in the top-level objects, are created and the build_phase ends and is followed by connect_phase
2. connect_phase is bottoms-up:
Now that all objects have been created and their handles present in the upper levels, the lower level objects make the necessary connections , i.e. agent will connect the 1) driver’s seq_item_port with that of the sequencer’s and 2) monitor’s analysis port with the one in the agent itself.
UVM then moves to complete the connect_phase of the upper level component – env, and pulls in the agent’s analysis port for further connection.
Thank you once again.
Your understanding is perfect!
Thank you!
I am not sure about how “third” is initiated.
UVM is just a methodology. It is supposed to behave like a regular System Verilog code.
What starts this:
“Third, “build_phase” function of the low-level objects is executed, ex: agent, and the above steps repeat for the agent and it’s low-level components like driver and monitor.”?
“new” or “create” doesn’t start a build_phase.
Component’s
build_phase
callsnew
orcreate
to create its child(ren), not the other way around. Theuvm_phase
callsbuild_phase
recursively starting from the component you specified inrun_test
task or by a command-line plusarg+UVM_TESTNAME
.Hi,
In create method , what is type Id ??
Inside Candy Factory explains the
type_id
.Hi,
Thanks for the wonderful article.
I have a doubt. I believe run_phase is bottom_up and time consuming (because it is a task).
If that is the case, the run_phase of which bottom most component will get executed? Will the parent component run_phase wait till the child component run_phase gets over? Or the run_phase of all components starts in parallel?
Each component’s
run_phase
is executed in a separate process. The parent component does not wait for its child processes to finish.Hi Keisuke,
I have a few questions about phasing,
1) Dose run phase of all components run in parallel? I feel it need to run in parallel.
2) Dose m_phase_proc.kill() wait until any single objection is pending? or dose it kill the tasks of the components that did not raise objection
and wait for the components who have raised objection to drop theirs before killing the task?
3) Monitor’s run_phase task as I see is in forever loop. If in my top testbench module, say I only need to observe and I just instantiate monitors in this top module,
do I need to raise objection in the monitor’s run_phase() task? I don’t think this would work as because of forever loop, objection would never be dropped.
How then I can ensure that monitor continues to observe through out simulation?
4) How dose uvm_phase find the components that are instantiated?
Regards,
Gautam
run_phase
is executed in a separate process.uvm_phase
traverses all the child components starting from theuvm_top
. Note that any component whose parent is specified asnull
becomes a child of theuvm_top
.thanks for the great article.I have a doubt can we add delay elements in the final phase if so how?
The
final_phase
is afunction
, so you cannot use delay elements. You can use them in theshutdown_phase
, which is atask
, though.