Passing parameters to a Verilog function - verilog

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.

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.

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.

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.

Related

Utilization of parameters in Verilog

I have always used Verilog parameters in the traditional manner, i.e. passing them to a module instantiation to allow different specification to be used. In other words, used to replace text in the HDL code with the given parameter value.
Can I also use it to perform logical calculations?
If I declare the following parameter:
parameter CONST = 100;
As I understand it, the CONST will be of 32 bits (integer).
So can I for example perform bit-wise operations with it:
assign tmp = CONST^net; //net is a 32-bit long wire
Thanks!
You are correct. A value parameter declared without an explicit datatype is implicitly the datatype of the value being assigned to it. In your example the value 100 has implicitly a 32-bit signed datatype with a decimal radix.
However, if CONST is a module parameter that get overwritten, the datatype of the value overwriting the declared value also overrides the implicit data type.
module #(CONST= 100) mod();
endmodule
module top;
mod #(2'b10) m1(); // in this instance CONST is 2 bits(unsigned);
mod #(8'h0F) m2(); // in this instance CONST is 8 bits(unsigned);
However if you declared CONST with an explicit datatype, only the value gets overwritten, not the datatype. I strongly recommend this approach.
module #(int CONST= 100) mod();
endmodule
module top;
mod #(2'b10) m1(); // in this instance CONST is 32 bits(signed);
mod #(8'h0F) m2(); // in this instance CONST is 32 bits(signed);

Multiple assignments to function return value

In a SystemVerilog function, is it legal to do multiple assignments to the implicitly-declared return variable? See the following function for an example:
localparam int Q=1,I=0;
function logic [1:0][Q:I][15:0] Cast24to16(input logic [1:0][Q:I][23:0] din);
foreach (Cast24to16[n,iq])
Cast24to16[n][iq] = din[n][iq][23 -: 8];
endfunction
The language reference manual, IEEE Std 1800-2017, sec 13.4.1 states:
Function return values can be specified in two ways, either by using a return statement or by assigning a value to the internal variable with the same name as the function.
This seems a little unclear as to whether you can assign multiple times, like in my example. Furthermore, the example from the LRM directly after this statement and also all other examples I can find online all show the implicit return value only being assigned once. This makes me feel a bit unsettled.
The LRM also says just before the section you quoted
The function definition shall implicitly declare a variable, internal to the function, with the same name as the function.
I think you can safely assume that if there is no explicit return statement, it effectively inserts an implicit return (var_function_name);
Also, if you declare your function with a static lifetime (which is the implicit default lifetime in a module), that implicit return variable has a static lifetime as well. That means it retains its value from the last time you called the function regardless of whether you assign it or not.
module top;
function int countme;
countme++;
endfunction
initial repeat (10) $display(countme());
endmodule

Multi-dimentional packed parameter declaration using function

Recently I am thinking pre-calculating all necessary parameters using simple mathematical equations before instantiating logic units in generate struct. In the case where I need to have a 2-D parameter, it's easy if the 2-D array is a set of fixed values, such as
parameter para_1[1:0][2:0] = '{2{3{0}}};.
But what I want is a function that dictates the values, such that complicated equations can be implemented inside the function before assigning the return values to the parameter.
Ideally I want something as follows:
function func01 [1:0][1:0] (input int test);
int index;
for (index=0; index<=2; index++) begin
func01[index] = index + $floor(index/2) + $mod(index, 2) + test;
end
endfunction
parameter test1 = 1;
parameter logic test2 [1:0][1:0] = func01(test1);
But ModelSim complains about the first line of code straightaway - ** Error: (vlog-13069) near "[": syntax error, unexpected '[', expecting ';' or '('.
Ultimately the idea is to have a block that calculates all parameters/constants at elaboration time, so that these can be used in generate block for instantiation, also at elaboration time.
Hope it makes sense, and thanks a lot in advance.
Best,
Taihai
First, if you want packed arrays, the dimension ranges goes to the left of the array name. And I suggest using an explicit data type.
parameter bit [1:0][2:0] para_1 = '{2{3{0}}};.
Then, the return type of function declaration comes before the function name.
function bit [1:0][1:0] func01 (input int test);
BTW if you want the return type of a function to have an unpacked dimension, you must use a typedef, which is a good practice in any case.
typedef bit [1:0] myParam_t [2:0];
parameter myParam_t para_1 = '{default:0};
function myParam_t func01 (input int test);

module selection in verilog

I have a group of modules, say module_1, module_2, ... module_N. They perform similar yet different logic operations (out = logic_n). However, since the N is very large (thousands), it is unfeasible to use them in a higher level module by manually instantiate them. I was trying to write a python code for this. I was also wondering is that possible to use parameterized module for this purpose? What I mean is something like.
module module_generic(in, out)
parameter module_number;
case (module_number)
0 : out = logic_1;
1 : out = logic_2;
...
N : out = logic_N;
endcase
endmodule
By doing this, I can use generate statement to easily generate the code in the higher level module. Has anyone try this method before? Can it behave like the way I want? After synthesis, is it equivalent to the brute-force solution?
Something like this?
module module_generic#(
parameter module_number = 0
)(
input logic in,
output logic out);
generate
case (module_number)
0 : assign out = in;
//1 : assign out = logic_2;
//...
//N : assign out = logic_N;
endcase
endgenerate
endmodule
This module can now be instantiated in your code with different inputs for parameter module_number. Unless a lot of the same code is used for each logic_X I don't really understand why you would want to do this. Nevertheless, as far as I could understand from your question, this should do it.
Edit in reply to comment:
Generate is used to generate repeated and conditional parts of the code based on some parameter (e.g. in a parameterized module). For example:
generate
if(INPUT_PARAMETER)
assign out = in;
else //Tie low
assign out = 0;
endgenerate
or
genvar N;
generate
for(N = 0; N < INPUT_PARAMETER; N++) begin :la_someModule
someModule(.out(out[N]), .in(in[N]));
end
endgenerate
or both. (Take note of the label la_someModule. It is very smart to include this when using generate for , it simplifies debugging.)
Specifically answering the question you have asked here is hard. You have not provided enough information for me to understand what you need.

Why is "gen_srl16" used in a standard "SRL16E" instantiation?

I've got this code snip. It's a standard instantiation, but why is gen_srl16 used? I always thought SRL16E srl16e (... should be enough.
genvar i;
generate
for (i=0;i<WIDTH;i=i+1)
begin :
gen_srl16
SRL16E srl16e(
.Q(dataout[i]),
.A0(a[0]),.A1(a[1]),.A2(a[2]),.A3(a[3]),
.CE(write),.CLK(clk),.D(datain[i])); // CE -clock enable
end
endgenerate
In this situation gen_srl16 is just a name of a generate for-loop. It has nothing to do with submodule instantiation.
Following Verilog spec (IEEE Std 1800-2012, ch. 27.4):
Generate blocks in loop generate constructs can be named or unnamed (...) If the generate block is named, it is a declaration of an array of generate block instances. The index values in this array are the values assumed by the genvar during elaboration. This can be a sparse array because the genvar values do not have to form a contiguous range of integers. The array is considered to be declared even if the loop generate scheme resulted in no instances of the generate block.

Resources