0% found this document useful (0 votes)
312 views54 pages

Clue Logic

The document provides an overview of a UVM tutorial that uses a hypothetical jelly bean generator example to demonstrate UVM verification components and methodology. The verification components include a jelly_bean_sequencer that creates jelly bean recipes, a jelly_bean_driver that generates jelly beans from the recipes, a jelly_bean_monitor that captures jelly bean properties, and a jelly_bean_scoreboard that checks the response of the jelly_bean_taster design under test. Transactions and sequences are used to represent the jelly bean recipes and generate different types of jelly beans.

Uploaded by

Nitesh Jain
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
312 views54 pages

Clue Logic

The document provides an overview of a UVM tutorial that uses a hypothetical jelly bean generator example to demonstrate UVM verification components and methodology. The verification components include a jelly_bean_sequencer that creates jelly bean recipes, a jelly_bean_driver that generates jelly beans from the recipes, a jelly_bean_monitor that captures jelly bean properties, and a jelly_bean_scoreboard that checks the response of the jelly_bean_taster design under test. Transactions and sequences are used to represent the jelly bean recipes and generate different types of jelly beans.

Uploaded by

Nitesh Jain
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 54

UVM Tutorial for Candy Lovers – 1.

Overview

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


______________________________________________________________________________
______________________________________________________________________________
use +UVM_TESTNAME=jelly_bean_test option when you run VCS

Can you please elaborate the pros and cons of config_db and resource_db. And, the
recommended one?

A general rule of thumb is that you should use uvm_config_db if a hierarchical context
is important, otherwise uvm_resource_db should be used. For example,
the set function of uvm_config_db takes a uvm_component as the first argument to
facilitate the specification of the hierarchical context.
static function void uvm_config_db#(T)::set( uvm_component
cntxt,
string inst_name,
string field_name,
T value );
whereas the set function of uvm_resource_db doesn’t:
static function void uvm_resource_db#(T)::set( string scope,
// any string
string name,
T val,
uvm_object accessor = null );
Having said that, we used uvm_resource_db in our tutorial. We should have
used uvm_config_db instead.

Would you please tell me is there any reason why we use this piece of code?
`ifndef clk_trans
`define clk_trans
....
`endif
It is called include guard, which avoids compiling the same definition more than once.
______________________________________________________________________________
______________________________________________________________________________

UVM Tutorial for Candy Lovers – 2. Recipe

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.
Class Diagram of Verification Objects

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

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.
1 class jelly_bean_transaction extends uvm_sequence_item;
2 typedef enum bit[2:0] { NO_FLAVOR, APPLE, BLUEBERRY,
3 BUBBLE_GUM, CHOCOLATE } flavor_e;
4 typedef enum bit[1:0] { RED, GREEN, BLUE } color_e;
5 typedef enum bit[1:0] { UNKNOWN, YUMMY, YUCKY } taste_e;
6
7 rand flavor_e flavor;
8 rand color_e color;
9 rand bit sugar_free;
10 rand bit sour;
11 taste_e taste;
12
13 constraint flavor_color_con { flavor != NO_FLAVOR;
14 flavor == APPLE -> color != BLUE; flavor == BLUEBERRY
15 -> color == BLUE; }
16 function new(string name = "");
17 super.new(name);
18 endfunction: new
19
20 `uvm_object_utils_begin(jelly_bean_transaction)
21 `uvm_field_enum(flavor_e, flavor, UVM_ALL_ON)
22 `uvm_field_enum(color_e, color, UVM_ALL_ON)
23 `uvm_field_int(sugar_free, UVM_ALL_ON)
24 `uvm_field_int(sour, UVM_ALL_ON)
25 `uvm_field_enum(taste_e, taste, UVM_ALL_ON)
26 `uvm_object_utils_end
27 endclass: jelly_bean_transaction
28
29

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 class sugar_free_jelly_bean_transaction extends
3 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.
1 class one_jelly_bean_sequence extends
2 uvm_sequence#(jelly_bean_transaction);
3 `uvm_object_utils(one_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 =
12 jelly_bean_transaction::type_id::create(.name("jb_tx"),
13 .contxt(get_full_name())); start_item(jb_tx);
14 assert(jb_tx.randomize()); finish_item(jb_tx);
15 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 class same_flavored_jelly_beans_sequence extends


2 uvm_sequence#(jelly_bean_transaction);
3 rand int unsigned num_jelly_beans; // knob
4
5 constraint num_jelly_beans_con { num_jelly_beans inside {
6 [2:4] }; }
7 function new(string name = "");
8 super.new(name);
9 endfunction: new
10
11 task body();
12 jelly_bean_transaction jb_tx;
13 jelly_bean_transaction::flavor_e jb_flavor;
14
15 jb_tx =
16 jelly_bean_transaction::type_id::create(.name("jb_tx"),
17 .contxt(get_full_name())); assert(jb_tx.randomize());
18 jb_flavor = jb_tx.flavor;
19
20 repeat (num_jelly_beans) begin jb_tx =
21 jelly_bean_transaction::type_id::create(.name("jb_tx"),
22 .contxt(get_full_name())); start_item(jb_tx);
23 assert(jb_tx.randomize() with { jb_tx.flavor == jb_flavor; });
24 finish_item(jb_tx); end endtask: body
25
26 `uvm_object_utils_begin(same_flavored_jelly_beans_sequence)
27 `uvm_field_int(num_jelly_beans, UVM_ALL_ON)
28 `uvm_object_utils_end
29 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.
______________________________________________________________________________
______________________________________________________________________________
what advantage does registering all of your fields for automation have.i.e why not just
use ‘uvm_object_utils(jelly_bean_transaction)?
The `uvm_field_* macros automatically implement methods like copy and compare.
But better approach would be creating your own do_copy and do_compare methods.
Please see “do” Hooks for more detail.
The benefits of using the “do” hooks over the field macro are more flexibility and much
less overhead. I think the same benefits apply to sequences, too.

Can you please explain the usage and importance of p_sequencer and m_sequencer in
UVM??
Each sequence has the m_sequencer property that points to the sequencer on which
the sequence is running. The m_sequencer can be used to access configuration
information and other resources in the component hierarchy from the sequence.
For example, if you start a sequence called foo_sequence on a sequencer called
bar_sequencer, foo_sequence.start( bar_sequencer ), then the start() task
will assign foo_sequence.m_sequencer to be the bar_sequencer. Now
the foo_sequence can use the m_sequencer to access the component hierarchy. For
example, you can get the full hierarchical name of the sequencer by
calling m_sequencer.get_full_name(). Note that the type of
the m_sequencer is uvm_sequencer_base. This means if you have your own
sequencer and you have defined new properties in your sequencer, you cannot access
them through the m_sequencer because it does not know them. Now
the p_sequencer comes to the rescue.
The p_sequencer is instantiated if you use `uvm_declare_p_sequencer macro in your
sequence.
For example, you can use the macro in the foo_sequence like this:

class foo_sequence extends uvm_sequence #( my_transaction );


`uvm_object_utils( foo_sequence )
`uvm_declare_p_sequencer( bar_sequencer_class )
// ...

The macro declares the p_sequencer using the sequencer type you provided (in this
case, bar_sequencer_class). Then the macro defines the function
called m_set_p_sequencer which casts the m_sequencer to the p_sequencer. Now
you can access the properties defined in the bar_sequencer_class through
the p_sequencer like p_sequencer.my_property from inside the body task of
the foo_sequence.
Let me know if you have further questions.

can i have a p_sequencer in single agent,As you have told above that if we have own
sequencer we can use p_sequencer.As my own sequencer will be extending from the
uvm_sequencer class so it can have access to all configuration details .Then why
p_sequencer is required.
Yes, once you have a handle to your own sequencer, you can access your own
properties in it. The p_sequencer is the handle to your own sequencer from your
sequence.For example,
foo_sequence accesses my_property in bar_sequencer using the p_sequencer
handle as shown below.

Where do you put “raise_objection” and “drop_objection” in your code?


The raise_objection and the drop_objection are used in the run_phase function of
the jelly_bean_test class. Please see Tasting for more detail.

