Parameterize a function or task

I have found it very handy to be able to parameterize an interface, but I have made a change to my class structure (added some hierarchy), where I now need the ability to parameterize a task that is defined in an interface. I have done a lot of google searches on this topic, and even tried to do it, but I always get compile or elaboration errors. I suspect the answer is that it is not possible to parameterize a function or task, but that the parameterization must be declared in the interface or class definition. Is that correct?

Here is a simplified example of what I would like to do:

// begin code (fails to compile)
interface tester // #(type T) // removed type parameter at the interface definition
// trying to reuse code that can output 3 different types of leaf classes
task automatic compare #(type T) (input base_class bc, ref T leaf_class_inst);
// implementation
endtask
endinterface
// end code

// begin hacked work around (compiles)
interface tester #(type T)
task automatic compare(input base_class bc, ref T leaf_class_inst);

endtask
endinterface

tester i1;
base_class comp;

initial begin
comp = new();
end

initial begin
automatic leaf_class t1 = new();
automatic leaf_class t2 = new();
automatic base_class b1 = t1;
automatic base_class b2 = t2;
i1.compare(comp, b1);
i1.compare(comp, b2);
//i1.compare(comp, t1);// elaboration error
//i1.compare(comp, t2);// elaboration error
end
// end hacked work around

You need to be more specific about what you mean by parameterize. You can declare parameters in any scope, but the override mechanism exists only for instances of modules, interfaces and classes. It would really help to see the actual error messages you are getting since you only supplied a partial example.

And there are a few other things about your code that do not make sense.

Normally, a compare routine would be a virtual method of the class, not a stand alone routine. You would use inheritance to override the base class, not parameterization.

You should only use tasks for routines that have the possibility of consuming time. Otherwise, use a function.

You almost never need to pass class handle to a routine as a ref argument. A class variable is already a reference to a class object. You normally just want it to be an input. Passing arguments by ref have many more restrictions, that could be the source of some of your errors.

In reply to dave_59:

Ok…I will try to provide some more information without disclosing too much proprietary information. Thanks for your response.

The class is a model of an ethernet packet. I call the constructor externally and then populate the data in the task and pass the result out via the reference. The output packet will then be the input that is compared (in another instance of the interface) in the next module in the simulation. The problem is that the output packet needs to be a different type at different times in the test. Hence the need for a parameter in the interface–I want to reuse the interface task code as much as I can.

Note that the task is consuming time. I did not add the task to the class because this code is pretty specific to this particular test, and the packet model class is being used in other places where the requirements are different.

I don’t have access to the specific error message anymore (as I am not at my desk, and at any rate have fixed my code). But it was an elaboration error that said I was using the wrong class (the base class instead of the child class). I am now successfully using the technique I tried to describe above, but the solution I came up with feels kind of kludgy to me.

I think you have confirmed my suspicion that a “generic” (or “parameterization”) is not available at the function level in System Verilog. I was assuming this is possible because you can do this in C#, C++, and presumably any other modern OO language that implements generics. But maybe this is part of my problem–I suspect I am not being very “standard” in my approach.

In reply to slittle:

You may want to look at section _13.8 Parameterized tasks and functions_in the LRM. You can wrap a static task or function in a parameterized class, and that class does not need to be constructed.

In reply to dave_59:

I hadn’t thought of using a static class. But your suggestion does make sense. I will need to think of the best way to use a static class in conjunction with an interface. Thanks.

In reply to slittle:
Hi,
Just for completion, i will put my two cents with an example



class create_xfers_with_type_c#(type T = int);

  static function void create_xfer( ref T xfer, input int BITS_ID_EXTRA);
     xfer =  T::type_id::create($sformatf("master_%0d_xfer",BITS_ID_EXTRA));//it creates the item
  endfunction

  //just as dummy example using a task
  static task create_xfer_delay( ref T xfer, input int BITS_ID_EXTRA, event donow);
     @donow;
     xfer =  T::type_id::create($sformatf("master_%0d_xfer",BITS_ID_EXTRA ));//it creates the item after delay
  endtask

endclass : create_xfers_with_type_c


And you just call the static function or task using the following


axi_xfer_t     AXI_xfercreated_out;
int            index;
event          now_event;


create_xfers_with_type_c#(axi_xfer_t)::create_xfer(AXI_xfercreated_out, index);//example of function static call

fork 
  begin
    #10us;
    ->now_event;
  end
join_none
create_xfers_with_type_c#(axi_xfer_t)::create_xfer_delay(AXI_xfercreated_out, index, now_event);//example of task static call blocking until event



Please notice that you don’t have to create the parameterized class since you use a static operator.