Sunday, 23 July 2017

Advantage of uvm_event over SV event and how to disable uvm_event using callback

The uvm_event#(type T = uvm_object) class is an extension of the abstract uvm_event_base class. The optional parameter T allows the user to define a data type which can be passed during an event trigger. uvm_event class is an abstract wrapper class around SystemVerilog event construct.

It provides additional services such as over traditional SystemVerilog event like,
1) pass data when event is triggered,
A traditional Systemverilog event does not have functionality to pass data when event is triggered. While uvm_event adds this functionality. So, you can pass the transaction class handle when some event is triggered.
By calling trigger task we can trigger the event and optionally give the data which we want to pass using uvm_event.
By calling wait_trigger()/wait_ptrigger() task of uvm_event we can wait for event to be trigger and then by calling get_trigger_data() function we can get data.
or we can directly use only one task wait_trigger_data()/wait_ptrigger_data() of uvm_event to wait for event to be triggered and to get the data.

2) setting callbacks,
We can also add callbacks whenever an event is triggered. This is done by registering a callback class with particular event.

3) maintaining the number of waiters,
We can get the number of processes waiting on the event (using get_num_waiters() function).

4) maintaining the time when event was triggered,
We can get the time that this event was last triggered (using get_trigger_time() function)

Like SystemVerilog event has trigger (@event) and persistent trigger (wait(event.triggered)) mode, uvm_event also has trigger (wait_trigger task) and persistent trigger (wait_ptrigger task).

Let's go through below example and see how we can transfer data using uvm_event and how we can disable uvm_event from triggering using callbacks of uvm_event.


As shown in above code, one uvm_event named PKT_TX_CMPLT_EV is taken in driver.
In build phase of driver we get global handle of event pool using static method get_event_pool of uvm_event_pool class.
Then PKT_TX_CMPLT_EV is added into associative array of uvm_event_pool using get/add method of uvm_event_pool. Note that here PKT_TX_CMPLT_EV event is added in associative array of uvm_event_pool using key (in string format) DRV_EVENT.
In run phase of driver when stimulus is driven, trigger method of uvm_event is called and transaction class is passed in argument of trigger method.

uvm_event also provides facility of callback when event is triggered.
In code my_event_callback (callback for uvm_event) class which extended from uvm_event_callback.
uvm_event_callback provides two hookups, 1) pre_trigger, 2) post_trigger.

Called just before triggering the associated event. If this function returns 1, then the event will not trigger and the post-trigger callback is not called.

Called after triggering the associated event.

As shown in above code, in sequence, event callback is registered with associated event using add_callback method of uvm_event and also deleted using delete_callback method of uvm_event


Through uvm_event we can pass data(transaction class) when event is triggered, then why do we need TLM/Analysis ports in UVM?
If event is triggered again before receiver gets the data then data will be overwritten.

Reset Testing using Phase Jump in UVM

Reset testing is a crucial element of functional sign-off for any chip. The architectural components of the entire verification environment need to be correctly synchronized to be made aware of the reset condition. Scoreboards, drivers and monitors need to be tidied up, and the complex stimulus generation needs to be killed gracefully.

As we know, in UVM, there are twelve phases parallel to run_phase:
  1. pre_reset_phase(), reset_phase(), post_reset_phase(): Phases involved in reset activity.
  2. pre_configure_phase(), configure_phase(), post_configure_phase(): Phases involved in configuring DUT.
  3. pre_main_phase(), main_phase(), post_main_phase(): Phases involved in driving main stimulus to the DUT.
  4. pre_shutdown_phase(), shutdown_phase and post_shutdown_phase(): Phases involved in settling down the DUT after driving main stimulus.

Using these phases instead of using only run_phase, we can achieve synchronization between all components of verification environment also easily test reset functionality.

In reset testing, user drives random sequence to the DUT and in between data transmission, reset is applied followed by driving restart sequence. We will see how the reset functionality could be easily tested using phases parallel to run_phase and phase jump feature of UVM.

