Jump to content

uvm phasing


Recommended Posts

I am trying to understand the new phasing mechanism of uvm. But this seems to be unusally big and complicated stuff. For example, even the simplest of hello world test programs not using the new phasing mechanism directly is still creating 48 uvm_phase objects and 47 uvm_objection objects, and the command line is parsed 48 times to check whether phase tracing should be enabled or not, and another 48 times to check whether or not to use ovm run semantics, even if both settings are shared by all phases.

Can anyone explain the rationale behind this, i.e what problem the new phasing is solving, perhaps with a simple example demonstrating how and why the new phasing is an improvement over ovm phasing.

Regards,

Erling

Link to comment
Share on other sites

Hi Erling,

UVM phasing is not rocket science. It just divides the OVM run phase into a few sub phases. The import ones are reset, configure, main and shutdown. These subphases run in parallel to the run_phase. Logically, we have done something like this:

fork
  run_phase();
  begin
    reset_phase();
    configure_phase();
    main_phase();
    shutdown_phase();
  end
join

So in your test you might want to do things like this:

class my_test extends uvm_test;
  // missed a lot of stuff out ...
  task reset_phase(uvm_phase phase);
    phase.raise_objection(this, "my_test reset_phase");
    //Reset the DUT
    reset_seq_h.start(ahb_seqr_h);
    phase.drop_objection(this, "my_test reset_phase");
  endtask : reset_phase
 
  task configure_phase(uvm_phase phase);
    phase.raise_objection(this, "my_test configure_phase");
    //Configure the DUT
    fork
      config_buffer_seq_h.start(ahb_seqr_h);
      config_usb_seq_h.start(ahb_seqr_h);
      config_eth_seq_h.start(ahb_seqr_h);
    join
    phase.drop_objection(this, "my_test configure_phase");
  endtask : configure_phase
 
  task main_phase(uvm_phase phase);
    phase.raise_objection(this, "my_test main_phase");
    //Send Random Data
    fork
      random_usb_seq_h.start(usb_seqr_h);
      random_eth_seq_h.start(eth_seqr_h);
    join
    phase.drop_objection(this, "my_test main_phase");
  endtask : main_phase
 
  task shutdown_phase(uvm_phase phase);
    phase.raise_objection(this, "my_test shutdown_phase");
    //Read some performance registers in the DUT
    extract_perf_nums_seq_h.start(ahb_seqr_h);
    phase.drop_objection(this, "my_test shutdown_phase");
  endtask : reset_phase
endclass

The raising and dropping of objections is there to make sure that multiple components all agree when to move on to the next sub phase.

Transactors ( ie, drivers and monitors ) should continue to use the run_phase as they do in OVM, whatever you decide to do in the test.

This kind of technique is by no means compulsory. You can continue to use the run phase as you do at the moment in OVM.

There is a lot of complicated stuff in the implementation of uvm phasing, and there is also a lot of debate about it in Accellera. But the basic idea is pretty simple. It just provides a mechanism for chopping the run_phase into sub phases.

Link to comment
Share on other sites

hi,

please note that even if you are not aware to use the new phasing its present and executing. it executes the old style run_phase as well as the other 12 fine granular runtime phases. in addition to that there are advanced concepts such as domains/schedules which are from uvm_phase derived objects.

objections are being used for distributed control of phase progression so every phase does have a end_of_* objection.

>Can anyone explain the rationale behind this, i.e what problem the new phasing is solving, perhaps with a simple example demonstrating how and why the new phasing is an improvement over ovm phasing.

for the initial point of view not much has changed apart that you now have 12 fine granular runtime phases running in addition to the old ovm run_phase. however the phasing has been made extensible (can have own phases, schedules, domains) and there is the capability to jump forward/backward from/to phases. this has lots of implications and the phasing commitee is working to establish a methodology/recommendation howto use the phasing safely to accomplish things like reset-during-runtime.