I noticed that in the section “Same-Flavored Jelly Beans”, inside of the repeat cycle,
every time a new transaction is created, the methods start_item() and finish_item() are
called in the code (lines 20 and 22) but before that, no start_item() and finish_item()
were called for the single transaction. Why?
The lines 14 to 16 are used only for randomizing the flavor. Then, the line 21 uses this
flavor in the in-line constraint so that all jelly beans have the same flavor. It is fine to
“sandwich” the line 15 with start_item() and finish_item() as you mentioned. That
will create (num_jelly_beans+1) jelly beans, though.

In the Same-Flavored Jelly Beans example, you specified “The line 14 creates a single
jelly bean, and the line 15 randomizes its color and flavor”. But in the transaction item
there are other properties or types like taste, sugar free etc. defined as rand. Won’t they
also get randomized.
Yes, the line 15 randomizes all the properties of jelly_bean_transaction. The line 21
guarantees the same flavor, but other properties such as color are still randomized.

I am trying to build a AHB vip, just to get hold of the basics of UVM. I am finding
difficulty in randomizing the various AHB protocol signals. For e.g, I can randomize
HBURST type , HSIZE type and the starting address. But how do i randomize or create
the HTRANS, HADDR for the same burst type. Based on the burst type, my HADDR
values will change; similarly the HTRANS values. I am presently handling such things in
the driver by generating a loop for the number of beat transfer as dictated by HBURST
type. What will be the best way to handle this?
I think you are doing fine. You don’t need to randomize the HADDR for each beat
because the driver can calculate the next HADDR based on the other information. For
the HTRANS, you can also let the driver insert random BUSY cycles. However, this
approach will not give you the controllability of the delays. I would create an array of
BUSY cycles in the AHB transaction so that I can control the BUSY cycle of each beat.
For example, you could have rand int unsigned busy_cycles[]; in the transaction.
You should also have a constraint for the busy_cycles[] so that you can have no
BUSY-, some BUSY-, or many BUSY-cycle scenarios.

task body();
same_flavored_jelly_beans_sequence jb_seq;
repeat (num_jelly_bean_flavors) begin
jb_seq =
same_flavored_jelly_beans_sequence::type_id::create(.name("jb_se
q"), .contxt(get_full_nam()));
assert(jb_seq.randomize());
jb_seq.start(.sequencer(m_sequencer), .parent_sequence(this));
end
endtask: body
why are you using the start method within the sequence, I think its good to do that in
test. Correct me if I am wrong
You can start another sequence from within a sequence. In this example, jb_seq is
started using the same sequencer (m_sequencer) that
the gift_boxed_jelly_beans_sequence is running on.

I have question that the sequence same_flavored_jelly_beans_sequence is not register


with any of the sequenceas there is no uvm_sequence_utils or uvm_object_utils so
when the sequence start by calling jb_seq.start(.sequencer(m_sequencer),
.parent_sequence(this)); on which sequencer will in be run will it run on the parent
sequencer
If i want to run the sequence in a particular sequencer then I need to register with that
particular sequencer in the sequence right ? please correct me it iam wrong
1. The same_flavored_jelly_beans_sequence uses `uvm_object_utils m
acros. Please see the lines 26 to 28.
2. However, the `uvm_object_utils macro is nothing to do with registering
the sequence to a sequencer. The macro registers
the same_flavored_jelly_beans_sequence type with the UVM factory.
3. The same_flavored_jelly_beans_sequence will be run on the same
sequencer the gift_boxed_jelly_beans_sequence runs on. Please see
the diagram below.
4. You specify which sequencer to use when you call start(). In the example
below, the sequencer jb_env.jb_agent.jb_seqr is used. Please see this
article for how to start the gift_boxed_jelly_beans_sequence.

If we are having our own sequencer where we have defined new properties then can we
use the same m_sequencer – “jb.seq.start (m_sequencer)”?? Or we need to use
p_sequencer ?? If we need to use p_sequencer then can you please tell us how to use
that. Please correct me if I am wrong.
You can use the m_sequencer if you just want to start a sequence. But if you want to
access the new properties, you need to use the p_sequencer.

can u please explain .contxt(get_full_name()) what does this exactly mean ?


The jelly_bean_transaction is a uvm_sequence_item. That means it is
a uvm_object, but not a uvm_component. Therefore it does not have a hierarchical
name. .contxt( get_full_name() ) specifies the context (hierarchical name) of
the jelly_bean_transaction. By specifying the context, we can override a specific
instance of jelly_bean_transaction later. You can use any string as the context, but
in our one_jelly_bean_sequence case, we used the return value of
the get_full_name function. The get_full_name function is defined in
the uvm_sequence_item class (an ancestor class of the one_jelly_bean_sequence),
and it returns a string that concatenates the full name of the sequencer
this one_jelly_bean_sequence was started on and the name of
this one_jelly_bean_sequence instance.

why sequence is required? I mean if the testcase is simple then driver and interface
combination can solve my query…please help like how sequence and driver ia
differentiated.
Traditional driver-BFMs use functions and tasks to drive DUT pins. One of the
disadvantages of the BFMs is that it is not easy to randomize stimulus. UVM uses
sequence objects as the scenario of stimulus. Since they are objects, it is easy to
randomize and we often obtain interesting scenarios we have not thought of. UVM
drivers get the sequences and converts them into pin-level activity.

Can you tell me the difference between uvm_object_utils and uvm_component_utils.


And how to decide which one to use where?
Both macros expand to define the utility functions for factory operation. If you are
interested, please see Inside Candy Factory to look under the hood, although you don’t
have to know the details. If your class extends uvm_component, then
use `uvm_component_utils, otherwise, use `uvm_object_utils. For example, in our
tutorial, we
used `uvm_component_utils for jelly_bean_test, jelly_bean_env, jelly_bean_a
gent, and jelly_bean_driver, which are sub-classes of uvm_component. On the
other hand, we used `uvm_object_utils for jelly_bean_transaction and
sequences, which are uvm_object, but not uvm_component.

Can You please explain in detail the difference b/w `uvm_do macros with clear
examples ?
All `uvm_do* macros call `uvm_do_on_pri_with macro internally.
The `uvm_do_on_pri_with macro takes four arguments
(SEQ_OR_ITEM, SEQR, PRIORITY, and CONSTRAINTS). Each `uvm_do* macro
specifies a subset of the arguments as follows:
SEQ_OR_ITE PRIORIT CONSTRAINT
Macro\Argument SEQR
M Y S

m_sequence
`uvm_do -1 {}
r

m_sequence
`uvm_do_with -1
r
SEQ_OR_ITE PRIORIT CONSTRAINT
Macro\Argument SEQR
M Y S

m_sequence
`uvm_do_pri {}
r

m_sequence
`uvm_do_pri_with
r

`uvm_do_on -1 {}

`uvm_do_on_with -1

`uvm_do_on_pri {}

`uvm_do_on_pri_wit
h
The `uvm_do_on_pri_with creates a SEQ_OR_ITEM, randomize it
with CONSTRAINTS, and start it on the SEQR with specified PRIORITY.

I have a question about the constraints.


say I have the following examples:
=============================
class A_seq extends uvm_sequence;
rand bit [2:0] data_master;

endclass

class B_seq extends umv_sequence


rand bit [2:0] data_master;

constraint c {data_master = 1};

virtual task body()

A_seq a_seq;

begin

uvm_do_with (a_seq, {a_seq.data_master == data_master})

end
endtask
===============================
so when the simulation starts, the data_master in A_seq will be 1. is that correct?
Your `uvm_do_with macro does not constrain the data_master. Here is why.
The `uvm_do_with macro expands to a randomize() function:
a_seq.randomize() with { a_seq.data_master == data_master; };
The names in the in-lined constrait block are searched first in the scope of
the randomize() with object class (A_seq) followed by the scope containing the
method call (B_seq). This means both data_master in the above constraint refer to the
same variable in the A_seq, so you do not constrain them. To solve this issue, you can
use local:: scope resolution:
a_seq.randomize() with { data_master == local::data_master; };
// means a_seq.data_master == this.data_master
This constrains the data_master of a_seq to be equal to the value
of data_master of B_seq.
You can see the entire code here.

I have some doubt in same flavoured jelly beans,


In line 16, if you defined jb_flavor = jb_tx.flavor; then in line 21 why in constraint again
you are defining -> with { jb_tx.flavor == jb_flavor; });
Can you explain about the 16 and 21 line in – > Same-Flavored Jelly Beans (Sequence
of Transactions)
The line 19 creates a new jelly_bean_transaction object. This means that
the jb_tx on line 16 and the jb_tx on line 21 refer different objects even though they
have the same name. The line 21 constrains that the newly created object has the same
flavor as the jb_tx created on line 14.

I see that you have made a separate class for sugar-free jelly bean and in that class you
have a single constraint. However, usually in a complex system, you might have tens or
hundreds of these kind of “more restricted” class. In your example, you can apple-flavor-
only jelly bean, blue-color-only jelly bean and so on… That basically means you’ll have
tens or hundreds files for each type of special jelly bean you make.
To solve this case, I would usually make these variables (i.e. flavor, color, etc..) as
command line variable. For example, +JELLY_BEAN_FLAVOR=APPLE.
Correspondingly, in the sequence class, when you are randomizing the transaction
using “randomize() with {}”, you can put if-statement to see if these command line
arguments are set, and if set, you can constrain the flavor, color, etc… accordingly.
Is this how you would go about solving this too?

I agree using plusargs would be a convenient way to specify constraint knobs. I usually
define the knobs in a transaction class itself so that the user of the class (a sequence,
for example) does not have to use “randomize() with {}“. The reason I don’t use
“randomize with” is that we cannot override the constraints defined by “with” as they
are hard-coded. The following is what I would do for
the jelly_bean_transaction class to make it more controllable.
1 class jelly_bean_transaction extends uvm_sequence_item;
2 typedef enum bit[2:0] { NO_FLAVOR, APPLE, BLUEBERRY,
3 BUBBLE_GUM, CHOCOLATE } flavor_e;
4 typedef enum bit[1:0] { RED, GREEN, BLUE } color_e;
5 typedef enum bit[1:0] { UNKNOWN, YUMMY, YUCKY } taste_e;
6
7 rand flavor_e flavor;
8 rand color_e color;
9 rand bit sugar_free;
10 rand bit sour;
11 taste_e taste;
12
13 // knobs
14
15 byte unsigned apple_wt = 10;
16 byte unsigned blueberry_wt = 10;
17 byte unsigned bubble_gum_wt = 10;
18 byte unsigned chocolate_wt = 10;
19
20 byte unsigned red_wt = 10;
21 byte unsigned green_wt = 10;
22 byte unsigned blue_wt = 10;
23
24 byte unsigned sugar_free_pct = 50; // between 0 and
25 100
26 byte unsigned sour_pct = 50;
27
28 constraint flavor_con {
29 flavor dist { APPLE := apple_wt,
30 BLUEBERRY := blueberry_wt,
31 BUBBLE_GUM := bubble_gum_wt,
32 CHOCOLATE := chocolate_wt };
33 }
34
35 constraint color_con {
36 color dist { RED := red_wt,
37 GREEN := green_wt,
38 BLUE := blue_wt };
39 }
40
41 constraint sugar_free_con {
42 sugar_free dist { 0 := ( 100 - sugar_free_pct ),
43 1 := sugar_free_pct };
44 }
45
46 constraint sour_con {
47 sour dist { 0 := ( 100 - sour_pct ),
48 1 := sour_pct };
49 }
50
51 constraint flavor_color_con {
52 flavor != NO_FLAVOR;
53 flavor == APPLE -> color != BLUE;
54 flavor == BLUEBERRY -> color == BLUE;
55 }
56
57 function new(string name = "");
58 super.new(name);
59 void'( $value$plusargs( "APPLE_WT=%d", apple_wt
60 ) );
61 void'( $value$plusargs( "BLUEBERRY_WT=%d", blueberry_wt
62 ) );
63 void'( $value$plusargs( "BUBBLE_GUM_WT=%d",
64 bubble_gum_wt ) );
65 void'( $value$plusargs( "CHOCOLATE_WT=%d", chocolate_wt
66 ) );
67
68 void'( $value$plusargs( "RED_WT=%d", red_wt ) );
69 void'( $value$plusargs( "GREEN_WT=%d", green_wt ) );
70 void'( $value$plusargs( "BLUE_WT=%d", blue_wt ) );
71
72 void'( $value$plusargs( "SUGAR_FREE_PCT=%d",
sugar_free_pct ) );
void'( $value$plusargs( "SOUR_PCT=%d", sour_pct
) );
endfunction: new

