Vous êtes sur la page 1sur 119

UVM Tutorial for Candy Lovers – 1.

Overview
July 24, 2011 Keisuke Shimizu
Last Updated: April 4, 2014

Accellera’s recently released UVM may change the future of verification, as


verification methodology seems to be consolidated in this UVM. This post will provide a simple
tutorial on this new verification methodology. Rather than focusing on AXI, OCP, or other system
buses in existence, this tutorial will be based on the hypothetical example of a jelly-bean generator.
The test bench will generate many jelly-bean flavors in a constrained random manner and the
system will evaluate palatable flavors. This does not require the knowledge of any system bus.

The verification components used in the process will be described below.

Verification Components
The left figure shows the relationship of the verification components. The jelly_bean_taster is the
design-under-test (DUT) module.
The jelly_bean_sequencer will create jelly-bean recipes and send them to
the jelly_bean_driver. From the information provided in the recipe, the driver creates jelly beans.
The driver passes the jelly beans through the jelly-bean interface (jelly_bean_if) to
the jelly_bean_taster, which will check the jelly-bean taste.

Concurrently, as the jelly beans are being created, the jelly_bean_monitor will capture the flavor
and color of the recently produced. This information will be passed down to the jelly-bean functional
coverage subscriber, referred to as the jelly_bean_fc_subscriber. The subscriber records and
totals the jelly beans based on their color and flavor.

The jelly_bean_scoreboard is a component that checks if the jelly_bean_taster is responding


correctly. The scoreboard subscribes the information from the jelly_bean_monitor.

Class Diagram of Verification Components


UVM Tutorial for Candy Lovers – 2. Recipe
July 24, 2011 Keisuke Shimizu
Last Updated: April 4, 2014

While the last post clarified the verification components of the jelly-bean taster, this post will provide
a focus for the jelly-bean recipe.

The jelly-bean recipe is passed as a transaction from the jelly_bean_sequencer to


the jelly_bean_driver. The transaction is referred to as the jelly_bean_transaction. The
transaction defines the jelly bean, based on its flavor, color, and other characteristics. The
similar jelly_bean_transaction is passed from the jelly_bean_monitor to every subscriber as
well.

The jelly_bean_sequencer does not only produce the recipe of the individual jelly bean – rather it
is able to produce a similar flavor of multiple jelly beans, as a sequence of transactions, and a
collection of multiple-flavored jelly beans in the form of a gift box, as a sequence of sequences .

The class diagram of the transaction and sequence is shown below. The colors of this diagram
correspond to the colors listed in the previous post. The light blue boxes refer to the classes in the
UVM basic class library, while the darker boxes indicate the classes created in this tutorial.

Objects Class Diagram of Verification


Verification Components
Though this post ends on this note, the next will provide a focus on the SystemVerilog code itself.
UVM Tutorial for Candy Lovers – 3. Transactions
and Sequences
July 26, 2011 Keisuke Shimizu
Last Updated: June 29, 2014

This post will provide an explanation on the SystemVerilog code itself. Please see Recipe for
the class diagram.

Transactions
Jelly-Bean Transaction
The jelly_bean_transaction class defines the jelly-bean recipe based on flavor, color, and other
characteristics. It also has the property called taste to hold the reaction of
the jelly_bean_taster (DUT) to the flavor. The relationship between the flavor and color is
defined in the constraint block (line 12). For example, if the flavor is apple, the color must be red or
green (not blue).

Update (April 2, 2014): In this example, we used so-called UVM field macros (lines 23 to 27) to save
the effort of writing “do-hook” functions. I would recommend writing your own “do-hook” functions
instead of using these macros once you become familiar with UVM. For more information, please
see Field Macros and “Do” Hooks.

class jelly_bean_transaction extends uvm_sequence_item;


typedef enum bit[2:0] { NO_FLAVOR, APPLE, BLUEBERRY, BUBBLE_GUM, CHOCOLATE } flavor_e;
typedef enum bit[1:0] { RED, GREEN, BLUE } color_e;
typedef enum bit[1:0] { UNKNOWN, YUMMY, YUCKY } taste_e;

rand flavor_e flavor;


rand color_e color;
rand bit sugar_free;
rand bit sour;
taste_e taste;

constraint flavor_color_con { flavor != NO_FLAVOR; flavor == APPLE -> color !=


BLUE; flavor == BLUEBERRY -> color == BLUE; }
function new(string name = "");
super.new(name);
endfunction: new

`uvm_object_utils_begin(jelly_bean_transaction)
`uvm_field_enum(flavor_e, flavor, UVM_ALL_ON)
`uvm_field_enum(color_e, color, UVM_ALL_ON)
`uvm_field_int(sugar_free, UVM_ALL_ON)
`uvm_field_int(sour, UVM_ALL_ON)
`uvm_field_enum(taste_e, taste, UVM_ALL_ON)
`uvm_object_utils_end
endclass: jelly_bean_transaction
Sugar-Free Jelly Bean
The jelly_bean_transaction class can be extended to various classes. For example, to create
only sugar-free jelly beans, the child class can define the constraint as shown in line 4.

1
2
3 class sugar_free_jelly_bean_transaction extends jelly_bean_transaction;
4 `uvm_object_utils(sugar_free_jelly_bean_transaction)
5
6 constraint sugar_free_con { sugar_free == 1; }
7 function new(string name = "");
8 super.new(name);
9 endfunction: new
10 endclass: sugar_free_jelly_bean_transaction
11

Sequences
One Jelly Bean
The sequence creates the recipes of the jelly beans being generated. The first sequence is the
simplest, as it is designed to create a single jelly bean. The line 10 creates a single jelly-bean
transaction (a recipe), then the line 12 randomizes the recipe.

class one_jelly_bean_sequence extends uvm_sequence#(jelly_bean_transaction);


`uvm_object_utils(one_jelly_bean_sequence)

function new(string name = "");


super.new(name);
endfunction: new

task body();
jelly_bean_transaction jb_tx;
jb_tx = jelly_bean_transaction::type_id::create(.name("jb_tx"), .contxt(get_full_name()));
start_item(jb_tx);
assert(jb_tx.randomize()); finish_item(jb_tx);
endtask: body
endclass: one_jelly_bean_sequence

Same-Flavored Jelly Beans (Sequence of Transactions)


Now let’s create the multiple jelly beans of the same flavor. The number of jelly beans being created
is specified with the class property called num_jelly_beans. The line 4 constrains
the num_jelly_beans to be between 2 and 4. The line 14 creates a single jelly bean, and the line 15
randomizes its color and flavor. With this jelly bean as the standard, the line 18 creates jelly beans of
the same flavor, as many as specified by the num_jelly_beans. In-line constraint on the line 21
guarantees the same flavor to be generated.
1
2 class same_flavored_jelly_beans_sequence extends
3 uvm_sequence#(jelly_bean_transaction);
4 rand int unsigned num_jelly_beans; // knob
5
6 constraint num_jelly_beans_con { num_jelly_beans inside { [2:4]
7 }; }
8 function new(string name = "");
9 super.new(name);
10 endfunction: new
11
12 task body();
13 jelly_bean_transaction jb_tx;
14 jelly_bean_transaction::flavor_e jb_flavor;
15
16 jb_tx =
17 jelly_bean_transaction::type_id::create(.name("jb_tx"),
18 .contxt(get_full_name())); assert(jb_tx.randomize());
19 jb_flavor = jb_tx.flavor;
20
21 repeat (num_jelly_beans) begin jb_tx =
22 jelly_bean_transaction::type_id::create(.name("jb_tx"),
23 .contxt(get_full_name())); start_item(jb_tx);
24 assert(jb_tx.randomize() with { jb_tx.flavor == jb_flavor; });
25 finish_item(jb_tx); end endtask: body
26
27 `uvm_object_utils_begin(same_flavored_jelly_beans_sequence)
28 `uvm_field_int(num_jelly_beans, UVM_ALL_ON)
29 `uvm_object_utils_end
endclass: same_flavored_jelly_beans_sequence

Gift-Boxed Jelly Beans (Sequence of Sequences)


The code below demonstrates how to create multiple-flavored jelly beans. The number of flavors is
specified with the class property called num_jelly_bean_flavors (line 2). On the 4th line, the
sequence randomly picks two to three flavors. The main part of the sequence is the repeat block
(from the line 12), where a sequence of same_flavored_jelly_beans_sequences is created.

1 class gift_boxed_jelly_beans_sequence extends


2 uvm_sequence#(jelly_bean_transaction);
3 rand int unsigned num_jelly_bean_flavors; // knob
4 constraint num_jelly_bean_flavors_con { num_jelly_bean_flavors
5 inside { [2:3] }; }
6 function new(string name = "");
7 super.new(name);
8 endfunction: new
9
10 task body();
11 same_flavored_jelly_beans_sequence jb_seq;
12 repeat (num_jelly_bean_flavors) begin jb_seq =
13 same_flavored_jelly_beans_sequence::type_id::create(.name("jb_seq"),
14 .contxt(get_full_name())); assert(jb_seq.randomize());
15 jb_seq.start(.sequencer(m_sequencer), .parent_sequence(this));
16 end endtask: body
17
18 `uvm_object_utils_begin(gift_boxed_jelly_beans_sequence)
19 `uvm_field_int(num_jelly_bean_flavors, UVM_ALL_ON)
20 `uvm_object_utils_end
21 endclass: gift_boxed_jelly_beans_sequence
22

This post has an emphasis on the transactions and sequences in the jelly-bean verification
environment. The next post will provide an explanation on the verification components.

UVM Tutorial for Candy Lovers – 4. Agent


July 26, 2011 Keisuke Shimizu
Last Updated: July 12, 2015

The last post concentrated on the transactions and sequences of the jelly-bean taster
system. This post will explain the verification components in the verification environment further in
depth.

Interface
In this segment, a general explanation of the interface (jelly_bean_if) and its role of binding
verification components and the jelly-bean taster (DUT) will be provided.

The inside of the jelly_bean_if is shown below. In essence, the jelly_bean_if contains signals
that have been transformed from the properties in the jelly_bean_transaction.

interface jelly_bean_if(input bit clk);


logic [2:0] flavor;
logic [1:0] color;
logic sugar_free;
logic sour;
logic [1:0] taste;

` clocking master_cb @ (posedge clk); default input #1step output #1ns;


output flavor, color, sugar_free, sour; input taste;
endclocking: master_cb
clocking slave_cb @ (posedge clk); default input #1step output #1ns;
input flavor, color, sugar_free, sour; output taste;
endclocking: slave_cb
modport master_mp(input clk, taste, output flavor, color, sugar_free, sour);
modport slave_mp(input clk, flavor, color, sugar_free, sour, output taste);
modport master_sync_mp(clocking master_cb);
modport slave_sync_mp(clocking slave_cb);
endinterface: jelly_bean_if
The timings of the respective signals are defined by the clocking blocks. The master_cb defines the
timings from the bus-master point of view, while the slave_cb defines the timings from the bus-slave
point of view.

The interface also has the modport lists, which define the directions of the signals within the
interface. We define four modport lists:
Directions Seen
Name Timing Used by
from

master_mp Master Asynchronous DUT

slave_mp Slave Asynchronous DUT

Synchronous
master_sync_mp Master Test-bench
with master_cb

Synchronous
slave_sync_mp Slave Test-bench
with slave_cb

The DUT uses the asynchronous modport lists (master_mp and slave_mp), while the test-bench
uses the synchronous modport lists (master_sync_mp and slave_sync_mp).

Update (April 2, 2014): We noticed that some simulators don’t like #1step (lines 9 and 15). You can
change it to #1ns if that is the case.

DUT
The code below shows a simple structural example of the jelly_bean_taster. The line 1 specifies
the jelly_bean_if we just defined above, and the slave_mp to select the appropriate directional
information for the interface signals. The taster will only respond negatively to the sour chocolate
flavor!

1 module jelly_bean_taster( jelly_bean_if.slave_mp jb_slave_if );


2 import jelly_bean_pkg::*;
3 always @ ( posedge jb_slave_if.clk ) begin
4 if ( jb_slave_if.flavor ==
5 jelly_bean_transaction::CHOCOLATE &&
6 jb_slave_if.sour ) begin
7 jb_slave_if.taste <= jelly_bean_transaction::YUCKY;
8 end else begin
9 jb_slave_if.taste <= jelly_bean_transaction::YUMMY;
10 end
11 end
endmodule: jelly_bean_taster
Alternatively, you can specify the modport list in the port connection with the module instance.

module jelly_bean_taster( jelly_bean_if jb_slave_if ); // no modport specified


// ...
endmodule

module top;
reg clk;
jelly_bean_if jb_slave_if( clk );
jelly_bean_taster jb_taster( jb_slave_if.slave_mp ); // modport specified
// ...
endmodule
You can specify the modport list in both the module declaration and the module instance, but they
must be identical if you do so. If no modport list is specified at all, the signals in the interface are
assumed to have inout access.

Sequencer
The jelly-bean sequence is processed by the sequencer. The uvm_sequencer is used as is, as jelly-
bean sequencer is not involved with any kind of extended features.

typedef uvm_sequencer#(jelly_bean_transaction) jelly_bean_sequencer;


Driver
The driver receives the jelly_bean_transaction through the seq_item_port on the line 22,
leading to pin wiggling. The driver uses the interface between the driver and the DUT to drive the
signals. The interface is stored in the uvm_resource_db (line 12).

Update (April, 2, 2014): In this tutorial, we used uvm_resource_db to retrieve


the jelly_bean_if (lines 12 and 13). UVM recommends using uvm_config_db instead
of uvm_resource_db as the former is more robust. You can replace the lines 12 and 13 with:

assert( uvm_config_db#( virtual jelly_bean_if )::


get( .cntxt( this ), .inst_name( "" ), .field_name( "jelly_bean_if" ),
.value( jb_vi ) ) );
For more information about the configuration database, please see Configuration
Database and Configuration Database Revisited.

1
class jelly_bean_driver extends uvm_driver#(jelly_bean_transaction);
2
`uvm_component_utils(jelly_bean_driver)
3
4
virtual jelly_bean_if jb_vi;
5
6
function new(string name, uvm_component parent);
7
super.new(name, parent);
8
endfunction: new
9
10
function void build_phase(uvm_phase phase);
11
super.build_phase(phase);
12
void'(uvm_resource_db#(virtual jelly_bean_if)::read_by_name
13
(.scope("ifs"), .name("jelly_bean_if"), .val(jb_vi))); endfunction:
14
build_phase
15
16
task run_phase(uvm_phase phase);
17
jelly_bean_transaction jb_tx;
18
19
forever begin
20
@jb_vi.master_cb;
21
jb_vi.master_cb.flavor <= jelly_bean_transaction::NO_FLAVOR;
22
23 seq_item_port.get_next_item(jb_tx); @jb_vi.master_cb;
24 jb_vi.master_cb.flavor <= jb_tx.flavor;
25 jb_vi.master_cb.color <= jb_tx.color;
26 jb_vi.master_cb.sugar_free <= jb_tx.sugar_free;
27 jb_vi.master_cb.sour <= jb_tx.sour;
28 seq_item_port.item_done(); end
29 endtask: run_phase
30 endclass: jelly_bean_driver
31

The lines 20 and 23 are so-called clocking block events. They are equivalent to @( posedge
jb_vi.clk ).

Monitor
The flow of data to the monitor is the opposite direction, yet similar to the driver. The interface to the
DUT (jelly_bean_if) is found in the uvm_resource_db (line 14). The monitor closely monitors
the jelly_bean_if, and takes in the value of the signals. Our monitor watches a
non NO_FLAVOR jelly bean (line 23) and creates a jelly_bean_transaction (line 24). The created
transaction is sent via the analysis port to the subscribers on line 31.

Update (April, 2, 2014): We used the uvm_resource_db to retrieve the jelly_bean_if (lines 14
and 15). Please see the above section for how to use uvm_config_db instead of
the uvm_resource_db.

1
2 class jelly_bean_monitor extends uvm_monitor;
3 `uvm_component_utils(jelly_bean_monitor)
4
5 uvm_analysis_port#(jelly_bean_transaction) jb_ap;
6
7 virtual jelly_bean_if jb_vi;
8
9 function new(string name, uvm_component parent);
10 super.new(name, parent);
11 endfunction: new
12
13 function void build_phase(uvm_phase phase);
14 super.build_phase(phase);
15 void'(uvm_resource_db#(virtual jelly_bean_if)::read_by_name
16 (.scope("ifs"), .name("jelly_bean_if"), .val(jb_vi)));
17 jb_ap = new(.name("jb_ap"), .parent(this));
18 endfunction: build_phase
19
20 task run_phase(uvm_phase phase);
21 forever begin
22 jelly_bean_transaction jb_tx;
23 @jb_vi.slave_cb;
24 if (jb_vi.slave_cb.flavor != jelly_bean_transaction::NO_FLAVOR)
25 begin jb_tx =
26 jelly_bean_transaction::type_id::create(.name("jb_tx"),
27 .contxt(get_full_name()));
28
29 jb_tx.flavor = jelly_bean_transaction::flavor_e'(jb_vi.slave_cb.flavor);
30 jb_tx.color = jelly_bean_transaction::color_e'(jb_vi.slave_cb.color);
31 jb_tx.sugar_free = jb_vi.slave_cb.sugar_free;
32 jb_tx.sour = jb_vi.slave_cb.sour;
33 @jb_vi.master_cb;
34 jb_tx.taste = jelly_bean_transaction::taste_e'(jb_vi.master_cb.taste);
35 jb_ap.write(jb_tx);
end
end
endtask: run_phase
endclass: jelly_bean_monitor
Agent
The sequencer, driver, monitor – all are collected in the agent. These components are created in
the build_phase, and the created components are connected in the connect_phase. As the
subscriber is not an asset included in the agent, an analysis port is created to communicate with the
subscriber. The analysis ports, in both the agent and the monitor, are connected to each other on
line 26.

class jelly_bean_agent extends uvm_agent;


1 `uvm_component_utils(jelly_bean_agent)
2
3 uvm_analysis_port#(jelly_bean_transaction) jb_ap;
4
5 jelly_bean_sequencer jb_seqr;
6 jelly_bean_driver jb_drvr;
7 jelly_bean_monitor jb_mon;
8
9 function new(string name, uvm_component parent);
10 super.new(name, parent);
11 endfunction: new
12
13 function void build_phase(uvm_phase phase);
14 super.build_phase(phase);
15
16 jb_ap = new(.name("jb_ap"), .parent(this));
17 jb_seqr = jelly_bean_sequencer::type_id::create(.name("jb_seqr"),
18 .parent(this));
19 jb_drvr = jelly_bean_driver::type_id::create(.name("jb_drvr"),
20 .parent(this));
21 jb_mon = jelly_bean_monitor::type_id::create(.name("jb_mon"),
22 .parent(this));
23 endfunction: build_phase
24
25 function void connect_phase(uvm_phase phase);
26 super.connect_phase(phase);
27 jb_drvr.seq_item_port.connect(jb_seqr.seq_item_export);
28 jb_mon.jb_ap.connect(jb_ap);
endfunction: connect_phase
endclass: jelly_bean_agent
Though this post ends here, the next will continue to provide an explanation for other verification
components.
UVM Tutorial for Candy Lovers – 5. Environment
July 27, 2011 Keisuke Shimizu
Last Updated: April 4, 2014

This post will provide a continued explanation on the rest of the verification
components.

Subscribers
Functional Coverage
The functional coverage subscriber (jelly_bean_fc_sucbscriber) identifies the generated jelly
beans to take a total tally. The jelly_bean_transaction sent from the monitor is sampled by
the write function on the line 21, and takes the cross coverage of the flavor, color, and other
characteristics of the jelly bean.

class jelly_bean_fc_subscriber extends


1 uvm_subscriber#(jelly_bean_transaction);
2 `uvm_component_utils(jelly_bean_fc_subscriber)
3
4 jelly_bean_transaction jb_tx;
5
6 covergroup jelly_bean_cg; flavor_cp:
7 coverpoint jb_tx.flavor;
8 color_cp:
9 coverpoint jb_tx.color;
10 sugar_free_cp:
11 coverpoint jb_tx.sugar_free;
12 sour_cp:
13 coverpoint jb_tx.sour;
14 cross flavor_cp, color_cp, sugar_free_cp, sour_cp;
15 endgroup: jelly_bean_cg
16 function new(string name, uvm_component parent);
17 super.new(name, parent);
18 jelly_bean_cg = new;
19 endfunction: new
20
21 function void write(jelly_bean_transaction t);
22 jb_tx = t;
23 jelly_bean_cg.sample();
endfunction: write
endclass: jelly_bean_fc_subscriber
Scoreboard
In the functional coverage subscriber, the jelly_bean_transaction was sampled in
the write function. Similar to the functional coverage subscriber, focus on the writefunction is the
key. The scoreboard subscriber uses the write function to call
the check_jelly_bean_taste function in the parent component (jelly_bean_scoreboard).
This check_jelly_bean_taste function compares the DUT response against the expected
response.

