Showing posts with label UVM. Show all posts
Showing posts with label UVM. Show all posts

Friday, 9 November 2018

Pass enum value from command line in UVM


In verification activity, many times the requirement comes to choose specific value of enum variable from command line in either randomization of transaction class or configuring agent.

System Verilog provides support to pass value of variable of type int/bit/logic/string from command line using $value$plusargs function but it does not direct support to pass value of enum variable from command line.

In earlier version of UVM (uvm 1.1d and prior), we can achieve this by passing string value from command line and manually converting it into enum by writing code in TB.

Let’s go through example how we can achieve this,
-------------------------------------------------------------------------
-------------------------------------------------------------------------


In UVM 1.2 provides inbuilt facility to convert string into enumerated value.
In UVM 1.2 library contains uvm_enum_wrapper#(T) class and this class contains from_name() function.
from_name() function try to convert any string passed as argument into enumerated value.
If any matching enumerated value found then this function returns 1 else it returns 0.
-------------------------------------------------------------------------
-------------------------------------------------------------------------

Let’s go through example,
-------------------------------------------------------------------------
-------------------------------------------------------------------------


In above approach from_name() function can convert only one string at a time.
Now think about scenario where to randomize variable of enum user wants to select from multiple values. But above approach works fine only with one string value.

UVM provides uvm_split_string() function which splits the string based on a delimiter and returns a queue with sub string.
-------------------------------------------------------------------------
-------------------------------------------------------------------------

Now after getting queue of substring from uvm_split_string() function, user can pass one one element of substring queue to from_name() function in some loop and convert it into enumerated value.
Let’s go through example to understand this better,
-------------------------------------------------------------------------
-------------------------------------------------------------------------

Reference:

Tuesday, 6 November 2018

Drop all objection manually in UVM


Test requirement is like,
1) When polling for more than one interrupts by continuously reading different registers in parallel and based on some indication move to next phase of UVM
2) Waiting for some timer to be expired and then moving to next phase of UVM

Let’s take one scenario where,
Driver is raising objection but never dropping objection and requirement is to move to next phase (extract_phase) after #50 timeunit. But because all the objection are not dropped in run_phase, simulation is not going to extract_phase.
Let’s go through example.
-------------------------------------------------------------------------
-------------------------------------------------------------------------


Now let’s take another scenario,
Here driver is dropping objection after #105 timeunit. So simulation will move to next phase after #105. But the requirement is to move to next phase which is extract_phase after #50.
Let’s go through example.
-------------------------------------------------------------------------
-------------------------------------------------------------------------


To fulfill the requirement we need to any how drop the pending objection after #50 to move to next phase after #50.
We can do this by getting list of all uvm_components (through get_objector method of uvm_objection class) who raised objection and then we can manually drop the objection after #50 timeunit.
Let’s go through example.
-------------------------------------------------------------------------
-------------------------------------------------------------------------

Reference:
1) uvm_objection.svh file 

Automatic configuration through uvm_config_db

UVM has an internal database table in which we can store values (of any data type including user defined data type) with unique name and can be retrieved later by some other testbench component.
There are two interface through which we can access configuration database.
1) uvm_resource_db, 2) uvm_config_db

Uvm_config_db is a great way to pass different resources (variable/object/virtual interface) within UVM testbench component hierarchy.

The uvm_config_db class provides a convenience interface on top of the uvm_resource_db to simplify the basic interface that is used for configuring uvm_component instances.

Let’s go through basic example:
-------------------------------------------------------------------------
-------------------------------------------------------------------------

UVM provides automatic configuration of resources (variable/object etc…) which are registered in the respective component (which is extended from uvm_component) by calling apply_config_settings method.
Any component which is extended from uvm_component should call super.build_phase(phase) to execute apply_config_settings method.
Let's go through basic example of automatic configuration.
-------------------------------------------------------------------------
-------------------------------------------------------------------------

But,
This is not recommended approach as it introduces extremely poor simulation performance and big confusion on what kind of field/resource it supports and does not supports.

Some of the types get automatically configured and while some are not.

Let’s go through one more example of automatic configuration,
-------------------------------------------------------------------------
-------------------------------------------------------------------------

