0% found this document useful (0 votes)
25 views11 pages

Uvm 1

This chapter discusses UVM topics and capabilities of the UVM Class Library that are beyond the essential material covered in the previous chapters. Consult this chapter as needed.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
25 views11 pages

Uvm 1

This chapter discusses UVM topics and capabilities of the UVM Class Library that are beyond the essential material covered in the previous chapters. Consult this chapter as needed.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 11

UVM 1.

2 User’s Guide October 8, 2015

6. Advanced Topics

This chapter discusses UVM topics and capabilities of the UVM Class Library that are beyond the essential
material covered in the previous chapters. Consult this chapter as needed.

6.1 The uvm_component Base Class

All the infrastructure components in an UVM verification environment, including environments and tests,
are derived either directly or indirectly from the uvm_component class. These components become part
of the environment hierarchy that remains in place for the duration of the simulation.1 Typically, you will
derive your classes from the methodology classes, which are themselves extensions of uvm_component.
However, understanding the uvm_component is important because many of the facilities that the
methodology classes offer are derived from this class. User-defined classes derived from this class inherit
built-in automation, although this feature is entirely optional.

The following sections describe some of the capabilities that are provided by the uvm_component base
class and how to use them.The key pieces of functionality provided by the uvm_component base class
include:
— Phasing and execution control
— Configuration methods
— Factory convenience methods
— Hierarchical reporting control.

6.2 The Built-In Factory and Overrides

6.2.1 About the Factory

UVM provides a built-in factory to allow components to create objects without specifying the exact class of
the object being creating. The mechanism used is referred to as an override and the override can be by
instance or type. This functionality is useful for changing sequence functionality or for changing one version
of a component for another. Any components which are to be swapped shall be polymorphically compatible.
This includes having all the same TLM interface handles and TLM objects need to be created by the new
replacement component. The factory provides this capability with a static allocation function that you can
use instead of the built-in new function. The function provided by the factory is:

type_name::type_id::create(string name, uvm_component parent)

Since the create() method is automatically type-specific, it may be used to create components or objects.
When creating objects, the second argument, parent, is optional.

A component using the factory to create data objects would execute code like the following:

task mycomponent::run_phase(uvm_phase phase);


mytype data; // Data must be mytype or derivative.
data = mytype::type_id::create("data");
$display("type of object is: %0s", data.get_type_name());
...
endtask

1
Contrast to uvm_sequence, sequence_item, and transaction, which are transient —they are created, used, and then
garbage collected when dereferenced.

Copyright © 2011 - 2015 Accellera. All rights reserved. 129


October 8, 2015 UVM 1.2 User’s Guide

In the code above, the component requests an object from the factory that is of type mytype with an
instance name of data.

When the factory creates this object, it will first search for an instance override that matches the full instance
name of the object. If no instance-specific override is found, the factory will search for a type-wide override
for the type mytype. If no type override is found then the type created will be of type mytype.

6.2.2 Factory Registration

