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_if
s. 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
CANDY_APPLE // 2 APPLE + 1 CINNAMON
} recipe_e;
rand recipe_e recipe;
jelly_bean_sequencer jb_seqr1;
jelly_bean_sequencer jb_seqr2;
same_flavored_jelly_beans_sequence jb_seq1;
same_flavored_jelly_beans_sequence jb_seq2;
function new( string name = "" );
super.new( name );
endfunction: new
task body();
jb_seq1 = same_flavored_jelly_beans_sequence::type_id::create( .name( "jb_seq1" ), .contxt( get_full_name() ) );
jb_seq2 = same_flavored_jelly_beans_sequence::type_id::create( .name( "jb_seq2" ), .contxt( get_full_name() ) );
case ( recipe )
LEMON_MERINGUE_PIE: begin
jb_seq1.flavor = jelly_bean_transaction::LEMON;
jb_seq2.flavor = jelly_bean_transaction::COCONUT;
jb_seq1.num_jelly_beans = 2;
jb_seq2.num_jelly_beans = 2;
end
STRAWBERRY_SHORTCAKE: begin
jb_seq1.flavor = jelly_bean_transaction::STRAWBERRY;
jb_seq2.flavor = jelly_bean_transaction::VANILLA;
jb_seq1.num_jelly_beans = 2;
jb_seq2.num_jelly_beans = 2;
end
CANDY_APPLE: begin
jb_seq1.flavor = jelly_bean_transaction::APPLE;
jb_seq2.flavor = jelly_bean_transaction::CINNAMON;
jb_seq1.num_jelly_beans = 2;
jb_seq2.num_jelly_beans = 1;
end
endcase // case ( recipe )
`uvm_info( get_name(), { "\n", this.sprint() }, UVM_LOW )
fork
jb_seq1.start( .sequencer( jb_seqr1 ), .parent_sequence( this ) );
jb_seq2.start( .sequencer( jb_seqr2 ), .parent_sequence( this ) );
join
endtask: body
`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_sequencer
s 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.
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 );
jelly_bean_recipe_virtual_sequence jb_vseq;
phase.raise_objection( .obj( this ) );
jb_vseq = jelly_bean_recipe_virtual_sequence::type_id::create( .name( "jb_vseq" ), .contxt( get_full_name() ) );
jb_vseq.jb_seqr1 = jb_env.jb_agent1.jb_seqr;
jb_vseq.jb_seqr2 = jb_env.jb_agent2.jb_seqr;
assert( jb_vseq.randomize() );
jb_vseq.start( .sequencer( null ) );
#100ns ;
phase.drop_objection( .obj( this ) );
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 -
------------------------------------------------------------------------------------
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