1
2 typedef class jelly_bean_scoreboard;
3
4 class jelly_bean_sb_subscriber extends uvm_subscriber#(jelly_bean_transaction);
5 `uvm_component_utils(jelly_bean_sb_subscriber)
6
7 function new(string name, uvm_component parent);
8 super.new(name, parent);
9 endfunction: new
10
11 function void write(jelly_bean_transaction t);
12 jelly_bean_scoreboard jb_sb;
13 $cast( jb_sb, m_parent );
14 jb_sb.check_jelly_bean_taste(t);
15 endfunction: write
16 endclass: jelly_bean_sb_subscriber

The check_jelly_bean_taste function expects the DUT module to “respond negatively to the sour
chocolate-flavor jelly bean, while reacting positively to the other combinations.” When the DUT
responds properly, the jelly-bean flavor and color are printed. When the DUT is not functioning
correctly, the function will print an error message.

1 class jelly_bean_scoreboard extends uvm_scoreboard;


2 `uvm_component_utils(jelly_bean_scoreboard)
3
4 uvm_analysis_export#(jelly_bean_transaction) jb_analysis_export;
5 local jelly_bean_sb_subscriber jb_sb_sub;
6
7 function new(string name, uvm_component parent);
8 super.new(name, parent);
9 endfunction: new
10
11 function void build_phase(uvm_phase phase);
12 super.build_phase(phase);
13 jb_analysis_export = new( .name("jb_analysis_export"), .parent(this));
14 jb_sb_sub =
15 jelly_bean_sb_subscriber::type_id::create(.name("jb_sb_sub"), .parent(this));
16 endfunction: build_phase
17
18 function void connect_phase(uvm_phase phase);
19 super.connect_phase(phase);
20 jb_analysis_export.connect(jb_sb_sub.analysis_export);
21 endfunction: connect_phase
22
23 virtual function void check_jelly_bean_taste(jelly_bean_transaction jb_tx);
24 uvm_table_printer p = new;
25 if (jb_tx.flavor == jelly_bean_transaction::CHOCOLATE && jb_tx.sour)
26 begin if (jb_tx.taste == jelly_bean_transaction::YUCKY)
27 begin `uvm_info("jelly_bean_scoreboard",
28 { "You have a good sense of taste.\n", jb_tx.sprint(p) }, UVM_LOW);
29 end
30 else begin `uvm_error("jelly_bean_scoreboard",
31 { "You lost sense of taste!\n", jb_tx.sprint(p) }); end end
32 else begin if (jb_tx.taste == jelly_bean_transaction::YUMMY) begin
33 `uvm_info("jelly_bean_scoreboard", { "You have a good
34 sense of taste.\n", jb_tx.sprint(p) }, UVM_LOW); end
35 else begin `uvm_error("jelly_bean_scoreboard",
36 { "You lost sense of taste!\n", jb_tx.sprint(p) }); end end
37 endfunction: check_jelly_bean_taste
38 endclass: jelly_bean_scoreboard
39
40
41
42

Many consider it a hassle to create a separate scoreboard class. In our case, it may be more
straightforward to check expected response in the jelly_bean_sb_subscriber, because the
expected response is created from the inside. Yet, there are times when two analysis ports are
necessary, one for expected data and the other for actual data. The write function of a subscriber
does not support multiple analysis ports. One solution to this problem is to develop a scoreboard
class with two subscribers. Thinking about the extendability in the future, creating two layers like our
scoreboard might be a good idea.

Environment
To provide a conclusion, this section will explain the verification environment that contains all the
verification components. Simply stated, the environment connects the previously explained agent
and the subscribers. Taking a look at the first post of the series will help better understand this
concept.

1 class jelly_bean_env extends uvm_env;


2 `uvm_component_utils(jelly_bean_env)
3
4 jelly_bean_agent jb_agent;
5 jelly_bean_fc_subscriber jb_fc_sub;
6 jelly_bean_scoreboard jb_sb;
7
8 function new(string name, uvm_component parent);
9 super.new(name, parent);
10 endfunction: new
11
12 function void build_phase(uvm_phase phase);
13 super.build_phase(phase);
14 jb_agent = jelly_bean_agent::type_id::create(.name("jb_agent"),
15 .parent(this));
16 jb_fc_sub =
17 jelly_bean_fc_subscriber::type_id::create(.name("jb_fc_sub"), .parent(this));
18 jb_sb = jelly_bean_scoreboard::type_id::create(.name("jb_sb"),
19 .parent(this));
20 endfunction: build_phase
21
22 function void connect_phase(uvm_phase phase);
23 super.connect_phase(phase);
24 jb_agent.jb_ap.connect(jb_fc_sub.analysis_export);
jb_agent.jb_ap.connect(jb_sb.jb_analysis_export);
endfunction: connect_phase
endclass: jelly_bean_env
The next post will show the anticipated test using these components.

UVM Tutorial for Candy Lovers – 6. Tasting


July 27, 2011 Keisuke Shimizu
Last Updated: September 1, 2014

The anticipated culmination of the UVM for Candy Lovers series is revealed in this
post. Using the created verification components and writing out a test class, the actual simulation is
prepared to run.

Test
This particular example will show you how to develop a variety of sugar-free jelly beans in the form
of a gift box. The test class is described below.

1 class jelly_bean_test extends uvm_test;


2 `uvm_component_utils(jelly_bean_test)
3
4 jelly_bean_env jb_env;
5
6 function new(string name, uvm_component parent);
7 super.new(name, parent);
8 endfunction: new
9
1 function void build_phase(uvm_phase phase);
0 super.build_phase(phase);
1 begin
1 jelly_bean_configuration jb_cfg;
1
2 jb_cfg = new;
1 assert(jb_cfg.randomize());
3 uvm_config_db#(jelly_bean_configuration)::set
1 (.cntxt(this), .inst_name("*"), .field_name("config"), .value(jb_cfg));
4 jelly_bean_transaction::type_id::set_type_override(sugar_free_jelly_bean_
1 transaction::get_type());
5 jb_env = jelly_bean_env::type_id::create(.name("jb_env"),
1 .parent(this));
6 end
1 endfunction: build_phase
7
1 task run_phase(uvm_phase phase);
8 gift_boxed_jelly_beans_sequence jb_seq;
1
9 phase.raise_objection(.obj(this));
2 jb_seq =
0 gift_boxed_jelly_beans_sequence::type_id::create(.name("jb_seq"),
2 .contxt(get_full_name()));
1 assert(jb_seq.randomize());
2 `uvm_info("jelly_bean_test", { "\n",
2 jb_seq.sprint() }, UVM_LOW)
2 jb_seq.start(jb_env.jb_agent.jb_seqr);
3 #10ns ;
2 phase.drop_objection(.obj(this));
4 endtask: run_phase
2 endclass: jelly_bean_test

The build_phase executes three actions:

1. the registration of the configuration of the simulation into the database (line 17)
2. the direction on the usage of the sugar_free_jelly_bean transaction rather than
the jelly_bean_transaction (line 19)
3. the creation of the jelly_bean_env (line 20)

Lines 13 to 18 were provided for future configuration uses, and were added as placeholders. An
explanation of the configuration was omitted from this series due to the lack of configuration needs.
Please see Configurations if you are interested in this topic.

In the run_phase, the created gift_boxed_jelly_beans_sequence is ready to run.

Configuration
For reference, the source code for the jelly_bean_configuration class is inserted below.

1
2 class jelly_bean_configuration extends uvm_object;
3 `uvm_object_utils(jelly_bean_configuration)
4
5 function new(string name = "");
6 super.new(name);
7 endfunction: new
endclass: jelly_bean_configuration

Top
Before proceeding with the simulation, there is a need to write the top module, which instantiates the
DUT module, registers the jelly_bean_if in the resource database, and runs the test. The top
module is responsible for clock generation as well.

Update (April 2, 2014): In this tutorial, we used uvm_resource_db to store


the jelly_bean_if (lines 15 and 16). UVM recommends using uvm_config_db instead
of uvm_resource_db as the former is more robust. You can replace the lines 15 and 16 with:

uvm_config_db#( virtual jelly_bean_if )::set( .cntxt( null ), .inst_name( "uvm_test_top.*"


.field_name( "jelly_bean_if" ), .value( jb_slave_if ) );
For more information about the configuration database, please see Configuration
Database and Configuration Database Revisited.

1
2 module top;
3 import uvm_pkg::*;
4
5 reg clk;
6 jelly_bean_if jb_slave_if(clk);
7 jelly_bean_taster jb_taster(jb_slave_if);
8
9 initial begin // clock generation
10 clk = 0;
11 #5ns ;
12 forever #5ns clk = ! clk;
13 end
14
15 initial begin
16 uvm_resource_db#(virtual jelly_bean_if)::set
17 (.scope("ifs"), .name("jelly_bean_if"), .val(jb_slave_if));
18 run_test();
19 end
endmodule: top

Simulation
It is now time to compile and run the simulation, as part of the finale. Posted below are the results for
the simulation.

UVM_INFO @ 0: reporter [RNTST] Running test jelly_bean_test...


UVM_INFO jb.sv(481) @ 0: uvm_test_top [jelly_bean_test]
-----------------------------------------------------------------------
Name Type Size Value
-----------------------------------------------------------------------
jb_seq gift_boxed_jelly_beans_sequence - @772
num_jelly_bean_flavors integral 32 'h2
req object -
rsp object -
-----------------------------------------------------------------------

UVM_INFO jb.sv(406) @ 40: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
----------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------
jb_tx sugar_free_jelly_bean_transaction - @818
flavor flavor_e 3 CHOCOLATE
color color_e 2 RED
sugar_free integral 1 'h1
sour integral 1 'h1
taste taste_e 2 YUCKY
----------------------------------------------------------------

UVM_INFO jb.sv(406) @ 60: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
----------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------
jb_tx sugar_free_jelly_bean_transaction - @834
flavor flavor_e 3 CHOCOLATE
color color_e 2 GREEN
sugar_free integral 1 'h1
sour integral 1 'h0
taste taste_e 2 YUMMY
----------------------------------------------------------------

UVM_INFO jb.sv(406) @ 80: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
------------------------------------------------------------
Name Type Size Value
------------------------------------------------------------
jb_tx sugar_free_jelly_bean_transaction - @842
flavor flavor_e 3 APPLE
color color_e 2 GREEN
sugar_free integral 1 'h1
sour integral 1 'h1
taste taste_e 2 YUMMY
------------------------------------------------------------

UVM_INFO jb.sv(406) @ 100: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
------------------------------------------------------------
Name Type Size Value
------------------------------------------------------------
jb_tx sugar_free_jelly_bean_transaction - @850
flavor flavor_e 3 APPLE
color color_e 2 RED
sugar_free integral 1 'h1
sour integral 1 'h0
taste taste_e 2 YUMMY
------------------------------------------------------------
With these six posts, the basic UVM tutorial is complete. While this jelly-bean machine is
hypothetical, it has the ability of being rephrased with other DUT. In the next possible opportunity, I
would like to review other topics omitted from these posts, such as Virtual Sequence. I hope this
tutorial assisted in your further understanding on UVM.

UVM Tutorial for Candy Lovers – 7. Virtual


Sequence
January 17, 2012 Keisuke Shimizu
Last Updated: March 26, 2016 (added the link to EDA Playground)

Did you know the mix of two lemon and two coconut jelly beans will create
the flavor of lemon meringue pie? And the mix of two strawberry and two vanilla jelly beans will
create the flavor of strawberry shortcake? This post will provide an explanation on the virtual
sequence to create these new jelly-bean recipes.

Overview
The first figure shows the relationship of the verification components used in this post.
The jelly_bean_taster (DUT) from the previous posts was “enhanced” to take two jelly-bean
flavors at the same time through two jelly_bean_ifs. This new DUT is referred to as
the jelly_bean_taster_subsystem. To drive the two interfaces, two instances
of jelly_bean_agent are used. The jelly_bean_recipe_virtual_sequence orchestrates the
creation of jelly-bean flavors in order to make a new flavor. The second figure at the bottom of the
page shows the verification components in a class diagram, and the third figure shows the
verification objects in a class diagram.

Verification Platform

Virtual Sequence
The virtual sequence defines three new jelly-bean recipes
(LEMON_MERINGUE_PIE, STRAWBERRY_SHORTCAKE, and CANDY_APPLE) on the second line. Each
recipe requires two jelly-bean flavors. For example, to create the LEMON_MERINGUE_PIE recipe,
two LEMON jelly beans and two COCONUT jelly beans are necessary. Two sub-sequences
(same_flavored_jelly_beans_sequence) are created (line 19) to generate two flavors.
The case statement on the line 21 prepares two jelly-bean flavors based on the recipe. At the end of
the body task, the two sub-sequences are started in parallel (line 42).
class jelly_bean_recipe_virtual_sequence extends uvm_sequence#(
uvm_sequence_item );
typedef enum bit[1:0] { LEMON_MERINGUE_PIE, // 2 LEMON + 2 COCONUT
STRAWBERRY_SHORTCAKE, // 2 STRAWBERRY + 2 VANILLA
9 CANDY_APPLE // 2 APPLE + 1 CINNAMON
10 } recipe_e; rand recipe_e recipe;
11
12 jelly_bean_sequencer jb_seqr1;
13 jelly_bean_sequencer jb_seqr2;
14
15 same_flavored_jelly_beans_sequence jb_seq1;
16 same_flavored_jelly_beans_sequence jb_seq2;
17
18 function new( string name = "" );
19 super.new( name );
20 endfunction: new
21
22 task body();
23 jb_seq1 = same_flavored_jelly_beans_sequence::type_id::create( .name(
24 "jb_seq1" ), .contxt( get_full_name() ) );
25 jb_seq2 = same_flavored_jelly_beans_sequence::type_id::create( .name(
26 "jb_seq2" ), .contxt( get_full_name() ) );
27 case ( recipe ) LEMON_MERINGUE_PIE:
28 begin
29 jb_seq1.flavor = jelly_bean_transaction::LEMON;
30 jb_seq2.flavor = jelly_bean_transaction::COCONUT;
31 jb_seq1.num_jelly_beans = 2;
32 jb_seq2.num_jelly_beans = 2;
33 end
34 STRAWBERRY_SHORTCAKE: begin j
35 b_seq1.flavor = jelly_bean_transaction::STRAWBERRY;
36 jb_seq2.flavor = jelly_bean_transaction::VANILLA;
37 jb_seq1.num_jelly_beans = 2;
38 jb_seq2.num_jelly_beans = 2;
39 end
40 CANDY_APPLE: begin
41 jb_seq1.flavor = jelly_bean_transaction::APPLE;
42 jb_seq2.flavor = jelly_bean_transaction::CINNAMON;
43 jb_seq1.num_jelly_beans = 2;
44 jb_seq2.num_jelly_beans = 1;
45 end
46 endcase // case ( recipe )
47 `uvm_info( get_name(), { "\n", this.sprint() }, UVM_LOW )
48 fork
49 jb_seq1.start( .sequencer( jb_seqr1 ), .parent_sequence( this ) );
50 jb_seq2.start( .sequencer( jb_seqr2 ), .parent_sequence( this ) );
51 join endtask: body
52
53 `uvm_object_utils_begin( jelly_bean_recipe_virtual_sequence )
`uvm_field_enum ( recipe_e, recipe, UVM_ALL_ON )
`uvm_field_object( jb_seq1, UVM_ALL_ON )
`uvm_field_object( jb_seq2, UVM_ALL_ON )
`uvm_object_utils_end
endclass: jelly_bean_recipe_virtual_sequence
Test
The jelly_bean_recipe_test class creates the above mentioned virtual sequence. Firstly, the test
assigns two jelly_bean_sequencers to the virtual sequence (line 13 and 14). By doing this, the
sub-sequence, jb_seq1, will run on the sequencer in the agent #1, and the sub-sequence, jb_seq2,
will run on the sequencer in the agent #2. The test randomizes the virtual sequence and starts the
sequence on the line 15 and 16. Note that the sequencer argument of the start task
takes null since there is no sequencer associated with the virtual sequence.

1 class jelly_bean_recipe_test extends jelly_bean_base_test;


2 `uvm_component_utils( jelly_bean_recipe_test )
3
4 function new( string name, uvm_component parent );
5 super.new( name, parent );
6 endfunction: new
7
8 task main_phase( uvm_phase phase );
9 jelly_bean_recipe_virtual_sequence jb_vseq;
10
11 phase.raise_objection( .obj( this ) );
12 jb_vseq = jelly_bean_recipe_virtual_sequence::type_id::create( .name(
13 "jb_vseq" ), .contxt( get_full_name() ) );
14 jb_vseq.jb_seqr1 = jb_env.jb_agent1.jb_seqr;
15 jb_vseq.jb_seqr2 = jb_env.jb_agent2.jb_seqr;
16 assert( jb_vseq.randomize() );
17 jb_vseq.start( .sequencer( null ) );
18 #100ns ;
19 phase.drop_objection( .obj( this ) );
20 endtask: main_phase
endclass: jelly_bean_recipe_test

Simulation
Let’s run a simulation to see what flavors the virtual sequence generates. In my case, the sequence
generated a CANDY_APPLE recipe. It in turn made the first sequence (jb_seq1) generate
two APPLE jelly beans, and made the second sequence (jb_seq2) generate one CINNAMON jelly
bean.

1 UVM_INFO jb.sv(266) @ 0: reporter@@jb_vseq [jb_vseq]


------------------------------------------------------------------------------------
2 Name Type Size Value
3 ------------------------------------------------------------------------------------
4 jb_vseq jelly_bean_recipe_virtual_sequence - @1196
5 recipe recipe_e 2 CANDY_APPLE jb_seq1
num_jelly_beans integral 32 'h2 flavor je
6 object -
7 rsp object -
8 jb_seq2 same_flavored_jelly_beans_sequence - @1204
9 num_jelly_beans integral 32 'h1 flavor je
object -
10 rsp object -
11 depth int 32 'd1
12 parent sequence (name) string 0 ""
13 parent sequence (full name) string 0 ""
sequencer string 0 ""
14 req object -
15 rsp object -
------------------------------------------------------------------------------------
Component Class Diagram

Object Class Diagram


I hope this tutorial helped you understand the virtual sequence. Oh, for those of you who are

interested in more jelly-bean recipes, consult this site

UVM Tutorial for Candy Lovers – 8.


Configurations
January 31, 2012 Keisuke Shimizu
Last Updated: April 4, 2014

This post will give an explanation on UVM configuration objects, since the earlier posts did not cover
much on them. The jelly-bean verification platform uses two kinds of configuration
objects, jelly_bean_agent_config and jelly_bean_env_config. The former configures
the jelly_bean_agent and the latter configures the jelly_bean_env. The figures below show the
verification platform and the class diagram of the configuration-related classes.

Verification Platform
Class Diagram of the Configuration Classes

Agent Configuration
The jelly_bean_agent_config class configures the jelly_bean_agent. The class has two
switches; active and has_jb_fc_sub (lines 4 and 5). The active switch controls whether the
agent is in active mode or in passive mode. In the active mode, a sequencer
(jelly_bean_sequencer) and a driver (jelly_bean_driver) will be created. In the passive mode,
no sequencer or driver will be created. Similarly, the has_jb_fc_sub switch controls whether the
agent instantiates a functional coverage subscriber (jelly_bean_fc_subscriber) or not. Based on
the values of the switches, the agent will be structured as one of the four possible configurations
shown in the figure below. The jelly_bean_agent_config class also has a handle to
the jelly_bean_if (line 7).

class jelly_bean_agent_config extends uvm_object;


`uvm_object_utils( jelly_bean_agent_config )
1
2 uvm_active_passive_enum active = UVM_ACTIVE; bit has_jb_fc_sub = 1;
3 // switch to instantiate a functional coverage subscriber
4 virtual jelly_bean_if jb_if;
5 function new( string name = "" );
6 super.new( name );
7 endfunction: new
8 endclass: jelly_bean_agent_config
9

Four Possible Agent Configurations


Environment Configuration
Similar to the jelly_bean_agent_config, the jelly_bean_env_config configures
the jelly_bean_env. The class has four switches to define the structure of the environment (lines 4
to 7). It also has two handles to the jelly_bean_agent_config; one handle per agent (lines 9 and
10).