// ...

endclass: jelly_bean_transaction
______________________________________________________________________
______________________________________________________________________

UVM Tutorial for Candy Lovers – 4. Agent


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.
1
2
3
4 interface jelly_bean_if(input bit clk);
5 logic [2:0] flavor;
6 logic [1:0] color;
7 logic sugar_free;
8 logic sour;
9 logic [1:0] taste;
10
11 clocking master_cb @ (posedge clk); default input
12 #1step output #1ns; output flavor, color, sugar_free,
13 sour; input taste; endclocking: master_cb
14 clocking slave_cb @ (posedge clk); default input
15 #1step output #1ns; input flavor, color, sugar_free,
16 sour; output taste; endclocking: slave_cb
17 modport master_mp(input clk, taste, output flavor, color,
18 sugar_free, sour); modport slave_mp(input clk, flavor,
19 color, sugar_free, sour, output taste); modport
20 master_sync_mp(clocking master_cb); modport
21 slave_sync_mp(clocking slave_cb);endinterface: jelly_bean_if
22
23
24
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:
Name Directions Seen from Timing Used by

master_mp Master Asynchronous DUT


Name Directions Seen from Timing Used by

slave_mp Slave Asynchronous DUT

master_sync_mp Master Synchronous with master_cb Test-bench

slave_sync_mp Slave Synchronous with slave_cb Test-bench

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!

module jelly_bean_taster( jelly_bean_if.slave_mp jb_slave_if


1
); import jelly_bean_pkg::*;
2
always @ ( posedge jb_slave_if.clk ) begin
3
if ( jb_slave_if.flavor ==
4
jelly_bean_transaction::CHOCOLATE &&
5
jb_slave_if.sour ) begin
6
jb_slave_if.taste <= jelly_bean_transaction::YUCKY;
7
end else begin
8
jb_slave_if.taste <= jelly_bean_transaction::YUMMY;
9
end
10
end
11
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
2 uvm_driver#(jelly_bean_transaction);
3 `uvm_component_utils(jelly_bean_driver)
4
5 virtual jelly_bean_if jb_vi;
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 void'(uvm_resource_db#(virtual
14 jelly_bean_if)::read_by_name (.scope("ifs"),
15 .name("jelly_bean_if"), .val(jb_vi))); endfunction:
16 build_phase
17
18 task run_phase(uvm_phase phase);
19 jelly_bean_transaction jb_tx;
20
21 forever begin
22 @jb_vi.master_cb;
23 jb_vi.master_cb.flavor <=
24 jelly_bean_transaction::NO_FLAVOR;
25 seq_item_port.get_next_item(jb_tx);
26 @jb_vi.master_cb; jb_vi.master_cb.flavor <=
27 jb_tx.flavor; jb_vi.master_cb.color <=
28 jb_tx.color; jb_vi.master_cb.sugar_free <=
29 jb_tx.sugar_free; jb_vi.master_cb.sour <=
30 jb_tx.sour; seq_item_port.item_done(); end
31 endtask: run_phase
endclass: jelly_bean_driver

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 class jelly_bean_monitor extends uvm_monitor;
2 `uvm_component_utils(jelly_bean_monitor)
3
4 uvm_analysis_port#(jelly_bean_transaction) jb_ap;
5
6 virtual jelly_bean_if jb_vi;
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 void'(uvm_resource_db#(virtual
15 jelly_bean_if)::read_by_name (.scope("ifs"),
16 .name("jelly_bean_if"), .val(jb_vi))); jb_ap =
17 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 !=
25 jelly_bean_transaction::NO_FLAVOR) begin jb_tx =
26 jelly_bean_transaction::type_id::create(.name("jb_tx"),
27 .contxt(get_full_name())); jb_tx.flavor =
28 jelly_bean_transaction::flavor_e'(jb_vi.slave_cb.flavor);
29 jb_tx.color =
30 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; jb_tx.taste =
34 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.

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_sequencer jb_seqr;
7 jelly_bean_driver jb_drvr;
8 jelly_bean_monitor jb_mon;
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_ap = new(.name("jb_ap"), .parent(this));
18 jb_seqr =
19 jelly_bean_sequencer::type_id::create(.name("jb_seqr"),
20 .parent(this));
21 jb_drvr =
22 jelly_bean_driver::type_id::create(.name("jb_drvr"),
23 .parent(this));
24 jb_mon =
25 jelly_bean_monitor::type_id::create(.name("jb_mon"),
26 .parent(this));
27 endfunction: build_phase
28
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
jb_drvr.seq_item_port.connect(jb_seqr.seq_item_export);
jb_mon.jb_ap.connect(jb_ap); endfunction:
connect_phaseendclass: jelly_bean_agent

