UVM factory is a mechanism to improve flexibility and scalability of the testbench by allowing the user to substitute an existing class object by any of its inherited child class objects.

For this purpose, the factory needs to know all the types of classes created within the testbench by a process called as registration. There are UVM macros that allow classes to be registered with the factory, and methods that allow certain types and instances of class objects to be overridden by its derived types.

factory

Factory Override Methods

  
  
// Override all the objects of a particular type
set_type_override_by_type ( uvm_object_wrapper original_type, 
                            uvm_object_wrapper override_type, 
                            bit replace=1);
 
set_type_override_by_name ( string original_type_name,
                            string override_type_name,
                            bit replace=1);
 
// Override a type within a particular instance
set_inst_override_by_type (uvm_object_wrapper original_type,
                           uvm_object_wrapper override_type,
                           string full_inst_path);
 
set_inst_override_by_name (string original_type_name,
                           string override_type_name,
                           string full_inst_path);

  

Method Examples

Shown below is an example testbench structure comprising of a base agent inside a base environment that is instantiated within the test class. We will try to use factory override methods to override the base class with child class.

  
  
// Define a base class agent
class base_agent extends uvm_agent;
  `uvm_component_utils(base_agent)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
endclass

// Define child class that extends base agent
class child_agent extends base_agent;
  `uvm_component_utils(child_agent)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
endclass

// Environment contains the agent
class base_env extends uvm_env;
  `uvm_component_utils(base_env)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  // 'm_agent' is a class handle to hold base_agent
  // type class objects
  base_agent m_agent;
  
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    
    // Use create method to request factory to return a base_agent
    // type of class object
    m_agent = base_agent::type_id::create("m_agent", this);
    
    // Now print the type of the object pointing to by the 'm_agent' class handle
    `uvm_info("AGENT", $sformatf("Factory returned agent of type=%s, path=%s", m_agent.get_type_name(), m_agent.get_full_name()), UVM_LOW)
  endfunction
endclass

  

1. Type override by Type/Name

  
  
class base_test extends uvm_test;
  `uvm_component_utils(base_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  base_env m_env;
  
  virtual function void build_phase(uvm_phase phase);
  	// Get handle to the singleton factory instance
    uvm_factory factory = uvm_factory::get();
    
    super.build_phase(phase);

    // Set factory to override 'base_agent' by 'child_agent' by type
    set_type_override_by_type(base_agent::get_type(), child_agent::get_type());
    
    // Or set factory to override 'base_agent' by 'child_agent' by name
    // factory.set_type_override_by_name("base_agent", "child_agent");
    
    // Print factory configuration
    factory.print();
    
    // Now create environment 
    m_env = base_env::type_id::create("m_env", this);
  endfunction
endclass

  

The following log shows that a type override is configured and replaces base_agent with child_agent. This is also evident from the info statement that prints type of the returned object.

Simulation Log

UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_factory.svh(1645) @ 0: reporter [UVM/FACTORY/PRINT] 
#### Factory Configuration (*)

No instance overrides are registered with this factory

Type Overrides:

  Requested Type  Override Type
  --------------  -------------
  base_agent      child_agent

All types registered with the factory: 54 total
  Type Name
  ---------
  base_agent
  base_env
  base_test
  child_agent
(*) Types with no associated type name will be printed as 

####


UVM_INFO testbench.sv(32) @ 0: uvm_test_top.m_env [AGENT] Factory returned agent of type=child_agent, path=uvm_test_top.m_env.m_agent
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

2. Instance override by Type/Name

This is most useful when only a few instances of the given type has to be overriden. A type override by type will replace all instances of the given type by the new type.

  
  
class base_test extends uvm_test;
  `uvm_component_utils(base_test)
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  base_env m_env;
  
  virtual function void build_phase(uvm_phase phase);
  	// Get handle to the singleton factory instance
    uvm_factory factory = uvm_factory::get();
    
    super.build_phase(phase);

    // Set factory to override all instances under m_env of type 'base_agent' by 'child_agent'
    set_inst_override_by_type("m_env.*", base_agent::get_type(), child_agent::get_type());
    
    // Or set factory to override all instances under 'm_env' called 'base_agent' by 'child_agent' by name
    // factory.set_inst_override_by_name("base_agent", "child_agent", {get_full_name(), ".m_env.*"});
    
    // Print factory configuration
    factory.print();
    
    // Now create environment 
    m_env = base_env::type_id::create("m_env", this);
  endfunction
