This post will give an explanation on UVM configuration objects, since the earlier posts did not cover much on them. The jelly-bean verification platform uses two kinds of configuration objects, jelly_bean_agent_config
and jelly_bean_env_config
. The former configures the jelly_bean_agent
and the latter configures the jelly_bean_env
. The figures below show the verification platform and the class diagram of the configuration-related classes.
Verification Platform
Class Diagram of the Configuration Classes
Agent Configuration
The jelly_bean_agent_config
class configures the jelly_bean_agent
. The class has two switches; active
and has_jb_fc_sub
(lines 4 and 5). The active
switch controls whether the agent is in active mode or in passive mode. In the active mode, a sequencer (jelly_bean_sequencer
) and a driver (jelly_bean_driver
) will be created. In the passive mode, no sequencer or driver will be created. Similarly, the has_jb_fc_sub
switch controls whether the agent instantiates a functional coverage subscriber (jelly_bean_fc_subscriber
) or not. Based on the values of the switches, the agent will be structured as one of the four possible configurations shown in the figure below. The jelly_bean_agent_config
class also has a handle to the jelly_bean_if
(line 7).
class jelly_bean_agent_config extends uvm_object;
`uvm_object_utils( jelly_bean_agent_config )
uvm_active_passive_enum active = UVM_ACTIVE;
bit has_jb_fc_sub = 1; // switch to instantiate a functional coverage subscriber
virtual jelly_bean_if jb_if;
function new( string name = "" );
super.new( name );
endfunction: new
endclass: jelly_bean_agent_config
Four Possible Agent Configurations
Environment Configuration
Similar to the jelly_bean_agent_config
, the jelly_bean_env_config
configures the jelly_bean_env
. The class has four switches to define the structure of the environment (lines 4 to 7). It also has two handles to the jelly_bean_agent_config
; one handle per agent (lines 9 and 10).
class jelly_bean_env_config extends uvm_object;
`uvm_object_utils( jelly_bean_env_config )
bit has_jb_agent1 = 1; // switch to instantiate an agent #1
bit has_jb_agent2 = 1; // switch to instantiate an agent #2
bit has_jb_sb1 = 1; // switch to instantiate a scoreboard #1
bit has_jb_sb2 = 1; // switch to instantiate a scoreboard #2
jelly_bean_agent_config jb_agent_cfg1;
jelly_bean_agent_config jb_agent_cfg2;
function new( string name = "" );
super.new( name );
endfunction: new
endclass: jelly_bean_env_config
Top Module
The top Verilog module instantiates two jelly_bean_if
s (line 6 and 7) and stores them in configuration database (lines 17 to 20). The cntxt
and inst_name
provide the scope information of the virtual interface being stored. Since the top
module is not a uvm_component
, null
is used as the cntxt
. The uvm_test_top
is the name of the top-level uvm_component
instantiated by the run_test()
task of the uvm_root
class.
module top;
import uvm_pkg::*;
reg clk;
jelly_bean_if jb_if1( clk );
jelly_bean_if jb_if2( clk );
jelly_bean_subsystem dut( jb_if1, jb_if2 );
initial begin
clk = 0;
#5ns ;
forever #5ns clk = ! clk;
end
initial begin
uvm_config_db#( virtual jelly_bean_if )::set
( .cntxt( null ), .inst_name( "uvm_test_top" ), .field_name( "jb_if1" ), .value( jb_if1 ) );
uvm_config_db#( virtual jelly_bean_if )::set
( .cntxt( null ), .inst_name( "uvm_test_top" ), .field_name( "jb_if2" ), .value( jb_if2 ) );
run_test();
end
endmodule: top
Base Test
The base test builds configuration objects as follows:
- The base test creates one configuration object (
jb_env_cfg
) for the verification environment, and two configuration objects (jb_agent_cfg1
andjb_agent_cfg2
) for the jelly-bean agents (lines 16 to 18). - The
jelly_bean_if
s, which we’ve created in thetop
module, are retrieved from the configuration database. Then each retrieved interface is assigned to the corresponding agent configuration (lines 20 to 27). - The agent configurations are assigned to the
jb_env_cfg
(lines 29 and 30). - The
jb_env_cfg
is stored in the configuration database so that the verification environment can get its configuration from the database later (line 32 and 33).
class jelly_bean_base_test extends uvm_test;
`uvm_component_utils( jelly_bean_base_test )
jelly_bean_env jb_env;
jelly_bean_env_config jb_env_cfg;
jelly_bean_agent_config jb_agent_cfg1;
jelly_bean_agent_config jb_agent_cfg2;
function new( string name, uvm_component parent );
super.new( name, parent );
endfunction: new
function void build_phase( uvm_phase phase );
super.build_phase( phase );
jb_env_cfg = jelly_bean_env_config ::type_id::create( "jb_env_cfg" );
jb_agent_cfg1 = jelly_bean_agent_config::type_id::create( "jb_agent_cfg1" );
jb_agent_cfg2 = jelly_bean_agent_config::type_id::create( "jb_agent_cfg2" );
if ( ! uvm_config_db#( virtual jelly_bean_if )::get
( .cntxt( this ), .inst_name( "" ), .field_name( "jb_if1" ), .value( jb_agent_cfg1.jb_if ) ) ) begin
`uvm_error( "jelly_bean_test", "jb_if1 not found" )
end
if ( ! uvm_config_db#( virtual jelly_bean_if )::get
( .cntxt( this ), .inst_name( "" ), .field_name( "jb_if2" ), .value( jb_agent_cfg2.jb_if ) ) ) begin
`uvm_error( "jelly_bean_test", "jb_if2 not found" )
end
jb_env_cfg.jb_agent_cfg1 = jb_agent_cfg1;
jb_env_cfg.jb_agent_cfg2 = jb_agent_cfg2;
uvm_config_db#( jelly_bean_env_config )::set
( .cntxt( this ), .inst_name( "*" ), .field_name( "jb_env_cfg" ), .value( jb_env_cfg ) );
jb_env = jelly_bean_env::type_id::create( .name( "jb_env" ), .parent( this ) );
endfunction: build_phase
endclass: jelly_bean_base_test
Environment
The verification environment builds itself using its configuration object as follows:
- The environment class gets its configuration object (
jb_env_cfg
) from the configuration database (lines 17 to 20). - If the configuration object indicates the agent #1 to be created, then the environment creates it and stores its configuration (
jb_agent_cfg1
) to the configuration database (lines 23 to 25). - A scoreboard is created if the configuration object indicates to do so (lines 27 to 29).
- The analysis port of the agent and the export of the scoreboard are connected if the both objects are instantiated (lines 47 and 48).
class jelly_bean_env extends uvm_env;
`uvm_component_utils( jelly_bean_env )
jelly_bean_env_config jb_env_cfg;
jelly_bean_agent jb_agent1;
jelly_bean_agent jb_agent2;
jelly_bean_scoreboard jb_sb1;
jelly_bean_scoreboard jb_sb2;
function new( string name, uvm_component parent );
super.new( name, parent );
endfunction: new
function void build_phase( uvm_phase phase );
super.build_phase( phase );
if ( ! uvm_config_db#( jelly_bean_env_config )::get
( .cntxt( this ), .inst_name( "" ), .field_name( "jb_env_cfg" ), .value( jb_env_cfg ) ) ) begin
`uvm_error( "jelly_bean_env", "jb_env_cfg not found" )
end
if ( jb_env_cfg.has_jb_agent1 ) begin
uvm_config_db#( jelly_bean_agent_config )::set
( .cntxt( this ), .inst_name( "jb_agent1*" ), .field_name( "jb_agent_cfg" ), .value( jb_env_cfg.jb_agent_cfg1 ) );
jb_agent1 = jelly_bean_agent::type_id::create( .name( "jb_agent1" ), .parent( this ) );
if ( jb_env_cfg.has_jb_sb1 ) begin
jb_sb1 = jelly_bean_scoreboard::type_id::create( .name( "jb_sb1" ), .parent( this ) );
end
end
if ( jb_env_cfg.has_jb_agent2 ) begin
uvm_config_db#( jelly_bean_agent_config )::set
( .cntxt( this ), .inst_name( "jb_agent2*" ), .field_name( "jb_agent_cfg" ), .value( jb_env_cfg.jb_agent_cfg2 ) );
jb_agent2 = jelly_bean_agent::type_id::create( .name( "jb_agent2" ), .parent( this ) );
if ( jb_env_cfg.has_jb_sb2 ) begin
jb_sb2 = jelly_bean_scoreboard::type_id::create( .name( "jb_sb2" ), .parent( this ) );
end
end
endfunction: build_phase
function void connect_phase( uvm_phase phase );
super.connect_phase( phase );
if ( jb_env_cfg.has_jb_agent1 && jb_env_cfg.has_jb_sb1 ) jb_agent1.jb_ap.connect( jb_sb1.jb_analysis_export );
if ( jb_env_cfg.has_jb_agent2 && jb_env_cfg.has_jb_sb2 ) jb_agent2.jb_ap.connect( jb_sb2.jb_analysis_export );
endfunction: connect_phase
endclass: jelly_bean_env
Agent
The agent builds itself using its configuration object as follows:
- The agent class gets its configuration object (
jb_agent_cfg
) from the configuration database (lines 19 to 22). - If the configuration object indicates the agent is active, then the agent creates a sequencer and a driver (lines 24 to 27).
- A functional coverage subscriber is created if the configuration object indicates to do so (lines 29 to 31).
- The port of the driver and the export of the sequencer are connected if the agent is active. The virtual interface of the driver is also connected (lines 42 to 45).
- The analysis port of the agent and the analysis export of the functional-coverage subscriber are connected if the functional-coverage subscriber exists (lines 47 to 49).
class jelly_bean_agent extends uvm_agent;
`uvm_component_utils( jelly_bean_agent )
jelly_bean_agent_config jb_agent_cfg;
jelly_bean_sequencer jb_seqr;
jelly_bean_driver jb_drvr;
jelly_bean_monitor jb_mon;
jelly_bean_fc_subscriber jb_fc_sub;
uvm_analysis_port#( jelly_bean_transaction ) jb_ap;
function new( string name, uvm_component parent );
super.new( name, parent );
endfunction: new
function void build_phase( uvm_phase phase );
super.build_phase( phase );
if ( ! uvm_config_db#( jelly_bean_agent_config )::get
( .cntxt( this ), .inst_name( "" ), .field_name( "jb_agent_cfg" ), .value( jb_agent_cfg ) ) ) begin
`uvm_error( "jelly_bean_agent", "jb_agent_cfg not found" )
end
if ( jb_agent_cfg.active == UVM_ACTIVE ) begin
jb_seqr = jelly_bean_sequencer::type_id::create( .name( "jb_seqr" ), .parent( this ) );
jb_drvr = jelly_bean_driver ::type_id::create( .name( "jb_drvr" ), .parent( this ) );
end
if ( jb_agent_cfg.has_jb_fc_sub ) begin
jb_fc_sub = jelly_bean_fc_subscriber::type_id::create( .name( "jb_fc_sub" ), .parent( this ) );
end
jb_mon = jelly_bean_monitor::type_id::create( .name( "jb_mon" ), .parent( this ) );
endfunction: build_phase
function void connect_phase( uvm_phase phase );
super.connect_phase( phase );
jb_mon.jb_if = jb_agent_cfg.jb_if;
jb_ap = jb_mon.jb_ap;
if ( jb_agent_cfg.active == UVM_ACTIVE ) begin
jb_drvr.seq_item_port.connect( jb_seqr.seq_item_export );
jb_drvr.jb_if = jb_agent_cfg.jb_if;
end
if ( jb_agent_cfg.has_jb_fc_sub ) begin
jb_ap.connect( jb_fc_sub.analysis_export );
end
endfunction: connect_phase
endclass: jelly_bean_agent
Sequence Diagram
The following sequence diagram summarizes the configuration process described above.
Sequence Diagram of the Build Phases
I hope this tutorial helped you to understand the UVM configuration process.