What is uvm_component ?

uvm_component is a fundamental base class that serves as the foundation for all UVM components like drivers, monitors and scoreboards in a verification environment. It has the following features:

  • Hierarchy: Supports a hierarchical structure, where each component can have child components, forming a tree-like structure and provides methods for searching and traversing the tree.
  • Phasing: Components can participate in the UVM phasing mechanism, which organizes the simulation into different phases like build, connect, run, and cleanup. Components can perform specific tasks during each phase.
  • Reporting: Components can use the UVM messaging infrastructure to report events, warnings, and errors during simulation.
  • Factory: Components can be registered with the UVM factory mechanism, enabling dynamic object creation and lookup.

Class Hierarchy

uvm_component_class_hier

All major verification testbench components like drivers, monitors, and scoreboards are derivatives of the uvm_component class and inherit standard phasing and reporting mechanisms of the UVM framework.

UVM components use the phasing mechanism to perform initialization and configuration tasks in a specific order. Phases allow components to construct their internal hierarchy, connect to other components, and finalize their configurations before the actual test starts. This ensures a well-structured setup and configuration process.

uvm_component_uvc

Class Declaration


virtual class uvm_component extends uvm_report_object;

uvm_component class definition provides many functions and tasks that are used to find, build, connect, and configure sub-components. They are broadly categorized into those which support navigating the component hierarchy, controlling the phases that govern how a testbench should be built and run, and allowing reusability of testbench using a factory.

Hierarchy Methods


	// Get handle to parent uvm component that instantiated current component
	extern virtual function uvm_component get_parent ();

	// Get full hierarchical name of the current component
	extern virtual function string get_full_name ();

	// Get queue of all children instantiated under current component, not nested
 	extern function void get_children(ref uvm_component children[$]);

	// Get handle to a child by the given name
 	extern function uvm_component get_child (string name);

	// Used in an iterable to get the next child component handle
  	extern function int get_next_child (ref string name);
	
	// Get the first child handle under current component
  	extern function int get_first_child (ref string name);
	
	// Get total number of child components, not nested
  	extern function int get_num_children ();

	// Returns 1 if there is atleast one component instantiated 
  	extern function int has_child (string name);

	// Get nested child component handle by hierarchical path
  	extern function uvm_component lookup (string name);

	// Depth of the current component from uvm_top=0, uvm_test=1, etc
  	extern function int unsigned get_depth();

Consider the following example of a hierarchical instantiation of component classes and standard functions in uvm_component can be used to find its children.

uvm_component_hier_example


class apb_monitor extends uvm_component;

class apb_agent extends uvm_component;
	apb_monitor	 	m_apb_monitor;
endclass

class child_comp extends uvm_component;