1
2
3 class jelly_bean_env_config extends uvm_object;
4 `uvm_object_utils( jelly_bean_env_config )
5
6 bit has_jb_agent1 = 1; // switch to instantiate an agent #1 bit has_jb_agent2 =
7 1; // switch to instantiate an agent #2 bit has_jb_sb1 = 1; // switch to
8 instantiate a scoreboard #1 bit has_jb_sb2 = 1; // switch to instantiate a
9 scoreboard #2
10 jelly_bean_agent_config jb_agent_cfg1; jelly_bean_agent_config jb_agent_cfg2;
11 function new( string name = "" );
12 super.new( name );
13 endfunction: new
14 endclass: jelly_bean_env_config
15

Top Module
The top Verilog module instantiates two jelly_bean_ifs (line 6 and 7) and stores them in
configuration database (lines 17 to 20). The cntxt and inst_name provide the scope information of
the virtual interface being stored. Since the top module is not a uvm_component, null is used as
the cntxt. The uvm_test_top is the name of the top-level uvm_component instantiated by
the run_test() task of the uvm_root class.

1
module top;
2
import uvm_pkg::*;
3
4
reg clk;
5
6
jelly_bean_if jb_if1( clk ); jelly_bean_if jb_if2( clk );
7
jelly_bean_subsystem dut( jb_if1, jb_if2 );
8
9
initial begin
10
clk = 0;
11
#5ns ;
12
forever #5ns clk = ! clk;
13
end
14
15
initial begin
16
uvm_config_db#( virtual jelly_bean_if )::set ( .cntxt( null ),
17
.inst_name( "uvm_test_top" ), .field_name( "jb_if1" ), .value( jb_if1 ) );
18
uvm_config_db#( virtual jelly_bean_if )::set ( .cntxt( null ), .inst_name(
19
"uvm_test_top" ), .field_name( "jb_if2" ), .value( jb_if2 ) ); run_test();
20
end
21
endmodule: top
Base Test
The base test builds configuration objects as follows:

1. The base test creates one configuration object (jb_env_cfg) for the verification environment,
and two configuration objects (jb_agent_cfg1 and jb_agent_cfg2) for the jelly-bean agents
(lines 16 to 18).
2. The jelly_bean_ifs, which we’ve created in the top module, are retrieved from the
configuration database. Then each retrieved interface is assigned to the corresponding agent
configuration (lines 20 to 27).
3. The agent configurations are assigned to the jb_env_cfg (lines 29 and 30).
4. The jb_env_cfg is stored in the configuration database so that the verification environment can
get its configuration from the database later (line 32 and 33).

1
2 class jelly_bean_base_test extends uvm_test;
3 `uvm_component_utils( jelly_bean_base_test )
4
5 jelly_bean_env jb_env;
6 jelly_bean_env_config jb_env_cfg;
7 jelly_bean_agent_config jb_agent_cfg1;
8 jelly_bean_agent_config jb_agent_cfg2;
9
10 function new( string name, uvm_component parent );
11 super.new( name, parent );
12 endfunction: new
13
14 function void build_phase( uvm_phase phase );
15 super.build_phase( phase );
16
17 jb_env_cfg = jelly_bean_env_config ::type_id::create( "jb_env_cfg" );
18 jb_agent_cfg1 = jelly_bean_agent_config::type_id::create( "jb_agent_cfg1" );
19 jb_agent_cfg2 = jelly_bean_agent_config::type_id::create( "jb_agent_cfg2" );
20
21 if ( ! uvm_config_db#( virtual jelly_bean_if )::get ( .cntxt(
22 this ), .inst_name( "" ), .field_name( "jb_if1" ), .value( jb_agent_cfg1.jb_if
23 ) ) )
24 begin `uvm_error( "jelly_bean_test", "jb_if1 not found" ) end
25 if ( ! uvm_config_db#( virtual jelly_bean_if )::get ( .cntxt(
26 this ), .inst_name( "" ), .field_name( "jb_if2" ), .value( jb_agent_cfg2.jb_if
27 ) ) )
28 begin `uvm_error( "jelly_bean_test", "jb_if2 not found" ) end
29
30 jb_env_cfg.jb_agent_cfg1 = jb_agent_cfg1;
31 jb_env_cfg.jb_agent_cfg2 = jb_agent_cfg2;
32 uvm_config_db#( jelly_bean_env_config )::set ( .cntxt( this ),
33 .inst_name( "*" ), .field_name( "jb_env_cfg" ), .value( jb_env_cfg ) );
34 jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent(
35 this ) );
36 endfunction: build_phase
37 endclass: jelly_bean_base_test
Environment
The verification environment builds itself using its configuration object as follows:

1. The environment class gets its configuration object (jb_env_cfg) from the configuration
database (lines 17 to 20).
2. If the configuration object indicates the agent #1 to be created, then the environment creates it
and stores its configuration (jb_agent_cfg1) to the configuration database (lines 23 to 25).
3. A scoreboard is created if the configuration object indicates to do so (lines 27 to 29).
4. The analysis port of the agent and the export of the scoreboard are connected if the both objects
are instantiated (lines 47 and 48).

1 class jelly_bean_env extends uvm_env;


2 `uvm_component_utils( jelly_bean_env )
3
4 jelly_bean_env_config jb_env_cfg;
5 jelly_bean_agent jb_agent1;
6 jelly_bean_agent jb_agent2;
7 jelly_bean_scoreboard jb_sb1;
8 jelly_bean_scoreboard jb_sb2;
9
10 function new( string name, uvm_component parent );
11 super.new( name, parent );
12 endfunction: new
13
14 function void build_phase( uvm_phase phase );
15 super.build_phase( phase );
16
17 if ( ! uvm_config_db#( jelly_bean_env_config )::get (
18 .cntxt( this ), .inst_name( "" ), .field_name( "jb_env_cfg" ), .value(
19 jb_env_cfg ) ) ) begin `uvm_error( "jelly_bean_env", "jb_env_cfg not
20 found" ) end
21 if ( jb_env_cfg.has_jb_agent1 ) begin
22 uvm_config_db#( jelly_bean_agent_config )::set ( .cntxt(
23 this ), .inst_name( "jb_agent1*" ), .field_name( "jb_agent_cfg" ), .value(
24 jb_env_cfg.jb_agent_cfg1 ) ); jb_agent1 =
25 jelly_bean_agent::type_id::create( .name( "jb_agent1" ), .parent( this ) );
26
27 if ( jb_env_cfg.has_jb_sb1 ) begin jb_sb1 =
28 jelly_bean_scoreboard::type_id::create( .name( "jb_sb1" ), .parent( this )
29 ); end end
30
31 if ( jb_env_cfg.has_jb_agent2 ) begin
32 uvm_config_db#( jelly_bean_agent_config )::set
33 ( .cntxt( this ), .inst_name( "jb_agent2*" ), .field_name(
34 "jb_agent_cfg" ), .value( jb_env_cfg.jb_agent_cfg2 ) );
35 jb_agent2 = jelly_bean_agent::type_id::create( .name( "jb_agent2"
36 ), .parent( this ) );
37
38 if ( jb_env_cfg.has_jb_sb2 ) begin
39 jb_sb2 = jelly_bean_scoreboard::type_id::create( .name( "jb_sb2"
40 ), .parent( this ) );
41 end
42 end
43
44 endfunction: build_phase
45
46 function void connect_phase( uvm_phase phase );
47 super.connect_phase( phase );
48
49 if ( jb_env_cfg.has_jb_agent1 && jb_env_cfg.has_jb_sb1 )
50 jb_agent1.jb_ap.connect( jb_sb1.jb_analysis_export );
51 if ( jb_env_cfg.has_jb_agent2 && jb_env_cfg.has_jb_sb2 )
jb_agent2.jb_ap.connect( jb_sb2.jb_analysis_export );
endfunction: connect_phase

endclass: jelly_bean_env
Agent
The agent builds itself using its configuration object as follows:

1. The agent class gets its configuration object (jb_agent_cfg) from the configuration database
(lines 19 to 22).
2. If the configuration object indicates the agent is active, then the agent creates a sequencer and
a driver (lines 24 to 27).
3. A functional coverage subscriber is created if the configuration object indicates to do so (lines 29
to 31).
4. The port of the driver and the export of the sequencer are connected if the agent is active. The
virtual interface of the driver is also connected (lines 42 to 45).
5. The analysis port of the agent and the analysis export of the functional-coverage subscriber are
connected if the functional-coverage subscriber exists (lines 47 to 49).

1
class jelly_bean_agent extends uvm_agent;
2
`uvm_component_utils( jelly_bean_agent )
3
4
jelly_bean_agent_config jb_agent_cfg;
5
jelly_bean_sequencer jb_seqr;
6
jelly_bean_driver jb_drvr;
7
jelly_bean_monitor jb_mon;
8
jelly_bean_fc_subscriber jb_fc_sub;
9
10
uvm_analysis_port#( jelly_bean_transaction ) jb_ap;
11
12
function new( string name, uvm_component parent );
13
super.new( name, parent );
14
endfunction: new
15
16
function void build_phase( uvm_phase phase );
17
super.build_phase( phase );
18
19
if ( ! uvm_config_db#( jelly_bean_agent_config )::get (
20
.cntxt( this ), .inst_name( "" ), .field_name( "jb_agent_cfg" ), .value(
21
jb_agent_cfg ) ) ) begin `uvm_error( "jelly_bean_agent",
22
"jb_agent_cfg not found" ) end
23
if ( jb_agent_cfg.active == UVM_ACTIVE ) begin jb_seqr =
24
jelly_bean_sequencer::type_id::create( .name( "jb_seqr" ), .parent( this ) );
25
jb_drvr = jelly_bean_driver ::type_id::create( .name( "jb_drvr" ), .parent(
26
this ) ); end
27
28 if ( jb_agent_cfg.has_jb_fc_sub ) begin jb_fc_sub =
29 jelly_bean_fc_subscriber::type_id::create( .name( "jb_fc_sub" ), .parent(
30 this ) ); end
31 jb_mon = jelly_bean_monitor::type_id::create( .name( "jb_mon" ),
32 .parent( this ) );
33 endfunction: build_phase
34
35 function void connect_phase( uvm_phase phase );
36 super.connect_phase( phase );
37
38 jb_mon.jb_if = jb_agent_cfg.jb_if;
39 jb_ap = jb_mon.jb_ap;
40
41 if ( jb_agent_cfg.active == UVM_ACTIVE ) begin
42 jb_drvr.seq_item_port.connect( jb_seqr.seq_item_export );
43 jb_drvr.jb_if = jb_agent_cfg.jb_if; end
44 if ( jb_agent_cfg.has_jb_fc_sub ) begin jb_ap.connect(
45 jb_fc_sub.analysis_export ); end
46 endfunction: connect_phase
47 endclass: jelly_bean_agent
48
49
50
51

Sequence Diagram
The following sequence diagram summarizes the configuration process described above.

Sequence Diagram of the Build Phases


I hope this tutorial helped you to understand the UVM configuration process.
UVM Tutorial for Candy Lovers – 9. Register
Abstraction
October 29, 2012 Keisuke Shimizu
Last Updated on November 6, 2016

This post will explain how to use the UVM Register Abstraction Layer (RAL) to generate register
transactions. The figure below shows the verification platform used for this post. Among other things,
the jelly_bean_reg_block, the jelly_bean_reg_adapter, and
the jelly_bean_reg_predictor are the classes used for the register abstraction.

Verification Platform
The figure below shows the diagram of the RAL-related classes. The standard UVM classes are
shown in pink, while the jelly-bean classes are shown in light blue. The diagram looks busy, but bear
in mind that I will explain each jelly-bean class one by one.
Diagram of the Jelly-Bean-Register Related Classes

Register Definitions
In the previous posts, the DUT had no accessible registers. We are going to add the registers that
hold jelly-bean recipe information and its taste. We will also add a command input port to the DUT so
that we can write a jelly-bean recipe to the register and read its taste. The figure below shows the
register definition of the DUT.

DUT
Registers

The source code of the DUT (jelly_bean_taster) is shown below. When the command input
is WRITE, the values of flavor, color, sugar_free, and sour input ports are written to the RECIPE
register (line 22 to 25). When the command input is READ, the TASTE register is read out and
the taste output is driven accordingly (line 27).

1 module jelly_bean_taster( jelly_bean_if.slave_mp jb_slave_if );


2 import jelly_bean_pkg::*;
3
4 reg [2:0] flavor;
5 reg [1:0] color;
6 reg sugar_free;
7 reg sour;
8 reg [1:0] command;
9 reg [1:0] taste;
10
11 initial begin
12 flavor = 0;
13 color = 0;
14 sugar_free = 0;
15 sour = 0;
16 command = 0;
17 taste = 0;
18 end
19
20 always @ ( posedge jb_slave_if.clk ) begin
21 if ( jb_slave_if.command == jelly_bean_types::WRITE ) begin
22 flavor < = jb_slave_if.flavor;
23 color <= jb_slave_if.color;
24 sugar_free <= jb_slave_if.sugar_free;
25 sour <= jb_slave_if.sour; end
26 else if ( jb_slave_if.command == jelly_bean_types::READ ) begin
27 jb_slave_if.taste <= taste; end
28 end
29
30 always @ ( posedge jb_slave_if.clk ) begin
31 if ( jb_slave_if.flavor == jelly_bean_types::CHOCOLATE &&
32 jb_slave_if.sour ) begin
33 taste <= jelly_bean_types::YUCKY;
34 end else if ( jb_slave_if.flavor != jelly_bean_types::NO_FLAVOR ) begin
35 taste <= jelly_bean_types::YUMMY;
36 end
37 end
38 endmodule: jelly_bean_taster
39

Register Model
The model of the RECIPE register is defined by extending the uvm_reg class. Each field of the
register is defined as a uvm_reg_field (line 4 to 7). The fields are configured in the build function.
Note that the name, build, is used for convenience. Do not confuse it with the build_phase of
the uvm_component because the uvm_reg is not a uvm_component.

1 class jelly_bean_recipe_reg extends uvm_reg;


2 `uvm_object_utils( jelly_bean_recipe_reg )
3
4 rand uvm_reg_field flavor;
5 rand uvm_reg_field color;
6 rand uvm_reg_field sugar_free;
7 rand uvm_reg_field sour;
8 constraint flavor_color_con {
9 flavor.value != jelly_bean_types::NO_FLAVOR;
10 flavor.value == jelly_bean_types::APPLE
11 -> color.value != jelly_bean_types::BLUE;
12 flavor.value == jelly_bean_types::BLUEBERRY
13 -> color.value == jelly_bean_types::BLUE;
14 flavor.value < = jelly_bean_types::CHOCOLATE;
15 }
16
17 function new( string name = "jelly_bean_recipe_reg" );
18 super.new( .name( name ), .n_bits( 7 ), .has_coverage( UVM_NO_COVERAGE ) );
19 endfunction: new
20
21 virtual function void build();
22 flavor = uvm_reg_field::type_id::create( "flavor" );
23 flavor.configure( .parent ( this ),
24 .size ( 3 ),
25 .lsb_pos ( 0 ),
26 .access ( "WO" ),
27 .volatile ( 0 ),
28 .reset ( 0 ),
29 .has_reset ( 1 ),
30 .is_rand ( 1 ),
31 .individually_accessible( 0 ) );
32
33 color = uvm_reg_field::type_id::create( "color" );
34 color.configure( .parent ( this ),
35 .size ( 2 ),
36 .lsb_pos ( 3 ),
37 .access ( "WO" ),
38 .volatile ( 0 ),
39 .reset ( 0 ),
40 .has_reset ( 1 ),
41 .is_rand ( 1 ),
42 .individually_accessible( 0 ) );
43
44 sugar_free = uvm_reg_field::type_id::create( "sugar_free" );
45 sugar_free.configure( .parent ( this ),
46 .size ( 1 ),
47 .lsb_pos ( 5 ),
48 .access ( "WO" ),
49 .volatile ( 0 ),
50 .reset ( 0 ),
51 .has_reset ( 1 ),
52 .is_rand ( 1 ),
53 .individually_accessible( 0 ) );
54
55 sour = uvm_reg_field::type_id::create( "sour" );
56 sour.configure( .parent ( this ),
57 .size ( 1 ),
58 .lsb_pos ( 6 ),
59 .access ( "WO" ),
60 .volatile ( 0 ),
61 .reset ( 0 ),
62 .has_reset ( 1 ),
63 .is_rand ( 1 ),
64 .individually_accessible( 0 ) );
65 endfunction: build
66 endclass: jelly_bean_recipe_reg
67

The model of the TASTE register is similarly defined.

1 class jelly_bean_taste_reg extends uvm_reg;


2 `uvm_object_utils( jelly_bean_taste_reg )
5
6 rand uvm_reg_field taste;
7 function new( string name = "jelly_bean_taste_reg" );
8 super.new( .name( name ), .n_bits( 2 ), .has_coverage( UVM_NO_COVERAGE ) );
9 endfunction: new
10 virtual function void build();
11 taste = uvm_reg_field::type_id::create("taste");
12 taste.configure( .parent ( this ),
13 .size ( 2 ),
14 .lsb_pos ( 0 ),
15 .access ( "RO" ),
16 .volatile ( 1 ),
17 .reset ( 0 ),
18 .has_reset ( 1 ),
19 .is_rand ( 1 ),
20 .individually_accessible( 1 ) );
21 endfunction: build
22 endclass: jelly_bean_taste_reg
Register Block
The jelly_bean_reg_block contains the two registers created above and defines a register map.

class jelly_bean_reg_block extends uvm_reg_block;


