5

I want to pass a parameter to a function and use it as a parameter (e.g. to select bits) but I don't know how to tell the function that this input is a constant.

For example, if I wanted to do this:

assign foo = bar[MY_PARAM:0];

I want to write my_function so that I could do this:

assign foo = my_function(bar, MY_PARAM);

In my case I need to do a little more that just select bits but not too much, and I'll want it to work for inputs of different bit widths. If I just wanted to select a bit I could use the function below and I'd hope for a solution of similar form but I can't work out the syntax:

function my_function;
  input [3:0] data, my_bit;
  begin
    my_function = data[my_bit];
  end
endfunction

As per Silicon1602's answer, the code I'd need for this would be:

virtual class myClass#(parameter LOCAL_PARAM);
  static function [LOCAL_PARAM:0] my_function;
    input [LOCAL_PARAM:0] data;
    begin
      my_function = data[LOCAL_PARAM:0];
    end
  endfunction
endclass
assign foo = myClass#(MY_PARAM)::my_function(bar);

At first I forgot about the [LOCAL_PARAM] part and was just getting 1-bit back.

3 Answers 3

9

Since the question was also tagged as 'verilog', a similar trick could be played in a simple verilog. You can use parameterized modules to achieve the same effect. For example:

module util#(
  parameter int W = 10)();

  function funct;
    input [W-1:0] inp;
    funct = inp;
  endfunction

endmodule


module top(out, in);
  parameter W = 8;

  output wire [W-1:0] out;
  input wire [W-1:0] in;  

  util#(W) u1(); // inst util module with a parameter
  assign out = u1.funct(in); // call the function

  initial #1 $finish;

endmodule

By default, all functions declared within a module are static.

Sign up to request clarification or add additional context in comments.

Comments

7

The SystemVerilog LRM has a section on your particular case: 13.8 Parameterized tasks and functions. It says:

SystemVerilog provides a way to create parameterized tasks and functions, also known as parameterized subroutines. [...] The way to implement parameterized subroutines is through the use of static methods in parameterized classes (see 8.10 and 8.25).

In your case, you should declare your function like this:

virtual class myClass#(parameter MY_PARAM);
    static function my_function;
        input [MY_PARAM-1:0] data, my_bit;
        begin
            my_function = data[my_bit];
        end
    endfunction
endclass

You could then call your function like this:

assign my_function_output = myClass#(MY_PARAM)::my_function(data, my_bit);

Please note that you may declare multiple functions in your abstract class. So, if you have a whole bunch of functions which all depend on a parameter in the same way, you could all declare them in the same class.


Some additional information on the virtual and static keyword in the aforementioned context:

Section 8.10 of the LRM talks about static methods.

A static method is subject to all the class scoping and access rules, but behaves like a regular subroutine that can be called outside the class, even with no class instantiation. A static method has no access to non-static members (class properties or methods), but it can directly access static class properties or call static methods of the same class.

By using the virtual keyword for the class declaration, you show the compiler that this is an abstract class (see Section 8.21 in the LRM). Creating an object of a virtual class causes a compilation error. This enforces strict static usage of the method.

2 Comments

Is it synthesizable? Doesn't seem to be unfortunately.
It depends on toolchain support. The latest version of Vivado (2023.2) still does not support virtual classes.
0

You can use macro expansion to achieve this. I wanted a function that would check different test stimulus. The simulation arrays of 'bus' signals (or multi-bit values) and this was my 'parameter'.

`define MY_FUNCTION(LOCAL_PARAM)                \
function my_function_``LOCAL_PARAM``;           \
  input [LOCAL_PARAM:0] data, my_bit;           \
  begin                                         \
    my_function_``LOCAL_PARAM`` = data[my_bit]; \
  end                                           \
endfunction                                     \

Later...

`MY_FUNCTION(10)
 my_function_10 (data_ten, my_bit);  // Really my_bit is size $clog of LOCAL_PARAM.

Like Serge's answer, this works with Verilog (2001). Also, you can use tasks and then the entire module net is available. The macro call is equivalent the module instantiation with a parameter. It is basically like the elaboration phase.

Probably the module solution has more valid syntax and constructs. However, a macro of a function can achieve a similar result for simulation and could be suitable for some synthesis cases.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.