What is a UVM agent ?
An agent encapsulates a Sequencer, Driver and Monitor into a single entity by instantiating and connecting the components together via TLM interfaces. Since UVM is all about configurability, an agent can also have configuration options like the type of UVM agent (active/passive), knobs to turn on features such as functional coverage, and other similar parameters.
Types of Agents
Active |
|
Passive |
|
How to find out if a UVM agent is active or passive ?
User-defined agent classes derived from uvm_agent
also have another function called get_is_active()
which will return the state of the requested UVM agent.
// Assume this is inside the user-defined agent class
if (get_is_active()) begin
// Build driver and sequencer
end
// Build monitor
Class Hierarchy
Steps to create a UVM agent
1. Create a custom class inherited fromuvm_agent
, register with factory and callnew
// my_agent is user-given name for this class that has been derived from "uvm_agent"
class my_agent extends uvm_agent;
// [Recommended] Makes this agent more re-usable
`uvm_component_utils (my_agent)
// This is standard code for all components
function new (string name = "my_agent", uvm_component parent = null);
super.new (name, parent);
endfunction
// Code for rest of the steps come here
endclass
2. Instantiate agent components
// Create handles to all agent components like driver, monitor and sequencer
// my_driver, my_monitor and agent_cfg are custom classes assumed to be defined
// Agents can be configured via a configuration object that can be passed in from the test
my_driver m_drv0;
my_monitor m_mon0;
uvm_sequencer #(my_data) m_seqr0;
agent_cfg m_agt_cfg;
3. Instantiate and build components
virtual function void build_phase (uvm_phase phase);
// If this UVM agent is active, then build driver, and sequencer
if (get_is_active()) begin
m_seqr0 = uvm_sequencer#(my_data)::type_id::create ("m_seqr0", this);
m_drv0 = my_driver::type_id::create ("m_drv0", this);
end
// Both active and passive agents need a monitor
m_mon0 = my_monitor::type_id::create ("m_mon0", this);
//[Optional] Get any agent configuration objects from uvm_config_db
endfunction
4. Connect agent components together
virtual function void connect_phase (uvm_phase phase);
// Connect the driver to the sequencer if this agent is Active
if (get_is_active())
m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);
endfunction
What does a UVM agent do ?
Usually, it makes sense to create an agent that provides protocol specific tasks to generate transactions, check the results and perform coverage. For example, a UVM agent can be created for the WishBone protocol whose sequencer will generate data items which can be sent to the driver. The driver then converts the data item class object into actual pin level signals and drive them to the DUT. The monitor may passively collect the outputs from the DUT, convert them back into another data item class object and distribute it among all the components in the testbench waiting for the item.

How to configure a UVM agent as active or passive ?
A user-defined agent derived from uvm_agent
has an internal variable called is_active which is of enum type uvm_active_passive_enum
. By default, all agents are active.
// Set the configuration called "is_active" to the agent's path to mark the given agent as passive
uvm_config_db #(int) :: set (this, "path_to_agent", "is_active", UVM_PASSIVE);
// Set the configuration called "is_active" to the agent's path to mark the given agent as active
uvm_config_db #(int) :: set (this, "path_to_agent", "is_active", UVM_ACTIVE);
Example of a UVM Agent
Each agent should have a configuration object which will contain a reference to the virtual interface that can be used by its driver and monitor to access pin level signals. This object can also contain other data members which will control which of the agents sub-components are built and it may also contain information that affects the behavior of the agents components. Some of the other functionalities that can be included are :
- Functional coverage monitor to collect protocol information
- Scoreboard if required to check protocol data
- API sequences that might be useful for implementing an API layer
class my_agent extends uvm_agent;
`uvm_component_utils (my_agent)
my_driver m_drv0;
my_monitor m_mon0;
uvm_sequencer #(my_data) m_seqr0;
agent_cfg m_agt_cfg;
function new (string name = "my_agent", uvm_component parent=null);
super.new (name, parent);
endfunction
// If Agent is Active, create Driver and Sequencer, else skip
// Always create Monitor regardless of Agent's nature
virtual function void build_phase (uvm_phase phase);
super.build_phase (phase);
uvm_config_db #(agent_cfg) :: get (this, "*", "agt_cfg", m_agt_cfg);
if (get_is_active()) begin
m_seqr0 = uvm_sequencer#(my_data)::type_id::create ("m_seqr0", this);
m_drv0 = my_driver::type_id::create ("m_drv0", this);
m_drv0.vif = m_agt_cfg.vif;
end
m_mon0 = my_monitor::type_id::create ("m_mon0", this);
m_mon0.vif = m_agt_cfg.vif;
endfunction
// Connect Sequencer to Driver, if the agent is active
virtual function void connect_phase (uvm_phase phase);
if (get_is_active())
m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);
endfunction
endclass
Note the following:
- An agent should be derived from the uvm class
uvm_agent
- Agent being a component, is registered with the factory using macro
`uvm_component_utils
- Instantiations m_seqr0, m_drv0, m_mon0 are sequencer, driver and monitor respectively
uvm_active_passive_enum
is a UVM enum declaration that stores UVM_ACTIVE or UVM_PASSIVE. This is usually used to configure the agent to be either active/passive- In the build_phase(), sequencer and driver are created only if the agent is configured to be active. The variable is_active can be set either at environment level or via a configuration object retrieved from resource database.
- Similarly, connection between sequencer and driver needs to be done only if they were built during the build phase.
Recommended Practice
It is recommended to derive your own agents from the base class uvm_agent
for the following reasons :
- Allows you to distinguish agents from other component types also using its inheritance
- Any future changes and additions to the base UVM agent class will be automatically included in your derived class
- Has
get_is_active()
method which will return the state of an agent.