Though this post ends here, the next will continue to provide an explanation for other
verification components.

______________________________________________________________________
______________________________________________________________________

Can you elaborate in detail why a sequencer is needed, Also what the above one line
code of the sequencer do ??
Typically a sequence generates requests and a sequencer transfers the requests to a
driver.
When we start a sequence, its start() task calls the body() task of itself.
Our one_jelly_bean_sequence generates a jelly_bean_transaction in
the body() task. The body() task calls start_item() followed by finish_item().
The start_item() asks a grant for the sequencer. The finish_item() sends the
request to the sequencer. The sequencer pushes the request to its request FIFO. The
driver calls get_next_item() to the sequencer to fetch the request. When the driver
finishes the request, it calls item_done() and the sequencer pops the request out of
the request FIFO. The illustration below might be helpful to understand the flow.

The uvm_sequencer is a parameterized class. Our sequencer transfers the requests of


the jelly_bean_transaction type to the driver, so we specialized
the uvm_sequencer with the jelly_bean_transaction type. You can
use uvm_sequencer#(jelly_bean_transaction) when you instantiate the
sequencer, but it is verbose, so we used typedef to give it a shorter name
(jelly_bean_sequencer). This is what the one-line typedefdoes.

Can you elaborate more on wait for grant() and wait for item_done() ? say.. which
statement in sequence body will be blocked and when will be that blocked statement will
be proceeded further .. ?
Please see the diagram below. The red bars show blocking tasks.

I have small doubter regardiing sequencer driver interaction.


why fifo is required in the sequencer any only one item is processed in the driver. In the
above diagram wait for grant is shown from sequencer . when wait for grant is given by
sequencer before or after execution get_next_item in driver.
The request FIFO is used to store and arbitrate multiple sequences in the sequencer.
The wait_for_grant and get_next_item are independent. Either one can come first.

Can you please give me one scenario where wait for grant comes first with out
get_next_item in driver is called
Let’s say the driver is in low-power mode or in hibernate, and it takes some time to
wake it up. When you send a sequence to the sequencer, it calls wait_for_grant, but
since the driver is not ready, it does not call get_next_item.

If I execute 3 sequences parallely then wait for grant will come for 3 sequences based
on priority it will store in fifo. Am I right?
That is correct. Sequence Arbitration explains the sequence selection in more detail.

In your monitor code, line 29 and 30, why do you use master_cb rather than slave_cb?
To capture the value of a signal, the signal has to be an input. The master_cb declares
the taste as an input, so I used the master_cb instead of the slave_cb. You could
also define monitor_cb as follows and use it throughout the monitor instead of mixing
the master_cb and slave_cb.
clocking monitor_cb @ (posedge clk);
default input #1step output #1ns;
input flavor, color, sugar_free, sour, taste;
endclocking: monitor_cb

In Monitor Code ,line no 24 to 28 you are capturing the signal value from
interface(jb_vi.slave_cb) but ,not understanding why you are creating & using the
jelly_bean_transaction for flavour & colour, but not for sugar_free, sour ? can you
explain this breif
The type of jb_vi.slave_cb.flavor is logic[2:0] and the type
of jb_vi.slave_sb.color is logic[1:0], whereas the type
of jb_tx.flavor is jelly_bean_transaction::flavor_e and the type
of jb_tx.color is jelly_bean_transaction::color_e. To assign a logic value to
an enum value, a type cast (parentheses that are prefixed with the casting type and an
apostrophe) is required (lines 25 and 26). On the other hand, the type
of jb_tx.sugar_free and jb_tx.sour is bit, so you don’t need a type cast on lines
27 and 28.

I was wondering what would happen if you use slave_sync_mp interface modport to
connect to the dut instead of slave_mp. I wanted to know why is it necessary here to
define a asynchronous modport and sync modport. Why sync modport alone is not
sufficient.
If you use the slave_sync_mp for the DUT, it won’t compile (you can try it). While the
synchronous modport is used by the test-bench to have a higher level of timing
abstraction, the DUT uses the asynchronous modport because the timing is design
dependent.

In the agent class there is an analysis port declared


“uvm_analysis_port#(jelly_bean_transaction) jb_ap”. So this becomes analysis port in
the agent to analysis port in monitor connection. Can I instead use uvm_analysis_export
object since i have to export the transaction to subscriber?
No. The uvm_analysis_export is to export a lower-level uvm_analysis_imp to its
parent, not to broadcast a transaction to subscribers. The latter is done by
the uvm_analysis_port.

could u give some examples about uvm_analysis_export/ uvm_analysis_port /


uvm_analysis_imp, most of time when I use these ports to do connection, l have some
confusion about that which port could be connected , and could u give some introduce ?
thank u so much
These articles might help:
 Analysis Port
 Analysis FIFO
 TLM1
 TLM1 Example
Could we use model to control inside driver ?
I mean we instance a model inside a drive to control dut.
Yes, you can.

I want to access signals of one agent from another agent…means how to modify signals
of another agent?
I don’t know if it is a good idea, but if you pass a virtual interface to the agent, it can
modify the signals in the interface.

I have a question related to clocking blocks. In the code for the driver, after the “forever”
statement, I see the clock used like this “@jb_vi.master_cb”. I wanted to ask what would
be different if i write “@jb_vi.clk”?
@jb_vi.master_cb is equivalent to @( posedge jb_vi.clk ) in our case (see the
clocking declaration of master_cb; line 8 of jelly_bean_if)
whereas @jb_vi.clk waits for any value change of the jb_vi.clk (not just posedge).

Why synchronous modport is used for testbench while asynchronous modport for DUT?
Please see this discussion. Let me know if you need further clarification.

could you please tell me the reason behind creating NO_FLAVOR and why
uvm_monitor should poll for NO_FLAVOR in the jelly_bean_if interface.

I used NO_FLAVOR for an idle or no-operation cycle. The monitor creates


a jelly_bean_transaction only if there is some flavor.

understand the relation between sequence-sequencer-driver..


If i see it correctly the sequencer is in charge of the randomization of the sequence
(while sequence may contain constraint),
and to pass is through the fifo to the driver.
1. Is this correct?
2. So why actually do we need the driver? the driver could do the generation part and
thats it?
So what am i missing here?
The sequencer does not randomize sequences. It controls the flow of sequences. The
driver pulls sequence items from the sequencer and convert them into pin wiggles.

______________________________________________________________________
______________________________________________________________________

UVM Tutorial for Candy Lovers – 5. Environment


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.

