An Overview of UVM End-of-Test Mechanisms

A lot traffic coming from Google to the blog is from searches about setting the UVM drain time. That's because one of my first posts was about how to set the drain time prior to going into the run phase. At the time of writing, this was the third most viewed post.


This is a companion discussion topic for the original entry at https://verificationgentleman.netlify.app/2016/03/25/an-overview-of-uvm-end-of-test-mechanisms.html

Great Article!!! Keep going.
I would wish to see article on phase.raise_objection mechanism.

Thanks
Taahir

Hi Tudor,

Raising objections in the scoreboard comes with some drawbacks.
The most obvious is that if there is a bug in the RTL in which a packet is not outputted then the test will never end.
An other thing that one has to take care of is dropping the relevant number of objections when there is a reset in the middle of the test (e.g. scoreboard not empty), or if there is some “clear FIFO” functionality in the design.

But if these little corner cases are handled I think that this is a great way to control when a test is ended.

An other place where I would raise and drop objections is in the monitor, at the beginning and at the end of an item. This will prevent stopping the test in the middle of a transfer.

Cristi

You’re right, you’re at the mercy of DUT bugs in that case. This is why it’s important to set up a global timeout (UVM already has something per default) or use the heartbeat mechanism to decide if the simulation is hanging.

Making sure you don’t stop during an item is also important. Even there, in case of performance problems with objections, you could use ‘phase_ready_to_end(…)’.

Hey Tudor,

Generally, I would recommend sending your stimulus during the main phase and have your “drain” time during the shutdown phase.

Also, using a watchdog to handle Cristi’s comment about DUT bugs is not always going to be sufficient. For one thing, some tests may just generally take longer due to random constraints selecting sub-optimal rates. In other words, you’ll find cases where 100,000ns just isn’t long enough, and then you’ll be pushing it out to 150, and later 200, and so on. That’s the tail wagging the dog.

Instead, I highly recommend taking a look at the deadlock checker in Chapter 6 of my book, Advanced UVM. This checker will cause your environment to phase jump to the check phase if a sufficient amount of time has gone by without seeing anything coming out of the device. The book also includes a lean objection mechanism for that UVM 1.1 problem you mentioned.

We’ve been using some form of this deadlock checker for many years now and it’s saved us tons of simulation time.

Cheers,
Brian

Another great post Tudor! i use a very similar mechanism in the scoreboard and i agree with the other posts as well, this only works when there is a complete DUT mess up in terms of interface protocol.
It might be an idea for a subsequent post…

At my previous job I actually did some bench-marking with Mentor’s own simulator testing various objection scenarios. Switching from from per-test objections to per-transaction objections lead to only a few percentage points increase in run time. If you want to speed up your simulations there are probably bigger fish to fry…

alternatively, you can raise the objection in write north and drop it immediately in same function. set an drain time for that objection. Do same in write_south. set drain time as worst case transaction delay.

Now what will happen, after drop_objection, before decrementing objection count it will wait. In between if same objection comes again timer will reset.

keep a check for no_of_packet in report phase. If none of the packet is received, it will error out at end but test won’t hang.

You won’t need a heartbeat also, this will effectively serve as heartbeat.

Hi, Could please let me know how ARM based mobile SoC booting process works in terms of Hardware perspective to verification engineer. Thanks.

How can delay_phase which is task called from function void phase_ready_to_end

‘delay_phase_phase()’ is started in parallel. It’s not called within ‘phase_ready_to_end()’, it’s scheduled to start once the currently running process (which includes ‘phase_ready_to_end()’ and any other functions that are called after) gets blocked by a waiting statement.

Its an interesting article to read, but i have a few doubts about the scalability of this scoreboard when we wish to use it at a higher level ( IP level to subModule and maybe Soc). Is the assumption here been made that this is the highest level of scoreboard present and this scoreboard will be the deciding component when we move it to another env ?
If that has not been considered what happens if the test in the bigger or another env doesnt want to end on a condition met by and monitored by this scoreboard component. Is it not wise to keep the control in test writers hand to monitor multiple components done mechanism using some global variable or object or some event wait mechanism and then conclude when to end ? Would this approach not make it less reusable? Isnt it a good practice to control from the test case ?
Ofcourse one way would be to have every component decide for itself but will that not obstruct the overall cause to keep all the components alive based on some condition which satisfies the higher purpose as i think once a component drops the objection its no longer doing anything in that simulation unless there is way to bring it back to life without adding more complex phase jumping mechanism that i can think of.

Would like to know the opinion of many intelligent and smart people out there.
I seek to learn. This debate of how to and where to drop the final objection to end the simulation gracefully haunts me.

Hi Tudor ,
I have two threads -
One running in configure phase , and one running in run_phase.
Due to some events, objections are raised in run phase thread,while configure phase is running
These objections are of configure phase. Will configure phase wait for these objections to drop to finish its phase

If I get you correctly, in run_phase(…) you’re calling something like configure_phase.raise_objection(…). The configure phase should wait in these objections to drop, meaning that post_configure, pre_main, main and so on will be stalled.

In run_phase, something like phase.raise_objection(this) , when configure phase is running in parallel.

Hey,
as far as i understand the following
run_phase = uvm_run_phase::get();
will return a STATIC run_phase object rather then the actual run_phase object of the specific
component (scoreboard in this case) . I think this solution has a pitfalls especially when there are several uvm domains in the env…
what do u think?

phase ready to end gets called again when itself ends, solved my problem. In my case, I did not apply guard to phase ready to end and it got called 20 times at end of run phase, then test proceeded without fatal error, I was wondering why it was called 20 times

Hi Tudor,

I tried phase_ready_to_end method to read some AHB registers at the end of my test run phase. Since drop_objection is done at the end of the task forked out inside this function, it is triggering the function phase_ready_to_end another 18 times. I can make the register reads happen only once by adding an extra variable which is set after the first call, but is there a way to stop executing the function all together after first call?
I’m using a pretty old UVM version (1.1).

This could be an issue. I don’t have any experience with multiple domains, unfortunately.

I don’t know what the exact architecture is. Is there one run phase per domain? If this is the case, there should be some way of getting the phase object for that component.