class top_comp extends uvm_component;

	// Child component instantiations
	child_comp 		m_child_comp_sa [5];
	apb_agent 		m_apb_agent;


  	virtual function void end_of_elaboration_phase(uvm_phase phase);

		// Local variables to hold temporary results 
    	uvm_component 	l_comp_h;
    	uvm_component 	l_comp_q [$];

		// Get parent and print it
    	l_comp_h = get_parent();
    	`uvm_info ("tag", $sformatf("get_parent=%s", l_comp_h.get_name()), UVM_LOW)
    
		// Get all children and print them
    	get_children(l_comp_q);
    	foreach (l_comp_q[i]) 
      		`uvm_info ("tag", $sformatf("child_%0d = %s", i, l_comp_q[i].get_name()), UVM_LOW)
    	
    	// Get a specific child component and print name
    	l_comp_h = get_child("m_child_comp_2");
    	`uvm_info ("tag", $sformatf("Found %s", l_comp_h.get_name()), UVM_LOW)
    
		// Other function calls
    	`uvm_info ("tag", $sformatf("number of children = %0d", get_num_children()), UVM_LOW)
	    `uvm_info ("tag", $sformatf("has_child('abc') = %0d", has_child("abc")), UVM_LOW)
    	`uvm_info ("tag", $sformatf("has_child('m_apb_agent') = %0d", has_child("m_apb_agent")), UVM_LOW)
    	`uvm_info ("tag", $sformatf("get_depth = %0d", get_depth()), UVM_LOW)
    
		// Lookup failure will result in a warning
    	l_comp_h = lookup("m_apb_monitor");
    	if (l_comp_h) 
      		`uvm_info ("tag", $sformatf("Found %s", l_comp_h.get_full_name()), UVM_LOW)
      
		// Lookup requires full hierarchical path to the component
      	l_comp_h = lookup("m_apb_agent.m_apb_monitor");
    	if (l_comp_h) 
      		`uvm_info ("tag", $sformatf("Found %s", l_comp_h.get_full_name()), UVM_LOW)    
    
  	endfunction	
endclass
 Simulation Log
UVM_INFO @ 0: reporter [RNTST] Running test my_test...
UVM_INFO testbench.sv(67) @ 0: uvm_test_top.m_top_comp [tag] get_parent=uvm_test_top
UVM_INFO testbench.sv(71) @ 0: uvm_test_top.m_top_comp [tag] child_0 = m_apb_agent
UVM_INFO testbench.sv(71) @ 0: uvm_test_top.m_top_comp [tag] child_1 = m_child_comp_0
UVM_INFO testbench.sv(71) @ 0: uvm_test_top.m_top_comp [tag] child_2 = m_child_comp_1
UVM_INFO testbench.sv(71) @ 0: uvm_test_top.m_top_comp [tag] child_3 = m_child_comp_2
UVM_INFO testbench.sv(71) @ 0: uvm_test_top.m_top_comp [tag] child_4 = m_child_comp_3
UVM_INFO testbench.sv(71) @ 0: uvm_test_top.m_top_comp [tag] child_5 = m_child_comp_4
UVM_INFO testbench.sv(75) @ 0: uvm_test_top.m_top_comp [tag] Found m_child_comp_2
UVM_INFO testbench.sv(77) @ 0: uvm_test_top.m_top_comp [tag] number of children = 6
UVM_INFO testbench.sv(79) @ 0: uvm_test_top.m_top_comp [tag] has_child('abc') = 0
UVM_INFO testbench.sv(80) @ 0: uvm_test_top.m_top_comp [tag] has_child('m_apb_agent') = 1
UVM_INFO testbench.sv(81) @ 0: uvm_test_top.m_top_comp [tag] get_depth = 2
UVM_WARNING /xcelium20.09/tools//methodology/UVM/CDNS-1.2/sv/src/base/uvm_component.svh(2017) @ 0: uvm_test_top.m_top_comp [Lookup Error] Cannot find child m_apb_monitor
UVM_INFO testbench.sv(89) @ 0: uvm_test_top.m_top_comp [tag] Found uvm_test_top.m_top_comp.m_apb_agent.m_apb_monitor

Phasing Methods

Components provide virtual methods for each UVM phase that can be overridden by child classes to implement functionality. For example, all testbench components are instantiated in their respective build_phase, connected in connect_phase and simulated in run_phase. There are a lot more pre-defined UVM phases that can be used to implement functionality.


// This child class implements only build_phase and connect_phase methods
// of uvm_component, while rest of the phase methods are left untouched and 
// depends on their definitions inside uvm_component

class apb_agent extends uvm_component; 

	...
	apb_monitor 	m_monitor;
	apb_driver 		m_driver;
	apb_sequencer 	m_seqr;

	// All children of this component are instantiated in build_phase
	virtual function void build_phase(uvm_phase phase);
		super.build_phase(phase);

		// obj_type::type_id::create is a way to get an object of 
		// the type "obj_type" from the factory

		m_monitor 	= apb_monitor::type_id::create("m_monitor", this);
		m_driver 	= apb_driver::type_id::create("m_driver", this);
		m_seqr 		= apb_sequencer::type_id::create("m_seqr", this);
	endfunction

	// Child components can be connected together in connect_phase
	virtual function void connect_phase(uvm_phase phase);
		super.connect_phase(phase);
	
		// Connect sequencer with driver
		m_driver.seq_item_port.connect(m_seqr.seq_item_export);
	endfunction

endclass

Read more about UVM Phases !

Factory Methods

Components are registered with the factory using a specific macro intended to be used only by components. Note that UVM object class definitions use the macro `uvm_object_utils.


class my_comp extends uvm_component;
	
	// Register with factory
	`uvm_component_utils (my_comp)

	function new(string name = "my_comp", uvm_component parent = null);
		super.new(name, parent);
	endfunction

	...

endclass

Factory is a singleton object and there is only one instance of the factory in a UVM environment. uvm_component provide a set of convenience functions that call the uvm_factory member functions with a simplified interface.


// For example, "set_type_override_by_type" is actually a function defined in the class uvm_factory
// A wrapper function with the same name is defined in uvm_component class and arguments are passed
// to the uvm_factory function call

function void uvm_component::set_type_override_by_type (uvm_object_wrapper original_type,
                                                        uvm_object_wrapper override_type,
                                                        bit    replace=1);

	// Get handle to the factory instance in UVM environment
	uvm_coreservice_t cs = uvm_coreservice_t::get();                                                     
  	uvm_factory factory = cs.get_factory();

	// Pass the same arguments to the factory function call
   	factory.set_type_override_by_type(original_type, override_type, replace);
endfunction 

// Usage : "m_comp" is a uvm_component class handle
m_comp.set_type_override_by_type(apb_driver::get_type(), apb_driver_2::get_type());

Read more about UVM Factory !