>Can anyone explain the rationale behind this, i.e what problem the new phasing is solving,

the main problem phasing is addressing is "coordination of distributed components/stimulus; providing a known "grid" from the tb"

regards

/uwe

Link to comment
Share on other sites

I understand that this is about dividing the run_phase into sub-phases, but doesn't that belong in user-land? Also, it seems the my_test class does not really need sub-phases, everything is serialized there. Thus it would seem that if this shall have meaning, then there has to be a need for multiple reset, configure, etc stages in parallel, but this will soon depend on the actual setup, and one should think it is enough primitives already in uvm/sv to implement this as needed without uvm making a failed moon landing out it.

Anyway, why is it necessary for a phase to raise an objection to avoid being terminated? It is as if the main-function in C had to start with a dont_kill_me() statement in order to have hello, world actually show up on the terminal. Why can't a phase simply signal "done" by returning to the caller?

Erling

Link to comment
Share on other sites

Erling,

You should join the Accellera phasing committee if your company is an Accellera member. Input along these lines would be very useful.

Yes, of course, you don't have to use the UVM phasing mechanism to coordinate phases. You can simply do it yourself, as people have been doing in OVM for a long time. You are also quite correct in pointing out that all the objection stuff is a waste of time if there is only one component raising and dropping objections. The value comes if two conditions hold : you want to use a standardized mechanism for doing this kind of thing, and you have multiple components participating in the phasing scheme. Maybe you have multliple tests running in parallel, maybe you have a phase aware scoreboard, or something like that.

And, as Uwe rightly points out, there are a number of features such as multiple domains, reset handling and phase jumping which are under intensive discussion in Accellera at the moment. If we can provide safe, reliable and standardized ways of using these features then we will have a standardized way of dealing with things like multiple reset and power domains in UVM.

But I repeat - apart from some minor changes, you can carry on using a single run phase and doing everything else in "user-land", and everything will work just fine, as it has for a long time in both OVM and UVM.

Adam.

Link to comment
Share on other sites

But I repeat - apart from some minor changes, you can carry on using a single run phase and doing everything else in "user-land", and everything will work just fine, as it has for a long time in both OVM and UVM.

A problem is that even if you stick to the old phasing, everything inside is based on the new phasing. And when you run into trouble, it happens that it is necessary to step inside uvm to figure out what's going on. That is a bumpy ride with the new phasing.

I guess the frustration is in part due to my problems with objections, I always felt it was something wrong with them, and now when they have invaded the phasing, I suddenly had 47 of them when I already had more than enough with the uvm_test_done_objection alone. Oh well.

Anyway, thank you for all replies.

Erling

Link to comment
Share on other sites

hi,

as adam said: if you stick to the run_phase and object to the end of run_phase instead of test_done objection everything works like before. the new phases provide you a !standardized! grid where you can hook your functionality in. this is especially useful if you integrate code/stimulus from different vendors/sites/origin. with fine granular phases comes that you need a mechanism to determine when its time to move on to the next phase (think you can have traffic crossing phase boundaries, sometimes you want all traffic to complete in a phase, sometimes you dont care about phase boundaries). the objections (sometimes also known as concensus) ARE a simple distributed mechanism to make that "can-i-move-forward" decision.

trouble or issues should only occur in a few scenarios right now:

- jumps

- incorrect use of objections - phases terminating too early, sequences/phases not starting, phases never ending (Including simulation timeout)

- bad assumptions on thread persistance over phase boundaries

i agree that some behavior of the library is not obvious and may lead to unexpected behaviour. right now the phasing committee tries to work out the route going forward to address the problems.

>I guess the frustration is in part due to my problems with objections, I always felt it was something wrong with them, and now when they have invaded the phasing, I suddenly had 47 of them when I already had more than enough with the uvm_test_done_objection alone. Oh well.

