Site icon Hardware Design and Verification

UVM questions and Answer Part3

Welcome to section uvm question and answer part ,

I will try to put around 20 to 30 questions and answer related to UVM

Lets Start

What is a virtual sequence and where do we use a virtual sequence? What are its benefits? company favorite question 

A virtual sequence is a sequence which controls stimulus generation across multiple sequencers. Since sequences, sequencers and drivers are focused on single interfaces, almost all testbenches require a virtual sequence to co-ordinate the stimulus and the interactions across different interfaces. Virtual sequences are also useful at a Subsystem or System level testbenches to have unit level sequences exercised in a co-ordinated fashion. Following diagram shows this conceptually where a Virtual sequence has handles to three sequencers which connect to drivers for three separate interface to DUT. The virtual sequence can then generate sub sequences on each of the interfaces and run them on the corresponding sub-sequencer.

What is a factory? NVDIA favorite

A “factory” in UVM methodology is a special look up table in which all of the UVM components and transactions are registered. The recommended way to create objects of components and transactions in UVM is by using the factory method called create() . Creating objects using factory also helps in substituting an object of one type with an object of a derived type without having to change the structure of the testbench or editing the testbench code.

What is the difference between creating an object using new() and

create() methods?

The recommended method in UVM for creating components or transaction objects is to

use the built-in method ::type_id::create() instead of calling the constructor new()

directly. The create method internally makes a call to the factory to look up the requested

type and then calls the constructor new() to actually create an object. This allows type

overriding easily as in the test, you can specify the type of class (base or one or derived)

and all the other testbench components will be able to create object of that class type

without any code change.

A new() constructor will only create an object of a given type and therefore using a new()

will not allow run time changing of class types. Hence, using a new() means the testbench

code will need to change based on the different types to be used.

How do we register an uvm_component class and uvm_sequence class

with factory?

The uvm_sequence class is registered with the factory using uvm_object_utils() macro

and passing the class name as argument. An Example below:

class test_seq_c extends uvm_sequence;

`uvm_object_utils(test_seq_c)

The uvm_component class is registered with the factory using uvm_component_utils()

macro and passing the class name as argument. An Example below:

class test_driver_c extends uvm_component;

`uvm_component_utils(test_driver_c)

Why should we register a class with factory?

A factory is a special look up table used in UVM for creating objects of component or transaction types. The benefit of object creation using factory is that a testbench build process can decide at run-time which type of object has to be created. Based on this, a class type could be substituted with another derived class type without any real code change. To ensure this feature and capability, all classes are recommended to be registered with factory. If you do not register a class with factory, then you will not be able to use the factory method ::type_id::create() to construct an object.

What is meant by factory override? NVDIA

The UVM factory allows a class to be substituted with another derived class at the point of construction. This can be useful for changing the behaviour of a testbench by substituting one class for another without having the need to edit or re-compile the testbench code.

 

What is the difference between instance override and type override? NVDIA

A type override means that every time a component class type is created in a testbench hierarchy, a substitute type is created in its place. This applies to all instances of that component type. On the other hand, an instance override means, overriding only a specific instance of a component class. A specific instance of a component is referenced by the position of that component in the UVM component hierarchy. Since only UVM component classes can have a hierarchy in UVM testbenches, only component classes can be overridden on an instance granularity while sequences (or UVM objects) can be only type overridden

Can instance override and type override be used for both UVM_component class and transaction types ?

No, only UVM_component classes are part of UVM testbench hierarchy and can be overridden on an instance granularity. The sequence items or sequences are not a part of UVM testbench hierarchy and hence can only be overridden using type override which will override all objects of that type.

 

What is the concept of objections and where are they useful?

The uvm_objection class provides a means for sharing a counter between multiple components and sequences. Each component/sequence may “raise” and “drop” objections asynchronously, which increases or decreases the counter value. When the counter reaches zero (from a non-zero value), an “all dropped” condition is said to occur. The objection mechanism is most commonly used in the UVM phasing mechanism to coordinate the end of each run-time phase. User-processes started in a phase raises an objection first and drops the objection once the process completes. When all processes in a phase drops the objects, the phase’s objection count goes to zero. This “all dropped” condition indicates to the phasing mechanism that every participant agrees the phase should be ended.

Following is an example of how a sequence ( my_test_sequence ) is started on a sequencer ( my_sequencer ) and the objection is dropped after sequence completes execution