1
2
3 class jelly_bean_fc_subscriber extends
4 uvm_subscriber#(jelly_bean_transaction);
5 `uvm_component_utils(jelly_bean_fc_subscriber)
6
7 jelly_bean_transaction jb_tx;
8
9 covergroup jelly_bean_cg; flavor_cp: coverpoint
10 jb_tx.flavor; color_cp: coverpoint jb_tx.color;
11 sugar_free_cp: coverpoint jb_tx.sugar_free; sour_cp:
12 coverpoint jb_tx.sour; cross flavor_cp, color_cp,
13 sugar_free_cp, sour_cp; endgroup: jelly_bean_cg
14 function new(string name, uvm_component parent);
15 super.new(name, parent);
16 jelly_bean_cg = new;
17 endfunction: new
18
19 function void write(jelly_bean_transaction t); jb_tx =
20 t; jelly_bean_cg.sample(); endfunction: writeendclass:
21 jelly_bean_fc_subscriber
22
23

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 typedef class jelly_bean_scoreboard;


2
3 class jelly_bean_sb_subscriber extends
4 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; $cast( jb_sb, m_parent );
13 jb_sb.check_jelly_bean_taste(t); endfunction: writeendclass:
14 jelly_bean_sb_subscriber
15
16

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)
5 jb_analysis_export;
6 local jelly_bean_sb_subscriber jb_sb_sub;
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_analysis_export = new( .name("jb_analysis_export"),
15 .parent(this));
16 jb_sb_sub =
17 jelly_bean_sb_subscriber::type_id::create(.name("jb_sb_sub"),
18 .parent(this));
19 endfunction: build_phase
20
21 function void connect_phase(uvm_phase phase);
22 super.connect_phase(phase);
23 jb_analysis_export.connect(jb_sb_sub.analysis_export);
24 endfunction: connect_phase
25
26 virtual function void
27 check_jelly_bean_taste(jelly_bean_transaction jb_tx);
28 uvm_table_printer p = new; if (jb_tx.flavor ==
29 jelly_bean_transaction::CHOCOLATE && jb_tx.sour)
30 begin
31 if (jb_tx.taste == jelly_bean_transaction::YUCKY) begin
32 `uvm_info("jelly_bean_scoreboard", {
33 "You have a good sense of taste.\n", jb_tx.sprint(p) },
34 UVM_LOW); end else begin
35 `uvm_error("jelly_bean_scoreboard", {
36 "You lost sense of taste!\n", jb_tx.sprint(p) }); end
37 end else begin if (jb_tx.taste ==
38 jelly_bean_transaction::YUMMY) begin
39 `uvm_info("jelly_bean_scoreboard", {
40 "You have a good sense of taste.\n", jb_tx.sprint(p) },
41 UVM_LOW); end else begin
42 `uvm_error("jelly_bean_scoreboard", {
"You lost sense of taste!\n", jb_tx.sprint(p) }); end
end endfunction: check_jelly_bean_tast
eendclass: jelly_bean_scoreboard

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 =
15 jelly_bean_agent::type_id::create(.name("jb_agent"),
16 .parent(this));
17 jb_fc_sub =
18 jelly_bean_fc_subscriber::type_id::create(.name("jb_fc_sub"),
19 .parent(this));
20 jb_sb =
21 jelly_bean_scoreboard::type_id::create(.name("jb_sb"),
22 .parent(this));
23 endfunction: build_phase
24
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
jb_agent.jb_ap.connect(jb_fc_sub.analysis_export);
jb_agent.jb_ap.connect(jb_sb.jb_analysis_export);
endfunction: connect_phaseendclass: jelly_bean_env
The next post will show the anticipated test using these components.

______________________________________________________________________
________________________________________________________________
What is the need for the scoreboard subscriber? You don’t create a handle to it in the
env, the scoreboard already has an analysis export…..
Is it because a scoreboard class doesn’t have a built in write() method?
Also in the fc subscriber you don’t create a handle to an analysis export, nor do you
reference it in the connect and build phases (the same goes for the scoreboard
subscriber) is that because these are extended uvm_subscribers and have this analysis
export by default ( called analysis_export)? I.e from the uvm base class
The jelly_bean_scoreboard instantiates the local jelly_bean_sb_subscriber. For
this tutorial, the jelly_bean_scoreboard class is not indispensable. I could have used
the jelly_bean_sb_subscriber only and moved
the check_jelly_bean_taste function from the jelly_bean_scoreboard to
the jelly_bean_sb_subscriber. As you mentioned, the reason I had a subscriber was
I wanted the write method in the scoreboard. I also wanted expandability of the
scoreboard so that I could add another subscriber in case I needed the second write.
Another approach to have the second write would be
using `uvm_analysis_imp_decl macro.
Regarding the analysis_export, the uvm_subscriber class (the base class of the fc
and sb subscribers) has the analysis export called analysis_export.

Also, can you give me a brief idea why we reuiqre a subscriber ? or waht is its
significance ?
Is it a gateway between the components in agent and outside ??
A subscriber is a component typically used with a monitor. It “subscribes” the
transactions the monitor sends via an analysis_port. Every time a transaction is sent
by the monitor, the write function of the subscriber is executed. You can use
the write function to collect functional coverage or check the validity of the transaction,
for example.

Can you please explain, why are you casting with m_parent at line no 13 in
jelly_bean_sb_subscriber and also could you please explain the significance of
m_parent there.
Each uvm_component has a property called m_parent, which points to the parent
component that created the component. In this example,
the m_parent of jelly_bean_sb_subscriber points to the jelly_bean_scoreboard.
Please see the line 14 of jelly_bean_scoreboard.
The $cast is required because the type of m_parent is uvm_component, which does
not know anything about the check_jelly_bean_taste function.

In the scoreboard class uvm analysis export object is declared


“uvm_analysis_export#(jelly_bean_transaction) jb_analysis_export” . Why cannot it be a
uvm_analysis_imp type object since “imp” class serves as termination for the analysis
port.
If you look at the Verification Components figure in Overview, you will find
the jelly_bean_sb_subscriber serves as the termination point, not
the jelly_bean_scoreboard. That’s why the scoreboard uses
a uvm_analysis_export instead of a uvm_analysis_imp.

my question is regarding the $cast.


in the write function of jelly_bean_sb_subscriber, the jelly_bean_scoreboard is already
defined.
hence the jb_sb would be aware of check_jelly_bean_taste task. then why is the cast
needed.
plus when a castis done as $cast(jb_sb,m_parent). the m_parent which point to
component class,will start pointing to a extended class which would be a error ,right ?
The jb_sb is declared as the jelly_bean_scoreboard type on the line 11, but the
value of jb_sb is null because we did not assign any value yet. On the line 13, we
attempted to assign the value of jb_sb to be m_parent so that jb_sb (object handle)
points to the m_parent object. Since the type of m_parent is uvm_component,
not jelly_bean_scorebord, we needed to use the $cast. This casting won’t cause
any errors as long as the m_parent is a handle to a jelly_bean_scoreboard object.

I have one small doubt, In jelly_bean_fc_subscriber we are having handle jb_tx of type
jelly_bean_transaction, So why we are not using create/new method to that handle.
The jb_tx is an object handle that points to the same object passed by
the write function (see the line 20 of the jelly_bean_fc_subscriber). Since we do
not create a new object, we call neither new nor create.

I somehow remembered only the uvm_analysis_imp can provide the write method, but
looks here the subscriber’s analysis_export is hooked up to the scoreboard. Could you
please explain why the write method is used with analysis_export?
Another thing I’m not clear is that in subscriber, the scoreboard is declared in “write”
function, in scoreboard, the subscriber is declared as local and further created in the
build_phase. Is there any problem with the double referencing to each other?
 The type of the analysis_export of the uvm_subscriber is
actually uvm_analysis_imp.
 As you mentioned, the jelly_bean_sb_subscriber and
the jelly_bean_scoreboard each need a handle to the other.
The typedef (the first line) of the jelly_bean_sb_subscriber provides a
forward declaration for the jelly_bean_scoreboard. The typedef lets the
compiler know the actual class definition of the jelly_bean_scoreboard will
follow.
______________________________________________________________________
_________________________________________________________________
UVM Tutorial for Candy Lovers – 6. Tasting

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.

class jelly_bean_test extends uvm_test;