endclass

  
Simulation Log

UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_factory.svh(1645) @ 0: reporter [UVM/FACTORY/PRINT] 
#### Factory Configuration (*)

Instance Overrides:

  Requested Type  Override Path         Override Type
  --------------  --------------------  -------------
  base_agent      uvm_test_top.m_env.*  child_agent

No type overrides are registered with this factory

All types registered with the factory: 54 total
  Type Name
  ---------
  base_agent
  base_env
  base_test
  child_agent
(*) Types with no associated type name will be printed as 

####


UVM_INFO testbench.sv(32) @ 0: uvm_test_top.m_env [AGENT] Factory returned agent of type=child_agent, path=uvm_test_top.m_env.m_agent
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER] 
--- UVM Report Summary ---

Why is UVM factory required ?

The new function is used in SystemVerilog to create a class object and is perfectly valid to be used in UVM as well. Assume that an existing testbench uses Wishbone v1.0 protocol data packet class and is used throughout the testbench in components like driver, monitor, scoreboard, and many other sequences. If Wishbone v2.0 is released and the testbench is required to update and start using packet definition for the new protocol, there would be many places in the testbench that would require an update in code which can prove to be cumbersome.

  
  
class wb_seq extends uvm_sequence_item;
	...
	
	virtual task body();
		// new() function allocates space for the new class object
		// and assigns the handle 'm_wb_pkt' to new object
		wb_pkt m_wb_pkt = new();
		
		// This new object may be used everywhere in the sequence
		start_item(m_wb_pkt);
		m_wb_pkt.randomize();
		...
	endtask
	
endclass

  

UVM has a feature called factory which allows users to modify or substitute type of the item created by the factory without having to modify existing class instantiations. Instead of text substitution of class name of existing data packet, a child class object can be created that makes necessary modifications for 2.0 and the factory can be used to return the newly defined class object in all places within the testbench instead of the first one. So, the preferred method of object creation in UVM testbench is by using create method.

  
  
class wb_seq extends uvm_sequence_item;
	...
	
	virtual task body();
		// By calling create() method, the factory will create an instance of the requested 
		// type and assign the handle 'm_wb_pkt' to the new object. This override of type 
		// can be done at a higher level like the test class
		wb_pkt m_wb_pkt = wb_pkt::type_id::create("wb_pkt", this);
		
		// This new object may be used everywhere in the sequence
		start_item(m_wb_pkt);
		m_wb_pkt.randomize();
		...
	endtask
	
endclass

  

Practical Example

To illustrate the example of a factory override, we'll create an environment as shown above with a couple of different drivers, sequences, and data object types. The image shown above is the default configuration.

factory override default configuration
Components:
- Agents 
	- my_agent         (Base)
		1. my_agent_v2   (child)
- Drivers
	base_driver       (Base)
	 	1. eth_driver   (child)
		2. spi_driver   (child)
- Sequencer - my_seqeuncer
- Sequences
	- base_sequence   (Base)
		1. seq1         (child)
		2. seq2         (child)
		3. seq3         (child)
- Data
	- eth_packet        (Basse)
		- eth_v2_packet   (child)

Sequencer within m_agnt2 operate on seq3, while the other two operate on seq1 by default.
example inheritance diagram

Let us first define a base data packet and derive a child class for it.

  
  
//------------------ eth_packet-----------------------------------
class eth_packet extends uvm_sequence_item;
   `uvm_object_utils (eth_packet)

	...

   function new (string name = "eth_packet");
      super.new (name);
      `uvm_info (get_type_name(), "Packet created", UVM_MEDIUM)
   endfunction
endclass   

