What is a Configuration Object in UVM & What is its utility..??
We heard about “Configuration Object“ and its usage/requirement in many places inside an UVM based Verification Environment. So today I’ll try to introduce about the concept of “Configuration Objects” & What is the utility behind using a Configuration Object inside an UVM Testbench.
As shown in my previous post, named as “UVM Components“ – there are static and dynamic components as part of the Testbench. Among those static components, UVM Architecture contains an important physical component that is called “Agent“. UVM Agent can be thought of as “Verification Component” dedicated for a specific logical interface to the DUT.
An Agent fundamentally contains the Driver, Sequencer & a Monitor. In addition, it may also contain the components related to functional coverage monitors or Scoreboard. An Analysis port used to be present on an Agent which is connected to the analysis port on the monitor which makes it possible to connect any external analysis component without the knowledge of Agent’s implementation.
The most important part is – the structure of an Agent is dependent on its configuration. Configuration can differ from one test to the another using a different configuration object for the same Agent. So we can say that “Configuration Object” places a very significant role in the structure of an Agent. Let’s see what all kind of details can be contained with-in a configuration object of an Agent.
- Sub-components to be created under an Agent
- The handle of the virtual interface for the Driver and Monitor to interface with the DUT.
- Functional behavior/configuration of the Agent.
Let us elaborate above points a bit more as follows:
- As per UVM convention, any Agent can act in ACTIVE or PASSIVE mode. In ACTIVE mode, Driver and Sequencer are constructed, yet in PASSIVE mode neither Driver nor Sequencer is constructed. Usually in PASSIVE mode, a Monitor is constructed. Selection between ACTIVE or PASSIVE mode of an agent can be done by a parameter named “active” which is a variable of enum type uvm_active_passive_enum ( with values as UVM_ACTIVE & UVM_PASSIVE). Default value of active is UVM_ACTIVE.
- The use of virtual interface using the Configuration Object has been shown in very details in one of my post called “Application of Virtual Interface and uvm_config_db“, please refer it for detailed explanation.
- To find out the functional coverage related to the Agent’s functionality, there may be a functional coverage monitor implemented as a sub-component of Agent or may not. It depends on the Testbench Architecture. The good part is – this behavior is controlled using a bit type variable named “has_functional_coverage” (meaningful name) inside a Configuration class/object that is being used for that Agent (We’ll see the application in the written UVM Configuration class code). Configuration object also plays a significant role in defining other features of the Agent & how an Agent behaves or configured with respect to certain testcase/scenario generation.
Interesting thing is – different test mode can easily configure Agent as per the configuration needs. For example, in case of APB bus – configuration object may have variables to define the memory map and determining which PSELx lines will be activated for which addresses.
In the provided code below, it is shown how a configuration class i.e. “ahb_master_config“ contains different variables which impacts the Agent configuration on the fly when configuration object propagated from the top by the testcase. Configuration object i.e. “m_cfg“ once received successfully inside the build_phase of Master Agent, it helps to decide the construction of the Agent’s sub-components and connections between built created sub-components.
Configuration Class & Agent Class Code:
////////// AHB Master Configuration Class //////////// class ahb_master_config extends uvm_object; `uvm_object_utils(ahb_master_config) /////// Virtual Interface Declaration \\\\\\\\ virtual ahb_if ahb_vi; /////// Constructor //////// function new (string name = "ahb_master_config"); super.new(name); endfunction: new /////// Different Variables Declaration //////// /// Agent Active or Passive /// uvm_active_passive_enum active = UVM_ACTIVE /// Agent Include Functional Coverage Monitor /// bit has_functional_coverage = 0; /// Agent Include Scoreboard /// bit has_scoreboard = 0; /// Address Decode For the Select Lines /// logic[31:0] start_address[15:0]; logic[31:0] r_data[15:0] int no_select_lines = 1; endclass: ahb_master_config ///////////// AHB Test Class ///////////// class ahb_master_test extends uvm_test; `uvm_component_utils(ahb_master_test) ahb_master_config m_cfg; ... ... function void build_phase(uvm_phase phase); super.build_phase(phase); uvm_config_db #(ahb_master_config)::set(this,"*", "ahb_master_config", m_cfg); endfunction: build_phase endclass: ahb_master_test /////////// AHB Master Agent Class //////////// class ahb_master_agent extends uvm_component; `uvm_component_utils(ahb_master_agent) /// Configuration Object Instantiation /// ahb_master_config m_cfg; /// Sub-component Instantiation /// ahb_driver m_driver; ahb_sequencer m_sequencer; ahb_monitor m_monitor; ahb_coverage_monitor m_func_monitor; uvm_analysis_port #(my_txn) ap; /// Standard UVM Methods /// extern function new(string name, uvm_component parent); extern function void build_phase(uvm_phase phase); extern function void connect_phase(uvm_phase phase); endclass: ahb_master_agent /// Constructor Method /// function ahb_master_agent::new(string name, uvm_component parent); super.new(name, parent); endfunction: ahb_master_agent /// Build Method /// function ahb_master_agent::build_phase(uvm_phase phase); super.build_phase(phase); if (!uvm_config_db #(ahb_master_config)::get(this,"*","ahb_master_config", m_cfg)) `uvm_fatal("FATAL MSG", Configuration object is not set properly"); /// Monitor will Always Be Available /// m_monitor = ahb_monitor::type_id::create("m_monitor", this); //// Driver & Sequencer will be Implemented only in ACTIVE mode if (m_cfg.active == UVM_ACTIVE) begin m_driver = ahb_driver::type_id::create("m_driver", this); m_sequencer = ahb_sequencer::type_id::create("m_sequencer", this); end /// Functional Coverage Monitor? /// if(m_cfg.has_functional_coverage) begin m_func_monitor = ahb_coverage_monitor::type_id::create("m_func_monitor", this); end endfunction: build_phase /// connect_phase /// function void ahb_master_agent::connect_phase(uvm_phase phase); m_monitor.ahb_vi = m_cfg.ahb_vi; ap = m_monitor.ap; /// Connect Driver and Sequencer only in ACTIVE mode /// if(m_cfg.active == UVM_ACTIVE) begin m_driver.seq_item_port.connect(m_sequencer.seq_item_export); m_driver.ahb_vi = m_cfg.ahb_vi; end /// Connect analysis_port (Monitor) to analysis_export (Functional Coverage) /// if (m_cfg.has_functional_coverage) begin m_monitor.ap.connect(m_func_monitor.analysis_export); end endfunction: connect_phase
With that, I conclude this blog, introductory information on the Configuration Object Concept in UVM and its utility/application in the UVM Testbenches. I hope you found it useful & helpful, please feel free to provide your feedback & suggestions for new topics.
I wish to see you again soon..Till then..Take care! Bye