You must tell the factory how to generate objects of specific types. In UVM, there are a number of ways to
do this allocation.
— The recommended method is to use the `uvm_object_utils(T) or `uvm_compo-
nent_utils(T) macro in a derivative uvm_object or uvm_component class declaration,
respectively. These macros contain code that will register the given type with the factory. If T is a
parameterized type, use `uvm_object_param_utils or
`uvm_component_param_utils, e.g.,
`uvm_object_utils(packet)
‘uvm_component_param_utils(my_driver)
— Use the `uvm_object_registry(T,S) or `uvm_component_registry(T,S) registra-
tion macros. These macros can appear anywhere in the declaration space of the class declaration of
T and will associate the string S to the object type T. These macros are called by the corresponding
uvm_*_utils macros, so you may only use them if you do not use the ‘uvm_*_utils macros.

6.2.3 Component Overrides

A global factory allows you to substitute a predefined-component type with some other type that is
specialized for your needs, without having to derive the container type. The factory can replace a component
type within the component hierarchy without changing any other component in the hierarchy. You need to
know how to use the factory, but not how the factory works.
NOTE—All type-override code should be executed in a parent prior to building the child(ren). This means that environ-
ment overrides should be specified in the test.

Two interfaces exist to replace default components. These interfaces allow overriding by type or instance,
and will be examined one at a time.

To override a default component:


a) Define a class that derives from the default component class.
b) Execute the override (described in the following sections).
c) Build the environment.

6.2.3.1 Type Overrides

The first component override replaces all components of the specified type with the new specified type. The
prototype is.

<orig_type>::type_id::set_type_override(<override_type>::get_type(),
bit replace = 1);

The set_type_override() method is a static method of the static type_id member of the
orig_type class. These elements are defined in the uvm_component_utils and

130 Copyright © 2011 - 2015 Accellera. All rights reserved.


UVM 1.2 User’s Guide October 8, 2015

uvm_object_utils macros. The first argument (override_type::get_type()) is the type that


will be returned by the factory in place of the original type. The second argument, replace, determines
whether to replace an existing override (replace = 1). If this bit is 0 and an override of the given type
does not exist, the override is registered with the factory. If this bit is 0 and an override of the given type
does exist, the override is ignored.

If no overrides are specified, the environment will be constructed using the original types. For example,
suppose the environment creates an ubus_master_driver type component inside
ubus_master_agent.build(). The set_type_override method allows you to override this
behavior in order to have an ubus_new_master_driver for all instances of
ubus_master_driver.

ubus_master_driver::type_id::set_type_override(ubus_new_master_driver::
get_type);

This overrides the original type (ubus_master_driver) to be the new type


(ubus_new_master_driver). In this case, we have overridden the type that is created when the
environment should create an ubus_master_driver. The complete hierarchy would now be built as
shown in Figure 31. Note that the ubus_master_agent definition does not change, but the factory was
used to create the child instances, the type override causes the new type to be created instead.

ubus_demo_tb0
(ubus_demo_tb) New type created by type override

ubus0 (ubus_env) xserial0 (xserial_env)

masters[0] (ubus_master_agent) tx (xserial_tx_agent)

driver (ubus_new_master_driver) driver (xserial_tx_driver)

sequencer (ubus_master_sequencer) sequencer (xserial_tx_sequencer)

monitor (ubus_master_monitor) monitor (xserial_tx_monitor)

slaves[0] (ubus_slave_agent) bus_monitor (xserial_bus_monitor)

driver (ubus_slave_driver)

sequencer (ubus_slave_sequencer)

monitor (ubus_slave_monitor)

bus_monitor (ubus_bus_monitor)

Figure 31—Hierarchy Created with set_type_override() Applied


NOTE—While only one ubus_master_driver instance is replaced in this example, any and all ubus_mas-
ter_driver instances would be replaced in an environment containing multiple ubus_master_drivers

Copyright © 2011 - 2015 Accellera. All rights reserved. 131


October 8, 2015 UVM 1.2 User’s Guide

6.2.3.2 Instance Overrides

The second component override replaces targeted components of the matching instance path with the new
specified type. The prototype for uvm_component is

<orig_type>::type_id::set_inst_override(<override_type>::get_type(), string
inst_path);

As shown in Section 6.2.3.1, set_inst_override() is a static method of the type_id member of


orig_type. The first argument is the type that will be returned in place of the original type when creating
the component at the relative path as specified by the second argument. The second argument can be
considered the “target” of the override.

Assume the ubus_new_slave_monitor has already been defined. Once the following code is
executed, the environment will now create the new type, ubus_new_slave_monitor, for all instances
that match the instance path.

ubus_slave_monitor::type_id::set_inst_override(ubus_new_slave_monitor::
get_type(), “slaves[0].monitor”);

In this case, the type is overridden that is created when the environment should create an
ubus_slave_monitor for only the slaves[0].monitor instance that matches the instance path in
the override. The complete hierarchy would now be built as shown in Figure 32. For illustration purposes,
this hierarchy assumes both overrides have been executed.
NOTE—Instance overrides are used in a first-match order. For each component, the first applicable instance override is
used when the environment is constructed. If no instance overrides are found, then the type overrides are searched for
any applicable type overrides. The ordering of the instance overrides in your code affects the application of the instance
overrides. You should execute more-specific instance overrides first. For example,
mytype::type_id::set_inst_override(newtype::get_type(), "a.b.*");
my_type::type_id::set_inst_override(different_type::get_type(), "a.b.c");
will create a.b.c with different_type. All other objects under a.b of mytype are created using newtype. If
you switch the order of the instance override calls then all of the objects under a.b will get newtype and the instance
override a.b.c is ignored.
my_type::type_id::set_inst_override(different_type::get_type(), "a.b.c");
mytype::type_id::set_inst_override(newtype::get_type(), "a.b.*");

132 Copyright © 2011 - 2015 Accellera. All rights reserved.


UVM 1.2 User’s Guide October 8, 2015

ubus_demo_tb0
(ubus_demo_tb) New type created by type override

ubus0 (ubus_env) xserial0 (xserial_env)

masters[0] (ubus_master_agent) tx (xserial_tx_agent)

driver (ubus_new_master_driver) driver (xserial_tx_driver)

sequencer (ubus_master_sequencer) sequencer (xserial_tx_sequencer)

monitor (ubus_master_monitor) monitor (xserial_tx_monitor)

slaves[0] (ubus_slave_agent) bus_monitor (xserial_bus_monitor)

driver (ubus_slave_driver)

sequencer (ubus_slave_sequencer)

monitor (ubus_new_slave_monitor) New type created by instance override

bus_monitor (ubus_bus_monitor)

Figure 32—Hierarchy Created with both Overrides Applied

6.3 Callbacks

Callbacks are an optional facility end users can use to augment component behavior. Callbacks may have a
noticeable impact on performance, so they should only be used when the desired functionality cannot be
achieved in any other way.

6.3.1 Use Model

To provide a callback facility to end-users, the component developer needs to:


a) Derive a callback class from the uvm_callback base. It should declare one or more methods that
comprise the “callback interface”.
b) Optionally, define a typedef to the uvm_callbacks pool typed to our specific component-call-
back combination.
c) Define the component to iteratively invoke the registered implementations of each method defined
in the callback class defined in Step (a) at the desired locations within a component main body of
code. Whether a function or a task is used is determined by the ability of the component’s code or
behavior to be suspended or not during its execution.

To use callbacks, the user needs to:

Copyright © 2011 - 2015 Accellera. All rights reserved. 133


October 8, 2015 UVM 1.2 User’s Guide

d) Define a new callback class extending from the callback base class provided by the developer, over-
riding one or more of the available callback methods.
e) Register one or more instances of the callback with the component(s) you wish to extend.

These steps are illustrated in the following simple example.

6.3.2 Example

The example below demonstrates callback usage. The component developer defines a driver component and
a driver-specific callback class. The callback class defines the hooks available for users to override. The
component using the callbacks (that is, calling the callback methods) also defines corresponding virtual
methods for each callback hook. The end-user may then define either a callback or a driver subtype to
extend driver’s behavior.

6.3.2.1 Developer Code

a) Define a callback class extending from uvm_callback.


The callback class defines an application-specific interface consisting of one or more function or
task prototypes. The signatures of each method have no restrictions. To be able to use the callback
invocation macros, functions should return void or bit type.
In the example below, a new bus_bfm_cb class extending from uvm_callback is defined. The
developer of the bus_bfm component decides to add two hooks for users, trans_received and
trans_executed:
1) trans_received—the bus driver calls this after it first receives a new transaction item. It
provides a handle to both itself and the new transaction. An additional argument determines
whether to drop (1) or execute (0) the transaction.
2) trans_executed—the bus driver calls this after executing the transaction, passing in a han-
dle to itself and the transaction, which may contain read data or other status information.
virtual class bus_driver_cb extends uvm_callback;
virtual function void trans_received(bus_driver driver, bus_tr tr,
ref bit drop);
endfunction
virtual task trans_executed(bus_driver driver, bus_tr tr);
endtask
function new(string name="bus_driver_cb");
super.new(name);
endfunction
endclass
b) Define a typedef to the uvm_callbacks pool typed to our specific component-callback com-
bination.
UVM callbacks are type-safe, meaning any attempt to register a callback to a component not
designed for that callback simply will not compile. In exchange for this type-safety we must endure
a bit of parameterized syntax as follows:
typedef uvm_callbacks #(bus_driver, bus_driver_cb) bus_driver_cb_pool;
The alias bus_driver_cb_pool can help both the component developer and the end-user pro-
duce more readable code.
c) Embed the callback feature in the component that will use it.

The developer of the bus_bfm adds the trans_received and trans_executed virtual methods,
with empty default implementations and utilizes some macros that implement the most common algorithms

134 Copyright © 2011 - 2015 Accellera. All rights reserved.


UVM 1.2 User’s Guide October 8, 2015

for executing all registered callbacks. With this in place, end-users can now customize component behavior
in two ways:
— extend bus_driver and override one or more of the virtual methods trans_received or
trans_executed. Then configure the factory to use the new type via a type or instance override.
— extend bus_driver_cb and override one or more of the virtual methods trans_received or
trans_executed. Then register an instance of the new callback type with all or a specific
instance of bus_driver. The latter requires access to the specific instance of the bus_driver.

class bus_driver extends uvm_driver;


function new (string name, uvm_component parent=null);
super.new(name,parent);
endfunction
`uvm_register_cb(bus_driver, bus_driver_cb)
virtual function bit trans_received(bus_tr tr, ref bit drop);
endfunction
virtual task trans_executed(bus_tr tr);
endtask
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
bus_tr tr;
bit drop = 0;
seq_item_port.get_next_item(tr);
‘uvm_info("bus_tr received",tr.convert2string(), UVM_LOW)
trans_received(tr, drop);
‘uvm_do_callbacks(bus_driver_cb, bus_driver,
trans_received(this, tr, drop))
if (drop) begin
‘uvm_info("bus_tr dropped",
"user callback indicated DROPPED", UVM_HIGH)
return;
end
#100;
trans_executed(tt);
`uvm_do_callbacks(bus_driver_cb, bus_driver,
trans_executed(this, tr))
‘uvm_info("bus_tr executed", tr.convert2string(), UVM_LOW)
seq_item_port.item_done(tr);
end // forever
endtask
endclass

The driver’s put task, which implements the component’s primary functionality, merely calls the virtual
methods and callbacks at the appropriate times during execution.

6.3.2.2 End User Code

Using the callback feature of a component involves the following steps:


a) Extend the developer-supplied callback class.
Define a new callback class that extends from the class provided by the component developer,
implementing any or all of the methods of the callback interface.
In our example, we define both hooks, trans_received and trans_executed. For
trans_received, we randomly choose whether to return 0 or 1. When 1, the bus_driver
will “drop” the received transaction. For trans_executed, we delay #10 to prevent back-to-
back transactions.

Copyright © 2011 - 2015 Accellera. All rights reserved. 135


October 8, 2015 UVM 1.2 User’s Guide

class my_bus_bfm_cb extends bus_bfm_cb;


function new(string name="bus_bfm_cb_inst");
super.new(name);
endfunction
`uvm_object_utils(my_bus_bfm_cb)
virtual function bit trans_received(bus_bfm driver, bus_tr tr);
‘uvm_info_context("trans_received_cb",
{" bus_bfm=",driver.get_full_name(),"
tr=",tr.convert2string()}, UVM_LOW, driver)
return $urandom & 1;
endfunction
virtual task trans_executed(bus_bfm driver, bus_tr tr);
‘uvm_info("trans_executed_cb",
{" bus_bfm=",driver.get_full_name(),"
tr=",tr.convert2string()}, UVM_LOW, driver)
#10;
endtask
endclass
b) Create callback object(s) and register with component you wish to extend.
To keep the example simple and focus on callback usage, we do not show a complete or compliant
UVM environment.
In the top module, we instantiate the bus_bfm and an instance of our custom callback class. To
register the callback object with the driver, we first get a handle to the global callback pool for our
specific driver-callback combination. Luckily, the developer provided a convenient typedef in his
Step (b) that makes our code a little more readable.
Then, we associate (register) the callback object with a driver using the callback pool’s add_cb
method. After calling display_cbs to show the registration was successful, we push several
transactions into the driver. The output shows that the methods in our custom callback implementa-
tion are called for each transaction the driver receives.
module top;
bus_tr tr = new;
bus_bfm driver = new("driver");
my_bus_bfm_cb cb = new("cb");

initial begin
bd_cb::add(driver,cb);
cbs.display_cbs();
for (int i=1; i<=5; i++) begin
tr.addr = i;
tr.data = 6-i;
driver.in.put(tr);
end
end
endmodule
c) Instance-specific callback registrations can only be performed after the component instance exists.
Therefore, those are typically done in the build() and end_of_elaboration() for exten-
sions that need to apply for the entire duration of the test and in the run() method for extensions
that need to apply for a specific portion of the testcase.
class error_test extends uvm_test;
function new(string name = “error_test”, uvm_component parent = null);
super.new(name, parent);
endfunction

virtual task run_phase(uvm_phase phase);


cbs = new;

136 Copyright © 2011 - 2015 Accellera. All rights reserved.


UVM 1.2 User’s Guide October 8, 2015

#1000;
bd_cb::add_by_name(“top.bfm”, cbs, this);
#100;
bd_cb::delete(this, cbs);
endtask
endclass

6.4 The Sequence Library

In UVM, it is possible to group similar sequences together into a sequence library. The
uvm_sequence_library is an extension of the uvm_sequence base class.

class uvm_sequence_library #(type REQ=int, RSP=REQ)


extends uvm_sequence#(REQ,RSP);

The uvm_sequence_library is a sequence that contains a list of registered sequence types. It can be
configured to create and execute these sequences any number of times using one of several modes of
operation, including a user-defined mode. When started (as any other sequence) the sequence library will
randomly select and execute a sequence from its sequences queue, depending on the
selection_mode chosen.
— UVM_SEQ_LIB_RAND: Randomly select from the queue.
— UVM_SEQ_LIB_RANDC: Randomly select from the queue without repeating until all sequences
have executed.
— UVM_SEQ_LIB_ITEM: Execute a single item.
— UVM_SEQ_LIB_USER: Call the select_sequence() method, which the user may override, to
generate an index into the queue to select a sequence to execute.

The selection mode may be set using the configuration database:

uvm_config_db#(uvm_sequence_lib_mode)::set(this, “<sequencer path>”,


“default_sequence.selection_mode”,
MODE);

To create a sequence library, declare your own extension of uvm_sequence_library and initialize it as
follows:

class my_seq_lib extends uvm_sequence_library #(my_item);


‘uvm_object_utils(my_seq_lib)
‘uvm_sequence_library_utils(my_seq_lib)
function new(string name=””);
super.new(name);
init_sequence_library();
endfunction
...
endclass

Individual sequence types may then be added to the sequence library, by type, using the
‘uvm_add_to_seq_lib macro:

class my_seq1 extends my_seq;


‘uvm_object_utils(my_seq1);
‘uvm_add_to_seq_lib(my_seq1, my_seq_lib)
‘uvm_add_to_seq_lib(my_seq1, my_other_seq_lib)
...
endclass

Copyright © 2011 - 2015 Accellera. All rights reserved. 137


October 8, 2015 UVM 1.2 User’s Guide

A sequence type may be added to more than one sequence library by having multiple
‘uvm_add_to_seq_lib calls in the sequence definition. The parameterization of the sequences and the
sequence library must be compatible.

Alternatively, sequences can be added to every instance of a particular type of sequence library using the
add_typewide_sequence() and/or add_typewide_sequences() methods:

class mem_seq_lib extends uvm_sequence_library #(mem_item);


...
function new(string name="mem_seq_lib");
super.new(name);
//Explicitly add the memory sequences to the library
add_typewide_sequences({mem_seq1::get_type(), mem_seq2::get_type(),
mem_seq3::get_type(), mem_seq4::get_type(), mem_seq5::get_type(),
mem_seq6::get_type()});
init_sequence_library();
endfunction : new
endclass : mem_seq_lib

The most convenient location to place the add_typewide_sequence() and/or


add_typewide_sequences() call is in the constructor of the sequence_library type itself. You
may also register a sequence with an individual instance of a sequence library by using the
add_sequence() or add_sequences() method. This would typically be done in the test where the
sequence is instantiated.

class test_seq_lib extends test_base;


...
task main_phase(uvm_phase phase);
phase.raise_objection(this, "Raising Main Objection");
//Register another sequence with this sequence library instance
seq_lib.add_sequence( mem_error_seq::get_type() );
//Start the mem sequence
seq_lib.start(m_mem_sequencer); //This task call is blocking
phase.drop_objection(this, "Dropping Main Objection");
endtask : main_phase
endclass : test_seq_lib

The sequence library is then just started as any other sequence.

class test_seq_lib extends test_base;


...
task main_phase(uvm_phase phase);
phase.raise_objection(this, "Raising Main Objection");
//Configure the constraints for how many sequences are run
seq_lib.min_random_count = 5;
seq_lib.max_random_count = 12;
//Randomize the sequence library
if (!seq_lib.randomize())
`uvm_error(report_id, "The mem_seq_lib library failed to randomize()")
//Start the mem sequence
seq_lib.start(m_mem_sequencer); //This task call is blocking
phase.drop_objection(this, "Dropping Main Objection");
endtask : main_phase
endclass : test_seq_lib

138 Copyright © 2011 - 2015 Accellera. All rights reserved.


UVM 1.2 User’s Guide October 8, 2015

6.5 Advanced Sequence Control

This section discusses advanced techniques for sequence control.

6.5.1 Implementing Complex Scenarios

This section highlights how to implement various complex scenarios.

6.5.1.1 Executing Multiple Sequences Concurrently

There are two ways you can create concurrently-executing sequences: the following subsections show an
example of each method.

6.5.1.1.1 Using the uvm_do Macros with fork/join

In this example, the sequences are executed with fork/join. The simulator schedules which sequence
requests interaction with the sequencer. The sequencer schedules which items are provided to the driver,
arbitrating between the sequences that are willing to provide an item for execution and selects them one at a
time. The a and b sequences are subsequences of the fork_join_sequence.

class fork_join_sequence extends uvm_sequence #(simple_item);


... // Constructor and UVM automation macros go here.
// See Section 4.7.2
a_seq a;
b_seq b;
virtual task body();
fork
`uvm_do(a)
`uvm_do(b)
join
endtask : body
endclass : fork_join_sequence

6.5.1.1.2 Starting several Sequences in Parallel

In this example, the concurrent_seq sequence activates two sequences in parallel. It waits for the
sequences to complete. Instead, it immediately finishes after activating the sequences. Also, the a and b
sequences are started as root sequences.

class concurrent_seq extends uvm_sequence #(simple_item);


... // Constructor and UVM automation macros go here.
// See Section 4.7.2
a_seq a;
b_seq b;
virtual task body();
// Initialize the sequence variables with the factory.
`a = a_seq::type_id::create(“a”);
b = b_seq::type_id::create(“b”);
// Start each subsequence as a new thread.
fork
a.start(m_sequencer);
b.start(m_sequencer);
join
endtask : body
endclass : concurrent_seq

Copyright © 2011 - 2015 Accellera. All rights reserved. 139

You might also like