5
\$\begingroup\$

Consider the following Verilog code which takes a byte and specifies whether its first and second nibbles are equal to 9.

module test(input [7:0] inp, 
             output[1:0] out
           );


    always @(*) begin
        out[0] = is9(inp, 0);
        out[1] = is9(inp, 1);
    end

    function reg is9 (input [7:0] a, input integer k);
        integer i;
        reg [k:0] W;
   
        for (i=0; i <= k; i = i+1) begin
            W[i] = (a[4*i +: 4] == 4'd9); 
        end
        is9 = & W;
    endfunction

endmodule

What is wrong about the is9 function that I have written? It shows an error that k is not a constant at the line where I declare reg [k:0] W.

I am learning how to work with functions in Verilog; therefore, I am not looking for another program which does the same job as the one that I have written here.

\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

So the basic declaration is:

function [<msb>:<lsb>] funcName(
   ... // inputs
);
//local vars
// e.g. reg [3:0] myVal;
begin
    //body of function
    //...
    //return value by assigning to function name:
    funcName = <value>;
end 
endfunction 

A function can return up to 1 value.

The width of the return value for the function is set by the range specifier in the declaration. This must be a constant width - e.g. a literal width, a parameter value, etc.

Returning a value is achieved by assigning a value to the function name. You can assign in multiple different places with the body, e.g. within for loops, if-else statements, or at the end.


In your code, you have several things I'd change.

  1. You have 'reg' in the function definition, rather than a range specifier. This is unnecessary and it may confuse some more picky synthesis tools.

    If you want it to be a single bit wide, simply omit the range and reg.

  2. Wrap the body of the function in begin-end for clarity.

  3. Verilog is not like C. You can't have values of variable width. So declaring the internal signal W as width k won't work.

    In your case, you could simply make the internal signal W wide enough for your desired maximum k value. You could also make it an integer if 32bit is enough. Then initialise the value to all 1's. The for loop can then modify k bits and the reduction-AND with them still work (e.g. ~16'b0 for a 16bit signal).

    You can also add a parameter outside the function to set the maximum width of you prefer. Parameters and local parameters in the module that is declaring the function are visible inside the function. Handy if you want to extend to more nibbles in the future.

Of course as pointed out in @toolic's answer, for your current case this can be done without a function by using simple comparisons.

\$\endgroup\$
4
  • \$\begingroup\$ Thanks for your response. It is interesting that verilog does not admit a parameterized vector length. \$\endgroup\$
    – CLAUDE
    Commented Aug 13 at 20:57
  • \$\begingroup\$ @CLAUDE You can use parameters for the widths. But when passing values in (even constants) as inputs to the function, they stop being parameters and instead become variables. \$\endgroup\$ Commented Aug 13 at 20:59
  • \$\begingroup\$ @CLAUDE there's also a clever example here for parameterising a function by wrapping it in a module. \$\endgroup\$ Commented Aug 13 at 21:13
  • \$\begingroup\$ Such a cool trick! Thanks for sharing \$\endgroup\$
    – CLAUDE
    Commented Aug 13 at 21:25
4
\$\begingroup\$

The compile error is telling you that Verilog does not allow you to declare a signal using a variable number of bits. You are trying to declare W with a variable number of bits (k is a variable), which is illegal Verilog syntax.

There is nothing wrong with your function. You just can not declare a signal that way. This compiles (but is untested):

module test(input [7:0] inp, 
             output reg [1:0] out
           );

    always @(*) begin
        out[0] = is9(inp, 0);
        out[1] = is9(inp, 1);
    end

    function reg is9 (input [7:0] a, input sel);
        reg [3:0] nib;
        nib = (sel) ? a[7:4] : a[3:0];   
        is9 = (nib == 4'd9);
    endfunction
endmodule

Verilog makes it trivial to do what you want without the function:

module test(input [7:0] inp, 
             output reg [1:0] out
           );

    always @(*) begin
        out[0] = inp[3:0] == 4'd9;
        out[1] = inp[7:4] == 4'd9;
    end
endmodule
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.