The factory pattern is an well known object-oriented design pattern. The factory method design pattern defining a separate method for creating the objects. , whose subclasses can then override to specify the derived type of object that will be created.
Using this method, objects are constructed dynamically based on the specification type of the object. User can alter the behavior of the pre-build code without modifying the code. From the testcase, user from environment or testcase can replace any object which is at any hierarchy level with the user defined object.
For example: In your environment, you have a driver component. You would like the extend the driver component for error injection scenario. After defining the extended driver class with error injection, how will you replace the base driver component which is deep in the hierarchy of your environment ? Using hierarchical path, you could replace the driver object with the extended driver. This could not be easy if there are many driver objects. Then you should also take care of its connections with the other components of testbenchs like scoreboard etc.
One more example: In your Ethernet verification environment, you have different drivers to support different interfaces for 10mbps,100mps and 1G. Now you want to reuse the same environment for 10G verification. Inside somewhere deep in the hierarchy, while building the components, as a driver components ,your current environment can only select 10mmps/100mps/1G drivers using configuration settings. How to add one more driver to the current drivers list of drivers so that from the testcase you could configure the environment to work for 10G.
Using the uvm fatroy, it is very easy to solve the above two requirements. Only classs extended from uvm_object and uvm_component are supported for this.
There are three basic steps to be followed for using uvm factory.
The factory makes it is possible to override the type of uvm component /object or instance of a uvm component/object in2 ways. They are based on uvm component/object type or uvm compoenent/object name.
While defining a class , its type has to be registered with the uvm factory. To do this job easier, uvm has predefined macros.
For uvm_*_param_utils are used for parameterized classes and other two macros for non-parameterized class. Registration is required for name-based overriding , it is not required for type-based overriding.
EXAMPLE: Example of above macros
class packet extends uvm_object;
class packet #(type T=int, int mode=0) extends uvm_object;
class driver extends uvm_component;
class monitor #(type T=int, int mode=0) extends uvm_component;
To construct a uvm based component or uvm based objects, static method create() should be used. This function constructs the appropriate object based on the overrides and constructs the object and returns it. So while constructing the uvm based components or uvm based objects , do not use new() constructor.
staticfunction T create(string name,
stringcontext = " ")
The Create() function returns an instance of the component type, T, represented by this proxy, subject to any factory overrides based on the context provided by the parents full name. The context argument, if supplied, supersedes the parents context. The new instance will have the given leaf name and parent.
Original_type_name and override_type_name are the class names which are registered in the factory. All the instances of objects with name "Original_type_name" will be overriden with objects of name "override_type_name" using set_inst_override_by_name() method.
Using the above method, request to create an object of original_type can be overriden with override_type.
bit replace = 1)
Using the above method, request to create an object of original_type_name can be overriden with override_type_name.
When multiple overrides are done , then using the argument "replace" , we can control whether to override the previous override or not. If argument "replace" is 1, then previous overrides will be replaced otherwise, previous overrides will remain.
print() method, prints the state of the uvm_factory, registered types, instance overrides, and type overrides.
Now we will see a complete example. This example is based on the environment build in topic UVM TESTBENCH . Refer to that section for more information about this example.
Lets look at the 3 steps which I discussed above using the example defined in UVM TESTBENCH
In all the class, you can see the macro `uvm_component_utils(type_name)
In file agant.sv file, monitor and driver are constructed using create() method.
3)In this example, a one testcase is already developed in topic UVM_TESTBENCH. There are no over rides in this test case.
Topology of this test environment is shown below.
In this example, there is one driver class and one monitor class. In this testcase , By extending driver class , we will define driver_2 class and by extending monitor class, we will define monitor_2 class.
From the testcase , Using set_type_override_by_type, we will override driver with driver_2 and Using set_type_override_by_name, we will override monitor with monitor_2.
To know about the overrides which are done, call factory.print() method of factory class.
class driver_2 extends driver;
function new(string name, uvm_component parent);
class monitor_2 extends monitor;
function new(string name, uvm_component parent);
class test_factory extends uvm_test;
function new (string name="test1", uvm_component parent=null);
super.new (name, parent);
t_env = new("t_env",this);
endfunction : new