Functional UVM
Functional UVM
Abstract- The main goal of functional verification in hardware design is to find out the bugs in design description given by
design engineers and to check the functionality of design whether the output matches with desired value or not and then
customize the design to get the desired functionality of DUT. Various verification techniques have been developed from past
few years to make the verification process much easier and user friendly. This paper presents a recent approach to using
UVM, the Universal Verification Methodology, for functional verification by mainstream users. The goal is to identify a
minimal set of concepts sufficient for constrained random coverage-driven verification in order to ease the learning
experience for engineers coming from a hardware design background who do not have extensive object-oriented
programming skills. We describe coding guidelines to address the canonical structure of UVM component and transaction,
the construction of the UVM component hierarchy, the interface with the design-under-test, the use of sequences, and the use
of the factory and configuration mechanisms. We also provide some key factors which allowed the user to migrate from
OVM to UVM for verification purpose.
ambiguous definition, and so take the very reasonable constrained random verification methodology
position of prioritizing their implementation choices because they enable reuse, yet these techniques are
according to customer demand. amongst the hardest to learn. OO techniques allow
verification components to be specialized to the needs
Nevertheless, certain areas of the SystemVerilog of a specific test bench or test without modifying
language do stand out as being well-defined and their source code and enables well-structured
consistently implemented across all the major communication between those components using
simulators. These include: function calls. We wanted to provide some of the
The concise RTL features such as ANSI- expressive power of the OO paradigm without getting
style port and parameter declarations, drawn into the full set of issues involved in OO
always_comb, always_ff, unique, priority, programming. In UVM, the class is used as a
and the abbreviated port connection syntax container to represent components, transactions,
C-like control constructs such as for, sequences, tests, and configurations. Let's take the
foreach, do-while, break, continue, return component. Unlike the VHDL design entity or the
SystemVerilog module, a component represents an
C-like data type features such as typedef,
abstraction across a whole family of possible
enum, struct, and the 2-valued integer types
structural building blocks. A component picks out
VHDL-like package and import features what is common across several such building blocks,
Classes and the features for constraints and but a component is not concrete, meaning that it is
functional coverage based thereon not the final once-and-for-all definition of the thing.
The class-based data types, namely strings, A VHDL design entity or a SystemVerilog module
queues, dynamic arrays and associative can describe a family of related components, but only
arrays if the variants are anticipated in advance and are
Interfaces and virtual interfaces sufficient explicitly captured in the source code by means of
for communication between classes and language features such as generic parameters and
modules generate statements. Because it is a class, a UVM
component can be extended after-the-fact in arbitrary
One reason that the features from the above list have ways. An extension can add new features or can
been implemented so thoroughly and consistently is modify existing features. In particular, we require this
that, with the exception of the concise RTL features, extension capability so that a test can extend a
they have been widely used to create the libraries of transaction or a sequence in order to add constraints,
base classes that underpin the functional verification and then use the factory mechanism to override the
methodologies AVM, URM, VMM, OVM, and now generation of those transactions or sequences.
UVM. Customer demand has pushed the EDA
vendors to support each others' methodology class IV. UVM CONCEPTS
libraries, which in turn has driven the
implementations to converge on a common The goal of this paper is to identify a minimal set of
understanding of the features and semantics of class- concepts sufficient for constrained random coverage-
based SystemVerilog. driven verification in order to ease the learning
experience for engineers coming from a hardware
SystemVerilog and UVM now form a virtuous circle. design background who do not have extensive object-
The class-based SystemVerilog features that support oriented programming skills. At the same time, we
constrained random verification are sufficiently well- did not want to strip down the conceptual framework
defined and well-implemented to allow the to the point where it lost all the expressive power of
development of robust and portable verification class the object-oriented paradigm. Some other attempts to
libraries, and the widespread use of those libraries present verification class libraries to hardware
ensures the ongoing support of the necessary designers have ended up doing little more than re-
language features by the tool vendors. In addition to present the semantics of VHDL or Verilog using
the Verilog-like and C-like features of classes, which was a pitfall we wished to avoid. In
SystemVerilog, we make use of classes, constraints, our experience, hardware designers moving to a new
covergroups, packages, interfaces and virtual language for verification, SystemVerilog in this case,
interfaces. UVM makes heavy use of type do indeed want to benefit from the increased
parameterization to classes, and fortunately all of the expressive power and flexibility afforded by a new
major simulator implementations now agree on the paradigm.
semantics in this area.
Our conceptual vocabulary is listed below. These
terms are elaborated later in the paper, and expanded
III. OBJECT ORIENTED CONCEPT definitions can be found in the documentation
accompanying the UVM distribution.
Object-oriented (OO) or aspect-oriented
programming concepts are key to contemporary Component a structural building block,
conceptually equivalent to a Verilog module
Review on Universal Verification Methodology (UVM) Concepts for Functional Verification
102
International Journal of Electrical, Electronics and Data Communication, ISSN: 2320-2084 Volume-2, Issue-3, March-2014
Transaction a bundle of data items, which verification environment, so has a standard structure
may be distributed over time and space in and conventions for how it can be customized.
the system, and which form a Components are created quasi-statically at the start of
communication abstraction such as a simulation.
handshake, bus cycle, or data packet
Sequence an ordered collection of Here is a skeleton component. The
transactions or of other sequences uvm_component_utils macro and the function new
should be treated as boilerplate code and written
Phase execution is subdivided into various
exactly as shown.
predefined phases that permit components to
agree on when to build components, connect
Class my_comp extends uvm_component;
ports, run simulation, and so forth
`uvm_component_utils(my_comp)
Factory a function call that returns a function new(string name, uvm_component parent);
component, transaction, or sequence, the super.new(name, parent);
type of which may be overridden from a test endfunction
Port and export connection points for ...
transaction-level communication between Endclass
components
Generation the creation of components, B. Transactions
transactions, or sequences, where the Transactions are the basic data objects that are passed
properties of each may be set between components. In contrast to VHDL signals
deterministically or at random under the and Verilog wires, transactions represent
control of constraints communication at an abstract level. In order to drive
Test a top-level component, which drives stimulus into the DUT, a so-called driver component
generation converts transactions into pin wiggles, while a so-
Configuration an object associated with a called monitor component performs the reverse
component which may be set or randomized operation, converting pin wiggles into transactions.
by a test and which it is used to configure Here is a skeleton transaction. Again, the
that component as the component hierarchy uvm_object_utils macro and function new should be
is built treated as boilerplates.
Sequencer a component that runs class my_tx extends uvm_sequence_item;
sequences and that sends transactions `uvm_object_utils(my_tx)
generated by those sequences downstream to function new (string name = "");
another sequencer or to a driver super.new(name);
endfunction
Driver a component that receives
...
transactions from a sequencer and that drives
Endclass
the signal-level interface of the Design
Under Test (DUT)
C. Phase
Monitor a component that senses the Every component implements the same set of phases,
signal-level interface of the DUT and that which are run in a predefined order during simulation
sends transactions to the verification in order to synchronize the behaviour of the
environment components. When compared with VHDL or Verilog,
Coverage functional coverage information UVM provides rather more temporal structure within
can be collected using SystemVerilog cover a simulation run. The standard phases are as follows:
groups within a component
Checking functional correctness of the 1. build create child component instances
DUT can be checked using either procedural 2. connect connect ports to exports on the
code within a component or SystemVerilog child components
assertions within an interface 3. End of elaboration housekeeping
4. Start of simulation housekeeping
A. Components 5. run runs simulation
Components are used to build a component hierarchy, 6. extract post-processing
conceptually very similar to the design hierarchy in 7. check post-processing
VHDL or the module hierarchy in Verilog. In this 8. report post-processing
case the component hierarchy is part of the
verification environment rather than the design, and Each phase is represented by a function within the
components represent stimulus generators, drivers, component, except for run, which is a task because it
monitors, coverage collectors, and checkers. The alone consumes simulation time. If a function is
component represents the reusable unit of the absent, that component will be inactive in the given
phase.
Review on Universal Verification Methodology (UVM) Concepts for Functional Verification
103
International Journal of Electrical, Electronics and Data Communication, ISSN: 2320-2084 Volume-2, Issue-3, March-2014
As you can infer from the above list, the primary function void build; // Build phase
distinction amongst the phases is between the phases super.build();
for building the component hierarchy, connecting the // Factory calls to create child components
ports, and running simulation, with additional b = B::type_id::create("b", this);
housekeeping phases pre-pended and appended to the c = C::type_id::create("c", this);
simulation phase. endfunction
...
D. Sequences Endclass
Sequences are assembled from transactions and are
used to build realistic sets of stimuli. A sequence F. Port and export
could generate a specific pre-determined set of Ports and exports are analogous to ports in VHDL or
transactions, a set of randomized transactions, or Verilog, but are used for transaction-level
anything in between. Sequences can run other communication rather than signal-level
sequences, possibly selecting which sequence to run communication. A component can send out a
at random. Sequences can be layered such that transaction out through a port, or receive an incoming
higher-level sequences send transactions to lower- transaction through an export. Transactions are
level sequences in a protocol stack. passed as arguments to function calls, which may be
non-blocking (return immediately) or blocking
Here is a skeleton sequence. It is similar to a (suspend and wait for some event before returning),
transaction in outline, but the base class uvm which is sufficient for basic synchronization within
sequence is parameterized with the type of the the verification environment. All detailed timing
transaction of which the sequence is composed. Also information should be pushed down into the driver
every sequence contains a body task, which when it and monitor components that connect to the DUT so
executes generates those transactions or runs other that the timing can be determined by the DUT
sequences. interface, which is typically locked to low-level
class my_seq extends uvm_sequence #(my_tx); clocks and other synchronization signals. Within the
`uvm_object_utils(my_seq) verification environment, control flow radiates
function new (string name = ""); outward from the DUT, with drivers calling get to
super.new(name); request transactions from sequencers when they are
endfunction ready for further stimulus, and monitors calling write
task body; to distribute transactions around the verification
... environment for analysis.
endtask
... A call to get from a driver may block if the stimulus
endclass generator is coordinating its activities with some
Transactions and sequences together represent the other part of the verification environment. A call to
domain of dynamic data within the verification write from a monitor is not permitted to block,
environment. because the DUT cannot be stalled waiting for
analysis activity.
E. Factory
The UVM factory mechanism is an implementation The example below shows a component A containing
of the so-called factory pattern described in the OO two child components B and C. The connect function
literature. The UVM factory can make components, connects p_port of component B to q_export of
transactions, and sequences. Use of the factory component C.
enables the choice of object type to be overridden class A extends uvm_component;
from the test, although a given component, `uvm_component_utils(A)
transaction or sequence can only be overridden with B b; // Child component having p_port
one that extends the class of the original. This is one C c; // Child component having q_export
of the main mechanisms by which a reusable function new(string name, uvm_component parent);
verification component can be customized to the super.new(name, parent);
current environment. endfunction
function void build; // Build phase
Here is an example of a component creating child super.build();
components during the build phase: b = B::type_id::create("b", this);
class A extends uvm_component; c = C::type_id::create("c", this);
`uvm_component_utils(A) endfunction
B b; // Child component function void connect; // Connect phase
C c; // Child component b.p_port.connect( c.q_export );
function new(string name, uvm_component parent); endfunction
super.new(name, parent); endclass
endfunction
Review on Universal Verification Methodology (UVM) Concepts for Functional Verification
104
International Journal of Electrical, Electronics and Data Communication, ISSN: 2320-2084 Volume-2, Issue-3, March-2014
G. Generation endclass
Generation exploits SystemVerilog randomization // A SEQUENCE is generated dynamically
and constraints. Component generation occurs quasi- class my_seq extends uvm_sequence #(my_tx);
statically during the so-called build phase of UVM, `uvm_object_utils(my_seq)
when a component is able to access its configuration function new (string name = "");
object (if there is one) in order to control the super.new(name);
generation of lower-level components. This is endfunction
analogous to the elaboration phase in VHDL or task body;
Verilog. Sequence generation occurs dynamically. my_tx tx;
Control over the precise sequence of transaction that tx = my_tx::type_id::create("tx");
finally arrives at the DUT can be distributed across start_item(tx);
pre-defined sequence generation components and the assert( tx.randomize() );
test, which is able to extend and constrain existing finish_item(tx);
sequences. endtask
endclass
Here is an example showing transaction generation class my_test extends uvm_test;
within the body task of a sequence: `uvm_component_utils(my_test)
class my_seq extends uvm_sequence #(my_tx); ...
... my_env env;
task body; ...
my_tx tx; task run;
tx = my_tx::type_id::create("tx"); my_seq seq;
start_item(tx); // Create the sequence
assert( tx.randomize() with { cmd == 0; } ); seq = my_seq::type_id::create("seq");
finish_item(tx); // randomize it
... assert( seq.randomize() );
endtask // and start it on the sequencer
endclass seq.start( env.agent.sqr );
In the example above, the transaction is being endtask
constrained as it is randomized in order to set the endclass
value of the command field to a specific value before
sending the transaction downstream. The start_item A sequencer can also receive transactions from other
and finish_item functions synchronize with the sequencers. Here is an example of one that does just
component that is pulling transactions from the that:
sequencer, which could be a driver or another class ano_sqr extends uvm_sequencer #(ano_tx);
sequencer. start_item waits for the downstream `uvm_component_utils(ano_sqr)
component to request the transaction, finish_item uvm_seq_item_pull_port #(my_tx) seq_item_port;
waits for the downstream component to indicate that ...
it has finished with the transaction. For its part, the function void build;
downstream component calls get to fetch the seq_item_port = new("seq_item_port", this);
transaction and may call put if it needs to send back a endfunction
response endclass