7 `uvm_object_utils( jelly_bean_reg_block )
8
9 rand jelly_bean_recipe_reg jb_recipe_reg;
10 rand jelly_bean_taste_reg jb_taste_reg;
11 uvm_reg_map reg_map;
12
13 function new( string name = "jelly_bean_reg_block" );
14 super.new( .name( name ), .has_coverage( UVM_NO_COVERAGE ) );
15 endfunction: new
16
17 virtual function void build();
18 jb_recipe_reg = jelly_bean_recipe_reg::type_id::create( "jb_recipe_reg" );
19 jb_recipe_reg.configure( .blk_parent( this ) );
20 jb_recipe_reg.build();
21 jb_taste_reg = jelly_bean_taste_reg::type_id::create( "jb_taste_reg" );
22 jb_taste_reg.configure( .blk_parent( this ) );
23 jb_taste_reg.build();
24 reg_map = create_map( .name( "reg_map" ), .base_addr( 8'h00 ),
25 ) );
26 reg_map.add_reg( .rg( jb_recipe_reg ), .offset( 8'h00 ), .rights( "WO" ) );
27 8'h01 ), .rights( "RO" ) );
28 lock_model(); // finalize the address mapping
endfunction: build

endclass: jelly_bean_reg_block
Register Adapter
The jelly_bean_reg_adapter class provides two functions to convert between
a uvm_reg_bus_op and a jelly_bean_transaction. The reg2bus function converts
a uvm_reg_bus_op into a jelly_bean_transaction, whereas the bus2reg function converts
a jelly_bean_transaction back to a uvm_reg_bus_op.

The uvm_reg_adapter is a uvm_object, not a uvm_component.

Advanced Topic: Even though a uvm_sequence is a uvm_sequence_item, you cannot let


the reg2bus() function create a uvm_sequence and return it. This is because when a register is
read/written, the uvm_reg_map calls uvm_sequence_base::start_item() passing the object
returned by the reg2bus(), but the start_item() does not expect a uvm_sequence. This will cause
a fatal error. For more details, please see Register Access Methods.

1 class jelly_bean_reg_adapter extends uvm_reg_adapter;


2 `uvm_object_utils( jelly_bean_reg_adapter )
3
4 function new( string name = "" );
5 super.new( name );
6 supports_byte_enable = 0;
7 provides_responses = 0;
8 endfunction: new
9
10 virtual function uvm_sequence_item reg2bus( const ref uvm_reg_bus_op rw );
11 jelly_bean_transaction jb_tx
12 = jelly_bean_transaction::type_id::create("jb_tx");
13
14 if ( rw.kind == UVM_READ )
15 jb_tx.command = jelly_bean_types::READ;
16 else if ( rw.kind == UVM_WRITE )
17 jb_tx.command = jelly_bean_types::WRITE;
18 else jb_tx.command = jelly_bean_types::NO_OP;
19 if ( rw.kind == UVM_WRITE ) { jb_tx.sour, jb_tx.sugar_free, jb_tx.color, jb_t
20 endfunction: reg2bus
21
22 virtual function void bus2reg( uvm_sequence_item bus_item,
23 ref uvm_reg_bus_op rw );
24 jelly_bean_transaction jb_tx;
25
26 if ( ! $cast( jb_tx, bus_item ) ) begin
27 `uvm_fatal( get_name(),
28 "bus_item is not of the jelly_bean_transaction type." )
29 return;
30 end
31
32 rw.kind = ( jb_tx.command == jelly_bean_types::READ ) ? UVM_READ : UVM_WRITE;
33 if ( jb_tx.command == jelly_bean_types::READ )
34 rw.data = jb_tx.taste;
35 else if ( jb_tx.command == jelly_bean_types::WRITE )
36 rw.data = { jb_tx.sour, jb_tx.sugar_free, jb_tx.color, jb_tx.flavor };
37 rw.status = UVM_IS_OK;
38 endfunction: bus2reg
39
40 endclass: jelly_bean_reg_adapter

Register Predictor
The register predictor updates the values of the register model based on observed bus transactions.
As the jelly-bean register predictor is not involved with any kind of extended features,
the uvm_reg_predictor is used as is.

typedef uvm_reg_predictor#( jelly_bean_transaction ) jelly_bean_reg_predictor;


Agent
The jelly_bean_agent instantiates the jelly_bean_reg_adapter (line 35).

1 class jelly_bean_agent extends uvm_agent;


2 `uvm_component_utils( jelly_bean_agent )
3
4 uvm_analysis_port#( jelly_bean_transaction ) jb_ap;
5
6 jelly_bean_agent_config jb_agent_cfg;
7 jelly_bean_sequencer jb_seqr;
8 jelly_bean_driver jb_drvr;
9 jelly_bean_monitor jb_mon;
10 jelly_bean_reg_adapter jb_reg_adapter;
11
12 function new( string name, uvm_component parent );
13 super.new( name, parent );
14 endfunction: new
15
16 function void build_phase( uvm_phase phase );
17 super.build_phase( phase );
18
19 if ( ! uvm_config_db#( jelly_bean_agent_config )::get( .cntxt( this ),
20 .inst_name ( "" ),
21 .field_name( "jb_agent_cfg"
22 .value( jb_agent_cfg ))) be
23 `uvm_error( "jelly_bean_agent", "jb_agent_cfg not found" )
24 end
25
26 jb_ap = new( .name( "jb_ap" ), .parent( this ) );
27 if ( jb_agent_cfg.active == UVM_ACTIVE ) begin
28 jb_seqr = jelly_bean_sequencer::type_id::create( .name( "jb_seqr" ),
29 .parent( this ) );
30 jb_drvr = jelly_bean_driver::type_id::create( .name( "jb_drvr" ),
31 .parent( this ) );
32 end
33 jb_mon = jelly_bean_monitor::type_id::create( .name( "jb_mon" ),
34 .parent( this ) );
35 jb_reg_adapter = jelly_bean_reg_adapter::type_id::create( .name( "jb_reg_adapter"
36
37 function void connect_phase( uvm_phase phase );
38 super.connect_phase( phase );
39
40 jb_mon.jb_if = jb_agent_cfg.jb_if;
41 if ( jb_agent_cfg.active == UVM_ACTIVE ) begin
42 jb_drvr.seq_item_port.connect( jb_seqr.seq_item_export );
43 jb_drvr.jb_if = jb_agent_cfg.jb_if;
44 end
45 jb_mon.jb_ap.connect( jb_ap );
46 endfunction: connect_phase
47 endclass: jelly_bean_agent
48

Environment Configuration
The jelly_bean_env_config has a handle to the jelly_bean_reg_block so that
the jelly_bean_env can access the register model.

1 class jelly_bean_env_config extends uvm_object;


2 `uvm_object_utils( jelly_bean_env_config )
3
4 bit has_jb_agent = 1;
5 bit has_jb_sb = 1;
6
7 jelly_bean_agent_config jb_agent_cfg;
8 jelly_bean_reg_block jb_reg_block;
9 function new( string name = "" );
10 super.new( name );
11 endfunction: new
12 endclass: jelly_bean_env_config
13

Environment
The jelly_bean_env instantiates the jelly_bean_agent and
the jelly_bean_reg_predictor (line 28 to 31), then connects register-related objects:

Firstly, in the connect_phase(), the set_sequencer() function associates the jelly-bean


sequencer and the register adapter with the register map (line 45 and 46).
The set_sequencer() must be called before starting the sequence based on a uvm_reg_sequence.
A register block may have more than one register map.

Secondly, the register map and the register adapter are associated with the register predictor (line
49 and 50). The register predictor will use the register map and the register adapter to convert
a jelly_bean_transaction back to a register operation.

Lastly, the register predictor is connected to the agent to subscribe the jelly_bean_transactions
from the agent (line 51).

1 class jelly_bean_env extends uvm_env;


2 `uvm_component_utils( jelly_bean_env )
3
4 jelly_bean_env_config jb_env_cfg;
5 jelly_bean_agent jb_agent;
6 jelly_bean_fc_subscriber jb_fc_sub;
7 jelly_bean_scoreboard jb_sb;
8 jelly_bean_reg_predictor jb_reg_predictor;
9
10 function new( string name, uvm_component parent );
11 super.new( name, parent );
12 endfunction: new
13
14 function void build_phase( uvm_phase phase );
15 super.build_phase( phase );
16 if ( ! uvm_config_db#( jelly_bean_env_config )::get
17 ( .cntxt( this ),
18 .inst_name( "" ),
19 .field_name( "jb_env_cfg" ),
20 .value( jb_env_cfg ) ) ) begin
21 `uvm_fatal( get_name(), "jb_env_cfg not found" )
22 end
23
24 uvm_config_db#( jelly_bean_agent_config )::set( .cntxt( this ),
25 .inst_name( "jb_agent*" ),
26 .field_name( "jb_agent_cfg" ),
27 .value( jb_env_cfg.jb_agent_cfg )
28 jb_agent = jelly_bean_agent::type_id::create( .name( "jb_agent" ),
29 ) ); jb_reg_predictor = jelly_bean_reg_predictor::type_id::create( .name( "jb_reg_p
30 .parent( this ) );
31 if ( jb_env_cfg.has_jb_sb ) begin
32 jb_sb = jelly_bean_scoreboard::type_id::create( .name( "jb_sb" ),
33 .parent( this ) );
34 end
35 jb_fc_sub = jelly_bean_fc_subscriber::type_id::create( .name( "jb_fc_sub" ),
36 .parent( this ) );
37 endfunction: build_phase
38
39 function void connect_phase( uvm_phase phase );
40 super.connect_phase( phase );
41 jb_agent.jb_ap.connect( jb_fc_sub.analysis_export );
42 jb_agent.jb_ap.connect( jb_sb.jb_analysis_export );
43 if ( jb_env_cfg.jb_reg_block.get_parent() == null ) begin // if the top-level env
44 jb_env_cfg.jb_reg_block.reg_map.set_sequencer( .sequencer( jb_agent.jb_seqr ),
45 .adapter( jb_agent.jb_reg_adapter ) ); end
46 jb_env_cfg.jb_reg_block.reg_map.set_auto_predict( .on( 0 ) );
47 jb_reg_predictor.map = jb_env_cfg.jb_reg_block.reg_map;
48 jb_reg_predictor.adapter = jb_agent.jb_reg_adapter;
49 jb_agent.jb_ap.connect( jb_reg_predictor.bus_in );
50 endfunction: connect_phase
51
52 endclass: jelly_bean_env
53
54

Base Test
The base test instantiates a jelly_bean_reg_block (line 16 and 17) and stores its handle in
the jelly_bean_env_config (line 19 and 20).

1 class jelly_bean_base_test extends uvm_test;


2 `uvm_component_utils( jelly_bean_base_test )
3
4 jelly_bean_env jb_env;
5 jelly_bean_env_config jb_env_cfg;
6 jelly_bean_agent_config jb_agent_cfg;
7 jelly_bean_reg_block jb_reg_block;
8
9 function new( string name, uvm_component parent );
10 super.new( name, parent );
11 endfunction: new
12
13 function void build_phase(uvm_phase phase);
14 super.build_phase(phase);
15
16 jb_reg_block = jelly_bean_reg_block::type_id::create( "jb_reg_block" );
17 jb_reg_block.build();
18 jb_env_cfg = jelly_bean_env_config::type_id::create( "jb_env_cfg" );
19 jb_env_cfg.jb_reg_block = jb_reg_block;
20 jb_agent_cfg = jelly_bean_agent_config::type_id::create( "jb_agent_cfg" );
21
22 if ( ! uvm_config_db#( virtual jelly_bean_if )::get( .cntxt( this ),
23 .inst_name( "" ),
24 .field_name( "jb_if" ),
25 .value(
26 jb_agent_cfg.jb_if ))) begin
27 `uvm_error( "jelly_bean_test", "jb_if not found" )
28 end
29
30 jb_env_cfg.jb_agent_cfg = jb_agent_cfg;
31
32 uvm_config_db#(jelly_bean_env_config)::set( .cntxt( null ),
33 .inst_name( "*" ),
34 .field_name( "jb_env_cfg" ),
35 .value( jb_env_cfg ) );
36 jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ),
37 .parent( this ) );
38 endfunction: build_phase
39
40 virtual function void start_of_simulation_phase( uvm_phase phase );
41 super.start_of_simulation_phase( phase );
42 uvm_top.print_topology();
43 endfunction: start_of_simulation_phase
44
45 endclass: jelly_bean_base_test
46

Sequence without Register Abstraction


We have created all the components by now and we are ready to create a register sequence. But
before doing that, let’s create a “regular” sequence without using the register abstraction for
comparison. The jelly_bean_sequence is a sequence to generate a sour-green-apple jelly bean.
The body of the sequence creates a jelly_bean_transaction, which will be used by the driver to
do pin wiggling.

1
2 class jelly_bean_sequence extends uvm_sequence#( jelly_bean_transaction );
3 `uvm_object_utils( jelly_bean_sequence )
4
5 function new( string name = "" );
6 super.new( name );
7 endfunction: new
8
9 task body();
10 jelly_bean_transaction jb_tx;
11 jb_tx = jelly_bean_transaction::type_id::create( .name( "jb_tx" ),
12 .contxt( get_full_name()));
13 start_item( jb_tx );
14 jb_tx.flavor = jelly_bean_types::APPLE;
15 jb_tx.color = jelly_bean_types::GREEN;
16 jb_tx.sugar_free = 0;
17 jb_tx.sour = 1;
18 finish_item(jb_tx);
19 endtask: body
endclass: jelly_bean_sequence
Sequence Using Register Abstraction
The jelly_bean_reg_sequence is another sequence to generate a sour-green-apple jelly bean, but
using the register abstraction. This sequence is extended from the uvm_reg_sequence class so that
we can use the convenience functions such as write_reg() and read_reg(). The body of the
sequence writes a recipe (line 23) to the RECIPE register, then reads back its taste from the TASTE
register (line 24). Note that we do not create a jelly_bean_transaction in the sequence. The
register adapter will convert the register operations into the
corresponding jelly_bean_transactions.

1
2 class jelly_bean_reg_sequence extends uvm_reg_sequence;
3 `uvm_object_utils( jelly_bean_reg_sequence )
4
5 function new( string name = "" );
6 super.new( name );
7 endfunction: new
8
9 virtual task body();
10 jelly_bean_reg_block jb_reg_block;
11 jelly_bean_types::flavor_e flavor;
12 jelly_bean_types::color_e color;
13 bit sugar_free;
14 bit sour;
15 uvm_status_e status;
16 uvm_reg_data_t value;
17
18 $cast( jb_reg_block, model );
19 flavor = jelly_bean_types::APPLE;
20 color = jelly_bean_types::GREEN;
21 sugar_free = 0;
22 sour = 1;
23
24 write_reg( jb_reg_block.jb_recipe_reg, status, { sour, sugar_free, color, flavor }
25 status, value ); endtask: body
26
27 endclass: jelly_bean_reg_sequence

Register Test
The jelly_bean_reg_test creates a jelly_bean_reg_sequence we have just created and starts
the sequence (line 12 to 15).

1 class jelly_bean_reg_test extends jelly_bean_base_test;


2 `uvm_component_utils( jelly_bean_reg_test )
3
4 function new( string name, uvm_component parent );
5 super.new( name, parent );
6 endfunction: new
7
8 task main_phase( uvm_phase phase );
9 jelly_bean_reg_sequence jb_reg_seq;
10
11 phase.raise_objection( .obj( this ) );
12 jb_reg_seq = jelly_bean_reg_sequence::type_id::create( .name( "jb_reg_seq" ),
13 .contxt( get_full_name())); jb_reg_seq.model = jb_reg_block;
14 jb_reg_seq.start( .sequencer( jb_env.jb_agent.jb_seqr ) );
15 #100ns;
16 phase.drop_objection( .obj( this ) );
17
18 endtask: main_phase
19 endclass: jelly_bean_reg_test
20
21

Simulation
Let’s look at a simulation result. The simulation successfully generated a sour-green apple and read
back its taste from the DUT.

1
2
3
4 UVM_INFO jb3.sv(727) @ 30: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
5 ---------------------------------------------------------
Name Type Size Value
6 ---------------------------------------------------------
7 jb_tx jelly_bean_transaction - @7929
8 flavor jelly_bean_types::flavor_e 3 APPLE color jelly_bean_types::color_e 2
9 GREEN sugar_free integral 1 'h0 sour integral 1 'h1
jelly_bean_types::taste_e 2 NO_TASTE
10 ---------------------------------------------------------
11
12 UVM_INFO jb3.sv(727) @ 60: uvm_test_top.jb_env.jb_sb [jelly_bean_scoreboard] You have a good sense of taste.
13 ----------------------------------------------------------
Name Type Size Value
14 ----------------------------------------------------------
15 jb_tx jelly_bean_transaction - @7928
16 flavor jelly_bean_types::flavor_e 3 NO_FLAVOR
17 color jelly_bean_types::color_e 2 NO_COLOR
sugar_free integral 1 'h0
18 sour integral 1 'h0
19 command jelly_bean_types::command_e 2 READ taste jelly_bean_types::taste_e 2
20 YUMMY----------------------------------------------------------
21
22

I hope this tutorial helped you to understand the UVM Register Abstraction.

UVM Tutorial for Candy Lovers – 10. Inside


Candy Factory
November 3, 2012 Keisuke Shimizu
Last Updated: September 9, 2013

UVM factory is used to create UVM objects and components. This post will explain the UVM factory
using jelly beans (as you expected) and reveal what happens behind the scenes in the factory.
::type_id
In Transactions and Sequences, we defined the jelly_bean_transaction class. Then
the one_jelly_bean_sequence created a jelly_bean_transaction object as follows:

jb_tx = jelly_bean_transaction::type_id::create( .name( "jb_tx" ) );


This is a well-known idiom of the UVM to create an object, but where does this ::type_id thing
come from? The type_id is defined by `uvm_object_utils(jelly_bean_transaction) macro
used in the jelly_bean_transaction class. The `uvm_object_utils() macro is further broken
down into the following sub-macros:

 `uvm_object_utils_begin()
 `m_uvm_object_registry_internal()
 `m_uvm_object_create_func()
 `m_uvm_get_type_name_func()
 `m_uvm_field_utils_begin()
 `uvm_object_utils_end()

The following pseudo code shows how the macro is expanded.

1 // This pseudo code shows how `uvm_object_utils macro is expanded.


2 //
3 // Assuming UVM_NO_DEPRECATED is defined.
4 // Consequently assuming UVM_NO_REGISTERED_CONVERTER is defined.
5 // Assuming UVM_OBJECT_MUST_HAVE_CONSTRUCTOR is defined.
6
7 class jelly_bean_transaction extends uvm_sequence_item;
8
9 // `uvm_object_utils(T)
10 // |
11 // +--> `uvm_object_utils_begin(T)
12 // | |
13 // | +--> `m_uvm_object_registry_internal(T,T)
14 // | |
15 // V V
16
17 typedef uvm_object_registry#( jelly_bean_transaction, "jelly_bean_transaction" )
18 type_id;
19 static function type_id get_type();
20 return type_id::get();
21 endfunction
22
23 virtual function uvm_object_wrapper get_object_type();
24 return type_id::get();
25 endfunction
26
27 // | |
28 // | +--> `m_uvm_object_create_func(T)
29 // | |
30 // V V
31
32 function uvm_object create( string name = "" );
33 jelly_bean_transaction tmp;
34 if ( name == "" ) tmp = new();
35 else tmp = new( name );
36 return tmp;
37 endfunction
38
39 // | |
40 // | +--> `m_uvm_get_type_name_func(T)
41 // | |
42 // V V
43
44 const static string type_name = "jelly_bean_transaction";
45
46 virtual function string get_type_name();
47 return type_name;
48 endfunction
49
50 // | |
51 // | +--> `uvm_field_utils_begin(T)
52 // |
53 // V
54
55 function void __m_uvm_field_automation( uvm_object tmp_data__,
56 int what__,
57 string str__ );
58 begin
59 jelly_bean_transaction local_data__; /* Used for copy and compare */
60 typedef jelly_bean_transaction ___local_type____;
61 string string_aa_key; /* Used for associative array lookups */
62 uvm_object __current_scopes[$];
63 if ( what__ inside { UVM_SETINT, UVM_SETSTR, UVM_SETOBJ } ) begin
64 if ( __m_uvm_status_container.m_do_cycle_check( this ) ) begin
65 return;
66 end else
67 __current_scopes=__m_uvm_status_container.m_uvm_cycle_scopes;
68 end
69 super.__m_uvm_field_automation( tmp_data__, what__, str__ );
70
71 /* Type is verified by uvm_object::compare() */
72
73 if ( tmp_data__ != null )
74
75 /* Allow objects in same hierarchy to be copied/compared */
76
77 if ( ! $cast( local_data__, tmp_data__ ) ) return;
78
79 // |
80 // +--> `uvm_object_utils_end
81
82 end
83 endfunction
84
85 endclass: jelly_bean_transaction

As you see on the line 17, the type_id is nothing but a uvm_object_registry type.
type_id

::create()
Now we know what the type_id is, so let’s look at the ::create() that follows the type_id. If you
look at the UVM source code, src/base/uvm_registry.svh, you will find that the create() is a
static function of the uvm_object_registry class.
The following sequence diagram shows how the one_jelly_bean_sequence creates
a jelly_bean_transaction.

Sequence diagram of creating a jelly_bean_transaction

 The one_jelly_bean_sequence calls


the jelly_bean_transaction::type_id::create() (steps 1 and 2)
 The create() static function calls the create_object_by_type() of uvm_factory class (step
3)
 The uvm_factory calls find_override_by_type() to check whether
the jelly_bean_transaction type is overridden by another class (step 4)
 If the jelly_bean_transaction type is not overridden, then the uvm_factory calls
the create_object() of the uvm_object_registry of the jelly_bean_transactionclass
(step 5). We will look at an overridden case later.
 The uvm_object_registry class creates a jelly_bean_transaction object by
calling new() (step 6)
 The jelly_bean_transaction object is returned to the one_jelly_bean_sequence (step 7)

The class diagram of the factory-related classes is shown below.

Class diagram of the factory-


related classes

Type Override
In Tasting, the jelly_bean_test class overrode the jelly_bean_transaction with
a sugar_free_jelly_bean_transaction by doing:

jelly_bean_transaction::type_id::set_type_override(sugar_free_jelly_bean_tran
saction::get_type());
As we saw earlier, the jelly_bean_transaction::type_id is a uvm_object_registry type.
The set_type_override() is another static function of the uvm_object_registry. The sequence
diagram below shows how the set_type_override() overrides a type. Overriding a type involves
the following steps:
 Firstly, the jelly_bean_test calls
the sugar_free_jelly_bean_transaction::get_type() to get the uvm_object_registry of
the sugar_free_jelly_bean_transaction(steps 1, 2, and 3)
 Secondly, the jelly_bean_test calls the set_type_override(), which in turn calls
the set_type_override_by_type() of the uvm_factory (steps 4 and 5)
 Lastly, the uvm_factory creates a uvm_factory_override object and put it in an override
queue (steps 6 and 7)

Then, when the one_jelly_bean_sequence creates a jelly_bean_transaction, the following


steps will be executed:

 The one_jelly_bean_sequence calls


the jelly_bean_transaction::type_id::create() (steps 8 and 9)
 The create() function calls the create_object_by_type() of the uvm_factory (step 10)
 The uvm_factory recursively calls find_override_by_type() to determine the final type of
the jelly_bean_transaction (step 11)
 In our scenario, the jelly_bean_transaction was overridden by
the sugar_free_jelly_bean_transaction. Therefore, the uvm_factory calls
the create_object() of the uvm_object_registry of
the sugar_free_jelly_bean_transaction (step 12)
 The uvm_object_registry creates a sugar_free_jelly_bean_transaction by
calling new() (step 13)
 The sugar_free_jelly_bean_transaction object created at the above step is returned (step
14)

Note that the one_jelly_bean_sequence called


the jelly_bean_transaction::type_id::create(),
not sugar_free_jelly_bean_transaction::type_id::create() to create a sugar-free jelly
bean. The factory took care of what type of jelly bean to create.

I hope you have a better idea of the UVM factory by now.


Sequence diagram of type override

UVM Tutorial for Candy Lovers – 11. Sequence


Item Port
November 10, 2012 Keisuke Shimizu
Last Updated: September 11, 2013

A UVM driver and a UVM sequencer are connected using a UVM sequence item port and an export.
This post will explain how the sequence item port works. In Agent, we connected the sequence item
port (seq_item_port) of the jelly-bean driver (jb_drvr) to the sequence item export
(seq_item_export) of the jelly-bean sequencer (jb_seqr) like this:

jb_drvr.seq_item_port.connect( jb_seqr.seq_item_export );
The driver called get_next_item() to get a jelly-bean transaction (jb_tx),

seq_item_port.get_next_item( jb_tx );
and then called item_done() at the end.
seq_item_port.item_done();
We will look at how the above code works in detail in this post. The class diagram related to the
sequence item port is shown below. UVM standard library classes are shown in pink, and the UVM
classes specialized with the jelly_bean_transaction type are shown in yellow.

Class Diagram Related to the


Sequence Item Port

Sequence Item Port


The seq_item_port of the jelly-bean driver is an object of the uvm_seq_item_pull_port class
specialized with the jelly_bean_transaction type. The uvm_seq_item_pull_port class is
defined in src/tlm1/uvm_sqr_connections.svh and consists of two macros:
 `UVM_SEQ_PORT
 `UVM_SEQ_ITEM_PULL_IMP

