The factory and parameterized classes

Hi All,

I have a question about the expected use of the factory to create parameterized classes.

As the factory create_<> functions are all string based what are we supposed to pass as arguemnts?

ie is it
ovm_factory::create_object(“myclass#(0)”,“”, “bob”, this);

or
ovm_factory::create_object(“myclass_0”,“”, “bob”, this);

Maybe I’m confused as some of the simulator implementations of paramterisation are not yet complete.

The only way I can think around this at the moment is using a static function to replace the get_type_name.

Then
ovm_factory::create_object(myclass#(0)::getTypeName(), “”, “bob”, this);

becomes possible and the # syntax is consistant.

Does anyone know what you are supossed to do?

See the following [thread]312[/thread].

Hi Dave,
Thanks for the quick reply, however I’m still not sure this is what I am talking about.

The problem I have is not with registering the parameterized class with the factory. I can simply use the macros

`ovm_component_utils(myClass#(WIDTH))

Which I think is what your example shows (in the expanded way).

What I still don’t understand is what you put in the create_component string. Is it underscores or hash?

As for your example I find using a constant itoa function less intuitive than a static returning a string, which is more standard SV. If this is the recommended OVM way then I think it should be addressed for v2.0 as it is hardly consistent or user friendly.

I’m also pretty sure that the example you supplied is not supported with IUS.

Could you expand the example showing the registered object being created by the factory to show the full syntax?

Thanks

Hi Andrew,

It really doesn’t matter whether you use # or underscore, because the factory does not support parameterized classes directly.

The workaround for this is what Dave’s example showed. You should declare a new non-parameterized class that extends a specialization of the parameterized class. You then register that extended class with the factory. If you use the component_utils macro for registration, then the name of the class is the string you use in create_component().

Regards,
-Kurt

Hi Kurt,

I understand the idea of creating a new un-parameterized class based on the parameterized one and registering it with the factory. The always using that one when creating a new object using the factory.

However I do believe this becomes awkward very quickly, particularly when instatiating parameterized objects within similarly parameterized objects.

In this case, assuming that all paramerterized classes have been registered with a standard param class ->string conversion, the user must then re-construct the string before passing it to the factory. Effectively replicating the same code in 2 places. One using atoi because of the const issue discussed and probably one using $psprintf/$sformat or the like when they are available. Which is hardly ideal.

This then all falls apart when the user picks up some “open” code from a 3rd party, who well within their rights, have chosen a different param class → string conversion. You then have to go through their code to figure out how to decode the un-parameterized class definition.

I think my original suggestion of providing a static function with the parameterized class to return its factory registered type is a universally un-ambiguous alternative. Where it is the writer of the base class that defines the factory hooks and provides a standard why of feeding this up to the user. I however am prepared to accept that the OVM developers may have better ideas on this.

Hi Andrew,

Sorry, I misunderstood what you were asking. I see now that Dave’s example actually is allowing the factory to work with a parameterized type without having to create an extended specialization.

I can simply use the macros
`ovm_component_utils(myClass#(WIDTH))

Actually, this won’t work like you expect. The component_utils macro “stringifies” its argument, then substitutes it as the second parameter in an ovm_component_registry declaration. What this means is that it will be registered, but the string name is literally “myClass#(WIDTH)” The specialized value for WIDTH is not substituted.

Now, this will actually work if you have something like

myClass #(4) c;

$cast(c, create_component(“myClass#(WIDTH)”,“c”));

However, if you try to create two different specializations, like
myClass #(4) c1;
myClass #(6) c2;

You will get an error from the factory that “myClass#(WIDTH)” is being registered twice.

I’ve worked up an example that works with QuestaSim using your idea of a static function. The reason that Dave had to create an itoa function is that you have to somehow convert numeric parameters into a string for the registry type name. He could not just use $itoa because system functions are not allowed in parameter expressions.

If you don’t like having an itoa function, you still have to somehow convert the number to a string, so I created a macro ITOA that is used inside the static function to do the conversion.

With this method, you are free to use whatever type string naming convention you like in your my_type() function, but you have to make sure that each specialization is unique, and you have to follow that convention when specifying the argument to create_component.

Here’s the example:

import ovm_pkg::*;

`include "ovm_macros.svh"

`define ITOA(i,s) \
  string conv = "0123456789"; \
  do s = {conv[i%10],s};\
    while ((i /= 10));

class param_class #(int W=8) extends ovm_threaded_component;

  typedef param_class #(W) this_type;
  parameter string TYPE_NAME = my_type();

  ovm_component_registry #(this_type, TYPE_NAME) is_auto_registered;

  function string get_param();
    return TYPE_NAME;
  endfunction

  static function string my_type();
    string str;
    int j = W;
    `ITOA(j,str)
    return {"param_class #(", str, ")"};
  endfunction

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

  task run();
    ovm_report_message("PARAM_CLASS", $psprintf("Message from class with parameter = %0d",W));
  endtask

endclass



class env extends ovm_env;

  `ovm_component_utils(env);

  param_class #(4) pc;
  param_class #(6) pc2;

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

  function void build();
    $cast(pc, create_component("param_class #(4)", "pc"));
    $cast(pc2, create_component("param_class #(6)", "pc2"));
  endfunction

  task run();
    #1;
    ovm_top.stop_request();
  endtask
  

endclass

module top;

  initial begin
    run_test("env");
  end
endmodule

Regards,
-Kurt

Thanks for the reply again.

This appears to be where the cadence/mentor implementation differs significantly.

The macro regiseration is fine for cadence (probably because SVPP expands it out as you would want). However I understand the questa implementation is probably more “pure”.

I still think that going forward OVM should clean this up. The whole methodology is too ambiguous in this area.

But thanks for the feedback.

Andrew,

It’s not an issue of cadence/mentor implementation, it’s the difference between using macros to create strings versus using parameters to create strings.

And you can use the TYPE_NAME paramter to access the factory name when you create the component.

function void build();
    $cast(pc, create_component(param_class #(4)::TYPE_NAME, "pc"));
    $cast(pc2, create_component(param_class #(6)::TYPE_NAME, "pc2"));
  endfunction

Dave Rich

Sorry its been a while to get back to this, but it really is a cadence v mentor implementation issue.

The example you provide will not work on cadence simulators.

  1. Incisive does not allow parameter declarations as class items.
  2. Incisive does not support the ovm_component_registry syntax

Maybe cadence want to join in the discussion.:)

However due to the svpp pre-processor cadence does support
`ovm_component_utils(param_class#(W))

I know that cadence plan to support parameters more completely in the future, but so far thi is what my experiments have lead to.

However, back to my original point. OVM is meant to be a methodology as well as a set of libraries. I don’t believe that the parameterised class support within the factory is any where near as complete as it should be mostly because of limitations in best pactise recomendations rather than function. If libraries provided a static function returning the type name, parameter or otherwise, and this documented as the best way to call the create_object/create_component functions these issues go away.

If libraries provided a static function returning the type name, parameter or otherwise, and this documented as the best way to call the create_object/create_component functions these issues go away.

Stay tuned for OVM2.0. I think you’ll like what you see.

The feedback we’ve received is that support for parameterized factories is necessary for building true production quality IP. We agree and have developed this capability for OVM-2.0.

We started down the path of finding a way to create unique strings for each parameter value, but this turned into a dead end. We could only do it for limited kinds of parameters and the implementation required a slew of macros which quickly became unwieldy. Instead, OVM-2.0 will support parameterized classes in the factory by providing a means for each wrapper to hold a static variable to an instance of itself. This is based on an idea originally suggested by jgg in this forum.

class ovm_object_registry #(type T=ovm_object, string Tname="")
                                           extends ovm_object_wrapper;
  typedef ovm_object_registry #(T,Tname) this_type;
 
  local static this_type me = get();
 
  static function this_type get();
    if (me == null) begin
      me = new;
      factory.auto_register(me);
    end
    return me;
  endfunction
 
  ...

The static handle, “me” can be used to uniquely refer to the wrapper type and serve as a proxy for the type passed in as a parameter. In your code you write:

class my_object #(type T=int, int R=8) extends ovm_object;
 
  typedef my_object#(T,R) this_type;
  typedef ovm_object_registry#(this_type) type_id;
 
  ...
 
endclass

Type_id causes a specialization of ovm_object_registry#() to come into existence. That specialization will contain the static local variable me. The first time get() is called me will be initialized. To set an override you write:

typedef my_object#(T,R) mytype;
typedef other_object#(T) othertype;
mytype::type_id::set_type_override(othertype::type_id::get())

These expressions can be made simpler with some convenience functions. I just want to give the flavor without going into all the details.

– Mark

Hi Andrew,
Sorry, I misunderstood what you were asking. I see now that Dave’s example actually is allowing the factory to work with a parameterized type without having to create an extended specialization.
Actually, this won’t work like you expect. The component_utils macro “stringifies” its argument, then substitutes it as the second parameter in an ovm_component_registry declaration. What this means is that it will be registered, but the string name is literally “myClass#(WIDTH)” The specialized value for WIDTH is not substituted.
Now, this will actually work if you have something like
myClass #(4) c;

$cast(c, create_component(“myClass#(WIDTH)”,“c”));
However, if you try to create two different specializations, like
myClass #(4) c1;
myClass #(6) c2;
You will get an error from the factory that “myClass#(WIDTH)” is being registered twice.
I’ve worked up an example that works with QuestaSim using your idea of a static function. The reason that Dave had to create an itoa function is that you have to somehow convert numeric parameters into a string for the registry type name. He could not just use $itoa because system functions are not allowed in parameter expressions.
If you don’t like having an itoa function, you still have to somehow convert the number to a string, so I created a macro ITOA that is used inside the static function to do the conversion.
With this method, you are free to use whatever type string naming convention you like in your my_type() function, but you have to make sure that each specialization is unique, and you have to follow that convention when specifying the argument to create_component.
Here’s the example:

import ovm_pkg::*;
`include "ovm_macros.svh"
`define ITOA(i,s) \
string conv = "0123456789"; \
do s = {conv[i%10],s};\
while ((i /= 10));
class param_class #(int W=8) extends ovm_threaded_component;
typedef param_class #(W) this_type;
parameter string TYPE_NAME = my_type();
ovm_component_registry #(this_type, TYPE_NAME) is_auto_registered;
function string get_param();
return TYPE_NAME;
endfunction
static function string my_type();
string str;
int j = W;
`ITOA(j,str)
return {"param_class #(", str, ")"};
endfunction
function new(string name, ovm_component parent);
super.new(name, parent);
endfunction
task run();
ovm_report_message("PARAM_CLASS", $psprintf("Message from class with parameter = %0d",W));
endtask
endclass
class env extends ovm_env;
`ovm_component_utils(env);
param_class #(4) pc;
param_class #(6) pc2;
function new(string name, ovm_component parent);
super.new(name, parent);
endfunction
function void build();
$cast(pc, create_component("param_class #(4)", "pc"));
$cast(pc2, create_component("param_class #(6)", "pc2"));
endfunction
task run();
#1;
ovm_top.stop_request();
endtask
endclass
module top;
initial begin
run_test("env");
end
endmodule

Regards,
-Kurt

The following solved the problem for me from OVM 2.0.1 onwards on IUS 8.2

`ovm_component_param_utils(param_class #(W))