Let’s go through complete example to understand how it is achieved using UVM phases and Phase jump feature.
As shown in above code,
Driver is waiting for Reset to be asserted (in reset_phase) by raising objection and then perform action which user want on assertion of Reset signal and at last drop the objection and move to post_reset_phase. In post_reset_phase driver is waiting for Reset to de-assert and then move to main_phase. In main_phase driver drives stimulus on interface and in parallel to that wait for indication from agent about assertion of reset.

Components such as monitors that attach to signaling interfaces should be designed to be phase independent because they are intended to mimic other real devices in the system. These components should watch the reset signal associated with their interface and reset themselves accordingly.

You may find that the driver, the sequencer, and their currently running sequences will squawk with errors if they are not synchronized properly. UVM requires that the sequencer first stop its sequences and then the driver must be certain to not call item_done on any outstanding sequences.  However, the order that a simulator executes threads in the various components is indeterminate. To synchronize these operations, the containing agent has a pre_reset_phase such as the above.

When test enters main_phase initially first time at that time run_count is 0, so on assertion of Reset test will do phase.jump method and move to pre_reset_phase from main_phase.
When test enters main_phase second time at that time run_count is 1, so at that time it will not do phase jumping.

Note: It is not good to use a phase jumping feature in case any of the components of testbench don’t use the sub-phases of UVM.


Saturday, 22 July 2017

Extend run_phase using set_drain_time and all_dropped callback after all objection drops

It is more often that two or more components in verification environment are not in sync with each other. And there may be a case where driver/transmitter finish it's job first and call item_done. After item_done is called from driver and if there is no action pending in sequence then start method of sequence finishes and objection is also dropped.
But we want that simulation (more precisely we can say run_phase) should extend to some more time after all objection are dropped, so that other components which are late with respect to driver can finish it's job. We can achieve this by using set_drain_time in UVM.

Let's go through example and see how we can achieve this using set_drain_time in UVM.


UVM also provides callback hookups when every objection raises and drops and when all objection drops.
Let's go through example and see how it works,




Once all the objections are dropped, Drain time takes effect. After Drain time is finished, all_dropped callback takes effect.

Pros and Cons of Pointers in C

Advantages of Pointer:

1) Pointers allow us to perform dynamic memory allocation and deallocation
2) Using pointer we can return more than one value from the function
3) Using pointer can pass array or string more efficiency
4) Pointer helps us to build complex data structure like linked-list, stack, tree, graphs etc.
5) Pointers permit references to functions and thereby facilitating passing of functions as arguments to other functions.
6) Use function call by reference.
4) Increase the execution speed of program. how?

Drawback of Pointer:

1) Uninitialized pointers might cause segmentation fault.
2) Dynamically allocated block needs to be freed explicitly.  Otherwise, it would lead to memory leak.
3) If pointers are updated with incorrect values, it might lead to memory corruption.
4) Pointer bugs are difficult to debug.

Thursday, 23 March 2017

Automatic raise/drop objection with UVM-1.2

Variable uvm_sequence_base::starting_phase is deprecated and replaced by two new methods set_starting_phase and get_starting_phase, which prevent starting_phase from being modified in the middle of a phase. This change is not backward-compatible with UVM 1.1, though variable starting_phase, although deprecated, has not yet been removed from the base class library.

New method uvm_sequence_base::set_automatic_phase_objection causes raise_objection and drop_objection to be called automatically around a sequence, avoiding the need to call raise/drop_objection manually in one common situation.

Lets understand it trough below mentioned example.



Simulation timeout in UVM using set_timeout and +UVM_TIMEOUT

Default timeout for simulation or you can say timeout for run_phase (as rest all phases are non-time consuming) is `UVM_DEFAULT_TIMEOUT, if not overridden by uvm_root::set_timeout or uvm_cmdline_processor::+UVM_TIMEOUT.

Default value of `UVM_DEFAULT_TIMEOUT is 9200 second.

Example 1 (Timeout at default time which is 9200 second):

1) Overridden by uvm_root::set_timeout


Example 2 (Timeout at specified time which is specified using set_timeout):

2) Overridden by uvm_cmdline_processor::+UVM_TIMEOUT