can you explain what is so hard with objections? typically you dont have to talk to 47? usually you only see them in only in a base sequence making sure the sequence is completed within a phase.

/uwe

Edited by uwes
Link to comment
Share on other sites

can you explain what is so hard with objections? typically you dont have to talk to 47? usually you only see them in only in a base sequence making sure the sequence is completed within a phase.

/uwe

In the beginning, it was the expensive, complicated and broken drop propagation. Also, keeping track of the objection count is a manual procedure, i.e the user has to make sure that raise and drop calls are balanced. It seems the only reasonable way to do this without corrupting the objection count goes something like this:

task whatever_phase(uvm_phase phase);

phase.phase_done.raise_objection();

work_not_touching_phase_done();

phase.phase_done.drop_objection();

end

But then it seems the raise and drop calls aren't actually needed, but have to be there because it has been decided that phases are to be terminated by default. Why is that? Had phases been allowed to run to completion, then the phase done objection would not be needed. Or have I missed something obvious?

Erling

Link to comment
Share on other sites

Erling,

Not all phase methods will necessarily object to the phase ending. You might deliberately write code like this:

task whatever_phase( uvm_phase phase );
  forever begin
    // do something
  end
endtask

which did not object to the phase ending.

Or you might do something like this:

task whatever_phase( uvm_phase phase );
  phase.raise_objection( this , "doing compulsory stuff" );
  compulsory_stuff();
  phase.drop_objection( this , "done compulsory stuff");
  do_some_other_stuff();
endtask

Or you might do something like this:

task whatever_phase( uvm_phase phase );
  forever begin
    wait_for_stuff_to_that_needs_doing();
    phase.raise_objection( this , "doing stuff" );
    do_stuff();
    phase.drop_objection( this , "done stuff" );
  end
endtask

Of course these variants all depend on at least the possibility of there being multiple processes executing in this phase.

Link to comment
Share on other sites

hi,

>In the beginning, it was the expensive, complicated and broken drop propagation. Also, keeping track of the objection count is a manual procedure, i.e the user has to make sure that raise and drop calls are balanced. It seems the only reasonable way to do this >without corrupting the objection count goes something like this:

i dont think objection are really hard to use here. you simply raise your objection and you drop it from some other place. really the ONLY rule is they have to be balanced and this is the case in most code. raise/drop simply encapsulate regions where you want to make sure noone ends without you agreeing to it. raising 3 and dropping 5 is a bad design to me.

>But then it seems the raise and drop calls aren't actually needed, but have to be there because it has been decided that phases are to be terminated by default.

raise and drops encapsulate the regions where you would like to have NO phase progression. btw phases end their threads may stop or not, they may be terminated or not.

>Had phases been allowed to run to completion, then the phase done objection would not be needed. Or have I missed something obvious?

if phases would run till completion, how would you then describe

- behaviour spawning multiple phases?

- how would you make the decision when to progress?

- when all threads are done? how would you handle spawned background threads