`uvm_component_utils(jelly_bean_test)

jelly_bean_env jb_env;

function new(string name, uvm_component parent);


super.new(name, parent);
endfunction: new

function void build_phase(uvm_phase phase);


super.build_phase(phase);
begin
jelly_bean_configuration jb_cfg;

jb_cfg = new;
assert(jb_cfg.randomize());
uvm_config_db#(jelly_bean_configuration)::set
(.cntxt(this), .inst_name("*"), .field_name("config"),
.value(jb_cfg));
jelly_bean_transaction::type_id::set_type_override(sugar_free_je
lly_bean_transaction::get_type()); jb_env =
jelly_bean_env::type_id::create(.name("jb_env"), .parent(this));
end
endfunction: build_phase

task run_phase(uvm_phase phase);


gift_boxed_jelly_beans_sequence jb_seq;

phase.raise_objection(.obj(this));
jb_seq =
gift_boxed_jelly_beans_sequence::type_id::create(.name("jb_seq")
, .contxt(get_full_name())); assert(jb_seq.randomize());
`uvm_info("jelly_bean_test", { "\n", jb_seq.sprint() }, UVM_LOW)
jb_seq.start(jb_env.jb_agent.jb_seqr); #10ns ;
phase.drop_objection(.obj(this));
endtask: run_phase
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 class jelly_bean_configuration extends uvm_object;
2 `uvm_object_utils(jelly_bean_configuration)
3
4 function new(string name = "");
5 super.new(name);
6 endfunction: new
7 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.

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

______________________________________________________________________
______________________________________________________________________
Can you tell me the need to randomize the sequence in the test?
The jb_seq is an object of gift_boxed_jelly_beans_sequence class.
The gift_boxed_jelly_beans_sequence class has one rand property,
called num_jelly_bean_flavors. The jb_seq.randomize() randomizes
this num_jelly_bean_flavors.

But have doubt like why have u randomised gift_box_jelly_bean sequence because it is
getting randmised in the same sequence class.
And one more thing.can u please explain the concept of config db and resource db and
the syntax direction to use it
The gift_boxed_jelly_beans_sequence is randomized in order to randomize its knob
(num_jelly_bean_flavors). Regarding the configuration database, I have written a
new article. Let me know if you have further questions. The uvm_config_db provides a
convenience interface to the uvm_resource_db. You don’t need to use
the uvm_resource_db.

Do you have any good idea for a chip level pin automation configuration in UVM
verification environment? For example, we always have some pin config to put the chip
into some specific working mode, such as Normal Function mode, Function Test mode,
Scan mode, etc. we could use pullup/pulldown as well as register configuration setting
to implement this function in module based testbench. But how do I do it in class based
testbench UVM?
If you need to drive these primary pins from a class-based object, I would probably
create an interface that includes these pins and pass it to the testbench using
a uvm_config_db.
// your interface
interface pin_interface;
logic test_mode;
logic scan_mode;
...

// your top-level module


module top;
pin_interface pin_if();

// your DUT
DUT dut( .test_mode( pin_if.test_mode ),
.scan_mode( pin_if.scan_mode ),
...
initial begin
uvm_config_db#( virtual pin_interface )::set( .cntxt( null
),
.inst_name( "uvm_test_top*" ), .field_name( "pin_if" ),
.value( pin_if ) );
end
...
The UVM-side gets the interface via the uvm_config_db and stores it as a virtual
interface and drives it as needed.
// your UVM driver
class your_driver extends uvm_driver#(your_transaction);
virtual pin_interface pin_if;

virtual function void build_phase( uvm_phase phase );


super.build_phase( phase );
assert( uvm_config_db#( virtual pin_interface )::get(
.cntxt( this ), .inst_name( "" ),
.field_name( "pin_if" ), .value( pin_if ) ) );
endfunction

virtual task main_phase( uvm_phase phase );


pin_if.test_mode <= 1;
pin_if.scan_mode <= 0;
...
Having said that, I guess the test mode and the scan mode might not use the normal
tests that the UVM testbench provides. If that is the case, you can assign the test mode
and the scan mode using the parameters of your testbench, instead of driving them
from the UVM testbench.
// your top-level module
module top;
parameter TEST_MODE = 0;
parameter SCAN_MODE = 0;
// your DUT
DUT dut( .test_mode( TEST_MODE ),
.scan_mode( SCAN_MODE ),
...
When you run a simulation, you can set the TEST_MODE and SCAN_MODE
by defparam top.TEST_MODE=1, etc.
In the above jelly_bean_test, if I am calling a C function in the task and assign the return
value to some local variable or jelly_bean_test property, then how do I print that value ?
EX:
class jelly_bean_test extends uvm_test:

logic [10:0] p;
real q;
int r;

function new ();


q = 1.0;
r = 10;
endfunction

task run_phase(uvm_phase phase);


gift_boxed_jelly_beans_sequence jb_seq;

p = c_function(q,r);

*****How do I print this P value here to verify the


returned value*****

endtask: run_phase
You can use $display("%h",p) or the `uvm_info macro as usual. Calling a C-
function is transparent to the caller. You can use a C-function as if it is a SystemVerilog
function.