Observation:
1)      If you set any object which is extended from uvm_object and set it into config_db using extended type it won't get automatically configured (obj2).
2)      If you set extended object into config_db using uvm_object as type then it will get automatically configured (obj1).
3)      Unpack array will not get automatically configured (ary_int).
4)      If resource (variable/object etc) is not registered in respective class using `uvm_field_* macro then it won't get automatically configured (idx1).
5)      Virtual interface won't get automatically configured as we can't register it using `uvm_field_* macro.
6)      If you set any vector (let’s say bit [47:0]) in config_db using uvm_config_db#(int)::set then it will get automatically configured. Even though width of int is 32 bit but still 48 bits will get automatically configured (idx2).
7)      But if you set same vector (bit [47:0]) in config_db using uvm_config_db#(bit[47:0])::set then it won't get automatically configured (idx22).


If you want to pass Unpack array using uvm_config_db then you can set it directly using for loop as done in above example. But this is not efficient approach, instead of that create a class (extended from uvm_object) that includes unpack arrays and then do set/get of that class using uvm_config_db.

Reference:
1) https://verificationacademy.com/forums/uvm/uvmconfigdb-usage-big-confusion

Sunday, 23 July 2017

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.

Reference:

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.

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.
------------------------------------------------

------------------------------------------------


Ref:
1) https://www.doulos.com/knowhow/sysverilog/uvm/uvm-1.2/

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().

Lock()
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.

Grab()
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.

Unlock()
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.

Ungrab()
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.

---------------------------------------------
---------------------------------------------

Friday, 26 August 2016

Callback vs Factory in UVM

Callbacks and factory address different areas of re-usability.

Callback: Add functionality to existing logic.

Factory: Change the existing component before build, keeps environment same.

Although the callback and factory can be interchangeably used to address the same problem. Depending on the need and demand, a wise decision should be made while adopting either of the techniques as they have their own merits and demerits.



Saturday, 30 April 2016

Override in UVM using factory with Example

One of the main advantage of UVM is Creating each components using factory enables them to be overridden in different tests or environments without changing underlying code base.

uvm_factory provides four different methods to override particular instance or all instances of particular class.
set_inst_override_by_type
set_inst_override_by_name
set_type_override_by_type
set_type_override_by_name


Let's go through example of each one of above four ways.

set_inst_override_by_type
-------------------------------------------------------

-------------------------------------------------------

-------------------------------------------------------

-------------------------------------------------------


set_inst_override_by_name

-------------------------------------------------------

-------------------------------------------------------

-------------------------------------------------------

-------------------------------------------------------


set_type_override_by_type
-------------------------------------------------------

-------------------------------------------------------

-------------------------------------------------------

-------------------------------------------------------


set_type_override_by_name
-------------------------------------------------------

-------------------------------------------------------

-------------------------------------------------------

-------------------------------------------------------

set_inst_override_* has high precedence than set_type_override_*.
i.e. If any particular instance of class is override by using set_inst_override_* and type of same class is overridden by using set_type_override_* then that particular instance of class is overridden by set_inst_override_* and rest other instances are overridden by set_type_override_*

-------------------------------------------------------

-------------------------------------------------------

-------------------------------------------------------

-------------------------------------------------------



UVM also support override through command line,
+uvm_set_inst_override=,,
+uvm_set_type_override=,[,]

work like the name based overrides in the factory--factory.set_inst_override_by_name() and factory.set_type_override_by_name().

Let's implement same example as above using command line arguments.
-------------------------------------------------------

-------------------------------------------------------

Simulation time argument:
+uvm_set_inst_override=A,A_override,uvm_test_top.env.a3 \
+uvm_set_inst_override=B,B_ovr,path1.b1 \
+uvm_set_type_override=A,A_ovr \
+uvm_set_type_override=B,B_override
-------------------------------------------------------

-------------------------------------------------------

Monday, 25 April 2016

UVM FAQ2



Why phasing is used? What are the different phases in uvm?
UVM Phases is used to control the behavior of simulation in a systematic way & execute in a sequential ordered to avoid race condition. This could also be done in system verilog but manually.

 



Which phases of UVM are Top-Down?
build_phase() & final_phase() are Top-Down, rest all phases are Bottom-Up.



