Atomic generator generates individual data items or transaction descriptors. Each item is generated independently of other items in a random fashion. Atomic generator is simple to describe and use.
Unlike atomic generator, a scenario generator generates a sequence of transaction.
Example of sequence of transaction:
CPU instructions like LOAD__A,LOAD__B,ADD_A_B,STORE_C.
Packets with incrementing length.
Do write operation to address "123" and then do the read to address "123".
It is very unlikely that atomic generator to generate transaction in the above ordered sequence. With scenario generator, we can generate these sequence of transaction.
VMM provides `vmm_scenario_gen() macro for quickly creating a scenario generator.
`vmm_scenario_gen (class_name , "Class Description" );
The macro defines classes named <class_name>_scenario_gen, <class_name>_scenario, <class_name>_scenario_election , <class_name>_scenario_gen_callbacks and <class_name>_atomic_scenario.
class custom_scenario_gen extends vmm_xactor ;
class custom_scenario extends vmm_data ;
class custom_atomic_scenario extends custom_scenario ;
class custom_scenario_election ;
class custom_scenario_gen_callbacks extends vmm_xactor_callbacks
<class_name>_scenario:
This scenario generator can generate more than one scenario. Each scenario which can contain more than one transaction is described in a class which is extended from <class_name>_scenario.
For each scenario, following variables must be constraint.
Length: number of transactions in an array.
Repeated: number of times to repeat this scenario.
<class_name>_atomic_scenario:
This class is a atomic scenario. This is the default scenario. This scenario is a random transactions.
<class_name>_scenario_election:
This class is the arbiter which determines the order that the known scenarios are applied. By default, scenarios are elected atomically. User can extend this class to define an order in which the scenarios should be picked.
<class_name>_scenario_gen:
This class is the scenario generator which generates the transactions and sends out using output channel. This class has a queue of scenario objects. Each scenario contains transaction instances in an array.
<class_name>_scenario_gen_callbacks:
This class provides callback mechanism. There are two callback methods define. pre_scenario_randomize() and post_scenario_gen() which are called at pre-randomization of the scenario and post-generation of the scenario respectively.
Example
Let us write an example.
Following is the transaction class which we will use to write a scenario generator.
class instruction extends vmm_data ;
vmm_log log ;
typedef enum { LOAD__A , LOAD__B , ADD_A_B , SUB_A_B , STORE_C } kinds_e ;
rand kinds_e inst ;
function new ();
super . new ( this .log );
endfunction : new
virtual function string psdisplay ( string prefix = "" );
psdisplay = $psprintf ( " Instruction : %s | stream_id : %0d | scenario_id : %0d " ,inst . name (),stream_id ,scenario_id );
endfunction :psdisplay
virtual function vmm_data allocate ();
instruction tr = new ;
allocate = tr ;
endfunction :allocate
virtual function vmm_data copy (vmm_data cpy = null );
instruction to ;
if (cpy == null )
to = new ;
else
if (! $cast (to , cpy )) begin
`vmm_fatal ( this .log , "Attempting to copy to a non instruction instance" );
return null ;
end
super .copy_data (to );
to .inst = this .inst ;
copy = to ;
endfunction :copy
endclass
The above transaction crests CPU instructions.
Let us consider that sequence of instruction LOAD__A,LOAD__B,ADD_A_B,STORE_C is a interesting scenario and LOAD__A,LOAD__B,SUB_A_B,STORE_C is also an interesting scenario.
When instructions are generated, we want to generate these 2 sequences of instruction. Let us see how to generate these 2 sequence of instructions .
As we have already discussed, `vmm_scenario_gen() creates use full class for cscenario generation.
1) Use `vmm_scenario_gen() macro to declare the scenario classes.
`vmm_scenario_gen (instruction , "Instruction Scenario Generator" )
This macro will create following classs
instruction_scenario_gen
instruction_scenario
instruction_atomic_scenario
instruction_scenario_election
instruction_scenario_gen_callbacks
2) Define interesting scenarios by extending inst_scenario;
class instruction_scenario_add_sub extends instruction_scenario ;
3) Define the first scenario. It is sequence of instructions for addition operation.
a) Declare a variable for identifying the scenario.
int addition_scenario_id ;
Each scenario has more than one inductions. All these instructions are in a queue "items" which is already defined in "instruction_scenario".
The rand varible "scenario_kind", which pre defined in the vmm_scenario class, will randomly select one of the defined scenarios. "scenario_kind" varible has the id of the current scenario. So we have to define the addition scenario, when the "scenario_kind" value is addition_scenario_id.
b) Constrain the scenario kind,
constraint addition_scenario_items {
if ( $void (scenario_kind ) == addition_scenario_id ) {
c) The number of instructions define in a scenario is specified by the predefined variable "length". In our example, we have 4 instructions. So constrain the length to 4.
length == 4 ;
d) The predefined variable "repeated" used to control if the VMM scenario generator would run the scenario more than once each time it is created. We are not interested in repeating so, constrain it to 0
repeated == 0 ;
e) Constrain the individual items based on the requirements if this scenario is selected. Our requirement in this example is that "inst" should follow the sequence LOAD__A,LOAD__B,ADD_A_B,STORE_C
foreach (items [i ])
if (i == 0 )
this .items [i ].inst == instruction :: LOAD__A ;
else if (i == 1 )
this .items [i ].inst == instruction :: LOAD__B ;
else if (i == 2 )
this .items [i ].inst == instruction :: ADD_A_B ;
else if (i == 3 )
this .items [i ].inst == instruction :: STORE_C ;
4)Define second scenario. It is Sequence of instructions for subtraction operation.
a) Declare a variable for identifying the scenario.
int subtraction_scenario_id ;
b) Constrain the scenario kind,
constraint subtraction_scenario_items {
if ( $void (scenario_kind ) == subtraction_scenario_id ) {
c) Constrain the length
length == 4 ;
d) Constrain the repeated
repeated == 0 ;
e) Constrain the items
foreach (items [i ])
if (i == 0 )
this .items [i ].inst == instruction :: LOAD__A ;
else if (i == 1 )
this .items [i ].inst == instruction :: LOAD__B ;
else if (i == 2 )
this .items [i ].inst == instruction :: SUB_A_B ;
else if (i == 3 )
this .items [i ].inst == instruction :: STORE_C ;
5)Define constructor method.
a) call the super.new() method.
Get a unique Ids from the define_scenario() method for each scenario.
define_scenario() is predefined in *_scenario class.
function new ();
this .addition_scenario_id = define_scenario ( " ADDITION " , 4 );
this .subtraction_scenario_id = define_scenario ( " SUBSTRACTION " , 4 );
endfunction
With this, we completed the implementation of scenarios.
Scenario Code
`vmm_scenario_gen (instruction , "Instruction Scenario Generator" )
class instruction_scenario_add_sub extends instruction_scenario ;
///
// ADDITION SCENARIO //
///
int addition_scenario_id ;
constraint addition_scenario_items {
if ( $void (scenario_kind ) == addition_scenario_id ) {
repeated == 0 ;
length == 4 ;
foreach (items [i ])
if (i == 0 )
this .items [i ].inst == instruction :: LOAD__A ;
else if (i == 1 )
this .items [i ].inst == instruction :: LOAD__B ;
else if (i == 2 )
this .items [i ].inst == instruction :: ADD_A_B ;
else if (i == 3 )
this .items [i ].inst == instruction :: STORE_C ;
}
}
///
// ASUBTRACTION SCENARIO //
///
int subtraction_scenario_id ;
constraint subtraction_scenario_items {
if ( $void (scenario_kind ) == subtraction_scenario_id ) {
repeated == 0 ;
length == 4 ;
foreach (items [i ])
if (i == 0 )
this .items [i ].inst == instruction :: LOAD__A ;
else if (i == 1 )
this .items [i ].inst == instruction :: LOAD__B ;
else if (i == 2 )
this .items [i ].inst == instruction :: SUB_A_B ;
else if (i == 3 )
this .items [i ].inst == instruction :: STORE_C ;
}
}
function new ();
this .addition_scenario_id = define_scenario ( " ADDITION " , 4 );
this .subtraction_scenario_id = define_scenario ( " SUBSTRACTION " , 4 );
endfunction
endclass
Testcase
Now we will write a test case to see how to above defined scenario works.
1) Declare scenario generator
instruction_scenario_gen gen ;
2) Declare the scenario which we defined earlier.
instruction_scenario_add_sub sce_add_sub ;
3) Construct the generator and scenarios.
gen = new ( "gen" , 0 );
sce_add_sub = new ();
4) set the number of instances and scenarios generated by generator to 20 and 4 respectevy.
gen .stop_after_n_insts = 20 ;
gen .stop_after_n_scenarios = 4 ;
5) Scenario generators store all the scenarios in scenario_set queue. So, we have to add the scenario which we constructed above to the queue.
gen .scenario_set [ 0 ] = sce_add_sub ;
6) Start the generator
gen .start_xactor ();
7) Similar t the Atomic generator, the transactions created by the scenario generator are sent to out_chan channel.
Get the instructions from the out_chan channel and print the content.
repeat ( 20 ) begin
gen .out_chan . get (inst );
inst .display ();
Testcase code
program test ();
instruction_scenario_gen gen ;
instruction_scenario_add_sub sce_add_sub ;
instruction inst ;
initial
begin
gen = new ( "gen" , 0 );
sce_add_sub = new ();
//gen.log.set_verbosity(vmm_log::DEBUG_SEV,"/./","/./");
gen .stop_after_n_insts = 20 ;
gen .stop_after_n_scenarios = 4 ;
gen .scenario_set [ 0 ] = sce_add_sub ;
gen .start_xactor ();
repeat ( 20 ) begin
gen .out_chan . get (inst );
inst .display ();
end
end
endprogram
Down load the source code
vmm_scenario.tar
Browse the code in vmm_scenario.tar
Command to simulate
vcs -sverilog -ntb_opts rvm -f filelist -R
Observe the log file , you can see the both the scenarios .
Logfile
Instruction : LOAD__A | stream_id : 0 | scenario_id : 0
Instruction : LOAD__B | stream_id : 0 | scenario_id : 0
Instruction : ADD_A_B | stream_id : 0 | scenario_id : 0
Instruction : STORE_C | stream_id : 0 | scenario_id : 0
Instruction : LOAD__A | stream_id : 0 | scenario_id : 1
Instruction : LOAD__B | stream_id : 0 | scenario_id : 1
Instruction : ADD_A_B | stream_id : 0 | scenario_id : 1
Instruction : STORE_C | stream_id : 0 | scenario_id : 1
Instruction : LOAD__A | stream_id : 0 | scenario_id : 2
Instruction : LOAD__B | stream_id : 0 | scenario_id : 2
Instruction : SUB_A_B | stream_id : 0 | scenario_id : 2
Instruction : STORE_C | stream_id : 0 | scenario_id : 2
Instruction : LOAD__A | stream_id : 0 | scenario_id : 3
Instruction : LOAD__B | stream_id : 0 | scenario_id : 3
Instruction : ADD_A_B | stream_id : 0 | scenario_id : 3
Instruction : STORE_C | stream_id : 0 | scenario_id : 3
Unlike atomic generator, a scenario generator generates a sequence of transaction.
Example of sequence of transaction:
CPU instructions like LOAD__A,LOAD__B,ADD_A_B,STORE_C.
Packets with incrementing length.
Do write operation to address "123" and then do the read to address "123".
It is very unlikely that atomic generator to generate transaction in the above ordered sequence. With scenario generator, we can generate these sequence of transaction.
VMM provides `vmm_scenario_gen() macro for quickly creating a scenario generator.
`vmm_scenario_gen (class_name , "Class Description" );
The macro defines classes named <class_name>_scenario_gen, <class_name>_scenario, <class_name>_scenario_election , <class_name>_scenario_gen_callbacks and <class_name>_atomic_scenario.
class custom_scenario_gen extends vmm_xactor ;
class custom_scenario extends vmm_data ;
class custom_atomic_scenario extends custom_scenario ;
class custom_scenario_election ;
class custom_scenario_gen_callbacks extends vmm_xactor_callbacks
<class_name>_scenario:
This scenario generator can generate more than one scenario. Each scenario which can contain more than one transaction is described in a class which is extended from <class_name>_scenario.
For each scenario, following variables must be constraint.
Length: number of transactions in an array.
Repeated: number of times to repeat this scenario.
<class_name>_atomic_scenario:
This class is a atomic scenario. This is the default scenario. This scenario is a random transactions.
<class_name>_scenario_election:
This class is the arbiter which determines the order that the known scenarios are applied. By default, scenarios are elected atomically. User can extend this class to define an order in which the scenarios should be picked.
<class_name>_scenario_gen:
This class is the scenario generator which generates the transactions and sends out using output channel. This class has a queue of scenario objects. Each scenario contains transaction instances in an array.
<class_name>_scenario_gen_callbacks:
This class provides callback mechanism. There are two callback methods define. pre_scenario_randomize() and post_scenario_gen() which are called at pre-randomization of the scenario and post-generation of the scenario respectively.
Example
Let us write an example.
Following is the transaction class which we will use to write a scenario generator.
class instruction extends vmm_data ;
vmm_log log ;
typedef enum { LOAD__A , LOAD__B , ADD_A_B , SUB_A_B , STORE_C } kinds_e ;
rand kinds_e inst ;
function new ();
super . new ( this .log );
endfunction : new
virtual function string psdisplay ( string prefix = "" );
psdisplay = $psprintf ( " Instruction : %s | stream_id : %0d | scenario_id : %0d " ,inst . name (),stream_id ,scenario_id );
endfunction :psdisplay
virtual function vmm_data allocate ();
instruction tr = new ;
allocate = tr ;
endfunction :allocate
virtual function vmm_data copy (vmm_data cpy = null );
instruction to ;
if (cpy == null )
to = new ;
else
if (! $cast (to , cpy )) begin
`vmm_fatal ( this .log , "Attempting to copy to a non instruction instance" );
return null ;
end
super .copy_data (to );
to .inst = this .inst ;
copy = to ;
endfunction :copy
endclass
The above transaction crests CPU instructions.
Let us consider that sequence of instruction LOAD__A,LOAD__B,ADD_A_B,STORE_C is a interesting scenario and LOAD__A,LOAD__B,SUB_A_B,STORE_C is also an interesting scenario.
When instructions are generated, we want to generate these 2 sequences of instruction. Let us see how to generate these 2 sequence of instructions .
As we have already discussed, `vmm_scenario_gen() creates use full class for cscenario generation.
1) Use `vmm_scenario_gen() macro to declare the scenario classes.
`vmm_scenario_gen (instruction , "Instruction Scenario Generator" )
This macro will create following classs
instruction_scenario_gen
instruction_scenario
instruction_atomic_scenario
instruction_scenario_election
instruction_scenario_gen_callbacks
2) Define interesting scenarios by extending inst_scenario;
class instruction_scenario_add_sub extends instruction_scenario ;
3) Define the first scenario. It is sequence of instructions for addition operation.
a) Declare a variable for identifying the scenario.
int addition_scenario_id ;
Each scenario has more than one inductions. All these instructions are in a queue "items" which is already defined in "instruction_scenario".
The rand varible "scenario_kind", which pre defined in the vmm_scenario class, will randomly select one of the defined scenarios. "scenario_kind" varible has the id of the current scenario. So we have to define the addition scenario, when the "scenario_kind" value is addition_scenario_id.
b) Constrain the scenario kind,
constraint addition_scenario_items {
if ( $void (scenario_kind ) == addition_scenario_id ) {
c) The number of instructions define in a scenario is specified by the predefined variable "length". In our example, we have 4 instructions. So constrain the length to 4.
length == 4 ;
d) The predefined variable "repeated" used to control if the VMM scenario generator would run the scenario more than once each time it is created. We are not interested in repeating so, constrain it to 0
repeated == 0 ;
e) Constrain the individual items based on the requirements if this scenario is selected. Our requirement in this example is that "inst" should follow the sequence LOAD__A,LOAD__B,ADD_A_B,STORE_C
foreach (items [i ])
if (i == 0 )
this .items [i ].inst == instruction :: LOAD__A ;
else if (i == 1 )
this .items [i ].inst == instruction :: LOAD__B ;
else if (i == 2 )
this .items [i ].inst == instruction :: ADD_A_B ;
else if (i == 3 )
this .items [i ].inst == instruction :: STORE_C ;
4)Define second scenario. It is Sequence of instructions for subtraction operation.
a) Declare a variable for identifying the scenario.
int subtraction_scenario_id ;
b) Constrain the scenario kind,
constraint subtraction_scenario_items {
if ( $void (scenario_kind ) == subtraction_scenario_id ) {
c) Constrain the length
length == 4 ;
d) Constrain the repeated
repeated == 0 ;
e) Constrain the items
foreach (items [i ])
if (i == 0 )
this .items [i ].inst == instruction :: LOAD__A ;
else if (i == 1 )
this .items [i ].inst == instruction :: LOAD__B ;
else if (i == 2 )
this .items [i ].inst == instruction :: SUB_A_B ;
else if (i == 3 )
this .items [i ].inst == instruction :: STORE_C ;
5)Define constructor method.
a) call the super.new() method.
Get a unique Ids from the define_scenario() method for each scenario.
define_scenario() is predefined in *_scenario class.
function new ();
this .addition_scenario_id = define_scenario ( " ADDITION " , 4 );
this .subtraction_scenario_id = define_scenario ( " SUBSTRACTION " , 4 );
endfunction
With this, we completed the implementation of scenarios.
Scenario Code
`vmm_scenario_gen (instruction , "Instruction Scenario Generator" )
class instruction_scenario_add_sub extends instruction_scenario ;
///
// ADDITION SCENARIO //
///
int addition_scenario_id ;
constraint addition_scenario_items {
if ( $void (scenario_kind ) == addition_scenario_id ) {
repeated == 0 ;
length == 4 ;
foreach (items [i ])
if (i == 0 )
this .items [i ].inst == instruction :: LOAD__A ;
else if (i == 1 )
this .items [i ].inst == instruction :: LOAD__B ;
else if (i == 2 )
this .items [i ].inst == instruction :: ADD_A_B ;
else if (i == 3 )
this .items [i ].inst == instruction :: STORE_C ;
}
}
///
// ASUBTRACTION SCENARIO //
///
int subtraction_scenario_id ;
constraint subtraction_scenario_items {
if ( $void (scenario_kind ) == subtraction_scenario_id ) {
repeated == 0 ;
length == 4 ;
foreach (items [i ])
if (i == 0 )
this .items [i ].inst == instruction :: LOAD__A ;
else if (i == 1 )
this .items [i ].inst == instruction :: LOAD__B ;
else if (i == 2 )
this .items [i ].inst == instruction :: SUB_A_B ;
else if (i == 3 )
this .items [i ].inst == instruction :: STORE_C ;
}
}
function new ();
this .addition_scenario_id = define_scenario ( " ADDITION " , 4 );
this .subtraction_scenario_id = define_scenario ( " SUBSTRACTION " , 4 );
endfunction
endclass
Testcase
Now we will write a test case to see how to above defined scenario works.
1) Declare scenario generator
instruction_scenario_gen gen ;
2) Declare the scenario which we defined earlier.
instruction_scenario_add_sub sce_add_sub ;
3) Construct the generator and scenarios.
gen = new ( "gen" , 0 );
sce_add_sub = new ();
4) set the number of instances and scenarios generated by generator to 20 and 4 respectevy.
gen .stop_after_n_insts = 20 ;
gen .stop_after_n_scenarios = 4 ;
5) Scenario generators store all the scenarios in scenario_set queue. So, we have to add the scenario which we constructed above to the queue.
gen .scenario_set [ 0 ] = sce_add_sub ;
6) Start the generator
gen .start_xactor ();
7) Similar t the Atomic generator, the transactions created by the scenario generator are sent to out_chan channel.
Get the instructions from the out_chan channel and print the content.
repeat ( 20 ) begin
gen .out_chan . get (inst );
inst .display ();
Testcase code
program test ();
instruction_scenario_gen gen ;
instruction_scenario_add_sub sce_add_sub ;
instruction inst ;
initial
begin
gen = new ( "gen" , 0 );
sce_add_sub = new ();
//gen.log.set_verbosity(vmm_log::DEBUG_SEV,"/./","/./");
gen .stop_after_n_insts = 20 ;
gen .stop_after_n_scenarios = 4 ;
gen .scenario_set [ 0 ] = sce_add_sub ;
gen .start_xactor ();
repeat ( 20 ) begin
gen .out_chan . get (inst );
inst .display ();
end
end
endprogram
Down load the source code
vmm_scenario.tar
Browse the code in vmm_scenario.tar
Command to simulate
vcs -sverilog -ntb_opts rvm -f filelist -R
Observe the log file , you can see the both the scenarios .
Logfile
Instruction : LOAD__A | stream_id : 0 | scenario_id : 0
Instruction : LOAD__B | stream_id : 0 | scenario_id : 0
Instruction : ADD_A_B | stream_id : 0 | scenario_id : 0
Instruction : STORE_C | stream_id : 0 | scenario_id : 0
Instruction : LOAD__A | stream_id : 0 | scenario_id : 1
Instruction : LOAD__B | stream_id : 0 | scenario_id : 1
Instruction : ADD_A_B | stream_id : 0 | scenario_id : 1
Instruction : STORE_C | stream_id : 0 | scenario_id : 1
Instruction : LOAD__A | stream_id : 0 | scenario_id : 2
Instruction : LOAD__B | stream_id : 0 | scenario_id : 2
Instruction : SUB_A_B | stream_id : 0 | scenario_id : 2
Instruction : STORE_C | stream_id : 0 | scenario_id : 2
Instruction : LOAD__A | stream_id : 0 | scenario_id : 3
Instruction : LOAD__B | stream_id : 0 | scenario_id : 3
Instruction : ADD_A_B | stream_id : 0 | scenario_id : 3
Instruction : STORE_C | stream_id : 0 | scenario_id : 3