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 ovm fatroy, it is very easy to solve the above two requirements. Only classs extended from ovm_object and ovm_component are supported for this.
There are three basic steps to be followed for using ovm factory.
1) Registration
2) Construction
3) Overriding
The factory makes it is possible to override the type of ovm component /object or instance of a ovm component/object in2 ways. They are based on ovm component/object type or ovm compoenent/object name.
Registration:
While defining a class , its type has to be registered with the ovm factory. To do this job easier, ovm has predefined macros.
For ovm_*_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 ovm_object;
`ovm_object_utils(packet)
endclass
class packet #(type T=int, int mode=0) extends ovm_object;
`ovm_object_param_utils(packet #(T,mode))
endclass
class driver extends ovm_component;
`ovm_component_utils(driver)
endclass
class monitor #(type T=int, int mode=0) extends ovm_component;
`ovm_component_param_utils(driver#(T,mode))
endclass
Construction:
To construct a ovm based component or ovm 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 ovm based components or ovm based objects , do not use new() constructor.
Syntax :
staticfunction T create(string name,
ovm_component parent,
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.
functionvoid set_type_override_by_name
(string original_type_name,
string override_type_name,
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 ovm_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 OVM 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 OVM TESTBENCH
1) Registration
In all the class, you can see the macro `ovm_component_utils(type_name)
2) Construction
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 OVM_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 print_all_overrides() method of factory class.
class driver_2 extends driver;
`ovm_component_utils(driver_2)
function new(string name, ovm_component parent);
super.new(name, parent);
endfunction
endclass
class monitor_2 extends monitor;
`ovm_component_utils(monitor_2)
function new(string name, ovm_component parent);
super.new(name, parent);
endfunction
endclass
class test_factory extends ovm_test;
`ovm_component_utils(test_factory)
env t_env;
function new (string name="test1", ovm_component parent=null);
super.new (name, parent);
factory.set_type_override_by_type(driver::get_type(),driver_2::get_type(),"*");
factory.set_type_override_by_name("monitor","monitor_2","*");
factory.print_all_overrides();
t_env = new("t_env",this);
endfunction : new
Command to run the example with the testcase which is defined above:
Your_tool_simulation_command +incdir+path_to_ovm -f filelist +OVM_TESTNAME=test_factory
Method factory.print_all_overrides() displayed all the overrides as shown below in the log file.
#### Factory Configuration (*)
No instance overrides are registered with this factory
Type Overrides:
Requested Type Override Type
-------------- -------------
driver driver_2
monitor monitor_2
In the below text printed by print_topology() method ,we can see overridden driver and monitor.