Search code examples
verilogsystem-veriloghdlyosysverilator

Verilog/SystemVerilog: "constant" function is considered non-constant


I have a Module which has a port whos width should depend on a value from a function: (Syntax is Verilog/Systemverilog mixed as i am using yosys for synthesis, which only supports a limited amount of SV)

module MY_MODULE#(
    parameter MY_PARAM = 2,
    parameter PARAM_TWO = 10
)(
    input logic CLK_CI,
    ...
    input logic [myfunc(MY_PARAM, PARAM_TWO):0] RND,
    ...
    output logic Finish_SO
);

The problem is that the function needs to calculate the value using a for loop, as i need the sum some calculated values. Example:

function automatic int myfunc(input loop_control, input variables);
    int carry;
    int variables_next = 0;
    for (int i = 0; i < loop_control; i++) begin
        carry = variables_next & 1;
        variables_next = (variables_next >> 1) + carry;
    end
    myfunc = carry;
endfunction

Technically, the output of the function is constant as long as the inputs are constant. In my case, i always call the function with localparams, which are constant. However as i am using a for loop and the carry variable changes, it is considered non-constant.

Error from verilator: Expecting expression to be constant, but can't determine constant for FUNCREF 'myfunc'

Error from yosys: Non-constant function call in constant expression.

Is there a way to have a function with a for loop as a constant function?

I could hardcode the values in a parameter array, however i would prefer to not do that.

I wanted to create a function which uses constant (localparams) inputs to create outputs which can be used with port definition. One approach i have tried was to get the values i needed within the generate for loop i have, however i have not found a way to add up the localparams in the generate for loop.


Solution

  • You can try precalculate your parameter as follows

    module MY_MODULE#(
        parameter MY_PARAM = 2,
        parameter PARAM_TWO = 10,
        localparam my_func_data = my_func(MY_PARAM, PARAM_TWO)
    )(
        input logic CLK_CI,
        input logic [my_func_data:0] RND,
        output logic Finish_SO
    );
    
    function automatic int my_func(input loop_control, input variables);
        int carry;
        int variables_next = 0;
        for (int i = 0; i < loop_control; i++) begin
            carry = variables_next & 1;
            variables_next = (variables_next >> 1) + carry;
        end
        my_func = carry;
    endfunction
    
    endmodule
    

    Alternatively, you can declare my_func_data as a parameter to be able to modify it externally.

    Another reason why Vivado, in my case, is also throwing an warning in your construction is that your function is named myfunc, but you are trying to call my_func. The first error raised in such a case is 'range must be bounded by constant expressions', and the second one is 'my_func' is not declared'.