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 agent (active/passive), knobs to turn on features such as functional coverage, and other similar parameters.


Usually, it makes sense to create an agent that provides protocol specific tasks to generate transactions, check the results and perform coverage. For example, an 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.

There are two operating modes of an agent:

  • Instantiates all three components [Sequencer, Driver, Monitor]
  • Enables data to be driven to DUT via driver
  • Only instantiate the monitor
  • Used for checking and coverage only
  • Useful when there's no data item to be driven to DUT

Now, let's look at how an agent looks like in real code.

   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); (name, parent);
      // 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;
         m_mon0 = my_monitor::type_id::create ("m_mon0", this);
         m_mon0.vif = m_agt_cfg.vif;
    // 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);

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.

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

Other Details

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.

Refer Class Definitions for more information.

Was this article helpful ?