You can use $display("%h",p) or the `uvm_info macro as usual. Calling a C-


function is transparent to the caller. You can use a C-function as if it is a SystemVerilog
function.
The jelly_bean_configuration does nothing here. I added this class as a
placeholder. The configuration object is used to configure the test bench. Please
see Configurations for more information.

How to decide what string to use for “.inst_name” argument in the uvm_config_db’s set
function? Can we use any arbitrary string for this argument?
You can use an arbitrary string for the inst_name as long as the object who gets the
field (getter) also knows the inst_name. But typically we use the instance name of the
getter (often with a wildcard, *, if you want multiple objects to get the field) to avoid
unnecessary dependency.

Thanks for all your answers. I have an issue while i try to run the jelly bean example in
this tutorial. The exact error is
UVM_ERROR @ 50: run [TEST_DONE_NOHIER] A non-hierarchical object,
‘common.run’ () was used in a call to uvm_test_done.drop_objection(). For this
objection, a sequence or component is required.
UVM_FATAL @ 50: run [OBJTN_ZERO] Object “common.run” attempted to drop
objection ‘run’ count below zero
I know that without looking at the code it will be difficult to say what is going wrong but
any idea on what could be going wrong? I don’t know where to start looking for to fix
this. Also how do we debug a uvm test bench, specifically is there a switch which will
enable printing of the sequence of task call like a call stack in other languages?
It seems that the first argument of the drop_objection call is incorrect. You can
specify +UVM_OBJECTION_TRACE to trace objection activities when you run a simulation.
Thanks Keisuke. I had make a mistake in the drop_objection argument. I had given
“phase” as the argument instead of the the test object

 Fix the typo +UVM_TEST_NAME to +UVM_TESTNAME in the Compile & Run


Options field on the left.

1. In module top , Is test instantiation not required ?? , if so why ??


2. What does run_test() do ?
The run_test() task calls the run_test() task of the uvm_root class. It executes all
the phases including the build_phase() that dynamically creates a test-bench. So, you
don’t need to instantiate the test-bench in the top-module.

Can you Please clarify about the difference when we call seq in run_phase (seq.start())
and specifying the default sequence in build phase. Can you Please explain.
If you have a default sequence, it is executed automatically at the beginning of the
specified phase (such as the main_phase). If you use start(), you can fully control its
execution (timing, conditional execution, and the number of executions, etc.).

I have a test say “class test_x extends base_test”


And i have few random variables declared inside test_x.
Now if I write few constraints as part test_x, will those be randomized by default?
Because we are not explicitly calling test_x.randomize() anywhere.
Unless you call the randomize() method, variables in an object are not randomized.
If test_x is the top-level component, you could call this.randomize() from
its build_phase for example.
______________________________________________________________________
______________________________________________________________________
UVM Tutorial for Candy Lovers – 7. Virtual
Sequence

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

1 class jelly_bean_recipe_virtual_sequence extends


2 uvm_sequence#( uvm_sequence_item );
3 typedef enum bit[1:0] { LEMON_MERINGUE_PIE, // 2 LEMON
4 + 2 COCONUT STRAWBERRY_SHORTCAKE, //
5 2 STRAWBERRY + 2 VANILLA CANDY_APPLE
6 // 2 APPLE + 1 CINNAMON }
7 recipe_e; rand recipe_e recipe;
8
9 jelly_bean_sequencer jb_seqr1;
10 jelly_bean_sequencer jb_seqr2;
11
12 same_flavored_jelly_beans_sequence jb_seq1;
13 same_flavored_jelly_beans_sequence jb_seq2;
14
15 function new( string name = "" );
16 super.new( name );
17 endfunction: new
18
19 task body();
20 jb_seq1 =
21 same_flavored_jelly_beans_sequence::type_id::create( .name(
22 "jb_seq1" ), .contxt( get_full_name() ) ); jb_seq2 =
23 same_flavored_jelly_beans_sequence::type_id::create( .name(
24 "jb_seq2" ), .contxt( get_full_name() ) ); case ( recipe
25 ) LEMON_MERINGUE_PIE: begin jb_seq1.flavor
26 = jelly_bean_transaction::LEMON; jb_seq2.flavor
27 = jelly_bean_transaction::COCONUT;
28 jb_seq1.num_jelly_beans = 2; jb_seq2.num_jelly_beans
29 = 2; end STRAWBERRY_SHORTCAKE: begin
30 jb_seq1.flavor = jelly_bean_transaction::STRAWBERRY;
31 jb_seq2.flavor = jelly_bean_transaction::VANILLA;
32 jb_seq1.num_jelly_beans = 2; jb_seq2.num_jelly_beans
33 = 2; end CANDY_APPLE: begin
34 jb_seq1.flavor = jelly_bean_transaction::APPLE;
35 jb_seq2.flavor = jelly_bean_transaction::CINNAMON;
36 jb_seq1.num_jelly_beans = 2; jb_seq2.num_jelly_beans
37 = 1; end endcase // case ( recipe )
38 `uvm_info( get_name(), { "\n", this.sprint() }, UVM_LOW )
39 fork jb_seq1.start( .sequencer( jb_seqr1 ),
40 .parent_sequence( this ) ); jb_seq2.start( .sequencer(
41 jb_seqr2 ), .parent_sequence( this ) ); join endtask:
42 body
43
44 `uvm_object_utils_begin( jelly_bean_recipe_virtual_sequence
45 )
46 `uvm_field_enum ( recipe_e, recipe, UVM_ALL_ON )
47 `uvm_field_object( jb_seq1, UVM_ALL_ON )
48 `uvm_field_object( jb_seq2, UVM_ALL_ON )
49 `uvm_object_utils_end
50 endclass: jelly_bean_recipe_virtual_sequence
51
52
53

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 =
13 jelly_bean_recipe_virtual_sequence::type_id::create( .name(
14 "jb_vseq" ), .contxt( get_full_name() ) );
15 jb_vseq.jb_seqr1 = jb_env.jb_agent1.jb_seqr;
16 jb_vseq.jb_seqr2 = jb_env.jb_agent2.jb_seqr; assert(
17 jb_vseq.randomize() ); 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.

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


----------------------------------------------------------------
--------------------
Name Type
Size Value
----------------------------------------------------------------
--------------------
jb_vseq
jelly_bean_recipe_virtual_sequence - @1196
recipe recipe_e
2 CANDY_APPLE jb_seq1
same_flavored_jelly_beans_sequence - @1200
num_jelly_beans integral
32 'h2 flavor
jelly_bean_transaction::flavor_e 3 APPLE req
object -
rsp object
-
jb_seq2
same_flavored_jelly_beans_sequence - @1204
num_jelly_beans integral
32 'h1 flavor
jelly_bean_transaction::flavor_e 3 CINNAMON req
object -
rsp object
-
depth int
32 'd1
parent sequence (name) string
0 ""
parent sequence (full name) string
0 ""
sequencer string
0 ""
req object
-
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


______________________________________________________________________
______________________________________________________________________
I have a basic doubt related to raising an objection.
In many examples i found the objection is raised from inside the sequences, or may be
some other components.
Here, in this example, the objection is raised from the testcase.
How to decide on the point of raising an objection? What all factors incfluence that?
Does it make any difference if i raise an objection from a sequence or from a testcase?
I have a basic doubt related to raising an objection.
In many examples i found the objection is raised from inside the sequences, or may be
some other components.
Here, in this example, the objection is raised from the testcase.
How to decide on the point of raising an objection? What all factors incfluence that?
Does it make any difference if i raise an objection from a sequence or from a testcase?
I found the post is very helpful for me to understand the uvm. I wonder what is the
reason that seq1.start needs to provide parent_sequence, but the gift_box example,
sequence of sequences does not need parent_sequence.
The jb_seq1 and jb_seq2 are children of
the jelly_bean_recipe_virtual_sequence. If the parent_sequence is provided,
the parent_sequence‘s pre_do, mid_do, and post_do methods will be called during
the execution of the jb_seq1 and jb_seq2.
Similarly the gift_boxed_jelly_beans_sequence should have provided
the parent_sequence when it called jb_seq.start(). I have corrected the example
(the line 15 of gift_boxed_jelly_beans_sequence of this article). Thank you for
pointing this out.

I am new to virtual_seq. In line 1,


class jelly_bean_recipe_virtual_sequence extends uvm_sequence#(
uvm_sequence_item );
Since virtual_seq does not work with a driver, why do we need uvm_sequence_item? Is
it ok to leave it out.
class jelly_bean_recipe_virtual_sequence extends uvm_sequence;
As you mentioned, the type parameter of the uvm_sequence# class is not important
here. Since the default type is uvm_sequence_item, you can omit the type.

Could also explain about Virtual Sequencers ? As per your explanation, I understood
that Virtual Sequences co-ordinates multiple sequences on different sequencers. how
does it differ from Virtual Sequencer.
Yes, a virtual sequence is nothing but a sequence. The only difference is that it co-
ordinates multiple sequences on different sequencers. A virtual sequencer also has the
handles to the coordinated sequencers, and you can create a virtual sequence that runs
on the virtual sequencer. However, this approach adds an unnecessary component
(virtual sequencer) to the verification environment and it negatively affects the
reusability of the environment. I would recommend not to use the virtual sequencer.

Why do we need virtual sequencer when the same can be done with the above
example.
What are the disadvantage of using the above example
A virtual sequencer is not needed. Please see my comment above.

What’s the benefit of using the virtual sequence? We can do pretty much do the same
thing in the test, right? Is it just to group the sequences? Thanks, Cate
There are a couple of benefits using the virtual sequence over doing the same in a test.
 It is easier to reuse. You can reuse the virtual sequence used in the module level
to the full-chip level, for example. If you coordinate the sequences in a test, you
will need to modify the test because it is tightly coupled with the component
hierarchy.
 You can easily create hierarchical sequences if you need to do so in the future.

Can you explain how to parameterize a uvm_sequence_item? For example, I have a


interface, and I want to have the address and data width parametrizable.
I think I can write the base class like.
class bus_item#( ADDR_WIDTH = 8, DATA_WIDTH=8) extends uvm_sequence_item
`uvm_object_utils( bus_item#(ADDR_WIDTH, DATA_WIDTH))

So when I create a derive class. Should it be?
class d_bus_item extends bus_item#(ADDR_WIDTH, DATA_WIDTH)
`uvm_object_utils( d_bus_item)
Also when I use the class my_seq extends uvm_sequence#(packet), is the packet the
parameter in uvm_sequence?
These are my confusion.
1. I am not sure where to place the parameter in the derived class. For example,
`uvm_object_utils( d_bus_item#(ADDR_WIDTH, DATA_WIDTH)).
2. If my base class is not parameterized, can I derived a class and parameterize it?
A parameterized class needs to be specialized (meaning you need to specify the
parameter values) before use. Your code won’t compile because the ADDR_WIDTH and
the DATA_WIDTH are not specified.
// This won't compile because ADDR_WIDTH and DATA_WIDTH are not
specified.
class d_bus_item extends bus_item#( ADDR_WIDTH, DATA_WIDTH );

You can either make the d_bus_item parameterized:


// make d_bus_item parameterized, too
class d_bus_item#( ADDR_WIDTH, DATA_WIDTH ) extends bus_item#(
ADDR_WIDTH, DATA_WIDTH );
`uvm_object_param_utils( d_bus_item #( ADDR_WIDTH, DATA_WIDTH
) ) // make sure to use "param_utils"

and specialize the d_bus_item when you instantiate it:


d_bus_item#( 8, 8 ) d;
Or, specialize the bus_item:
// d_bus_item is no longer a parameterized class
class d_bus_item extends bus_item#( 8, 8 );
`uvm_object_utils( d_bus_item )

Since the bus_item class has the default parameter values, you can also write:
class d_bus_item extends bus_item; // < -- same as writing
bus_item#( 8, 8 )
Similarly, when you did:
class my_seq extends uvm_sequence#( packet );
you specialized the uvm_sequence class with the packet type.
You can add new parameters to the derived class, too.

For a derived class also can change the parameters, I need to declare it this way.
class A_item#(int a) extends B_item#(int a)
`uvm_object_param_utils(A_item#(int a)) <– I used the param_utils, and I included the
param.
If my drived class will use a fixed parameter like 8, I need declare it this way.
class A_item extends B_item#(8) <– I used the fix parameter
`uvm_object_utils(A_item) <– this is not parameterized class
You are right.

Hi, I am just eager how a sequence will be run if sequencer handle is not passed.
Say, if vir_seq.start(null) is called , please explain how does it start() method works.
Basically the start method executes the body task of the vir_seq, but no sequencer is
involved when the sequencerargument is null.

How I set a value to variable in the sequence, when i start the sequence with
`uvm_do_* macro? Because I don’t need to create the sequence when I use uvm_do. I
tried setting the variable as rand type and tried to use uvm_do_with, but it doesn’t work.
If you could paste how did you call the `uvm_do_with, I might be able to help.

hanks, I was able to make it work with `uvm_do_with. But my requirement is that I don’t
want to set the variable always, but only on certain occasions. Making it as rand type
randomizes it always(even when I just use `uvm_do).
How about disabling random variables
with rand_mode like your_variable.rand_mode(0)?