+UVM_TIMEOUT=, allows users to change the global timeout of the UVM framework.  The argument (‘YES’ or ‘NO’) specifies whether user code can subsequently change this value.  If set to ‘NO’ and the user code tries to change the global timeout value, a warning message will be generated.

Example 3 (Timeout at 100s using simulation switch +UVM_TIMEOUT=100000000000):
Note: Here you need to give simulation time in format of timescale defined in simulation

Tuesday, 21 February 2017

Lock and Grab of sequencer in UVM

There are a number of modelling scenarios where one sequence needs to have exclusive access to a driver via a sequencer. One example of this type of scenario is a sequence which is responding to an interrupt.
In order to accommodate this requirement, the uvm_sequencer provides 2 types of mechanism called lock()-unlock() and grab()-ungrab().

If sequencer is doing some sequence and based on some external events if user wants sequencer to pause the current sequence, he/she can grab/lock sequencer and start another sequence. Once the started sequence finishes sequencer can resume the normal operation, upon ungrab/unlock.
This mechanism is generally used to mimic CPU behavior, where CPU is doing a normal bus operation and when interrupt comes, CPU needs to suspend the normal operation and start interrupt handling. Once interrupt handling is over, CPU should resume the normal operation from where it was suspended.

A lock might be used to model a prioritized interrupt and a grab might be used to model a non-mask able interrupt (NMI).
The lock() and grab() calls have antidote calls to release a lock and these are unlock() and ungrab().

1) A lock() request is put in back of the arbitration queue . A lock request will be arbitrated the same as any other request.
2) A lock is granted after all earlier requests are completed and no other locks or grabs are blocking this sequence.
3) A lock() is blocking task and when access is granted, it will unblock.
4) If no argument is supplied, then current default sequencer is chosen.

1) A grab() request is put in front of the arbitration queue. A grab() request will be arbitrated before any other requests.
2) A grab() is granted when no other grabs or locks are blocking this sequence. (The only thing that stops a sequence from grabbing a sequencer is a pre-existing lock() or grab() condition.)
3) A grab() is blocking task and when access is granted, it will unblock.
4) If no argument is supplied, then current default sequencer is chosen.

The unlock sequencer function is called from within a sequence to give up its lock or grab. A locking sequence must call unlock before completion; otherwise the sequencer will remain locked.

An alias of function unlock().

What happens if 2 sequences try to grab or lock the same sequencer?
The most recent grab goes to the front of the queue; the most recent lock goes to the back of the queue.

Priority in lock() and grab().
The most recent grab comes first, then the previous grab... then the oldest lock, then the most recent lock.

Let’s go through basic example of lock() and grab()
1) Lock

2) Grab
From output of above two examples, we can see the difference between lock() and grab() as mentioned above.

When a hierarchical sequence locks/grab a sequencer, then its child sequences will have access to the sequencer.
If one of the child sequences issues a lock/grab, then the parent sequence will not be able to start any parallel sequences or send any sequence_items until the child sequence has unlocked.

A locking or grabbing sequence must always unlock before it completes,

Now let’s go through one example where 2 virtual sequences are running in parallel and one virtual sequence put lock/grab on agent’s sequencer.


Thursday, 16 February 2017

Difference between @event and wait(event.triggered) in SystemVerilog and usage of non-blocking event

The event data type provides a handle to a synchronization object.

There are two ways through which we can wait for particular event to be triggered.
So let's understand what is the exact difference between those 2 ways.



An event trigger ->e is an instantaneous event. The event waiting process @ shall execute before the triggering process -> executes. If the trigger executes first, then the waiting process remains blocked.

The triggered event property evaluates to true (1'b1) if the given event has been triggered in the current time step and false (1'b0) otherwise.
Now you no longer have to worry which came first, the triggering process –> or the waiting process @ statement. But you still have to execute the waiting process @ in the current time slot to catch the event.

Let’s see the behavior with examples,




The other workaround is to use nonblocking event

Non-blocking event ->>
Nonblocking events are triggered using the ->> operator.

The effect of the ->> operator is that the statement executes without blocking, and it creates a nonblocking assign update event in the time in which the delay control expires or the event control occurs. The effect of this update event shall be to trigger the referenced event in the nonblocking assignment region of the simulation cycle.