The following pseudo code shows how these macros are expanded.

1 class uvm_seq_item_pull_port #( type REQ = jelly_bean_transaction,


2 type RSP = REQ )
3 extends uvm_port_base #( uvm_sqr_if_base #(REQ, RSP) );
4
5 // `UVM_SEQ_PORT( `UVM_SEQ_ITEM_PULL_MASK, "uvm_seq_item_pull_port" )
6 // | |
7 // | V
8 // | ( `UVM_SEQ_ITEM_GET_NEXT_ITEM_MASK |
9 // | `UVM_SEQ_ITEM_TRY_NEXT_ITEM_MASK |
10 // | `UVM_SEQ_ITEM_ITEM_DONE_MASK |
11 // | `UVM_SEQ_ITEM_HAS_DO_AVAILABLE_MASK |
12 // | `UVM_SEQ_ITEM_WAIT_FOR_SEQUENCES_MASK |
13 // | `UVM_SEQ_ITEM_PUT_RESPONSE_MASK |
14 // | `UVM_SEQ_ITEM_PUT_MASK |
15 // | `UVM_SEQ_ITEM_GET_MASK |
16 // | `UVM_SEQ_ITEM_PEEK_MASK ) = 9'h1FF
17 // V
18
19 function new( string name,
20 uvm_component parent,
21 int min_size = 0,
22 int max_size = 1 );
23 super.new( name, parent, UVM_PORT, min_size, max_size );
24 m_if_mask = 9'h1FF;
25 endfunction // new
26
27 // |
28 // +--> `UVM_TLM_GET_TYPE_NAME( "uvm_seq_item_pull_port" )
29
30 virtual function string get_type_name();
31 return "uvm_seq_item_pull_port";
32 endfunction // get_type_name
33
34 // `UVM_SEQ_ITEM_PULL_IMP( this.m_if,
35 // | jelly_bean_transaction,
36 // | jelly_bean_transaction, t, t )
37 // V
38
39 task get_next_item( output jelly_bean_transaction t );
40 this.m_if.get_next_item( t ); endtask // get_next_item
41 task try_next_item( output jelly_bean_transaction t );
42 this.m_if.try_next_item( t );
43 endtask // try_next_item
44
45 function void item_done( input jelly_bean_transaction t = null );
46 this.m_if.item_done( t ); endfunction // item_done
47 task wait_for_sequences();
48 this.m_if.wait_for_sequences();
49 endtask // wait_for_sequences
50
51 function bit has_do_available();
52 return this.m_if.has_do_available();
53 endfunction // has_do_available
54
55 function void put_response( input jelly_bean_transaction t );
56 this.m_if.put_response( t );
57 endfunction // put_response
58
59 task get( output jelly_bean_transaction t );
60 this.m_if.get( t );
61 endtask // get
62
63 task peek( output jelly_bean_transaction t );
64 this.m_if.peek( t );
65 endtask // peek
66
67 task put( input jelly_bean_transaction t );
68 this.m_if.put( t );
69 endtask // put
70
71 // end of macro expansion
72
73 bit print_enabled;
74 endclass // uvm_seq_item_pull_port
75
76
77
78

As you have seen above, both the get_next_item() and the item_done() simply delegates their
job to this.m_if (lines 39 and 47). But what is this.m_if? Before answering this question, let’s
look at the other side of the connection.

Sequence Item Export


The seq_item_export of the jelly-bean sequencer is an object of a uvm_seq_item_pull_imp class
specialized with the jelly_bean_transaction type. The uvm_seq_item_pull_imp class is also
defined in the src/tlm1/uvm_sqr_connections.svh and consists of two macros:

 `UVM_IMP_COMMON
 `UVM_SEQ_ITEM_PULL_IMP

The following pseudo code shows how these macros are expanded.

1 class uvm_seq_item_pull_imp
2 #( type REQ = jelly_bean_transaction,
3 type RSP = REQ,
4 type IMP = uvm_sequencer #( jelly_bean_transaction,
5 jelly_bean_transaction ) )
6 extends uvm_port_base #( uvm_sqr_if_base #( REQ, RSP ) );
7
8 // `UVM_IMP_COMMON( `UVM_SEQ_ITEM_PULL_MASK, "uvm_seq_item_pull_imp",
9 // | uvm_sequencer #( jelly_bean_transaction,
10 // | jelly_bean_transaction ) )
11 // V
12
13 local uvm_sequencer #( jelly_bean_transaction,
14 jelly_bean_transaction ) m_imp;
15
16 function new( string name,
17 uvm_sequencer #( jelly_bean_transaction,
18 jelly_bean_transaction ) imp );
19 super.new( name, imp, UVM_IMPLEMENTATION, 1, 1 );
20 m_imp = imp;
21 m_if_mask = 9'h1FF;
22 endfunction // new
23
24 // |
25 // +--> `UVM_TLM_GET_TYPE_NAME( "uvm_seq_item_pull_imp" )
26
27 virtual function string get_type_name();
28 return "uvm_seq_item_pull_imp";
29 endfunction // get_type_name
30
31 // `UVM_SEQ_ITEM_PULL_IMP( m_imp,
32 // | jelly_bean_transaction, jelly_bean_transaction,
33 // | t, t )
34 // V
35
36 task get_next_item( output jelly_bean_transaction t );
37 m_imp.get_next_item( t );
38 endtask // get_next_item
39 task try_next_item( output jelly_bean_transaction t );
40 m_imp.try_next_item( t );
41 endtask // try_next_item
42
43 function void item_done( input jelly_bean_transaction t = null );
44 m_imp.item_done( t ); endfunction // item_done
45 task wait_for_sequences();
46 m_imp.wait_for_sequences();
47 endtask // wait_for_sequences
48
49 function bit has_do_available();
50 return m_imp.has_do_available();
51 endfunction // has_do_available
52
53 function void put_response( input jelly_bean_transaction t );
54 m_imp.put_response( t );
55 endfunction // put_response
56
57 task get( output jelly_bean_transaction t );
58 m_imp.get( t );
59 endtask // get
60
61 task peek( output jelly_bean_transaction t );
62 m_imp.peek( t );
63 endtask // peek
64
65 task put( input jelly_bean_transaction t );
66 m_imp.put( t );
67 endtask // put
68
69 endclass // uvm_seq_item_pull_imp
70
71
72

Similar to the uvm_seq_item_pull_port class, the uvm_seq_item_pull_imp class has


the get_next_item() and the item_done() methods, and they delegate their job to m_imp.
The m_imp is the sequencer this sequence item export belongs to.

Connection
Now let’s connect the sequence item port and the sequence item export. The following sequence
diagram shows the steps involved in the connection.

Sequence
Diagram

 The UVM driver instantiates the seq_item_port (step 1), and the UVM sequencer instantiates
the seq_item_export (step 2).
 When seq_item_port.connect(jb_seqr.seq_item_export) is called,
the seq_item_export is stored in the array called m_provided_by (step 3)
 Just before entering end_of_elaboration_phase, resolve_bindings() function is called.
This function traverses the port connection and stores “implementation” to the array
called m_imp_list (step 4). The function checks the number of implementations. If it is 1, the
function stores the implementation as m_if (step 5). This is the m_if the sequence item port
delegated its jobs to. If the number of implementations is 0, the seq_item_port is left
unconnected. If the number of implementations is more than 1, it is an error.
 When the seq_item_port.get_next_item() is called, the task calls the get_next_item() of
the seq_item_export, which in turn calls the get_next_item() of the uvm_sequencer (steps 6
and 7).
 Similarly, when the seq_item_port.item_done() is called, the function calls
the item_done() of the seq_item_export, which in turn calls the item_done() of
the uvm_sequencer (steps 8 and 9).

I hope this post helped you to understand how the sequence item port works.

UVM Tutorial for Candy Lovers – 12. Analysis Port


November 17, 2012 Keisuke Shimizu
Last Updated: May 22, 2015 (fixed a typo)

This post will explain how analysis port and analysis export work.

In Agent, we connected the analysis port (jb_ap) of the jelly-bean monitor (jb_mon) to the analysis
port (jb_ap) of the jelly-bean agent (jb_agent) as follows:

jb_mon.jb_ap.connect( jb_ap ); // in the jelly_bean_agent


Then, in Environment, we connected the analysis port (jb_ap) of the jelly-bean agent to the analysis
export (analysis_export) of the jelly-bean-functional-coverage subscriber (jb_fc_sub):

jb_agent.jb_ap.connect( jb_fc_sub.analysis_export ); // in the jelly_bean_env


You might want to look at the verification component diagram in Overview to check these
connections.
Finally, in the run_phase() of the jelly-bean monitor, the monitor called the write() function of its
analysis port, every time it created a jelly-bean transaction (jb_tx):

jb_ap.write( jb_tx ); // in the jelly_bean_monitor


We will look at how the above code works in detail in this post. The class diagram related to the
analysis port is shown below. UVM standard library classes are shown in pink, while the UVM
classes specialized with the jelly_bean_transaction type are shown in yellow.
Class Diagram Related to the Analysis Port

Analysis Port
The jb_ap of the jelly-bean monitor is an object of the uvm_analysis_port class specialized with
the jelly_bean_transaction type. The following pseudo code shows how the class is specialized.
1
2 class uvm_analysis_port #( type T = jelly_bean_transaction )
3 extends uvm_port_base #( uvm_tlm_if_base #(T,T) );
4
5 function new( string name, uvm_component parent );
6 super.new( name, parent, UVM_PORT, 0, UVM_UNBOUNDED_CONNECTIONS );
7 m_if_mask = `UVM_TLM_ANALYSIS_MASK; // = 1 < < 8
8 endfunction // new
9
10 virtual function string get_type_name();
11 return "uvm_analysis_port";
12 endfunction // get_type_name
13
14 function void write( input jelly_bean_transaction t );
15 uvm_tlm_if_base #( jelly_bean_transaction, jelly_bean_transaction ) tif;
16 for ( int i = 0; i < this.size(); i++ ) begin
17 tif = this.get_if( i );
18 if ( tif == null )
19 uvm_report_fatal( "NTCONN",
20 { "No uvm_tlm interface is connected to ",
21 get_full_name(), " for executing write()" },
22 UVM_NONE );
23 tif.write( t ); end
24 endfunction // write
25
26 endclass // uvm_analysis_port

As you have seen above, the write() function of the uvm_analysis_port delegates its job to tif,
which is an object of uvm_tlm_if_base class. The uvm_tlm_if_base class is the base class
of uvm_port_base class, which in turn is the base class of uvm_analysis_imp class (line 22).

Analysis Export
The analysis_export of the jelly-bean-functional-coverage subscriber (jb_fc_sub) is an object of
the uvm_analysis_imp class specialized with the jelly_bean_transactiontype. The following
pseudo code shows how the class is specialized.

1 class uvm_analysis_imp #( type T = jelly_bean_transaction,


2 type IMP = uvm_subscriber #( jelly_bean_transaction ) )
3 extends uvm_port_base #( uvm_tlm_if_base #( jelly_bean_transaction,
4 jelly_bean_transaction ) );
5
6 // `UVM_IMP_COMMON( `UVM_TLM_ANALYSIS_MASK, "uvm_analysis_imp",
7 // | uvm_subscriber #( jelly_bean_transaction,
8 // | jelly_bean_transaction ) )
9 // V
10
11 local uvm_subscriber #( jelly_bean_transaction, jelly_bean_transaction ) m_imp;
12
13 function new( string name, uvm_subscriber #( jelly_bean_transaction,
14 jelly_bean_transaction ) imp );
15 super.new( name, imp, UVM_IMPLEMENTATION, 1, 1 );
16 m_imp = imp;
17 m_if_mask = `UVM_TLM_ANALYSIS_MASK; // = 1 < < 8
18 endfunction // new
19
20 // +--> `UVM_TLM_GET_TYPE_NAME( "uvm_analysis_imp" )
21
22 virtual function string get_type_name();
23 return "uvm_analysis_imp";
24 endfunction // get_type_name
25
26 // end of macro expansion
27
28 function void write( input jelly_bean_transaction t );
29 m_imp.write( t ); endfunction // write
30
31 endclass // uvm_analysis_imp
32

The write() function of the uvm_analysis_imp class delegates its job to m_imp. The m_imp is the
jelly-bean-functional-coverage subscriber (jb_fc_sub) we passed as a parameter to
the uvm_analysis_imp class. This means the write() function of the uvm_analysis_imp simply
calls the write() function of the jb_fc_sub.

Connection
Now let’s put the things together. The following sequence diagram shows the steps involved in the
connection.
Sequence Diagram

jb_mon.jb_ap.connect( jb_ap ); // in the jelly_bean_agent


When the connect() function is called in the jelly_bean_agent, the jb_ap of the jb_mon stores
the jb_ap of the jb_agent in an array called m_provided_by (step 1).

jb_agent.jb_ap.connect( jb_fc_sub.analysis_export ); // in the jelly_bean_env


Similarly, when the connect() function is called in the jelly_bean_env, the jb_ap of
the jb_agent stores the analysis_export of the jb_fc in its m_provided_by array (step 2).

Just before entering the end_of_elaboration_phase() of


the jb_mon, resolve_bindings() function is called. This function traverses the port connection. If
the port type is UVM_IMPLEMENTATION, the port is supposed to provide “implementation”. The
function stores the “implementation” to an array called m_imp_list (steps 3 to 7).
jb_ap.write( jb_tx ); // in the jelly_bean_monitor
The write() function iterates over the implementations stored in the m_imp_list array and calls
the write() function of each implementation (step 8).

That’s about the analysis port. I hope this post helped you to understand how the analysis port
works.

UVM Tutorial for Candy Lovers – 13.


Configuration Database
November 23, 2012 Keisuke Shimizu
Last Updated: July 24, 2016

This post will explain how configuration database (uvm_config_db) works. In Configurations, we
used the uvm_config_db to store a jelly_bean_if, a jelly_bean_env_config, and
two jelly_bean_agent_configs. This post will analyze how a configuration data is stored and
retrieved. The class diagram related to the configuration database is shown below. Note that we only
showed the classes related to the jelly_bean_if and jelly_bean_agent_config to avoid clutter.
UVM standard library classes are shown in pink, while the UVM classes specialized with
the jelly_bean_transaction type are shown in yellow.

Class Diagram Related to the Configuration Database


Setting a Virtual Interface
In Configurations, the top module stored two virtual interfaces (jb_if1 and jb_if2) into the
configuration database as follows:

uvm_config_db#( virtual jelly_bean_if )::set( .cntxt ( null ),


.inst_name ( "" ),
.field_name( "jb_if1" ),
.value ( jb_if1 ) );
uvm_config_db#( virtual jelly_bean_if )::set( .cntxt ( null ),
.inst_name ( "" ),
.field_name( "jb_if2" ),
.value ( jb_if2 ) );
The diagram below shows what will be created after these set functions have been called.

Storing the Virtual InterfacesThese are the steps involved in this course of action.
1. Since the top is a module, not a uvm_component, null is passed as the cntxt when set is
called. In this case, uvm_top, which is the singleton object of uvm_root class, will be used as
the cntxt.
2. An entry of an associative array (m_rsc) is created using the cntxt object as the key, if this
entry does not exist.
3. The entry of the associative array points to a uvm_pool, which has another associative array
(pool).
4. An entry of the associative array is created using the inst_name and the field_name as the key
(lookup). Note that the inst_name and the field_name are concatenated with a string
(“__M_UVM__“) when the lookup is created.
5. If the lookup does not exist, a uvm_resource object is created. If the inst_name is "", the full
name of the cntxt, which is "uvm_test_top", is stored as the scope. The value passed to
the set() function is also stored.
6. The handle to the uvm_resource object is stored in two kinds of uvm_queues. The first kind of
queues store the handles to the uvm_resource objects that have the common field_name.
A uvm_queue is created for every unique field_name. In our case, two uvm_queues are created;
one for the "jb_if1" and the other for the "jb_if2".
7. The second kind of queues store the handles to the uvm_resource objects that have the
common type. A uvm_queue is created for every unique type. In our case, one uvm_queue is
created for the virtual jelly_bean_if type.
8. The queues are registered in the singleton uvm_resource_pool. The uvm_resouce_pool has
two associative arrays. One array (rtab) uses the field_name as the key.
9. The other array (ttab) uses the type of the uvm_resource as the key.

Getting a Virtual Interface


In Configurations, the jelly_bean_base_test retrieved the jelly_bean_ifs as follows:

uvm_config_db#( virtual jelly_bean_if )::get( .cntxt ( this ),


.inst_name ( "" ),
.field_name( "jb_if1" ),
.value( jb_agent_cfg1.jb_if ) );
uvm_config_db#( virtual jelly_bean_if )::get( .cntxt ( this ),
.inst_name ( "" ),
.field_name( "jb_if2" ),
.value( jb_agent_cfg2.jb_if ) );
The diagram below shows how the jb_if1 is retrieved.
Retrieving the Virtual InterfaceThese are the steps involved in this course of action.

1. Look up the name table of the uvm_resource_pool with the field_name as the key.
2. If the name matched, get the handle of the uvm_queue.
3. Traverse the entries of the queue.
4. If the scope and the type of the object matches, that uvm_resource is stored in a newly
created uvm_queue.
5. The uvm_resource with the highest precedence is retrieved.
6. The value of the resource is returned.

Setting an Agent Configuration


Now let’s look at another example. In Configurations, the jelly_bean_env stored the jelly-bean-
agent configurations (jb_agent_cfg1 and jb_agent_cfg2) as follows:

uvm_config_db#( jelly_bean_agent_config )::set( .cntxt ( this ),


.inst_name ( "jb_agent1*" ),
.field_name( "jb_agent_cfg" ),
.value( jb_env_cfg.jb_agent_cfg1 ) );
uvm_config_db#( jelly_bean_agent_config )::set( .cntxt ( this ),
.inst_name ( "jb_agent2*" ),
.field_name( "jb_agent_cfg" ),
.value( jb_env_cfg.jb_agent_cfg2 ) );
The diagram below shows what will be created after these set functions have been called.
Storing the Agent ConfigurationsThese are the steps involved in this course of action.

1. The jelly_bean_env calls the set() function with this (jb_env) as the cntxt.
2. An entry of an associative array (m_rsc) is created using the cntxt object as the key, if this
entry does not exist.
3. The entry of the associative array points to a uvm_pool, which has another associative array
(pool).
4. An entry of the associative array is created using the inst_name and the field_name as the key
(lookup). Note that the inst_name and the field_name are concatenated with a string
(“__M_UVM__“) when the lookup is created.
5. If the lookup does not exist, a uvm_resource object is created. Unlike the virtual-interface case,
the inst_name is not "" this time. In this case, the full name of the cntxtobject concatenated
with the inst_name is stored as the scope. The value passed to the set() function is also
stored.
6. The handle to the uvm_resource object is stored in two kinds of uvm_queues. The first kind of
queues store the handles to the uvm_resource objects that have the common field_name.
A uvm_queue is created for every unique field_name. In our case, one uvm_queue is created for
the "jb_agent_cfg".
7. The second kind of queues store the handles to the uvm_resource objects that have the
common type. A uvm_queue is created for every unique type. In our case, one uvm_queue is
created for the jelly_bean_agent_config type.
8. The queues are registered in the singleton uvm_resource_pool. The uvm_resouce_pool has
two associative arrays. One array (rtab) uses the field_name as the key.
9. The other array (ttab) uses the type of the uvm_resource as the key.

One difference from the previous example is that the rtab has only one entry because both
the uvm_agent_cfg1 and the uvm_agent_cfg2 use the same field_name("uvm_agent_cfg").

Getting an Agent Configuration


In Configurations, the jelly_bean_agent retrieved the jb_agent_cfg as follows:

uvm_config_db#( jelly_bean_agent_config )::get( .cntxt ( this ),


.inst_name ( "" ),
.field_name( "jb_agent_cfg" ),
.value ( jb_agent_cfg ) );
The diagram below shows how the jb_agent_cfg is retrieved.
Retrieving the Agent ConfigurationThese are the steps involved in this course of action.

1. Look up the name table of the uvm_resource_pool with the field_name as the key.
2. If the name matched, get the handle of the uvm_queue.
3. Traverse the entries of the queue.
4. If the scope and the type of the object matches, that uvm_resource is stored in a newly
created uvm_queue. Note that there are two uvm_resource objects, but their scopes are
different. The full name of the cntxt object passed to the get() function distinguishes between
the two.
5. The uvm_resource with the highest precedence is retrieved.
6. The value of the resource is returned.
Summary
That’s about the configuration database. This diagram shows the main objects involved in this
article. I hope this post helped you to understand how the configuration database works.

UVM Tutorial for Candy Lovers – 14. Field Macros


December 1, 2012 Keisuke Shimizu
Last Updated: September 14, 2013