task main_phase( uvm_phase phase);

phase.raise_objection( this );

my_test_sequence.start(my_sequencer);

phase.drop_objection( this );

endtask

How can we implement a simulation timeout mechanism in UVM methodology?

A simulation time out mechanism helps to stop the simulation if the test doesn’t progress because of some bug beyond a maximum time. In UVM, set_global_timeout(timeout) – is a convenience function that sets uvm_top.phase_timeout variable with the timeout value. If the run() phase doesn’t end before this timeout value, then the simulation is stopped and an error is reported.

This function is called in the top level module which also starts the test as follows

module test;

initial begin

set_global_timeout(1000ns);

end

initial begin

run_test();

end

endmodule

What is the concept of phasing in UVM methodology?

Unlike a module based testbench (in which all modules exist statically in a hierarchy), class based testbench need to manage creation of different objects and execution of various tasks and functions in those objects. Phasing is an important concept in class based testbenches to have a consistent testbench execution flow. A test execution can be conceptually divided into following tasks – configuration, creation of testbench components, runtime stimulus, and end of test checks. UVM defines standard phases for each of these as part of the methodology.

What are the different phases of a UVM component? What are the subphases for the UVM run_phase()?

UVM uses standard phases to order the major steps that take place during simulation. There are three groups of phases, which are executed in the following order.

  1. Build phases – In the build phases; the testbench is configured and constructed. It has following sub-phases which are all implemented as virtual methods in uvm_component

base class.

1) build_phase()

2) connect_phase()

3) end_of_elaboration()

  1. Run time phases – These phases can consume time and this is where most of the test execution happens.

1) start_of_simulation()

2) run_phase()

The run_phase() is further divided into 12 sub-phases as below:

1) pre_reset

2) reset

3) post_reset

4) pre_configure

5) configure

6) post_configure

7) pre_main

8) main

9) post_main

10) pre_shutdown

11) shutdown

12) post_shutdown

  1. Clean up phase – This phase execute after the test ends and is used to collect, and report results and statistics from the test. This consists of following sub phases:

1) extract()

2) check()

3) report()

4) final()

Why is build_phase() executed top down in uvm_component hierarchy? General Interview Question

In UVM, all the testbench components like test, Env, Agent, Driver, Sequencer are based of uvm_component class and there is always a hierarchy for the testbench components. The build_phase() method is part of uvm_component class and is used to construct all the child components from the parent component. So, to build the testbench hierarchy you always need to have a parent object first, which can then construct its children, and that can further construct its children using build_phase. Hence, build_phase() is always executed top down.

For Example: The top level uvm_test class calls build_phase which should construct all the uvm_env components part of this test, and the build_phase() of each uvm_env class should construct all the uvm_agent components that are part of that uvm_env, and this goes on. For all other phases it really doesn’t matter in which order it is called. The run_phase() for all components runs in parallel.

What is the use of phase_ready_to_end() method in a uvm_component class?

phase_ready_to_end(uvm_phase phase) is a callback method available in a component class which gets called when all objections are dropped for that corresponding phase and the phase is going to end. A component class can use this callback method to define any functionality that it needs to perform when the phase is about to end. One example is if a component want to delay ending of phase until some condition even after all objections are dropped, it can be done using this callback method. Another example is if an irritator or reactive sequence is running until a main sequence is complete, phase_ready_to_end() callback method in main_phase() can be used to stop those irritator sequences.

What is uvm_config_db and what is it used for? General Question

The UVM configuration mechanism supports sharing of configurations and parameters across different testbench components. This is enabled using a configuration database called uvm_config_db. Any testbench component can populate the configuration database with variables, parameters, object handles etc. Other testbench components can get access to these variables, parameters, object handles from the configuration database without really knowing where it exists in the hierarchy. For Example, a top level testbench module can store a virtual interface pointer to the uvm_config_db . Then any uvm_driver or a uvm_monitor components can query the uvm_config_db to get handle to this virtual interface and use it for actually accessing the signals through the interface.

How do we use the get() and set() methods of uvm_config_db? General Question

The get() and set() are the primary methods used to populate or retrieve information from the uvm_config_db. Any verification component can use the set() method to populate the config_db with some configuration information and can also control which other components will have visibility to same information. It could be set to have global visibility or visible only to one or more specific testbench components. The get() function checks for a shared configuration from the database matching the parameters. The syntax for the get() and set() methods are as follows:

uvm_config_db#(<type>)::set(uvm_component context, string inst_name, string field_name,<type> value)

uvm_config_db#(<type>)::get(uvm_component context, string inst_name, string field_name, ref value)

The context specifies the current class or component from which get/set is called.The inst_name is the name of the instance of component from which get/set is called. The field_name is the name of the object or parameter or variable which is set/get in config_db. The <type> identifies the type of the configuration information set/get in

config_db. For object handles, this will have the class name while for other variables; it will be the type of that variable.

Is it possible for a component lower in testbench hierarchy to pass a handle to a component in higher level of hierarchy using get/set config methods?

This is not a recommended way of passing configuration objects in UVM. Normally the higher level component sets up configuration data base with handles and the lower level components do get them using get/set methods.

 

Explain how simulation ends in UVM methodology?

UVM has a phased execution which consists of a set of build phases, run phases and check phases. The run() phase is where the actual test simulation happens and during this phase every component can raise an objection in beginning and hold it until it is done with its activity. Once all components drops the objection, the run() phase completes and then check() phase of all components execute and then the test ends. This is how a normal simulation ends, but there are also controls on simulation timeouts to terminate the run() phase if some component hangs due to a bug in design or testbench. When the run() phase starts, a parallel timeout timer is also started. If the timeout timer reaches one of the specified timeout limits before the run() phase completes, the run() phase will timeout, an error message will be issued and then all phases post run() will get executed and test ends after that.

What is UVM RAL (UVM Register Abstraction Layer)?

UVM RAL (Register Abstraction Layer) is a feature supported in UVM that helps in verifying the registers in a design as well as in configuration of DUT using an abstract register model. The UVM register model provides a way of tracking the register content of a DUT and a convenience layer for accessing register and memory locations within the DUT. The register model abstraction reflects the structure of the design specification for registers which is a common reference for hardware and software engineers working on the design. Some other features of RAL include support for both front door and back door initialization of registers and built in functional coverage support.

What is UVM Call back?

The uvm_callback class is a base class for implementing callbacks, which are typically used to modify or augment component behavior without changing the component class. Typically, the component developer defines an application-specific callback class that extends from this class and defines one or more virtual methods, called as callback interface. The methods are used to implement overriding of the component class behavior. One common usage can be to inject an error into a generated packet before the driver sends it to DUT. Following pseudo code shows how this can be implemented.

1) Define the packet class with an error bit

2) Define the driver class that receives this packet from a sequence and sends it to

DUT

3) Define a driver callback class derived from the base uvm_callback class and

add a virtual method which can be used to inject an error or flip a bit in the packet.

4) Register the callback class using `uvm_register_cb() macro

5) In the run() method of driver that receives and send the packet to the DUT,

based on a probability knob, execute the callback to cause a packet corruption

class Packet_c;

byte[4] src_addr, dst_addr;

byte[] data;

byte[4] crc;

endclass

//User defined callback class extended from base class

class PktDriver_Cb extends uvm_callback;

function new (string name = “PktDriver_Cb”);

super.new(name);

endfunction

virtual task corrupt_packet (Packet_c pkt);

//Implement how to corrupt packet

//example – flip one bit of byte 0 in CRC

pkt.crc[0][0] = ~pkt.crc[0][0]

endtask

endclass : PktDriver_Cb

//Main Driver Class

class PktDriver extends uvm_component;

`uvm_component_utils(PktDriver)

//Register callback class with driver

`uvm_register_cb(PktDriver,PktDriver_Cb)

function new (string name, uvm_component parent=null);

super.new(name,parent);

endfunction

virtual task run();

forever begin

seq_item_port.get_next_item(pkt);

`uvm_do_callbacks(PktDriver,PktDriver_Cb, corrupt_packet(pkt))

//other code to derive to DUT etc

end

endtask

endclass

What is uvm_root class?

The uvm_root class serves as the implicit top-level and phase controller for all UVM components. Users do not directly instantiate uvm_root. The UVM automatically creates a single instance of uvm_root that users can access via the global (uvm_pkg-scope)

variable, uvm_top .

What is the parent class for uvm_test?

uvm_test class is the top level class that a user can implement and there is no explicit parent that is mentioned. However, UVM has a special component called uvm_top and it is assigned as the parent of test class.

 

Exit mobile version