//------------------ eth_v2_packet-----------------------------------
class eth_v2_packet extends eth_packet; 
   `uvm_object_utils (eth_v2_packet)

 	...
   
   function new (string name = "eth_v2_packet");
      super.new (name);
   endfunction
endclass  

  

Now let us create a base driver class and extend two more driver classes eth_driver and spi_driver. To show the effect of a factory override, we'll keep these classes to have minimum data and methods.

  
  
//------------------ base_driver-----------------------------------

class base_driver #(type T=eth_packet) extends uvm_driver;
   `uvm_component_utils (base_driver #(T))

   T pkt;

   function new (string name, uvm_component parent);
      super.new (name, parent);
   endfunction

   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      pkt = T::type_id::create ("pkt0");
   endfunction

   virtual task run_phase (uvm_phase phase);
      super.run_phase (phase);
      `uvm_info (get_type_name(), $sformatf("Driver running ...with packet of type : %s", pkt.get_type_name()), UVM_MEDIUM)
   endtask

endclass

//----------------- eth_driver-----------------------------------

class eth_driver #(type T=eth_packet) extends base_driver #(T);
   `uvm_component_utils (eth_driver #(T))

   function new (string name, uvm_component parent);
      super.new (name, parent);
   endfunction
endclass

//----------------- spi_driver-----------------------------------

class spi_driver #(type T=eth_packet) extends base_driver #(T); 
   `uvm_component_utils (spi_driver #(T))

   function new (string name, uvm_component parent);
      super.new (name, parent);
   endfunction
endclass 

  

Coming to the sequences which will be executed by our sequencer

  
  
//----------------- base_sequence-----------------------------------
class base_sequence extends uvm_sequence;
	`uvm_object_utils (base_sequence)
endclass

//----------------- seq1 -------------------------------------------
class seq1 extends base_eth_sequence;
   `uvm_object_utils (seq1)
	...
endclass

//----------------- seq2 -------------------------------------------
class seq2 extends base_eth_sequence;
   `uvm_object_utils (seq2)
   ...
endclass

//----------------- seq3 -------------------------------------------
class seq3 extends base_eth_sequence;
   `uvm_object_utils (seq3)
	...
endclass

  

We'll create two agents as described below.

  
  
//----------------- my_agent -------------------------------------------
class my_agent extends uvm_agent;
   `uvm_component_utils (my_agent)

   base_driver    m_drv0;
   my_sequencer   m_seqr0;

   function new (string name, uvm_component parent);
      super.new (name, parent);
   endfunction

   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      m_drv0 = base_driver::type_id::create ("m_drv0", this);
      m_seqr0 = my_sequencer::type_id::create ("m_seqr0", this);
   endfunction

   virtual function void connect_phase (uvm_phase phase);
      super.connect_phase (phase);
      m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);
   endfunction
endclass

//----------------- my_agent_v2 -------------------------------------------
class my_agent_v2 extends uvm_agent;
   `uvm_component_utils (my_agent_v2)

   eth_driver     m_drv0;
   my_sequencer   m_seqr0;

   function new (string name, uvm_component parent);
      super.new (name, parent);
   endfunction

   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      m_drv0 = eth_driver::type_id::create ("m_drv0", this);
      m_seqr0 = my_sequencer::type_id::create ("m_seqr0", this);
   endfunction

   virtual function void connect_phase (uvm_phase phase);
      super.connect_phase (phase);
      m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);
   endfunction
endclass

  

Note that my_agent has base_driver, while my_agent_v2 contains eth_driver. All drivers and sequencers are instantiated within the build_phase() and connected in connect_phase().

The top container "my_env" is still left.

  
  
class my_env extends uvm_env ;
   `uvm_component_utils (my_env)

   my_agent       m_agnt0; 
   my_agent       m_agnt1; 
   my_agent_v2    m_agnt2;

   function new (string name, uvm_component parent);
      super.new (name, parent);
   endfunction : new

   virtual function void build_phase (uvm_phase phase);
      super.build_phase (phase);
      m_agnt0 = my_agent::type_id::create ("m_agnt0", this);
      m_agnt1 = my_agent::type_id::create ("m_agnt1", this);
      m_agnt2 = my_agent_v2::type_id::create ("m_agnt2", this);
   endfunction : build_phase
endclass : my_env

  

We have put our components into boxes, and arranged everything. It's time to write the test, and this is where factory override helps. We'll be able to substitute drivers, data packets and sequences by calling a few factory methods.

factory override configuration
  
  
   class feature_test extends base_test;
      `uvm_component_utils (feature_test)

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

      virtual function void build_phase (uvm_phase phase);
         super.build_phase (phase); 

`ifdef PKT_OVERRIDE
         // Substitute all eth_packets with eth_v2_packet
         set_type_override_by_type (eth_packet::get_type(), eth_v2_packet::get_type());
`endif



// These are the three different styles to override something

`ifdef DRV_STYLE1
         // Substitute all instances of base_driver with driver2 
         set_type_override_by_type (base_driver::get_type(), spi_driver::get_type());
`elsif DRV_STYLE2
         // Substitute only eth_driver in agnt2 with spi_driver - by calling the component to be replaced method
         eth_driver::type_id::set_inst_override (spi_driver::get_type(), "m_top_env.m_agnt2.m_drv0", this);
`elsif DRV_STYLE3
         // Substitute base_driver only in agnt0 - by calling the factory method
         factory.set_inst_override_by_type (base_driver::get_type(), eth_driver::get_type(), {get_full_name(), ".m_top_env.m_agnt0.*"});
`endif



// Trying to override a sequence

`ifdef SEQ_TYPE
         // Substitute seq1 with seq2
         set_type_override_by_type (seq1::get_type(), seq3::get_type());
`elsif SEQ_INST
         // Substitute seq1 with seq2 only for agnt1
         set_inst_override_by_type ("m_top_env.m_agnt1.m_seqr0.*", seq1::get_type(), seq2::get_type());
`else
`endif
         factory.print();
      endfunction
      // Enter test code for feature here
   endclass

  

These results are for running the test with defines PKT_OVERRIDE, DRV_STYLE3 and SEQ_INST. Also, note that the factory configuration is shown in the log.

Simulation Log

----------------------------------------------------------------
CDNS-UVM-1.1d (14.10-s013)
(C) 2007-2013 Mentor Graphics Corporation
(C) 2007-2013 Cadence Design Systems, Inc.
(C) 2006-2013 Synopsys, Inc.
(C) 2011-2013 Cypress Semiconductor Corp.
----------------------------------------------------------------
UVM_INFO @ 0: reporter [RNTST] Running test feature_test...

#### Factory Configuration (*)

Instance Overrides:

  Requested Type    Override Path                             Override Type
  ----------------  ----------------------------------------  ---------------
  base_driver #(T)  uvm_test_top.m_top_env.m_agnt0.*          eth_driver #(T)
  seq1              uvm_test_top.m_top_env.m_agnt1.m_seqr0.*  seq2

Type Overrides:

  Requested Type    Override Type
  ----------------  ----------------------------------------
  eth_packet        eth_v2_packet

All types registered with the factory: 51 total
(types without type names will not be printed)

  Type Name
  ---------
  base_driver #(T)
  base_eth_sequence
  base_test
  eth_driver #(T)
  eth_packet
  eth_v2_packet
  feature_test
  my_agent
  my_agent_v2
  my_env
  my_sequencer
  reg_test
  seq1
  seq2
  seq3
(*) Types with no associated type name will be printed as 

####

UVM_INFO ./tb/my_pkg.sv(45) @ 0: reporter@@eth_v2_packet [eth_v2_packet] Packet created
UVM_INFO ./tb/my_pkg.sv(45) @ 0: reporter@@eth_v2_packet [eth_v2_packet] Packet created
UVM_INFO ./tb/my_pkg.sv(45) @ 0: reporter@@eth_v2_packet [eth_v2_packet] Packet created
UVM_INFO @ 0: reporter [UVMTOP] UVM testbench topology:
--------------------------------------------------------------
Name                       Type                    Size  Value
--------------------------------------------------------------
uvm_test_top               feature_test            -     @2657
  m_top_env                my_env                  -     @218
    m_agnt0                my_agent                -     @2772
      m_drv0               eth_driver #(T)         -     @2770
        rsp_port           uvm_analysis_port       -     @2963
        seq_item_port      uvm_seq_item_pull_port  -     @2912
      m_seqr0              my_sequencer            -     @2996
        rsp_export         uvm_analysis_export     -     @3054
        seq_item_export    uvm_seq_item_pull_imp   -     @3602
        arbitration_queue  array                   0     -
        lock_queue         array                   0     -
        num_last_reqs      integral                32    'd1
        num_last_rsps      integral                32    'd1
    m_agnt1                my_agent                -     @2751
      m_drv0               base_driver #(T)        -     @3676
        rsp_port           uvm_analysis_port       -     @3773
        seq_item_port      uvm_seq_item_pull_port  -     @3725
      m_seqr0              my_sequencer            -     @3754
        rsp_export         uvm_analysis_export     -     @3859
        seq_item_export    uvm_seq_item_pull_imp   -     @4399
        arbitration_queue  array                   0     -
        lock_queue         array                   0     -
        num_last_reqs      integral                32    'd1
        num_last_rsps      integral                32    'd1
    m_agnt2                my_agent_v2             -     @2802
      m_drv0               eth_driver #(T)         -     @4455
        rsp_port           uvm_analysis_port       -     @4553
        seq_item_port      uvm_seq_item_pull_port  -     @4505
      m_seqr0              my_sequencer            -     @3803
        rsp_export         uvm_analysis_export     -     @4639
        seq_item_export    uvm_seq_item_pull_imp   -     @5179
        arbitration_queue  array                   0     -
        lock_queue         array                   0     -
        num_last_reqs      integral                32    'd1
        num_last_rsps      integral                32    'd1
--------------------------------------------------------------

UVM_INFO ./tb/my_pkg.sv(94) @ 0: uvm_test_top.m_top_env.m_agnt2.m_drv0 [eth_driver #(T)] Driver running ...with packet of type : eth_v2_packet
UVM_INFO ./tb/my_pkg.sv(94) @ 0: uvm_test_top.m_top_env.m_agnt1.m_drv0 [base_driver #(T)] Driver running ...with packet of type : eth_v2_packet
UVM_INFO ./tb/my_pkg.sv(94) @ 0: uvm_test_top.m_top_env.m_agnt0.m_drv0 [eth_driver #(T)] Driver running ...with packet of type : eth_v2_packet
UVM_INFO ./tb/my_pkg.sv(278) @ 0: uvm_test_top.m_top_env.m_agnt2.m_seqr0@@seq3 [seq3] Executing pre_body
UVM_INFO ./tb/my_pkg.sv(250) @ 0: uvm_test_top.m_top_env.m_agnt1.m_seqr0@@seq1 [seq2] Executing pre_body
UVM_INFO ./tb/my_pkg.sv(282) @ 0: uvm_test_top.m_top_env.m_agnt2.m_seqr0@@seq3 [SEQ3] Starting seq3
UVM_INFO ./tb/my_pkg.sv(255) @ 0: uvm_test_top.m_top_env.m_agnt1.m_seqr0@@seq1 [SEQ2] Starting seq2
UVM_INFO ./tb/my_pkg.sv(232) @ 0: uvm_test_top.m_top_env.m_agnt0.m_seqr0@@seq1 [SEQ1] Starting seq1
UVM_INFO ./tb/my_pkg.sv(284) @ 10000: uvm_test_top.m_top_env.m_agnt2.m_seqr0@@seq3 [SEQ3] Ending seq3
UVM_INFO ./tb/my_pkg.sv(257) @ 10000: uvm_test_top.m_top_env.m_agnt1.m_seqr0@@seq1 [SEQ2] Ending seq2
UVM_INFO ./tb/my_pkg.sv(234) @ 10000: uvm_test_top.m_top_env.m_agnt0.m_seqr0@@seq1 [SEQ1] Ending seq1
UVM_INFO ./tb/my_pkg.sv(288) @ 10000: uvm_test_top.m_top_env.m_agnt2.m_seqr0@@seq3 [seq3] Executing post_body
UVM_INFO ./tb/my_pkg.sv(262) @ 10000: uvm_test_top.m_top_env.m_agnt1.m_seqr0@@seq1 [seq2] Executing post_body
UVM_INFO ./tb/my_pkg.sv(292) @ 10000: uvm_test_top.m_top_env.m_agnt2.m_seqr0@@seq3 [seq3] Executing post_start

--- UVM Report catcher Summary ---