I’m trying to determine how I can use a member function of an object that has overridden another object via the ovm_factory::set_inst_override function. The member function is declared & defined only in the new object and not in the original. I’ll explain the situation in more detail below:
I have 2 different component types called base_comp and over_comp. over_comp has an additional member function called some_func(). The environment uses OVM factory create_component to instantiate the base_comp (instance is called comp_inst). In my testbench, I use ovm_factory::set_inst_override to override base_comp (comp_inst) with over_comp. How do I call the some_func() member function from my testbench? If I try to reference it with something like “env.comp_inst.some_func()” I get a compile error that says some_func is not in comp_inst because the OVM override doesn’t happen until after compile time (i.e. when the sim is running already).
What is happening is that even though you are creating a different object, the original code that requests the component from the factory is using a handle to the original component.
Factory overriding works best when the overridden object inherits from the original object. To accomplish what you want, you need to make sure that your overridden class extends (derives from) your base class.
Create a virtual function in the base class that matches the function you want in your derived class. The virtual function does not have to actually do anything.
class base_comp extends ovm_component;
virtual void function some_func();
// empty function
endfunction
endclass
class comp_inst extends base_comp();
function void some_func();
// Actually do what you need to do here
endfunction
endclass
When the factory replaces the original component with the derived one, SystemVerilog will use polymorphism to call the comp_inst’s function even though you have a handle to the base_comp
Thanks for the response Kurts. What you describe does work. But…
It does seem rather limiting that parent classes need to be modified in order to add additional functionality to a child class. The major purpose of inheritance is to allow child classes to add functionality to parents without having to modify the parents. This also brings into question reuse or having verification IP that are based on inheritance. Often one does not have access to the parent classes and can’t add the virtual functions there.
Are there any other solutions?
Are there any plans for future versions of OVM to fix this?
Perhaps the rules/limitations of how the factory overrides work should be described somewhere, like the idea of you only being able to override instances with their children etc.
These concerns with inheritance is not strictly an OVM issue, but a general technique of object-oriented programming. The use of virtual methods is one mechanism to access derived functionality via polymorphism. Outside of that, somebodyhas to know about the existence of derived functionality.
You can downcast the object to a handle declared with the derived class. Then you can call your added some_func() using that handle.
You can also use combinations of virtual and non-virtual functions. For example, the build() method in ovm_component is virtual. When you call the virtual build(), it can call non-virtual methods that exist only in the derived class.
These concerns with inheritance is not strictly an OVM issue, but a general technique of object-oriented programming. The use of virtual methods is one mechanism to access derived functionality via polymorphism. Outside of that, somebodyhas to know about the existence of derived functionality.
I guess I should clearify. The issue I’m having is not with inheritance in general, but specifically with the OVM factory instance overrides. I understand that a virtual method can be overridden by a child class normally via inheritance/polymorphism. But in the specific case of a child class having an ADDITIONAL function that does not exist in the parent, if you use OVM factory instance override to override the parent with the child, you cannot call any child class functions unless they already exist in the parent. I think this is OVM specific, and stems from the fact that the factory does the override at run-time, and hence the compiler doesn’t know yet that a particular instance has been swapped. In the case of normal inheritance (i.e. everything is pure systemverilog and OVM is out of the picture), a child class can inherit from the parent, and have additional member functions that can be accessed/called by higher level code, without the parent having any knowledge of that additional member function.
You can downcast the object to a handle declared with the derived class. Then you can call your added some_func() using that handle.
No, this is not specific to the OVM, but is the principal behind an OO factory. You don’t have to use a factory in the OVM; you can simply construct the derived class directly and call your extended function.
But if you do use a factory in any methodology, the idea is that user of the factory has no knowledge of what may have been extended in the class just constructed, so they would not know to call an extended function.
Thanks Dave, I’ve tried your downcasting suggestion and it works. I’m having some trouble getting the set_inst_override to work, but the set_type_override is fine (I guess this is a question for another thread). The code is below for anyone interested:
`include "ovm_macros.svh"
import ovm_pkg::*;
class base_cfg extends ovm_threaded_component;
`ovm_component_utils(base_cfg)
int base_cfg_int;
function new(string name = "base_cfg", ovm_component parent = null);
super.new(name, parent);
ovm_report_message("", "Created a base_cfg");
endfunction
virtual function void build;
super.build();
base_cfg_int = 1;
endfunction
endclass
class over_cfg extends base_cfg;
`ovm_component_utils(over_cfg)
int over_cfg_int;
function new(string name = "over_cfg", ovm_component parent = null);
super.new(name, parent);
ovm_report_message("", "Created a over_cfg");
endfunction
virtual function void build;
super.build();
over_cfg_int = 3;
some_func();
endfunction
function void some_func();
$display("some_func called");
endfunction
virtual task run;
super.run();
$display("OVER_CFG: run task called");
endtask
endclass
class base_env extends ovm_env;
`ovm_component_utils(base_env)
base_cfg cfg;
base_cfg cfg_2;
function new(string name = "base_env", ovm_component parent = null);
super.new(name, parent);
ovm_report_message("", "Created a base_env");
endfunction
virtual function void build;
super.build();
$cast(cfg,create_component("base_cfg","cfg"));
$cast(cfg_2,create_component("base_cfg","cfg_2"));
ovm_report_message("", "Build a main_env");
endfunction
endclass
module test();
import ovm_pkg::*;
base_env env_0 = new;
initial begin
over_cfg tmp_over_cfg;
// ovm_factory::set_inst_override("env_0.cfg_2", "base_cfg", "over_cfg");
ovm_factory::set_type_override("base_cfg", "over_cfg");
ovm_factory::print_all_overrides(1);
env_0.do_test();
if (! $cast(tmp_over_cfg, env_0.cfg_2 ) ) $display ("CAST FAILED");
tmp_over_cfg.over_cfg_int = 8;
env_0.cfg_2.base_cfg_int = 9;
$display("base_cfg_int = %d", env_0.cfg_2.base_cfg_int);
tmp_over_cfg.base_cfg_int = 11;
$display("base_cfg_int = %d", env_0.cfg_2.base_cfg_int);
$display("over_cfg_int = %d", tmp_over_cfg.over_cfg_int);
$finish;
end
endmodule
The reason your inst_override is not working is because you specify the path “env0.cfg_2” as the instance path, but you create your environment with no name argument to new(). So, the environment is created with the name “base_env”, because of the default argument to new().
If you change the constructor call to be base_env env_0 = new(“env_0”) it should work
Hey Monkey,Kurt,
Thanks, this correspondence was helpful. I’m trying to do something very similar… except I want to do the override from my ovm_test rather then from the module… since the plan is to have one module and many tests which can be switched out. Leveraging Monkey’s example:
`include "ovm_macros.svh"
import ovm_pkg::*;
class base_cfg extends ovm_threaded_component;
`ovm_component_utils(base_cfg)
int base_cfg_int;
function new(string name = "base_cfg", ovm_component parent = null);
super.new(name, parent);
ovm_report_message("", "Created a base_cfg");
endfunction
virtual function void build;
super.build();
base_cfg_int = 1;
endfunction
virtual task run;
super.run();
$display("BASE_CFG: run task called");
endtask
endclass
class over_cfg extends base_cfg;
`ovm_component_utils(over_cfg)
int over_cfg_int;
function new(string name = "over_cfg", ovm_component parent = null);
super.new(name, parent);
ovm_report_message("", "Created a over_cfg");
endfunction
virtual function void build;
super.build();
over_cfg_int = 3;
some_func();
endfunction
function void some_func();
$display("some_func called");
endfunction
virtual task run;
super.run();
$display("OVER_CFG: run task called");
endtask
endclass
class base_env extends ovm_env;
`ovm_component_utils(base_env)
base_cfg cfg;
base_cfg cfg_2;
function new(string name = "base_env", ovm_component parent = null);
super.new(name, parent);
ovm_report_message("", "Created a base_env");
endfunction
virtual function void build;
super.build();
$cast(cfg,create_component("base_cfg","cfg"));
$cast(cfg_2,create_component("base_cfg","cfg_2"));
ovm_report_message("", "Build a main_env");
endfunction
endclass
class base_test extends ovm_test;
`ovm_component_utils(base_test)
// module test();
base_env env_0 = new;
function new(string name = "base_test", ovm_component parent=null);
super.new(name,parent);
endfunction : new
virtual function void build();
super.build();
$cast(env_0, create_component("base_env", "env_0"));
env_0.build();
endfunction : build
// initial begin run(); end
task run();
// ovm_factory::set_inst_override("env_0.cfg_2", "base_cfg", "over_cfg");
ovm_factory::set_type_override("base_cfg", "over_cfg");
ovm_factory::print_all_overrides(1);
env_0.run();
env_0.do_test();
endtask
//endmodule
endclass : base_test
module test;
import ovm_pkg::*;
initial begin
run_test();
end
endmodule
This is what gets reported
BASE_CFG: run task called
BASE_CFG: run task called
but I was expecting
OVER_CFG: run task called
If I change class base_test back into module test it works as expected. Probably something silly I’m missing but appreciate the help.
FYI the command lines:
vlog -suppress 2167 +incdir+<ovm-1.0.1>/ovm-1.0.1/src <ovm-1.0.1>/src/ovm_pkg.sv -novopt -nowarn 13 test.sv
vsim -c +OVM_TESTNAME=base_test -novopt test -do “add log -r /*; run -all; exit;” -suppress 3829 -solvefaildebug</ovm-1.0.1></ovm-1.0.1>
The code in your base_test class isn’t quite right.
The main reason why your override is not working is that you need to it set up before the build method method of the env is called (setting it in run is too late!)
If you move the overrides to the test_env build it should work OK:
class base_test extends ovm_test;
...
virtual function void build();
super.build();
ovm_factory::set_type_override("base_cfg", "over_cfg");
ovm_factory::print_all_overrides(1);
$cast(env_0, create_component("base_env", "env_0"));
env_0.build();
endfunction : build
The other issue is that you are explicitly calling
env_0.run();
env_0.do_test();
in base_test::run. You should not call either of these tasks explicitly. The run method is called implicitly during the run phase (and calling do_test makes no sense).
The problem is that set_type_override is being called too late. You have it in your run() task, and by the time the run phase is executed, the build phase has already been executed, and the components have been created by the factory.
You should move your call to set_type_override to the beginning of the build() function of your test class.
A couple other things I noticed about your example:
You manually create env_0 with new on the declaration line, then create it again with the factory. You should remove the new.
You have both a run_test and a do_test. You should probably remove the do_test, since you are using the OVM_TESTNAME plusarg to choose your top-level test, and do_test is deprecated.
Hi sand2snowice,
The code in your base_test class isn’t quite right.
The main reason why your override is not working is that you need to it set up before the build method method of the env is called (setting it in run is too late!)
If you move the overrides to the test_env build it should work OK:
and it does, thanks! I had the wrong impression the env had to be built to avoid NULL ref’s and then set_type_override would re-cast to the desired override class. But now I understand it needs to do this casting as its building … and it probably has to be this way to fit the rules of casting
env_0.run();
env_0.do_test();
in base_test::run. You should not call either of these tasks explicitly. The run method is called implicitly during the run phase (and calling do_test makes no sense).
Thanks Dave, I’m a newb to this flow and trying to learn combining concepts in different ovm examples. This can be miss leading since they show different flows.
The problem is that set_type_override is being called too late. You have it in your run() task, and by the time the run phase is executed, the build phase has already been executed, and the components have been created by the factory.
You should move your call to set_type_override to the beginning of the build() function of your test class.
Gottit, Thanks Kurt!
A couple other things I noticed about your example:
You manually create env_0 with new on the declaration line, then create it again with the factory. You should remove the new.
OK I removed new and now trying to get inst override to work
Seems like I have the right names being passed but didn’t work, however,
set_type_override did work. I did read your previous response to similar problem but since I removed the new not sure where the issue would be. Reposting full code below for anyone’s FYI
`include "ovm_macros.svh"
import ovm_pkg::*;
// -- base_cfg ----
class base_cfg extends ovm_threaded_component;
`ovm_component_utils(base_cfg)
int base_cfg_int;
function new(string name = "base_cfg", ovm_component parent = null);
super.new(name, parent);
ovm_report_message("", "Created a base_cfg");
endfunction
virtual function void build;
super.build();
base_cfg_int = 1;
endfunction
virtual task run;
super.run();
$display("BASE_CFG: run task called");
endtask
endclass
// -- over_cfg ----
class over_cfg extends base_cfg;
`ovm_component_utils(over_cfg)
int over_cfg_int;
function new(string name = "over_cfg", ovm_component parent = null);
super.new(name, parent);
ovm_report_message("", "Created a over_cfg");
endfunction
virtual function void build;
super.build();
over_cfg_int = 3;
some_func();
endfunction
function void some_func();
$display("some_func called");
endfunction
virtual task run;
super.run();
$display("OVER_CFG: run task called");
endtask
endclass
// -- base_env ----
class base_env extends ovm_env;
`ovm_component_utils(base_env)
base_cfg cfg;
base_cfg cfg_2;
function new(string name = "base_env", ovm_component parent = null);
super.new(name, parent);
ovm_report_message("", "Created a base_env");
endfunction
virtual function void build;
super.build();
$cast(cfg,create_component("base_cfg","cfg"));
$cast(cfg_2,create_component("base_cfg","cfg_2"));
ovm_report_message("", "Build a main_env");
cfg.build();
cfg_2.build();
endfunction
endclass
// -- base_test ----
class base_test extends ovm_test;
`ovm_component_utils(base_test)
base_env env_0 ;// = new; newd below with create_component
function new(string name = "base_test", ovm_component parent=null);
super.new(name,parent);
endfunction : new
virtual function void build();
super.build();
$cast(env_0, create_component("base_env", "env_0"));
// ovm_factory::set_type_override("base_cfg", "over_cfg");
ovm_factory::set_inst_override("env_0.cfg_2", "base_cfg", "over_cfg");
ovm_factory::print_all_overrides(1);
env_0.build();
endfunction : build
task run();
endtask
endclass : base_test
// -- test ----
module test;
import ovm_pkg::*;
initial begin
run_test();
end
endmodule
The instance path is not a relative path, like with the set_config_* calls.
When you use run_test, the top-level test is instantiated with the name “ovm_test_top”.
Change your instance path to “ovm_test_top.env_0.cfg_2” and it should work.
-Kurt
Ah so not a relative path.
very nice, muchas gracias