Which phases of UVM are task?
run_phase(),
pre_reset_phase(), reset_phase(), post_reset_phase(),
pre_configure_phase(), configure_phase(), post_configure_phase(),
pre_main_phase(), main_phase(), post_main_phase(),
pre_shutdown_phase(), shutdown_phase(), post_shutdown_phase()



How do uvm phases initiate?
Calling run_test() constructs the UVM environment root component and then initiates the UVM phasing.



Why is the build_phase() in UVM executed in a Top - Down fashion and the other phases in Bottom - Up fashion?
The build phase has to be that way because the parent component's build_phase constructs the child components. You couldn't call the child's build_phase before the parent's build_phase because they the child objects haven't been constructed yet. You need a constructed object to call its method.
The build_phase() is also executed top-down so that the parent can provide override setting that the children will use when they execute their build_phase()
The ordering within the other phases should not matter, except you might want know that the top level's report_phase comes last.



What is the order of execution of run_phase() ?
The run_phase() of each component is executed concurrently with no defined order you can depend on.



During the run_phase() is there something like super.run_phase(phase) called?
You only need to call super.method() if you are extending a class an need the functionality of the base method. There is nothing inside the run_phase() of a uvm_component, so there is no need to call super.run_phase() when extending from it.
You may want to call it when extending your classes from your base classes.





What is the difference between run_phase and main_phase in uvm_component?
Actually, you can start a sequence in any phase. It is more important to understand the domain/scheduling relationships between the task based (i.e. runtime) phases.
UVM undergoes a number of pre-simulation phases (build, connect, end_of_elaboration, start_of_simulation) that are all implemented with functions. Once those are completed, the task based phases begin.
The standard includes two schedules.

  • One is simply the run_phase, which starts executing at time zero and continues until all components have dropped their objections within the run_phase.
  • The other schedule contains twelve phases that execute parallel to the run phase. They are: pre_reset, reset, post_reset, pre_config, config, post_config, pre_main, main, post_main, pre_shutdown, shutdown, and post_shutdown. They execute in sequence.

Every component has the opportunity to define or not define tasks to execute these phases. A phase starts only when all components in the previous phase have dropped their objections. A phase continues to execute until all components have dropped their objections in the current phase.



Why connect_phase() is bottom-up?

 

Sunday, 24 April 2016

Difference between m_sequencer and p_sequencer in UVM

To understand difference between m_sequencer and p_sequencer, let's first go through couple of classes from UVM library.
-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------


-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------

When you call `uvm_declare_p_sequencer from sequence, it overrides m_set_p_sequencer function.
When you call start method of sequence, it calls set_item_context method,
Then set_item_context method calls set_sequencer method,
Then set_sequencer method assign handle of sequencer (which you pass in start method of sequence) to m_sequencer,
set_sequencer also calls m_set_p_sequencer method, but you have override the m_set_p_sequencer method by declaring `uvm_declare_p_sequencer macro.
So m_set_p_sequencer defined in macro will be executed, and it casts m_sequencer into p_sequencer,
where p_sequencer's type is specified by the argument of `uvm_declare_p_sequencer macro.


Conclusion:

m_sequencer and p_sequencer both point to the same thing (the sequencer on which the sequence is running). However, there are some important differences:
m_sequencer is a generic uvm sequencer pointer of type uvm_sequencer_base. It will always exist for an uvm_sequence and is initialized when the sequence is started.

p_sequencer is a reference to uvm_sequencer#(REQ,RSP), the user derived parameterized sequencer class.
Other way you can say that p_sequencer is a type specific sequencer pointer.
p_sequencer will not exist if the `uvm_declare_p_sequencer macros aren't used.

Advantage of p_sequencer over m_sequencer:
Being type specific, you will be able to access anything added to the sequencer (i.e. pointers to other sequencers, handle of environment class (which is defined in user derived virtual sequencer class) etc.).

Drawback of p_sequencer over m_sequencer:
p_sequencer creates an additional dependency, means sequence which use p_sequencer may not be executed on all the sequencers. Sequence requires resource in sequencer class which is declared as p_sequencer (i.e. pointers to other sequencers, handle of environment class, etc.)