1) I recently came across an interview question regarding Virtual sequencer.I know the
application of virtual sequencer.
Interview Question : What is virtual sequencer? Is it possible to drive two different
sequences with out using virtual sequences/sequencers on different agents?
– If Yes,Then what is the purpose of virtual sequences/sequencers?
– If No,what makes the advantage using it.
can you let me know with some scenario ?
You can start sequences on multiple sequencers without using a virtual sequence. But if
you do, you can extend it, reuse it, and create hierarchical sequences with it.

I have a question: can we start a virtual sequence in another virtual sequence (like in
no3 I see sequence of sequences by using m_sequencer ). Can we do similar things
with two (or more) virtual sequences. If possible could you please recommend the way
?
Yes, you can. This is a virtual sequence
(gift_boxed_jelly_beans_virtual_sequence) that runs another virtual sequence
(jelly_bean_recipe_virtual_sequence).
class gift_boxed_jelly_beans_virtual_sequence extends
uvm_sequence;
`uvm_object_utils( gift_boxed_jelly_beans_virtual_sequence )

rand int unsigned num_jelly_bean_flavors; // knob


jelly_bean_recipe_virtual_sequence jb_seq;

constraint num_jelly_bean_flavors_con {
num_jelly_bean_flavors inside { [2:3] }; }

function new( string name = "" );


super.new( name );
jb_seq =
jelly_bean_recipe_virtual_sequence::type_id::create( .name(
"jb_seq" ), .contxt( get_full_name() ) );
endfunction: new

task body();
repeat ( num_jelly_bean_flavors ) begin
assert( jb_seq.randomize() );
jb_seq.start( .sequencer( null ) ); // start another
virtual sequence
end
endtask: body
endclass: gift_boxed_jelly_beans_virtual_sequence
And this is a test that runs the above virtual sequence.
class jelly_bean_recipe_test extends jelly_bean_base_test;
`uvm_component_utils( jelly_bean_recipe_test )

function new( string name, uvm_component parent );


super.new( name, parent );
endfunction: new

task main_phase( uvm_phase phase );


gift_boxed_jelly_beans_virtual_sequence jb_vseq;

phase.raise_objection( .obj( this ) );


jb_vseq =
gift_boxed_jelly_beans_virtual_sequence::type_id::create( .name(
"jb_vseq" ) );
jb_vseq.jb_seq.jb_seqr1 = jb_env.jb_agent1.jb_seqr;
jb_vseq.jb_seq.jb_seqr2 = jb_env.jb_agent2.jb_seqr;
assert( jb_vseq.randomize() );
jb_vseq.start( .sequencer( null ) ); // start the virtual
sequence
#100ns ;
phase.drop_objection( .obj( this ) );
endtask: main_phase
endclass: jelly_bean_recipe_test

Hi, If I create a virtual_seq with only one sequence and start this virtual_seq in my test
with actual sequencer (instead of null), then will that be considered as normal
sequence? If yes, then only way simulator knows it is a virtual sequence is by the way
start is called?
I would say your sequence is a non-virtual sequence, not because you started the
sequence on a (non-null) sequencer, but because it is a sequence of another
sequence.
You can start a virtual sequence on a (non-null) sequencer. For example, the virtual
sequence can send sequence items to the sequencer while it starts other sequences on
their sequencers. My definition of virtual sequence is that it starts sub-sequences
on multiple sequencers.

Can we run a sequence on multiple sequencers in parallel, can you please explain.
You cannot start the same sequence on multiple sequencers in parallel unless you
clone it because the sequence keeps track of its sequence state (once a sequence is
started, it cannot be started again until it is finished or stopped).

virtual sequencer and physical sequencer exit from main phase prematurely before test
completes. have you seen this? if so, how to resolve it? Thank you.
Sorry for the very slow reply. It’s most likely objections are dropped prematurely. You
could use +UVM_OBJECTION_TRACE command-line argument to trace objection activity.

he definition of your “same_flavored_jelly_beans_sequence” class does not have a


member called “flavor”. How were you able to assign some values inside your
“jelly_bean_recipe_virtual_sequence” class for the “jb_seq1” and “jb_seq2” sequences?
Actually, I slightly modified the original same_flavored_jelly_beans_sequence to
have the flavor property for this tutorial. Sorry for the confusion.
class same_flavored_jelly_beans_sequence extends uvm_sequence#(
jelly_bean_transaction );

// knobs

rand int unsigned num_jelly_beans;


rand jelly_bean_transaction::flavor_e flavor;
local same_flavored_jelly_beans_sequence self = this;
constraint num_jelly_beans_con { num_jelly_beans inside {
[1:4] }; }

function new( string name = "" );


super.new( name );
endfunction: new

task body();
jelly_bean_transaction jb_tx;
repeat ( num_jelly_beans ) begin
jb_tx = jelly_bean_transaction::type_id::create( .name(
"jb_tx" ) );
start_item( jb_tx );
assert( jb_tx.randomize() with { this.flavor ==
self.flavor; } );
`uvm_info( get_name(), { "n", jb_tx.sprint() }, UVM_LOW )
finish_item( jb_tx );
end
endtask: body

`uvm_object_utils_begin( same_flavored_jelly_beans_sequence )
`uvm_field_int ( num_jelly_beans,
UVM_ALL_ON )
`uvm_field_enum( jelly_bean_transaction::flavor_e, flavor,
UVM_ALL_ON )
`uvm_object_utils_end
endclass: same_flavored_jelly_beans_sequence

For virtual sequence reused by different tests, should we put the virtual sequence in
env? Is that a good way? Otherwise is there any better way to handle the virtual
sequence?
I would create a package for the virtual sequence and let the test import it.

Will please explain the difference between import a package and include a file with a
good example?
The `include is a compiler directive used to insert a file as if it is copy & pasted.
`include "jelly_bean_pkg.sv" // contents of the
jelly_bean_pkg.sv is inserted here
On the other hand, the import declaration DOES NOT insert the contents of the
package. It just provides direct visibility of identifiers within a package. For example,
unless you import the package, you need to use the package name qualifier when you
use the jelly_bean_transaction declared in the jelly_bean_pkg.
jelly_bean_pkg::jelly_bean_transaction jb_xact; // note the
"jelly_bean_pkg::"
With the import declaration, you can use the jelly_bean_transaction without the
package name qualifier.
import jelly_bean_pkg::*; // all identifiers in the package are
"visible" in this scope

jelly_bean_transaction jb_xact; // no package qualifier is


needed

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy