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
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
Steps to create a UVM agent1. Create a custom class inherited from
uvm_agent, register with factory and call
2. Instantiate agent components
// 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
3. Instantiate and build 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;
4. Connect agent components together
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
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
- Agent being a component, is registered with the factory using macro
- Instantiations m_seqr0, m_drv0, m_mon0 are sequencer, driver and monitor respectively
uvm_active_passive_enumis 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.
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
get_is_active()method which will return the state of an agent.