This tutorial gives you the opportunity to experiment with an example that contains multiple agents and interfaces, and in particular to explore the features of the code generator that give you a degree of control over the hierarchical structure of the verification environment. By setting parameters in the code generator template files you can, amongst other things, set agents to run in active or passive mode and select whether each agent is instantiated in its own environment, in the top-level environment, or on the same environment as another agent. This tutorial also illustrates how the code generator uses configuration objects.
You can download the files for this example. The files are in the directory named ./multi_if.
The example used in this tutorial has four interfaces/agents, two of which use the register layer (bus1 and bus2) and two of which do not (clkndata and serial). The interface template file for each of these four interfaces includes the following lines, initially all commented out:
Filename *.tpl
#uvm_seqr_class = yes #agent_is_active = UVM_PASSIVE #agent_has_env = yes #additional_agent = serial
The parameter uvm_seqr_class can be set to yes or no. The default is no, which means that the sequencer type for this particular agent will be defined using a simple typedef:
Filename clkndata_sequencer.sv
typedef uvm_sequencer #(data_tx) clkndata_sequencer_t;
If the parameter is set to yes, the code generator will create a new sequencer class:
Filename clkndata.tpl
... uvm_seqr_class = yes ...
Filename clkndata_sequencer.sv
class clkndata_sequencer extends uvm_sequencer #(data_tx); `uvm_component_utils(clkndata_sequencer) ... endclass
You could modify or extend this sequencer class, although you would have to take steps to ensure that the modified code is not overwritten when the code generator is re-run. (Unlike some other parts of the generated code, these particular changes would not be preserved automatically when the code generator is re-run.)
The parameter agent_is_active can be set to UVM_ACTIVE or UVM_PASSIVE. The default is UVM_ACTIVE. This flag gives us the opportunity to take a look at how the code generator deals with the configuration of generated components. Setting the agent_is_active flag to UVM_PASSIVE has the direct effect of modifying the value of a field in a top-level configuration object that is set from the top-level module and is used to configure the top-level environment.
Filename clkndata.tpl
... agent_is_active = UVM_PASSIVE ...
Filename top_tb.sv
module top_tb; ... top_config env_config; initial begin env_config = new("env_config"); ... env_config.is_active_clkndata = UVM_PASSIVE; ... uvm_config_db #(top_config)::set(null, "uvm_test_top.m_env", "config", env_config);
As you can see above, there is a single configuration object that is used to configure the entire top-level verification environment. The values of the appropriate fields will be copied from the top-level configuration object to the configuration objects for the individual agents, clkndata in this case, in the build_phase method of the env:
Filename top_env.sv
class top_env extends uvm_env; ... clkndata_config m_clkndata_config; top_config m_config; ... endclass function void top_env::build_phase(uvm_phase phase); if (!uvm_config_db #(clkndata_config)::get(this, "", "config", m_config)) ... m_clkndata_config = new("m_clkndata_config"); ... m_clkndata_config.is_active = m_config.is_active_clkndata; ... uvm_config_db #(clkndata_config)::set(this, "m_clkndata_agent", "config", m_clkndata_config);
The build_phase method above is getting values from its own configuration object (m_config) and is setting those same values in the configuration objects of its children (such as m_clkndata_config). In other words, the values of the configuration parameters are being passed down through the UVM component hierarchy encapsulated within configuration objects that are associated with the components.
Finally, the value of this field is retrieved from the configuration database by the agent and is checked to determine whether the agent is to be built in active or passive mode. This is done by overriding the get_is_active method of the agent:
Filename clkndata_agent.sv
function void clkndata_agent::build_phase(uvm_phase phase); if (!uvm_config_db #(clkndata_config)::get(this, "", "config", m_config)) ... ... if (get_is_active() == UVM_ACTIVE) begin m_driver = clkndata_driver::type_id::create("m_driver", this); m_sequencer = clkndata_sequencer::type_id::create("m_sequencer", this); end endfunction
function uvm_active_passive_enum clkndata_agent::get_is_active(); ... m_is_active = m_config.is_active; ... return uvm_active_passive_enum'(m_is_active); endfunction
The parameter agent_has_env can be set to yes or no. The default is no, which means that the this particular agent will be instantiated from the top-level environment, unless it is accessed through a register model, in which case it will be instantiated within its own environment regardless of the value of this flag. If agent_has_env is set to yes, the agent will be instantiated in its own environment, which will be instantiated from the top-level environment.
By default, agents that are accessed through a register model will be instantiated in their own environments and all other agents will be instantiated from the top-level env:
Filename top_env.sv
class top_env extends uvm_env: ... // Child environments and associated objects bus1_env m_bus1_env; bus2_env m_bus2_env; bus1_env_config m_bus1_env_config; bus2_env_config m_bus2_env_config; // Child agents and associated objects clkndata_config m_clkndata_config; clkndata_agent m_clkndata_agent; clkndata_coverage m_clkndata_coverage; serial_config m_serial_config; serial_agent m_serial_agent; serial_coverage m_serial_coverage; ... endclass
Setting agent_has_env to yes in the corresponding template file will move the clkndata agent into its own env:
Filename clkndata.tpl
... agent_has_env = yes ...
Filename top_env.sv
class top_env extends uvm_env: ... // Child environments and associated objects clkndata_env m_clkndata_env; bus1_env m_bus1_env; bus2_env m_bus2_env; clkndata_config m_clkndata_config; bus1_env_config m_bus1_env_config; bus2_env_config m_bus2_env_config; // Child agents and associated objects serial_config m_serial_config; serial_agent m_serial_agent; serial_coverage m_serial_coverage; ... endclass
If we now do the same for the one remaining agent (serial) then every agent will be put into its own env:
Filename serial.tpl
... agent_has_env = yes ...
Filename top_env.sv
class top_env extends uvm_env: ... // Child environments and associated objects clkndata_env m_clkndata_env; bus1_env m_bus1_env; bus2_env m_bus2_env; serial_env m_serial_env; clkndata_config m_clkndata_config; bus1_env_config m_bus1_env_config; bus2_env_config m_bus2_env_config; serial_config m_serial_config; ... endclass
Note that in each case the top-level env creates a separate configuration object for each child env or agent. We saw earlier in this tutorial that the top-level env copies configuration parameters from its own configuration object to the configuration objects belonging to the individual agents.
If an agent has its own environment, the parameter additional_agent can be used to specify an additional agent to be instantiated in that environment, as opposed to being instantiated in its own environment or being instantiated at the top-level. An environment can instantiate any number of additional agents, each specified using its own additional_agent line in the template file. Note that an environment that instantiates a register model cannot contain additional agents.
For example, assuming that serial does not have its own env, it can be instantiated in the env of the clkndata agent:
Filename clkndata.tpl
... agent_has_env = yes additional_agent = serial ...
Filename clkndata_env.sv
class clkndata_env extends uvm_env: ... clkndata_config m_clkndata_config; clkndata_agent m_clkndata_agent; clkndata_coverage m_clkndata_coverage; serial_config m_serial_config; serial_agent m_serial_agent; serial_coverage m_serial_coverage; ... endclass
You can now experiment with the values of the above parameters in the four template files bus1.tpl, bus2.tpl, clkndata.tpl, and serial.tpl, re-running the code generator after each change to see their effect. You need to be aware that additional_agent has several limitations, as described in the Reference Guide.