- what does happen to the spawned threads (are they killed? do they continue? (how do i specify that i agree with my threads ending without having ending them myself)

the decision made during the design of uvm was to decouple the task/thread termination from the phase progression. only this separation enabled all the different use models and not just a particular one. saying that means that there are use models which would NOT require objections/phases etc but they are not the generic use models.

/uwe

Link to comment
Share on other sites

hi,

if phases would run till completion, how would you then describe

- behaviour spawning multiple phases?

- how would you make the decision when to progress?

- when all threads are done? how would you handle spawned background threads

- what does happen to the spawned threads (are they killed? do they continue? (how do i specify that i agree with my threads ending without having ending them myself)

/uwe

I understand that these issues have to be considered carefully, but I still do not understand what objections have to do with this. They add confusion, are prone to errors and turn things upside down, in my opinion. Consider an alternative without objections (at least in user-land), first a regular phase body:

task run_phase(uvm_phase phase);

just_run_to_completion_no_problem();

end

A backgrounder subject to termination, on the other hand, can spell that out, for example:

task run_phase(uvm_phase phase);

phase.progress_ok_for(this); // optional second parameter bit yes=1

forever whatever();

end

Observations:

- no need to understand nor use objections.

- regular phase bodies requires no encapsulation.

- backgrounders allows abortive termination explicitly.

- the code does what it says, for example:

forever whatever();

alone will do just that without abortive termination, because nothing has been said in the code to indicate otherwise.

Erling

Link to comment
Share on other sites

- how would you make the decision when to progress?

One thing that could help writing basic testbenches and also get started with uvm without having to jump directly into phase progress, objections and so, is to have uvm_test come with a different phase progress policy, i.e it could come pre-configured for progress on return in all task phases. That way, it wouldn't need to raise anything to stay alive, and could still drop phase_done at any time to become killable if needed.

Erling

Link to comment
Share on other sites

hi,

>task run_phase(uvm_phase phase);

>phase.progress_ok_for(this); // optional second parameter bit yes=1

>forever whatever();

>end

this doesnt work because it ties a phase to an object. BUT what you really want is to tie a thread to a phase ending (sure you can call "phase.progress_ok_..(threadID)").

>- no need to understand nor use objections.

>- regular phase bodies requires no encapsulation.

again, any default we choose is probably wrong for some people. IF UVM would raise/drop the objections by default - THEN the question is for the others - "why the hell do i need to drop?, how many?, why do i need to drop before i eventually raise again?". i think it would be bad design to spread raise/drop between library/user code. all of that should be in one place.

>- backgrounders allows abortive termination explicitly.

just to be explicit: ANY phase can be aborted even with objections raised. this happens during a "jump". you cannot object a "jump" - you can only object the normal ending of a phase.

Link to comment
Share on other sites

hi,

>One thing that could help writing basic testbenches and also get started with uvm without having to jump directly into phase progress, objections and so, is to have uvm_test come with a different phase progress policy, >i.e it could come pre-configured for progress on return in all task phases.

This is also dangerous because then the semantic of a sequence would depend where it is running. when running as phase sequence it would need to object to prevent killing, as "run" sequence it would not need to raise objections. if users now retarget a sequence from "run" to the phase their sequence would stop instantly ....

i think the more exceptions we make, the more special cases we invent the harder it gets. there should be no semantic differences in facilities which could lead to confusion. i do think that objections/barriers or what else does the same job are the right thing to use in this case. i think it is reasonable to factor out the raise/drop into an uvm_objecting_sequence so users can use that one instead of the base version and they get everything the way you want.

That way, it wouldn't need to raise anything to stay alive, and could still drop phase_done at any time to become killable if needed.

Link to comment
Share on other sites

hi,

This is also dangerous because then the semantic of a sequence would depend where it is running. when running as phase sequence it would need to object to prevent killing, as "run" sequence it would not need to raise objections. if users now retarget a sequence from "run" to the phase their sequence would stop instantly ....

It seems to me the semantic problem isn't due to the phase progress policy of components, but the starting_phase property of sequences. If the starting_phase property did not exist, then what fundamental problem would exist in uvm?

Erling

Link to comment
Share on other sites

hi,

if you do not know your starting_phase you do not know which phase ending you should be objecting to. keep in mind there is a set of active phases at any point of time (for instance run, post_reset but it could be more). so basically ONLY when someone calls your object and provides a pointer you know the correct phase. especially when time moves forward you have no real chance to identify your phase. due to the phasing graph there is nothing like get_current_phase().raise_objection();

on the other side starting_phase is only required if that sequence wants to object a phase ending - so typically only root sequences need it.

/uwe

Link to comment
Share on other sites

hi,

on the other side starting_phase is only required if that sequence wants to object a phase ending - so typically only root sequences need it.

/uwe

A root sequence is always started from some task phase, and that phase could easily protect the root sequence, and then sequences wouldn't need to know anything about phases, and could therefore have an invariant that is easier to understand.

However, a major problem with objections is the atypical case where you want to disable task progress temporarily outside the root sequence. This can't work in a reasonable way with objections. They are potentially slow, and incredibly so. What's needed is fast progress control with a generic interface, for example:

task run_phase(uvm_phase phase)

...

phase.lock(this, "for some reason");

// code not to be aborted here

phase.unlock(this, "ditto");

...

This could be implemented by means of objections, but that would have been an implementation detail, and could be replaced with something else without breaking user code.

Instead, we are stuck with a marriage between phases and objections exposing the underlaying mechanism. It is even allowed and necessary to peek into the phase to use the objection directly, otherwise it wouldn't be possible to prolong the test, etc.

My conclusion for now is that uvm does not have proper phase/test end support. This has simply been left as an exercise for the user.

Erling

Link to comment
Share on other sites

hi,

>A root sequence is always started from some task phase, and that phase could easily protect the root sequence, and then sequences wouldn't need to know anything about phases, and could therefore have an >invariant that is easier to understand.

this is wrong. you can start a root sequence in any context at any time (you can do that from a non-uvm module, you can start a root sequence without knowing (or referencing a phase), virtual sequences actually dont need a context/sequencer to run).

>However, a major problem with objections is the atypical case where you want to disable task progress temporarily outside the root sequence. This can't work in a reasonable way with objections.

strange, either its a "major issue" OR an "atypical" use case. it cant be both at the same time

>phase.lock(this, "for some reason");

frankly, where is this different to "phase.raise_objection(this)" ?

>It is even allowed and necessary to peek into the phase to use the objection directly, otherwise it wouldn't be possible to prolong the test, etc.

i dont understand what you mean you talk directly to the objection in the phase object you are getting. i mean if you you want the objection api exposed in the phase and hide the inner objection - fine.

>My conclusion for now is that uvm does not have proper phase/test end support. This has simply been left as an exercise for the user.

i disagree with that statement.

- end of test works fine given you do use the provided objection capabilities. so far placing them in the root sequence has been sufficient. extending that model to a per transaction objection interaction is something which has to be evaluated.

- phases support: as i wrote earlier the phases itself are ok however what seems very unclear to alot of people is howto interact with the phasing. that needs cleanup.

and again if you want to directly influence the directions and wnat to give feedback it would be great to join the viptsc /phasing subcommittee where these questions are discussed on a weekly schedule.

regards

/uwe

Link to comment
Share on other sites

this is wrong. you can start a root sequence in any context at any time (you can do that from a non-uvm module, you can start a root sequence without knowing (or referencing a phase), virtual sequences actually dont need a context/sequencer to run).

Not sure what you mean. A root sequence will find a non-null starting_phase. What does is mean, having a non-null starting_phase, and still being started in any context or even without knowing?

strange, either its a "major issue" OR an "atypical" use case. it cant be both at the same time

The major issue is that the phase/test end support does not scale. What's there works for root sequence protection, but not for repeated explicit protection. Please singlestep through an objection raise/drop pair. Is this something you would want to happen frequently in your testbench?

>phase.lock(this, "for some reason");

frankly, where is this different to "phase.raise_objection(this)" ?

It is different in that it does not expose the underlaying mechanimsm. It is shorter and clearer too.

Erling

Link to comment
Share on other sites

Of course these variants all depend on at least the possibility of there being multiple processes executing in this phase.

Yes, and this seems to indicate that it could help having one component act as a phase progress controller. I have a solution now in that direction which I am happy with so far. It turns objections into an implementation detail, with a single objection raise/drop pair behind the scenes for each task phase no matter how many explicitly protected sections there are in the test environment, making objection overhead negligible. Anyone interested in one way to do this without departure from recommended practice and with trivial revert if this idea isn't a good one after all, just send a private message.

Erling

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...