This post will explain how UVM field macros (`uvm_field_*) work. In Transactions and Sequences,
we used the UVM field macros to automatically implement the standard data methods, such
as copy(), compare(), and pack() for the jelly_bean_transaction.

1
`uvm_object_utils_begin(jelly_bean_transaction)
2
`uvm_field_enum(flavor_e, flavor, UVM_ALL_ON)
3
`uvm_field_enum(color_e, color, UVM_ALL_ON)
4
`uvm_field_int (sugar_free, UVM_ALL_ON)
5
`uvm_field_int (sour, UVM_ALL_ON)
6
`uvm_field_enum(taste_e, taste, UVM_ALL_ON)
7
`uvm_object_utils_end

The following pseudo code shows how these field macros are expanded.

Click here to expand the macros.


Wow! What a long code! Each field macro was expanded to about 80 lines of code. You don’t need
to fully understand the code, but basically the code does the followings:

 The `uvm_object_utils_begin() macro creates a virtual function


called __m_uvm_field_automation (the first highlighted block of code in yellow).
 Each `uvm_field_* macro creates a case statement (the second highlighted block) and
performs the functionality of copy, compare, and pack, depending on the value of
the what__ argument passed to the __m_uvm_field_automation() function.

The __m_uvm_field_automation() is then used in uvm_object class. As you see the following
diagram, the uvm_object::copy() calls the __m_uvm_field_automation() with UVM_COPY as the
value of the what__. Similarly uvm_object::compare() calls
the __m_uvm_field_automation() with UVM_COMPARE.
Some Standard Data Methods of the uvm_object ClassBy now you might think that these field
macros are convenient but not efficient. For more efficient and more flexible implementation, we can
use user definable do_*() hooks. As shown above the uvm_object calls the do_*() hook after
calling the __m_uvm_field_automation(). For example, the uvm_object::copy() calls
the do_copy() after calling the __m_uvm_field_automation(), and
the uvm_object::compare() calls the do_compare() after calling
the __m_uvm_field_automation(). By default these do_*() methods are empty. I will explain
more detail about the do_*() methods in the next post. If no field macros are used,
the __m_uvm_field_automation() does almost nothing (only the code between the two highlighted
blocks will remain).
I hope this post helped you to understand the field macros.

UVM Tutorial for Candy Lovers – 15. “Do” Hooks


January 4, 2013 Keisuke Shimizu
Posted on January 4, 2013 (Last Updated on April 4, 2014)

This post will explain user-definable do_* hook functions. In Field Macros, we saw that the standard
data methods, such as copy() and compare(), called the user-definable hook functions, such
as do_copy() and do_compare(). I will define these hook functions in this post.

Jelly Bean Transaction Class with “do” Functions


Let’s add the “do” functions to the jelly_bean_transaction class.

do_copy
The do_copy() method is called by the copy() method. The do_copy() method is used to copy all
the properties of a jelly_bean_transaction object (lines 10 to 14). Note that we have to cast
a uvm_object (rhs) to a jelly_bean_transaction (that) in order to access the properties of
the jelly_bean_transaction object (line 4). We must call the super.do_copy() to copy the
properties defined in the super class (line 9).

1 virtual function void do_copy( uvm_object rhs );


2 jelly_bean_transaction that;
3
4 if ( ! $cast( that, rhs ) ) begin `uvm_error( get_name(), "rhs is not
5 a jelly_bean_transaction" )
6 return;
7 end
8
9 super.do_copy( rhs ); this.flavor = that.flavor; this.color
10 = that.color; this.sugar_free = that.sugar_free; this.sour =
11 that.sour; this.taste = that.taste; endfunction: do_copy

do_compare
The do_compare() method is called by the compare() method. The do_compare() is used to
compare each property of the jelly_bean_transaction object. The do_compare()returns 1 if the
comparison succeeds, and returns 0 if the comparison fails (lines 6 to 11). Note that we have to cast
a uvm_object (rhs) to a jelly_bean_transaction (that) again in order to access the properties
of the jelly_bean_transaction object (line 4). We must call the super.do_compare() to compare
the properties of the super class (line 6). The uvm_comparer argument provides a policy object for
doing comparisons, but we do not use it.

1
2
3
4 virtual function bit do_compare( uvm_object rhs, uvm_comparer comparer );
5 jelly_bean_transaction that;
6
7 if ( ! $cast( that, rhs ) ) return 0;
8 return ( super.do_compare( rhs, comparer ) && this.flavor
9 == that.flavor && this.color == that.color &&
10 this.sugar_free == that.sugar_free && this.sour == that.sour
11 && this.taste == that.taste ); endfunction: do_compare
12

do_pack
The do_pack() method is called by the pack(), pack_bytes(), and pack_ints() methods.
The do_pack() is used to pack each propery of the jelly_bean_transaction object using
a uvm_packer policy object. Please see Register Abstraction for how each property is packed.
The packer determines how the packing should be done. We must call the super.do_pack() to
pack the properties of the super class (line 5).

1
virtual function void do_pack( uvm_packer packer );
2
bit R1; // reserved bit
3
bit [5:0] R6; // reserved bits
4
5
super.do_pack( packer ); packer.pack_field_int( .value( flavor ),
6
.size( 3 ) ); packer.pack_field_int( .value( color ), .size( 2 ) );
7
packer.pack_field_int( .value( sugar_free ), .size( 1 ) );
8
packer.pack_field_int( .value( sour ), .size( 1 ) );
9
packer.pack_field_int( .value( R1 ), .size( 1 ) );
10
packer.pack_field_int( .value( taste ), .size( 2 ) );
11
packer.pack_field_int( .value( R6 ), .size( 6 ) );
12
endfunction: do_pack
13
do_unpack
The do_unpack() method is called by the unpack(), unpack_bytes(),
and unpack_ints() methods. The do_unpack() is used to unpack each property of
the jelly_bean_transaction object using a uvm_packer policy object. The packer determines
how the unpacking should be done. We must call the super.do_unpack() to unpack the properties
of the super class (line 5).

virtual function void do_unpack( uvm_packer packer );


5 bit R1; // reserved bit
6 bit [5:0] R6; // reserved bits
7
8 super.do_unpack( packer ); flavor = flavor_e'(
9 packer.unpack_field_int( .size( 3 ) ) ); color = color_e '(
10 packer.unpack_field_int( .size( 2 ) ) ); sugar_free =
11 packer.unpack_field_int( .size( 1 ) ); sour =
12 packer.unpack_field_int( .size( 1 ) ); R1 =
13 packer.unpack_field_int( .size( 1 ) ); taste = taste_e '(
packer.unpack_field_int( .size( 2 ) ) ); R6 =
packer.unpack_field_int( .size( 6 ) );
endfunction: do_unpack
convert2string
The convert2string() method is called by the user to provide object information in the form of a
string. The convert2string() is used to convert each property of
the jelly_bean_transaction object into a string. We should call the super.convert2string() to
convert the properties of the super class into the string (line 2).

1
2 virtual function string convert2string();
3 string s = super.convert2string();
4 s = { s, $psprintf( "\nname : %s", get_name() ) };
5 s = { s, $psprintf( "\nflavor : %s", flavor.name() ) };
6 s = { s, $psprintf( "\ncolor : %s", color.name() ) };
7 s = { s, $psprintf( "\nsugar_free: %b", sugar_free ) };
8 s = { s, $psprintf( "\nsour : %b", sour ) };
9 s = { s, $psprintf( "\ntaste : %s", taste.name() ) };
10 return s;
endfunction: convert2string

Test the “do” Hooks


Let’s test the “do” hooks. We will define the run_phase() task of the jelly_bean_test class as
follows.

1. Create three jelly_bean_transactions; jb_tx1, jb_tx2 and jb_tx3 (lines 11 to 13).


2. Randomize the jb_tx1 (line 14).
3. Copy the properties of the jb_tx1 to jb_tx2 (line 18).
4. Copy the properties of the jb_tx1 to jb_tx3, but using the pack() and unpack() methods
instead of calling the copy() method (lines 24 and 25).
5. Now the jb_tx1, jb_tx2, and jb_tx3 should have the same property values. Verify it using
the compare() method (lines 29 to 39).
6. Print the properties of the jb_tx1, jb_tx2, and jb_tx3 using the convert2string() method to
visually verify the property values (lines 43 to 45).

task run_phase( uvm_phase phase );


jelly_bean_transaction jb_tx1;
jelly_bean_transaction jb_tx2;
17 jelly_bean_transaction jb_tx3;
18 uvm_packer jb_packer;
19 bit bitstream[];
20 int num_bits;
21
22 phase.raise_objection( .obj( this ) );
23
24 jb_tx1 = jelly_bean_transaction::type_id::create( "jb_tx1" );
25 jb_tx2 = jelly_bean_transaction::type_id::create( "jb_tx2" );
26 jb_tx3 = jelly_bean_transaction::type_id::create( "jb_tx3" );
27 assert( jb_tx1.randomize() );
28
// copy jb_tx1 to jb_tx2
29
30 jb_tx2.copy( jb_tx1 );
31 // create jb_tx3 by packing and unpacking jb_tx1
32
33 jb_packer = new; jb_packer.big_endian = 0;
34 num_bits = jb_tx1.pack ( bitstream, jb_packer );
35 num_bits = jb_tx3.unpack( bitstream, jb_packer );
36 // check if jb_tx1, jb_tx2 and jb_tx3 have the same properties
37
38 if ( jb_tx1.compare( jb_tx2 ) ) begin `uvm_info( get_name(), "jb_tx1
39 and jb_tx2 matched", UVM_NONE ) end
40 else begin `uvm_error( get_name(), "jb_tx1 and jb_tx2 mismatched" )
41 end
42 if ( jb_tx2.compare( jb_tx3 ) ) begin `uvm_info( get_name(), "jb_tx2
43 and jb_tx3 matched", UVM_NONE ) end
44 else begin `uvm_error( get_name(), "jb_tx2 and jb_tx3 mismatched" )
45 end
46 // print each object
47
48 `uvm_info( get_name(), jb_tx1.convert2string(), UVM_NONE ) `uvm_info(
get_name(), jb_tx2.convert2string(), UVM_NONE )
`uvm_info( get_name(), jb_tx3.convert2string(), UVM_NONE )
phase.drop_objection( .obj( this ) );
endtask: run_phase
You might have noticed that we created a uvm_packer object (jb_packer) on line 22 and set
its big_endian property to 0 (line 23). We used this packer when we pack and unpack
the jelly_bean_transaction (lines 24 and 25). This is to make sure we pack each property value
in little endian as we did in Register Abstraction. If you just want to pack and unpack and don’t care
about the bitstream representation, you don’t have to create the uvm_packer object. If you don’t
specify the uvm_packer, the global uvm_default_packer policy will be used, of which the value
of big_endian is 1. The following diagram shows how the value of big_endian affects the bitstream
representation. The m_bits is a property of the uvm_packer and it represents the packed bitstream
in the form of a bit array.
Pack in little endian and in big endian

Simulation
Here is a simulation result. As you see, all three jelly_bean_transactions have same property
values.

1 UVM_INFO jb15.sv(589) @ 0: uvm_test_top [uvm_test_top] jb_tx1 and jb_tx2 matched


2 UVM_INFO jb15.sv(595) @ 0: uvm_test_top [uvm_test_top] jb_tx2 and jb_tx3 matched
3 UVM_INFO jb15.sv(602) @ 0: uvm_test_top [uvm_test_top]
4 name : jb_tx1
5 flavor : CHOCOLATE
6 color : RED
7 sugar_free: 1
8 sour : 0
9 taste : UNKNOWN
10 UVM_INFO jb15.sv(603) @ 0: uvm_test_top [uvm_test_top]
11 name : jb_tx2
12 flavor : CHOCOLATE
13 color : RED
14 sugar_free: 1
15 sour : 0
16 taste : UNKNOWN
17 UVM_INFO jb15.sv(604) @ 0: uvm_test_top [uvm_test_top]
18 name : jb_tx3
19 flavor : CHOCOLATE
20 color : RED
21 sugar_free: 1
22 sour : 0
23 taste : UNKNOWN

I hope this tutorial helped you to understand the “do” hooks.

UVM Tutorial for Candy Lovers – 16. Register


Access Methods
February 1, 2013 Keisuke Shimizu
Last Updated on April 11, 2014

The register abstraction layer (RAL) of UVM provides several methods to access registers. This post
will explain how the register-access methods work. In Register Abstraction, we introduced the
overview of RAL and explained how to define registers. In this post, we will cover how to access the
registers.

Properties of uvm_reg_field
Before diving into the register-access methods, let’s look at how a register value is stored. As seen
in Register Abstraction, uvm_reg_field is the lowest register-abstraction layer which represents the
bits of a register. The uvm_reg_field uses several properties to store a variety of register-field
values:

 m_reset["HARD"] stores a hard reset value. Note that the m_reset is an associative array with
a kind of reset as the key.
 m_mirrored stores the value of what we think in our design under test (DUT).
 m_desired stores the value of what we want to set to the DUT.
 value stores the value to be sampled in a functional coverage, or the value to be constrained
when the field is randomized.

Note that among these properties, only the value property is public. The other properties are local,
thus we cannot access them directly from the out side of the class. We will show you how to access
these local properties using register-access methods later.

Properties of uvm_reg_field
configure()
The first thing we do after creating a uvm_reg_field is configuring it. In Register Abstraction, we
configured the flavor field as follows. Note that in Register Abstraction, we defined the flavor field
as "WO" (write-only), but we defined it as "RW" (read/write) here to make the field more generic.

1
2 flavor = uvm_reg_field::type_id::create( "flavor" );
3 flavor.configure( .parent ( this ),
4 .size ( 3 ),
5 .lsb_pos ( 0 ),
6 .access ( "RW" ),
7 .volatile ( 0 ),
8 .reset ( 0 ), .has_reset
9 ( 1 ),
10 .individually_accessible( 0 ) );

If the has_reset argument is 1, the value of reset argument is taken as the "HARD" reset value. If
the has_reset value is 0, the value of reset is ignored. The value of resetshould match the reset
state of the DUT. If you want to modify the reset value after the configuration, you can
use set_reset() method.

flavor.set_reset( .value( 0 ), .kind( "HARD" ) ); // kind == "HARD" by default

How the configure() and set_reset() methods work


reset()
The reset() method resets the properties of a register field, if the m_reset[kind] exists. The
default kind is "HARD". If the m_reset[kind] does not exist, the reset() method does nothing.
Note that the reset() method does not reset a register in the DUT. It only resets the properties of a
register-field object.

flavor.reset( .kind( "HARD" ) ); // kind == "HARD" by default

How the reset() method works

set()
The set() method sets the desired value of a register field. The set() method does not set the
value to a register in the DUT. It only sets the value to the m_desired and the value properties of a
register-field object. To actually set the value to the register in the DUT,
use write() or update() method. These methods will be explained later.

flavor.set( .value( 1 ) );
How the set() method works

get()
The get() method gets the desired value of a register field. The get() method does not get the
value from a register in the DUT. It only gets the value of the m_desiredproperty. To actually get the
value from the DUT, use read() or mirror() methods. These methods will be explained later.
Similarly to the get() method, there are two more getters to access the local properties.
The get_reset() retrieves the value of the m_reset[kind] property, while
the get_mirrored_value() method retrieves the value of the m_mirrored property.

uvm_reg_data_t desired_value = flavor.get();


uvm_reg_data_t reset_value = flavor.get_reset( .kind( "HARD" ) ); // kind == "HARD" by
uvm_reg_data_t mirrored_value = flavor.get_mirrored_value();
How the get(), get_reset(), and get_mirrored_value() methods work

randomize()
The randomize() method is a SystemVerilog method. It randomizes the value property of a
register-field object. After the randomization, the post_randomize() method copies the value of
the value property to the m_desired property. Note that the pre_randomize() method copies the
value of the m_desired to the value property if the rand_mode of the value property is OFF.

assert( flavor.randomize() );
How the randomize() method works

write()
The write() method actually writes a value to the DUT.

uvm_status_e status;

flavor.write( .status( status ), .value( 1 ) );


The write() method involves multiple steps.

1. A uvm_reg_item object corresponding to the write operation is created.


2. The uvm_reg_adapter converts the write operation to a corresponding bus transaction.
3. The uvm_driver executes the bus transaction to the DUT.
4. The uvm_monitor captures the bus transaction.
5. The uvm_reg_predictor asks the uvm_reg_adapter to convert the bus transaction to a
corresponding register operation.
6. The register operation is converted to a uvm_reg_item.
7. The uvm_reg_item is used to update the value, m_mirrored, and m_desired properties.

Note that if the individually_accessible argument was 0 when the register field was configured,
the entire register containing the field is written, because the field is not individually accessible. In
this case, the m_mirrored values are used as the write values for the other fields.
How the write() method works

read()
The read() method actually reads a register value from the DUT.

uvm_status_e status;
uvm_reg_data_t value;

flavor.read( .status( status ), .value( value ) );


Similarly to the write() method, the read() method involves multiple steps.

1. A uvm_reg_item object corresponding to the read operation is created.


2. The uvm_reg_adapter converts the read operation to a corresponding bus transaction.
3. The uvm_driver executes the bus transaction to the DUT.
4. The uvm_reg_apapter converts the bus transaction with read data to a register operation.
5. The read() method returns the read value to the caller.
6. In the mean time, the uvm_monitor captures the bus transaction.
7. The uvm_reg_predictor asks the uvm_reg_adapter to convert the bus transaction to a
corresponding register operation.
8. The register operation is converted to a uvm_reg_item.
9. The uvm_reg_item is used to update the value, m_mirrored, and m_desired properties.

Note that if the individually_accessible argument was 0 when the register field was configured,
the entire register containing the field is read. In this case, the m_mirroredvalues are updated for
the other fields as well.
How the read() method works

update()
The update() method actually writes a register value to the DUT. The update() method belongs to
the uvm_reg class. The uvm_reg_field class does not have the update()method.

uvm_status_e status;

jb_recipe_reg.update( .status( status ) );


The differences between the write() method and the update() method are:

 The write() method takes a value as its argument, while the update() method uses the value
of the m_desired property as the value to write.
 The update() method writes the value only if the m_mirrored and the m_desired are not
equal.
Before the update() is executed
The update() method internally calls the write( .value( m_desired ) ). Because of this, the
value of the m_mirrored will be updated as well, after the update.

After the
update() is executed

mirror()
The mirror() method actually reads a register from the DUT.

uvm_status_e status;

flavor.mirror( .status( status ), .check( UVM_CHECK ) );


The differences between the read() method and the mirror() method are:

 The read() method returns the register value to the caller, while the mirror() method does not
return the register value. The mirror() method only updates the value of
the m_mirrored property.
 The mirror() method compares the read value against the m_desired if the value of
the check argument is UVM_CHECK. Note that the UVM Class Library document states that it
compares the read value against the mirrored value, but if you look at the line 2,944
of uvm_reg.svh of uvm-1.1c code base, it actually compares against the desired value, not
against the mirrored value.

April 11, 2014: uvm-1.1d code base has corrected this issue. The mirror() compares the read
value against the mirrored value now. Please see the line 2,951 of uvm_reg.svh if you are
curious about this fix.)
Another caveat about the check is that if you set the volatile argument to be 1 when you
configured the register field, the register field won’t be checked even though you set
the check argument to be UVM_CHECK. This is because we cannot predict the value of the
register field deterministically as it might have been changed (volatile) in the DUT.

The mirror() method internally calls do_read() method. This is the same method
the read() method internally calls. Because of this, the mirror() method will update the value and
the m_desired properties, in addition to the m_mirrored property.

How the mirror() method works

predict()
The predict() method updates the mirrored value.
flavor.predict( .value( 1 ) );
The predict() method also updates the value and the m_desired properties.

How the predict() method works

Summary
The table below summarizes how each method updates the properties of the register-field object.

m_reset
Method ["HARD"] value m_desired

configure set the value of val


(.reset(val),
.has_reset(1))
m_reset
Method ["HARD"] value m_desired

set_reset(val) set the value of val

reset() copy the value copy the value of m_reset


of m_reset ["HARD"]
["HARD"]

set(val) set the value of val set the value of val

get_reset() return the value


of m_reset
["HARD"]

get() return the value


of m_desired

get_mirrored_value()

randomize() randomize copy the value of value

write(.value(val)) set the value of val set the value of val

read(.value(val)) set the read value set the read value

update() set the value set the value of m_desired


of m_desired

mirror() set the read value set the read value

predict set the value of val set the value of val


(.value(val))
In this post, we only covered so-called front-door access. We will cover back-door access in a
separate post. I hope this tutorial helped you to understand the register access methods.

UVM Tutorial for Candy Lovers – 17. Register


Read Demystified
September 28, 2013 Keisuke Shimizu
Last Updated: December 28, 2013

In the last post, Register Access Methods, we looked at the primary methods of RAL and showed
how they worked. This post will further focus on the read() method and show how the method
actually reads and returns the value of a register. In particular, we will explain the steps #1 to #5 of
the read() method we have seen in the last post in more detail.

The following sequence diagram shows the steps involved in the read() method. We assumed that
the user called the method like this:

uvm_status_e status;
uvm_reg_data_t value;
jb_reg_block.jb_taste_reg.read( status, value );
How the read() method reads and returns the value of a registerThe number in the angle bracket
below corresponds to the number in the figure.

 [1] The user calls the read() method.


 [3] The read() method calls the XreadX() method of the uvm_reg class.
The XreadX() method creates a uvm_reg_item object corresponding to the read.
 [4] The XreadX() method calls the do_read() method of the uvm_reg class.
 [6] and [7] The do_read() method retrieves the “path” and the register map (local_map) of
the register. We assume that we use the FRONTDOOR path.
 [8] The do_read() method calls the do_read() method of the uvm_reg_map class, which
creates a parent sequence. The parent sequence will be used to start a bus request in the later
step.
 [9] The do_read() method calls the do_bus_read() method of the uvm_reg_map class.
The do_bus_read() method creates a uvm_reg_bus_op that corresponds to the read.
 [10] and [11] The do_bus_read() method calls the reg2bus() method of
the uvm_reg_adapter class that is associated with the register map to obtain the corresponding
bus request.
 [12] The do_bus_read() method assigns the sequencer that the bus request will be processed
on to the bus request. We assume that the sequencer was associated with the register map.
 [13] The do_bus_read() method starts the parent sequence using the above sequencer.
 [14] The do_bus_read() method waits for the sequence to finish.
 [15] to [18] If the provides_responses property of the register adapter was set to be true, a
separate bus response is retrieved by calling the get_base_response(). The response is
converted to a uvm_reg_bus_op (shown as rw_access).
 [19] and [20] If the provides_responses property of the register adapter was set to be false,
the bus request, which also contains the bus response, is converted to
a uvm_reg_bus_op (shown as rw_access).
 [21] to [24] The status and the value of the rw_access is returned to the caller.

I hope this post helped to understand how the read() method works under the hood.

UVM Tutorial for Candy Lovers – 18.


Configuration Database Revisited
December 28, 2013 Keisuke Shimizu
Last updated: December 20, 2015

In the post, Configurations, we looked at the configuration flow of the jelly bean verification. We also
looked at the behind the scenes of the configuration flow in the post, Configuration Database. This
post will focus on the syntax and semantics of accessing the configuration
database. In Configurations, we stored and retrieved
the jelly_bean_if, jelly_bean_env_config, and jelly_bean_agent_config using the
configuration database. The following figure depicts the component hierarchy used
in Configurations on the left, and the configuration database (shown as clouds) on the right. The
code snippets to access the database are shown in the middle.
Accessing Configuration Database

Storing the jelly_bean_env_config


Let’s look at how to store and retrieve the jb_env_cfg (shown in gray in the figure) first as this might
be the easiest to understand. The jelly_bean_base_test called set()function to store
the jb_env_cfg in the database as follows.

uvm_config_db#( jelly_bean_env_config )::set( .cntxt( this ), .inst_name( "*" ),


.field_name( "jb_env_cfg" ), .value( jb_env_
The uvm_config_db is a parameterized class. In the above code, the uvm_config_db is specialized
with the jelly_bean_env_config type. The cntxt (context) and inst_name(instance name) are
used to create a scope in the database. The scope is created by concatenating the full name of the
context and the instance name ({ cntxt.get_full_name(), ".", inst_name }). In the above
example, the scope is { this.get_full_name(), ".", "*" }, which is evaluated
to "uvm_test_top.*". Note that the full name of the jelly_bean_base_test is assigned to
be "uvm_test_top" when the run_test() task is called in the top-level module (top). Note also
that the "*" is a wildcard character that matches any components. The object handle
of jb_env_cfg is stored under the name of "jb_env_cfg". The picture below conceptually shows
how the jb_env_cfg is tagged in the database.
The jb_env_cfg Stored in the
Configuration Database

Retrieving the jelly_bean_env_config


The jb_env called get() function to retrieve the jb_env_cfg from the database as follows.

uvm_config_db#( jelly_bean_env_config )::get( .cntxt( this ), .inst_name( "" ),


.field_name( "jb_env_cfg" ), .value( jb_env_
The meanings of the arguments of the get() function are the same as those of the set() function.
Similar to the set() function, the scope is defined as { cntxt.get_full_name(), ".",
inst_name }. In the above code, the scope is evaluated to "uvm_test_top.jb_env". It is typical to
use this as the cntxt and "" as the inst_name. Note that if the inst_name is empty (""), the dot
(".") is also omitted. The get() function searches the database and tries to find the object that
matches the type, scope, and field_name. Since the scope of the jb_env_cfg was defined
as "uvm_test_top.*" in the database, the scope, "uvm_test_top.jb_env", successfully matches
it.

Storing the jelly_bean_agent_config


Now let’s look at another example. This time, the jb_env stores the jb_agent_cfg, then
the jb_agent1 retrieves it (shown in orange in the figure on the top).
The jb_env called set() function to store the jb_agent_cfg in the database as follows.

uvm_config_db#( jelly_bean_agent_config )::set( .cntxt( this ), .inst_name( "jb_agent1*" )


.field_name( "jb_agent_cfg" ), .value( jb_env_cfg.jb_agent_cfg1 ) )
The uvm_config_db is specialized with the jelly_bean_agent_config type. The scope of the
object is { this.get_full_name(), ".", "jb_agent1*" }, which is evaluated
to "uvm_test_top.jb_env.jb_agent1*". Note that this scope matches jb_agent1 and any
components under it. The jb_env_cfg.jb_agent_cfg1 is stored under the name
of "jb_agent_cfg". The picture below conceptually shows how the jb_agent_cfg1 is tagged in the
database.
The jb_agent_cfg1 Stored
in the Configuration Database

Retrieving the jelly_bean_agent_config


The jb_agent1 called get() function to retrieve the jb_agent_cfg1 as follows.

uvm_config_db#( jelly_bean_agent_config )::get( .cntxt( this ), .inst_name( "" ),


.field_name( "jb_agent_cfg" ), .value( jb_
The scope is { this.get_full_name(), ".", "" }, which is evaluated
to "uvm_test_top.jb_env.jb_agent1". Since the scope of the jb_agent_cfg1 in the database
is "uvm_test_top.jb_env.jb_agent1*", it successfully matches the scope defined by
this get() function.

Storing the jelly_bean_if


Let’s look at the last example. The top-level module stores a virtual interface (shown in yellow in the
figure on the top). The top module called set() function as follows.

uvm_config_db#( virtual jelly_bean_if )::set( .cntxt( null ), .inst_name( "uvm_test_top" )


.field_name( "jb_if1" ), .value( jb_if1 ) )
Two things to note here. The first is that we used null as the cntxt. This was because the top-level
module is not a uvm_component and cannot obtain its full name by calling the get_full_name().
The second is that we used "uvm_test_top" as the inst_name. As we have seen,
the "uvm_test_top" is the full name of the top-level test component. Thus, it is important to use this
name as a part of the scope.
If the cntxt is null, the scope consists of the inst_name only, which is "uvm_test_top". The
picture below conceptually shows how the jb_if1 is tagged in the database.

The jb_if1 Stored in the Configuration


Database
Retrieving the jelly_bean_if
The jelly_bean_base_test called get() function to retrieve the jb_if1 as follows.

uvm_config_db#( virtual jelly_bean_if )::get( .cntxt( this ), .inst_name( "" ),


.field_name( "jb_if1" ), .value( jb_agent_cf
The scope is { this.get_full_name(), ".", "" }, which is evaluated
to "uvm_test_top" ("." is not included if the inst_name is ""). Since the scope of the jb_if1 in
the database is "uvm_test_top", it successfully matches the scope defined by this get() function.

Summary of the scopes


The table below summarizes which component matches the scopes defined in our example
assuming get() function uses this as the cntxt and "" as the inst_name. Note that the
scope "uvm_test_top.*" matches any components under the uvm_test_top, but it does not
match the scope "uvm_test_top" (1). On the other hand, the
scope "uvm_test_top" matches "uvm_test_top" only. It does not match any components under
the uvm_test_top (2).

Component/Scope uvm_test_top.* uvm_test_top.jb_env.jb_agent1* uvm_test_top

jelly_bean_base_test
(1) (2)
(uvm_test_top)

uvm_test_top.jb_env

uvm_test_top.jb_env.jb_agent1

uvm_test_top.jb_env.jb_agent2

I hope this post helped to understand how to access the configuration database.

UVM Tutorial for Candy Lovers – 19. Analysis


FIFO
February 11, 2014 Keisuke Shimizu
Last Updated: February 14, 2015

This post will explain how to use analysis FIFOs. Let’s assume I wanted a scoreboard that compares
two streams of jelly beans; one stream is for “expected” jelly beans, the other is for “actual” jelly
beans. Also assume the jelly beans are fed to the scoreboard asynchronously. To synchronize the
jelly beans, I used two analysis FIFOs:
Asynchronous Jelly Bean Scoreboard
This is a sample code to implement the above scoreboard. The code should be self-explanatory.
One note is that the get is a blocking task to get the next item from the FIFO (lines 43 and
44). Somehow this task is not documented in the UVM Class Reference, though.

February 14, 2015: Changed to explicitly use the get_peek_export instead of using
undocumented get task.

When both the expected jelly bean and the actual jelly bean become available, the scoreboard
compares them (line 45). Before finishing a simulation (extract_phase), the scoreboard checks
whether there are leftover jelly beans in either FIFO. Isn’t it easy?

1 class asynchronous_jelly_bean_scoreboard extends uvm_component;


2 `uvm_component_utils( asynchronous_jelly_bean_scoreboard )
3
4 uvm_analysis_export #( jelly_bean_transaction ) expected_analysis_export;
5 uvm_analysis_export #( jelly_bean_transaction ) actual_analysis_export;
6 uvm_tlm_analysis_fifo#( jelly_bean_transaction ) expected_jelly_bean_fifo;
7 uvm_tlm_analysis_fifo#( jelly_bean_transaction ) actual_jelly_bean_fifo;
8
9 // Function: new
10 //-------------------------------------------------------------------------
11 function new( string name, uvm_component parent );
12 super.new( name, parent );
13 endfunction: new
14
15 // Function: build_phase
16 //-------------------------------------------------------------------------
17 virtual function void build_phase( uvm_phase phase );
18 super.build_phase( phase );
19
20 expected_analysis_export = new( "expected_analysis_export", this );
21 actual_analysis_export = new( "actual_analysis_export", this );
22 expected_jelly_bean_fifo = new( "expected_jelly_bean_fifo", this );
23 actual_jelly_bean_fifo = new( "actual_jelly_bean_fifo", this );
24 endfunction: build_phase
25
26 // Function: connect_phase
27 //-------------------------------------------------------------------------
28 virtual function void connect_phase( uvm_phase phase );
29 super.connect_phase( phase );
30
31 expected_analysis_export.connect( expected_jelly_bean_fifo.analysis_export );
32 actual_analysis_export.connect( actual_jelly_bean_fifo.analysis_export );
33 endfunction: connect_phase
34
35 // Task: main_phase
36 //-------------------------------------------------------------------------
37 virtual task main_phase( uvm_phase phase );
38 jelly_bean_transaction expected_jelly_bean;
39 jelly_bean_transaction actual_jelly_bean;
40
41 super.main_phase( phase );
42 forever begin
43 expected_jelly_bean_fifo.get_peek_export.get( expected_jelly_bean );
44 actual_jelly_bean );
45 if ( expected_jelly_bean.compare( actual_jelly_bean ) == 0 ) begin
46 `uvm_error( "main_phase",
47 { "jelly bean mismatch: ",
48 "expected:", expected_jelly_bean.convert2string(),
49 "actual:", actual_jelly_bean.convert2string() } )
50 end
51 end
52 endtask: main_phase
53
54 // Function: extract_phase - checks leftover jelly beans in the FIFOs
55 //-------------------------------------------------------------------------
56 virtual function void extract_phase( uvm_phase phase );
57 jelly_bean_transaction jelly_bean;
58
59 super.extract_phase( phase );
60 if ( expected_jelly_bean_fifo.try_get( jelly_bean ) ) begin
61 `uvm_error( "expected_jelly_bean_fifo",
62 { "found a leftover jelly bean: ",
63 jelly_bean.convert2string() } )
64 end
65
66 if ( actual_jelly_bean_fifo.try_get( jelly_bean ) ) begin
67 `uvm_error( "actual_jelly_bean_fifo",
68 { "found a leftover jelly bean: ",
69 jelly_bean.convert2string() } )
70 end
endfunction: extract_phase
endclass: asynchronous_jelly_bean_scoreboard
UVM Objects Involved in This Article

UVM Tutorial for Candy Lovers – 20. TLM 1


April 6, 2014 Keisuke Shimizu
UVM supports ports (TLM 1) and sockets (TLM 2) as transaction-level interfaces. This post will
explain TLM 1. TLM 1 seems daunting as it has many ports, exports, and “imp”s, but once you
understand the basics, TLM 1 is not too difficult.

Ports
Ports define which access methods to use. There are twenty-three port classes in TLM 1. Each port
is a subclass of the uvm_port_base class, which in turn is a subclass of the uvm_tlm_if_base class
(or a subclass of the uvm_sqr_if_base class in case of the uvm_seq_item_pull_port). See the
class diagram below.
TLM 1 Port ClassesThe uvm_tlm_if_base class and the uvm_sqr_if_base class define the access
Class \ Method put try_ put can_ put get try_ get can_ get peek try_ peek

uvm_blocking_ X
put_port

uvm_nonblocking_ X X
put_port

uvm_put_port X X X

uvm_blocking_ X
get_port

uvm_nonblocking_ X X
get_port

uvm_get_port X X X

uvm_blocking_ X
peek_port

uvm_nonblocking_ X
peek_port

uvm_peek_port X X

uvm_blocking_ X X
get_peek_port

uvm_nonblocking_ X X X
get_peek_port

uvm_get_peek_port X X X X X

uvm_blocking_ X X X
master_port
uvm_nonblocking_ X X X X X
master_port

uvm_master_port X X X X X X X X

uvm_blocking_ X X X
slave_port

uvm_nonblocking_ X X X X X
slave_port

uvm_slave_port X X X X X X X X

uvm_blocking_
transport_port

uvm_nonblocking_
transport_port

uvm_transport_port

uvm_analysis_port

methods of a port. By default, each method issues an error message if it is called. Each port
overrides a subset of the access methods as listed below. For example,
the uvm_blocking_put_port overrides the put method (only). This means if you call a method
other than the put, you will get an error message defined in the uvm_tlm_if_base class.

Exports
Exports are used when you promote imps (see the next section) to a parent component. Similar to
the ports, each export is a subclass of the uvm_port_base class, which in turn is a subclass of
the uvm_tlm_if_base class (or a subclass of the uvm_sqr_if_base class in case of
the uvm_seq_item_pull_export). See the class diagram below.
TLM 1 Export ClassesSimilar to a port class, each export class supports a subset of the access
methods. See the corresponding port in the table above to figure out which methods are supported.
Imps
Imps provide the implementation of access methods. To be precise, the imps delegate the
implementation to the component that actually implements the access methods. The type of the
component is passed as a parameter when an imp is instantiated. Similar to the ports and exports,
each imp is a subclass of the uvm_port_base class, which in turn is a subclass
of uvm_tlm_if_base class (or a subclass of the uvm_sqr_if_base class in case of
the uvm_seq_item_pull_imp). See the class diagram below.
TLM 1 Imp ClassesSimilar to a port class, each imp class supports a subset of the access methods.
See the corresponding port in the table above to figure out which methods are supported.

FIFOs
TLM 1 defines two FIFOs; the uvm_tlm_fifo and the uvm_tlm_analysis_fifo. See the
component figure and the class diagram below.

TLM 1 FIFO Components

TLM 1 FIFO Class Diagram

Channels
TLM 1 defines two channels; the uvm_tlm_req_rsp_channel and
the uvm_tlm_transport_channel. See the component figure and the class diagram below.
TLM 1 Channel Components
TLM 1 Channel Class DiagramThe next post will give you an example of how to use the TLM 1
classes.

UVM Tutorial for Candy Lovers – 21. TLM 1


Example
April 6, 2014 Keisuke Shimizu
In the previous post, we looked at an overview of the TLM 1 classes. This post will give you a
sample code using some of the TLM 1 classes.

Components
We created the following components to demonstrate different kinds of TLM 1 interface.
The jelly_bean_sequencer (the leftmost component) creates an object of
the jelly_bean_transaction, called jb_req. The jb_req is transferred all the way to the right
(jelly_bean_transporter) via several TLM 1 interfaces.
The jelly_bean_transporter evaluates the flavor of the jb_req and returns
another jelly_bean_transaction called jb_rsp (jelly-bean response) with the
updated tasteproperty. The jb_rsp is transferred back to the jelly_bean_subscriber at the end.
Though this example is artificial, it will show you a variety of TLM 1 interfaces.

Sample TLM 1 Connections

jelly_bean_sequencer
The jelly_bean_sequencer is a uvm_sequencer specialized with the jelly_bean_transaction.

typedef uvm_sequencer#(jelly_bean_transaction) jelly_bean_sequencer;


jelly_bean_put_driver
The jelly_bean_put_driver is a uvm_driver, which means it has a seq_item_port. The line 4
declares another port (put_port). The run_phase gets a jb_req from the seq_item_port (line 19)
then puts it to the put_port (line 21).
1
2
3 class jelly_bean_put_driver extends uvm_driver#( jelly_bean_transaction );
4 `uvm_component_utils( jelly_bean_put_driver )
5
6 uvm_put_port#( jelly_bean_transaction ) put_port;
7 function new( string name, uvm_component parent );
8 super.new( name, parent );
9 endfunction: new
10
11 function void build_phase( uvm_phase phase );
12 super.build_phase( phase );
13 put_port = new( .name( "put_port" ), .parent( this ) );
14 endfunction: build_phase
15
16 task run_phase( uvm_phase phase );
17 jelly_bean_transaction jb_req;
18
19 forever begin
20 seq_item_port.get_next_item( jb_req ); `uvm_info( get_type_name(), "[se
21 put_port.put( jb_req ); seq_item_port.item_done();
22 end
23 endtask: run_phase
24 endclass: jelly_bean_put_driver
25

jelly_bean_master
The jelly_bean_master has one uvm_get_port (get_port, line 4),
one uvm_master_port (master_port, lines 5 and 6), and one uvm_analysis_port (rsp_ap, line
7). The run_phase gets a jb_req from the get_port (line 25), then puts it to the master_port (line
27). The jelly_bean_master waits for a jelly-bean response (jb_rsp, line 29), then writes it to
the rsp_ap (line 31).

1 class jelly_bean_master extends uvm_component;


2 `uvm_component_utils( jelly_bean_master )
3
4 uvm_get_port#( jelly_bean_transaction ) get_port;
5 uvm_master_port#( .REQ( jelly_bean_transaction ),
6 .RSP( jelly_bean_transaction ) ) master_port;
7 uvm_analysis_port#( jelly_bean_transaction ) rsp_ap;
8 function new( string name, uvm_component parent );
9 super.new( name, parent );
10 endfunction: new
11
12 function void build_phase( uvm_phase phase );
13 super.build_phase( phase );
14 get_port = new( .name( "get_port" ), .parent( this ) );
15 master_port = new( .name( "master_port" ), .parent( this ) );
16 rsp_ap = new( .name( "rsp_ap" ), .parent( this ) );
17 endfunction: build_phase
18
19 task run_phase( uvm_phase phase );
20 jelly_bean_transaction jb_req;
21 jelly_bean_transaction jb_rsp;
22
23 forever begin
24 get_port.get( jb_req );
25 `uvm_info( get_type_name(), "[get_port]-->{jb_req}-->[master_port]", UVM_NONE )
26 master_port.put( jb_req );
27 `uvm_info( get_type_name(), "{jb_rsp}< --[master_port]", UVM_NONE )
28 master_port.get( jb_rsp );
29 `uvm_info( get_type_name(), "{jb_rsp}-->[rsp_ap]", UVM_NONE )
30 rsp_ap.write( jb_rsp ); end
31 endtask: run_phase
32 endclass: jelly_bean_master
33
34

jelly_bean_slave
The jelly_bean_slave has one uvm_master_imp (master_export, lines 4 to 6), and
one uvm_transport_port (trans_port, lines 7 and 8). The jelly_bean_slave also has two
queues; the req_q stores the jb_reqs and the rsp_q stores the jb_rsps.
The jelly_bean_slave implements the access methods of a uvm_master_imp;
namely put, try_put, can_put, get, try_get, can_get, peek, try_peek, and can_peek (lines 34
to 81). The run_phase gets a jb_req from the req_q if available, then calls transport of
the trans_port (line 28).

1 class jelly_bean_slave extends uvm_component;


2 `uvm_component_utils( jelly_bean_slave )
3
4 uvm_master_imp#( .REQ( jelly_bean_transaction ),
5 .RSP( jelly_bean_transaction ),
6 .IMP( jelly_bean_slave ) ) master_export;
7 uvm_transport_port#( .REQ( jelly_bean_transaction ),
8 .RSP( jelly_bean_transaction ) ) trans_port;
9 jelly_bean_transaction req_q[$];
10 jelly_bean_transaction rsp_q[$];
11
12 function new( string name, uvm_component parent );
13 super.new( name, parent );
14 endfunction: new
15
16 function void build_phase( uvm_phase phase );
17 super.build_phase( phase );
18 master_export = new( "master_export", this );
19 trans_port = new( .name( "trans_port" ), .parent( this ) );
20 endfunction: build_phase
21
22 task run_phase( uvm_phase phase );
23 jelly_bean_transaction jb_rsp;
24
25 forever begin
26 wait ( req_q.size() > 0 );
27 `uvm_info( get_type_name(), "(master_export)---{jb_req}-->[trans_port]", UVM_NONE )
28 trans_port.transport( req_q.pop_front(), jb_rsp );
29 `uvm_info( get_type_name(), "{jb_rsp}< --[trans_port]", UVM_NONE )
30 rsp_q.push_back( jb_rsp );
31 end
32 endtask: run_phase
33
34 virtual task put( input jelly_bean_transaction t );
35 req_q.push_back( t );
36 endtask: put
37
38 virtual function bit try_put( input jelly_bean_transaction t );
39 req_q.push_back( t );
40 return 1;
41 endfunction: try_put
42
43 virtual function bit can_put();
44 return 1;
45 endfunction: can_put
46
47 virtual task get( output jelly_bean_transaction t );
48 wait ( rsp_q.size() > 0 );
49 t = rsp_q.pop_front();
50 endtask: get
51
52 virtual function bit try_get( output jelly_bean_transaction t );
53 if ( rsp_q.size() > 0 ) begin
54 t = rsp_q.pop_front();
55 return 1;
56 end else begin
57 return 0;
58 end
59 endfunction: try_get
60
61 virtual function bit can_get();
62 return rsp_q.size() > 0;
63 endfunction: can_get
64
65 virtual task peek( output jelly_bean_transaction t );
66 wait ( rsp_q.size() > 0 );
67 t = rsp_q[0];
68 endtask: peek
69
70 virtual function bit try_peek( output jelly_bean_transaction t );
71 if ( rsp_q.size() > 0 ) begin
72 t = rsp_q[0];
73 return 1;
74 end else begin
75 return 0;
76 end
77 endfunction: try_peek
78
79 virtual function bit can_peek();
80 return rsp_q.size() > 0;
81 endfunction: can_peek
82 endclass: jelly_bean_slave
jelly_bean_transporter
The jelly_bean_transporter has one uvm_transport_imp (trans_export, lines 4 to 6) and
implements its access methods; namely transport and nb_transport (lines 17 to 32). These
methods create a jelly-bean response (jb_rsp, line 24), evaluate the flavor of a jelly-bean request
and update the taste property of the jb_rsp accordingly (lines 26 to 29).

1
2
3 class jelly_bean_transporter extends uvm_component;
4 `uvm_component_utils( jelly_bean_transporter )
5
6 uvm_transport_imp#( .REQ( jelly_bean_transaction ),
7 .RSP( jelly_bean_transaction ),
8 .IMP( jelly_bean_transporter ) ) trans_export;
9 function new( string name, uvm_component parent );
10 super.new( name, parent );
11 endfunction: new
12
13 function void build_phase( uvm_phase phase );
14 super.build_phase( phase );
15 trans_export = new( "trans_export", this );
16 endfunction: build_phase
17
18 virtual task transport( input jelly_bean_transaction jb_req,
19 output jelly_bean_transaction jb_rsp );
20 assert( nb_transport( jb_req, jb_rsp ) );
21 endtask: transport
22
23 virtual function bit nb_transport( input jelly_bean_transaction jb_req,
24 output jelly_bean_transaction jb_rsp );
25 jb_rsp = jelly_bean_transaction::type_id::create( "jb_rsp" );
26 jb_rsp.copy( jb_req );
27 if ( jb_req.flavor == jelly_bean_transaction::CHOCOLATE && jb_req.sour )
28 jb_rsp.taste = jelly_bean_transaction::YUCKY;
29 else jb_rsp.taste = jelly_bean_transaction::YUMMY;
30 `uvm_info( get_type_name(), { "Returning:\n", jb_rsp.sprint() }, UVM_NONE )
31 return 1;
32 endfunction: nb_transport
33 endclass: jelly_bean_transporter

jelly_bean_subscriber
The jelly_bean_subscriber is a uvm_subscriber, which means it has an analysis_export.
The jelly_bean_subscriber implements the write method of the analysis_export (lines 8 to
10).

1 class jelly_bean_subscriber extends uvm_subscriber#( jelly_bean_transaction );


2 `uvm_component_utils( jelly_bean_subscriber )
3
4 function new( string name, uvm_component parent );
5 super.new( name, parent );
6 endfunction: new
7
8 function void write( jelly_bean_transaction t );
9 `uvm_info( get_type_name(), { "Received:\n", t.sprint() }, UVM_NONE )
10 endfunction: writeendclass: jelly_bean_subscriber
11

jelly_bean_agent
The jelly_bean_agent instantiates all the components and connect them together.

1
2
3
4 class jelly_bean_agent extends uvm_agent;
5 `uvm_component_utils( jelly_bean_agent )
6
7 jelly_bean_sequencer jb_seqr;
8 jelly_bean_put_driver jb_put_drvr;
9 uvm_tlm_fifo#( jelly_bean_transaction ) jb_fifo;
jelly_bean_master jb_master;
10
jelly_bean_slave jb_slave;
11 jelly_bean_transporter jb_trans;
12 jelly_bean_subscriber jb_sub;
13
14 function new( string name, uvm_component parent );
15 super.new( name, parent );
16 endfunction: new
17
function void build_phase( uvm_phase phase );
18 super.build_phase( phase );
19 jb_seqr = jelly_bean_sequencer ::type_id::create( .name( "jb_seqr" ), .parent( this ) );
20 jb_put_drvr = jelly_bean_put_driver ::type_id::create( .name( "jb_put_drvr" ), .parent( this ) );
21 jb_fifo = new( .name( "jb_fifo" ), .parent( this ) );
jb_master = jelly_bean_master ::type_id::create( .name( "jb_master" ), .parent( this ) );
22 jb_slave = jelly_bean_slave ::type_id::create( .name( "jb_slave" ), .parent( this ) );
23 jb_trans = jelly_bean_transporter::type_id::create( .name( "jb_trans" ), .parent( this ) );
24 jb_sub = jelly_bean_subscriber ::type_id::create( .name( "jb_sub" ), .parent( this ) );
25 endfunction: build_phase
26
function void connect_phase( uvm_phase phase );
27 super.connect_phase( phase );
28 jb_put_drvr.seq_item_port.connect( jb_seqr.seq_item_export );
29 jb_put_drvr. put_port.connect( jb_fifo.put_export );
30 jb_master. get_port.connect( jb_fifo.get_peek_export );
31 jb_master. master_port.connect( jb_slave.master_export );
32 jb_slave. trans_port.connect( jb_trans.trans_export );
33 jb_master. rsp_ap.connect( jb_sub.analysis_export );
34 endfunction: connect_phase
35 endclass: jelly_bean_agent
36

Simulation
Here is an excerpt of a simulation output.
# UVM_INFO ../src/tutorial_20.sv(152) @ 0: uvm_test_top.jb_env.jb_agent.jb_put_drvr [jelly_bean_put_driver] [seq_item_port]-->{jb_req}-->[put_port]
# UVM_INFO ../src/tutorial_20.sv(188) @ 0: uvm_test_top.jb_env.jb_agent.jb_master [jelly_bean_master] [get_port]-->{jb_req}-->[master_port]
# UVM_INFO ../src/tutorial_20.sv(223) @ 0: uvm_test_top.jb_env.jb_agent.jb_slave [jelly_bean_slave] (master_export)---{jb_req}-->[trans_port]
# UVM_INFO ../src/tutorial_20.sv(282) @ 0: uvm_test_top.jb_env.jb_agent.jb_trans [jelly_bean_transporter] Returning:
# UVM_INFO ../src/tutorial_20.sv(225) @ 0: uvm_test_top.jb_env. # UVM_INFO ../src/tutorial_20.sv(118)
@ 0: uvm_test_top.jb_env.jb_agent.jb_seqr@@jb_seq [one_jelly_bean_sequence] Generated:
jb_agent.jb_slave [jelly_bean_slave] {jb_rsp}< --[trans_port]
# UVM_INFO ../src/tutorial_20.sv(190) @ 0: uvm_test_top.jb_env.jb_agent.jb_master [jelly_bean_master] {jb_rsp}<--[master_port]
# UVM_INFO ../src/tutorial_20.sv(192) @ 0: uvm_test_top.jb_env.jb_agent.jb_master [jelly_bean_master] {jb_rsp}-->[rsp_ap]
# UVM_INFO ../src/tutorial_20.sv(298) @ 0: uvm_test_top.jb_env.jb_agent.jb_sub [jelly_bean_subscriber] Received:

I hope this tutorial helped you to understand the TLM 1 interfaces.

UVM Tutorial for Candy Lovers – 22. Phasing


August 9, 2014 Keisuke Shimizu
When we created the jelly_bean_driver in Agent, we coded the build_phase function and
the run_phase task, but who actually calls them? The answer is uvm_phase class.

UVM Phases
UVM has nine common phase classes (shown in yellow) and twelve run-time phase classes (shown
in pink). These phase classes are derived from the uvm_topdown_phase, uvm_bottomup_phase,
or uvm_task_phase classes, which in turn are derived from the uvm_phase class.
The uvm_phase class has a virtual function called exec_func and a virtual task called exec_task.
The phase classes derived from the uvm_topdown_phase and the uvm_bottomup_phase implement
the exec_func, while the phase classes derived from the uvm_task_phase implement
the exec_task. As shown in the diagram, each phase class calls the corresponding phase function
or task of the uvm_component. For example, the exec_func of the uvm_build_phase class calls
the build_phase function of the uvm_component, and the exec_task of the uvm_run_phase class
calls the run_phase task of the uvm_component, etc.
UVM Phase Classes

Simplified Flow
The way UVM phases are implemented is rather complicated, but here is a simplified flow.
Simplified Flow

1. We call run_test (if you don’t remember, see the line 17 of the top module in Tasting), which in
turn calls the run_test task of the uvm_root class.
2. The uvm_root calls the m_run_phases task of the uvm_phase class.
3. For each phase, the execute_phase task is called.
4. If the phase is a top-down or bottom-up phase, exec_func is called for each component.
5. For example, the exec_func calls the build_phase function of each component.
6. If the phase is a task phase, exec_task is called for each component.
7. For example, the exec_task calls the main_phase task of each component.
8. The uvm_phase checks if any objections are raised by the components. The phase_done is
the uvm_objection object that the uvm_phase keeps track of the number of objections with.
When we called phase.raise_objection() from inside the run_phase of
the jelly_bean_test class (see the line 27 of the jelly_bean_test class
in Tasting), phase_done.raise_objection() is called in the uvm_phase under the hood.
9. If no objection is raised, all the processes started by the exec_task are killed. In other words,
unless an objection is raised, the phase is immediately killed!
10. The steps 3 to 9 repeat until all phases are executed.
I hope this article helped in your understanding of the UVM phasing.

UVM Tutorial for Candy Lovers – 34. Component


Override
June 25, 2016 Keisuke Shimizu
Some people told me that sour-chocolate actually tastes good and there are many recipes for sour
cream chocolate cake. I understand that people have different tastes, so decided to replace our
scoreboard with more liberal one. This article shows how to override a component.

New Scoreboard
This is our new scoreboard that respects any tastes. For those of you who want to see the original
scoreboard, please see this.

1 class jelly_bean_liberal_scoreboard extends jelly_bean_sb_subscriber;


2 `uvm_component_utils( jelly_bean_liberal_scoreboard )
3
4 function new( string name, uvm_component parent );
5 super.new( name, parent );
6 endfunction: new
7
8 virtual function void write( jelly_bean_transaction t );
9 if ( t.taste == YUMMY ) begin
10 `uvm_info( "write", { "I know you like this.", t.convert2string() }, UVM_LOW )
11 end else begin
12 `uvm_info( "write", { "I know you don't like this.", t.convert2string() },
13 UVM_LOW )
14 end
15 endfunction: write
endclass: jelly_bean_liberal_scoreboard
Now, let’s replace our old scoreboard with this.

Type Override
There are several ways to override a component using its type.

uvm_component_registry#(T,Tname)::set_type_override(override
_type)
The first method is using the uvm_component_registry. I think this is the easiest way to override a
component type. The line 8 instructs that the factory to override
the jelly_bean_sb_subscriber with the jelly_bean_liberal_scoreboard.

1 class jelly_bean_test extends uvm_test;


2 `uvm_component_utils( jelly_bean_test )
3
4 jelly_bean_env jb_env;
5
6 function void build_phase( uvm_phase phase );
7 super.build_phase( phase );
8 jelly_bean_sb_subscriber::type_id::set_type_override(
9 jelly_bean_liberal_scoreboard::get_type() ); jb_env =
10 jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
11 endfunction: build_phase
12 // ...
endclass: jelly_bean_test

uvm_component::set_type_override_by_type(original_type,
override_type)
The second method is using the set_type_override_by_type function of the uvm_component.
Although the syntax is different, what it does is basically the same as the first method.

1 class jelly_bean_test extends uvm_test;


2 `uvm_component_utils( jelly_bean_test )
3
4 jelly_bean_env jb_env;
5
6 function void build_phase( uvm_phase phase );
7 super.build_phase( phase );
8 set_type_override_by_type( jelly_bean_sb_subscriber::get_type(),
9 jelly_bean_liberal_scoreboard::get_type() );
10 jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
11 endfunction: build_phase
12 // ...
endclass: jelly_bean_test

uvm_factory.set_type_override_by_type(original_type,
override_type)
The third method is using the set_type_override_by_type function of the uvm_factory. Actually
the above two methods we have seen are merely convenience functions for this third method. The
line 7 gets the current factory, and the line 10 calls the function of the factory.

1
2 class jelly_bean_test extends uvm_test;
3 `uvm_component_utils( jelly_bean_test )
4
5 jelly_bean_env jb_env;
6
7 function void build_phase( uvm_phase phase );
8 uvm_factory factory = uvm_coreservice_t::get().get_factory();
9 super.build_phase( phase );
10 factory.set_type_override_by_type( jelly_bean_sb_subscriber::get_type(),
11 jelly_bean_liberal_scoreboard::get_type() );
12 jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
13 endfunction: build_phase
14 // ...
endclass: jelly_bean_test
uvm_component::set_type_override(original_type_name,
override_type_name)
The fourth method is using the set_type_override function of the uvm_component. Unlike the
above three methods, this function takes two strings.

The first three methods above take uvm_object_wrapper as their argument(s).

1 class jelly_bean_test extends uvm_test;


2 `uvm_component_utils( jelly_bean_test )
3
4 jelly_bean_env jb_env;
5
6 function void build_phase( uvm_phase phase );
7 super.build_phase( phase );
8 set_type_override( jelly_bean_sb_subscriber::type_name,
9 jelly_bean_liberal_scoreboard::type_name );
10 jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
11 endfunction: build_phase
12
// ...
endclass: jelly_bean_test

The type_name represents the type name which was created by


the `uvm_component_utils macro. In our
case, jelly_bean_sb_subscriber::type_name is "jelly_bean_sb_subscriber",
and jelly_bean_liberal_scoreboard::type_name is "jelly_bean_liberal_scoreboard".

The `uvm_component_utils macro creates the type_name,


but `uvm_component_param_utils macro does not. You need to create your
own type_name variable and get_type_name function in the latter case.

uvm_factory.set_type_override_by_name(original_type_name,
override_type_name)
The last method is using the set_type_override_by_name function of the uvm_factory. Actually
the fourth method above is a convenience function of this method.

1
2 class jelly_bean_test extends uvm_test;
3 `uvm_component_utils( jelly_bean_test )
4
5 jelly_bean_env jb_env;
6
7 function void build_phase( uvm_phase phase );
8 uvm_factory factory = uvm_coreservice_t::get().get_factory();
9 super.build_phase( phase );
10 factory.set_type_override_by_name( jelly_bean_sb_subscriber::type_name,
11 jelly_bean_liberal_scoreboard::type_name );
12 jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
13 endfunction: build_phase
14 // ...
endclass: jelly_bean_test
Instance Override
You can override a specific component instance, instead of replacing all the components of the
same type. Similar to the type override, there are five different methods.

uvm_component_registry#(T,Tname)::set_inst_override(override
_type, inst_path, parent)
The first method is using the uvm_component_registry. I think this is the easiest way to override a
component instance. The line 8 instructs that the factory to override
the jelly_bean_sb_subscriber with the jelly_bean_liberal_scoreboard. We specified the
instance path ("jb_env.jb_sb") relative to the jelly_bean_test‘s hierarchical instance path.

1 class jelly_bean_test extends uvm_test;


2 `uvm_component_utils( jelly_bean_test )
3
4 jelly_bean_env jb_env;
5
6 function void build_phase( uvm_phase phase );
7 super.build_phase( phase );
8 jelly_bean_sb_subscriber::type_id::set_inst_override
9 ( jelly_bean_liberal_scoreboard::get_type(), "jb_env.jb_sb", this );
10 jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
11 endfunction: build_phase
12 // ...
endclass: jelly_bean_test

uvm_component::set_inst_override_by_type(relative_inst_path,
original_type, override_type)
The second method is using the set_inst_override_by_type function of the uvm_component.
Although the syntax is different, what it does is basically the same as the first method.

1 class jelly_bean_test extends uvm_test;


2 `uvm_component_utils( jelly_bean_test )
3
4 jelly_bean_env jb_env;
5
6 function void build_phase( uvm_phase phase );
7 super.build_phase( phase );
8 set_inst_override_by_type( "jb_env.jb_sb", jelly_bean_sb_subscriber::get_type(),
9 jelly_bean_liberal_scoreboard::get_type() );
10 jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
11 endfunction: build_phase
12 // ...
endclass: jelly_bean_test
uvm_factory.set_inst_override_by_type(original_type,
override_type, full_inst_path)
The third method is using the set_inst_override_by_type function of the uvm_factory. Actually
the above two methods we have seen are merely convenience functions for this third method. The
line 7 gets the current factory, and the line 10 calls the function of the factory. Note that the function
takes the full hierarchical instance path as the third argument. We created the full path by
concatenating the two paths.

1
2 class jelly_bean_test extends uvm_test;
3 `uvm_component_utils( jelly_bean_test )
4
5 jelly_bean_env jb_env;
6
7 function void build_phase( uvm_phase phase );
8 uvm_factory factory = uvm_coreservice_t::get().get_factory();
9 super.build_phase( phase );
10 factory.set_inst_override_by_type( jelly_bean_sb_subscriber::get_type(),
11 jelly_bean_liberal_scoreboard::get_type(), { get_full_name(), ".jb_env.jb_sb" } );
12 jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
13 endfunction: build_phase
14 // ...
endclass: jelly_bean_test

uvm_component::set_inst_override(relative_inst_path,
original_type_name, override_type_name)
The fourth method is using the set_inst_override function of the uvm_component. Unlike the
above three methods, this function takes three strings.

The first three methods above take uvm_object_wrapper as their type argument(s).

1 class jelly_bean_test extends uvm_test;


2 `uvm_component_utils( jelly_bean_test )
3
4 jelly_bean_env jb_env;
5
6 function void build_phase( uvm_phase phase );
7 super.build_phase( phase );
8 set_inst_override( "jb_env.jb_sb", jelly_bean_sb_subscriber::type_name,
9 jelly_bean_liberal_scoreboard::type_name );
10 jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
11 endfunction: build_phase
12 // ...
endclass: jelly_bean_test
The type_name represents the type name which was created by
the `uvm_component_utils macro. In our
case, jelly_bean_sb_subscriber::type_name is "jelly_bean_sb_subscriber",
and jelly_bean_liberal_scoreboard::type_name is "jelly_bean_liberal_scoreboard".
The `uvm_component_utils macro creates the type_name,
but `uvm_component_param_utils macro does not. You need to create your
own type_name variable and get_type_name function in the latter case.

uvm_factory.set_inst_override_by_name(original_type_name,
override_type_name, full_inst_path)
The last method is using the set_inst_override_by_name function of the uvm_factory. Actually
the fourth method above is a convenience function of this method. Similar to the third method above,
we created the full path by concatenating the two paths.

1
2 class jelly_bean_test extends uvm_test;
3 `uvm_component_utils( jelly_bean_test )
4
5 jelly_bean_env jb_env;
6
7 function void build_phase( uvm_phase phase );
8 uvm_factory factory = uvm_coreservice_t::get().get_factory();
9 super.build_phase( phase );
10 factory.set_inst_override_by_name( jelly_bean_sb_subscriber::type_name,
11 jelly_bean_liberal_scoreboard::type_name, { get_full_name(), ".jb_env.jb_sb" } );
12 jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
13 endfunction: build_phase
14 // ...
endclass: jelly_bean_test

I covered five different methods to override a type, and another five methods to override an instance.
Personally, I use the first methods because they are the simplest. I hope this article clarified the
difference of each method.

Vous aimerez peut-être aussi