- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
While writing a ternary adder tree (code below and in attached project archive), I wrote a constant function (IEEE 1800-2023 section 13.4.3) that uses a loop to calculate parameter values for lower level instances based on the given parameter. The code worked well in Vivado and Verilator, but is totally broken in Quartus Prime Lite 24.1.
The first problem is the loop not terminating on return, causing elaboration errors:
Error (10106): Verilog HDL Loop error at ternary_adder_tree.sv(15): loop must terminate within 5000 iterations
Error (10903): Verilog HDL error at ternary_adder_tree.sv(44): failed to elaborate task or function "smaller_pow_3"
Error (10192): Verilog HDL Defparam Statement error at ternary_adder_tree.sv(44): value for parameter "MaxSubN" must be constant expression
Error (12153): Can't elaborate top-level user hierarchy
The loop should terminate during second iteration (N = 9):
- p = 1; next_p = 3; cond false
- p = 3; next_p = 9; cond true - return from function with value 3
If the while (1) loop is replaced with an empty-body for (commented out in code), the loop terminates, but the function somehow returns a value that is not constant and also causes the 10192 error:
Warning (10242): Verilog HDL Function Declaration warning at ternary_adder_tree.sv(24): variable "p" may have a Don't Care value because it may not be assigned a value in every possible path through the statements preceding its use
Error (10192): Verilog HDL Defparam Statement error at ternary_adder_tree.sv(44): value for parameter "MaxSubN" must be constant expression
The warning (10242) emitted before the error is a false positive, because initialisation in for-loop construct is not conditional and in fact is the first thing that happens. Moving the initialisation of p to its declaration gets rid of the warning, but the error remains.
Replacing the loop with multiple if (x <= X) return Y; lines works, but is not scalable and prone to errors. Are loops in functions for some known reason broken/forbidden or am I doing something wrong? I've already seen loop generate constructs breaking things in Quartus, but I hope this time manual copy&paste is not necessary.
// Uncomment the line below to use a version that works in Quartus
// `define NO_LOOP
module ternary_adder_tree #(
parameter int WIDTH = 8,
parameter int N = 9
) (
input logic [WIDTH-1:0] inputs[N],
output logic [WIDTH-1:0] sum
);
function automatic int smaller_pow_3(input int x);
`ifndef NO_LOOP
int p = 1;
int next_p;
while (1) begin
next_p = p * 3;
if (next_p >= x) return p;
p = next_p;
end
// This doesn't work either, but is not "infinite"
// int p;
// for (p = 1; p*3 < x; p *= 3);
// return p;
`else
// Quartus-compatible version
if (x <= 3) return 1;
if (x <= 9) return 3;
if (x <= 27) return 9;
if (x <= 81) return 27;
if (x <= 243) return 81;
if (x <= 729) return 243;
if (x <= 2187) return 729;
if (x <= 6561) return 2187;
if (x <= 19683) return 6561;
if (x <= 59049) return 19683;
return 'x;
`endif
endfunction
function automatic int min(input int a, input int b);
return a <= b ? a : b;
endfunction
localparam int MaxSubN = smaller_pow_3(N);
localparam int Sub1N = MaxSubN;
localparam int Sub2N = min(MaxSubN, N - Sub1N);
localparam int Sub3N = min(MaxSubN, N - Sub1N - Sub2N);
// Quartus for some reason requires generate keyword (conditional generate constructs are not supported outside generate regions)
generate
if (N == 3) assign sum = inputs[0] + inputs[1] + inputs[2];
if (N == 2) assign sum = inputs[0] + inputs[1];
if (N == 1) assign sum = inputs[0];
if (N > 3) begin : g_adder_subadders
logic [WIDTH-1:0] s1;
logic [WIDTH-1:0] s2;
ternary_adder_tree #(.WIDTH(WIDTH), .N(Sub1N)) i_at3_1(.inputs(inputs[0:Sub1N-1]), .sum(s1));
ternary_adder_tree #(.WIDTH(WIDTH), .N(Sub2N)) i_at3_2(.inputs(inputs[Sub1N:Sub1N+Sub2N-1]), .sum(s2));
if (Sub3N > 0) begin : g_adder_sub_full3
logic [WIDTH-1:0] s3;
ternary_adder_tree #(.WIDTH(WIDTH), .N(Sub3N)) i_at3_3(.inputs(inputs[Sub1N+Sub2N:N-1]), .sum(s3));
assign sum = s1 + s2 + s3;
end else begin : g_adder_sub_only2
assign sum = s1 + s2;
end
end
endgenerate
endmodule
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Working version
After trying multiple different ways, I found a working version:
function automatic int smaller_pow_3(input int x);
smaller_pow_3 = 1;
while(smaller_pow_3 * 3 < x) smaller_pow_3 *= 3;
endfunction
This has:
- while instead of for
- no local variables
- no return statement (return value assigned to function name)
Not working versions
Other technically correct, but not working versions (based on the working one):
1. local variable + (optional) return
function automatic int smaller_pow_3(input int x);
int p = 1;
while (p * 3 < x) p *= 3;
return p;
// or
// smaller_pow_3 = p;
endfunction
Using a separate local variable makes Quartus think that the loop does not terminate. It does not matter whether a return statement or assignment to function name is used - just using a variable breaks synthesis.
2. Equivalent for loop
function automatic int smaller_pow_3(input int x);
for (smaller_pow_3 = 1; smaller_pow_3 * 3 < x; smaller_pow_3 *= 3)
;
endfunction
According to Verilog-2001 standard (IEEE 1364-2001, section 9.6) this should behave exactly like the working version with while loop, but in Quartus it warns that the function may return Don't Care (10241) and exits with an error, because the returned value is not a constant expression (10192).
Conclusions
I'm not sure why the not working versions don't, work, but if someone else has this problem (in SystemVerilog or Verilog) try the following.
In Quartus Prime Standard/Lite:
- don't use for loops - try an equivalent while
- don't use local variables in functions
- if you need a variable to return, use assignment to function name (even as temporary variable)
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Unfortunately, SystemVerilog has limited support in Quartus Standard or Lite Edition, and the software has been on maintenance mode years ago and no feature is added.
To know more about the SystemVerilog construct supported in Quartus Standard/Lite (available in offline only), you will need to go to the Quartus > Help > Help Topics. (You will need to install the "Quartus Prime Help <version>" first before navigate to the "Quartus Prime Standard Edition Help version 18.1"). Then, search for Quartus Prime Support for SystemVerilog.
https://www.intel.com/content/www/us/en/support/programmable/articles/000097917.html
If you have the Quartus Prime Pro software, your code or project will compile successfully.
Regards,
Richard Tan
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
the problem isn't specific to SystemVerilog, Quartus synthesis simply doesn't like infinite loops. You get quite similar behaviour e.g. in VHDL.
The function parameters are however numbers of finite length, e.g. 32 bit integer. All you need to do is to rewrite your function in a forms that limits the iteration count to a finite value. To be recognized as a constant value, the function result also must not depend on previous iterations, as you already found out.
int i;
for (i=0; i < 10; i++)
if (3**(i+1) >= x) return 3**i;
return 3**10;
Regards
Frank
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Working version
After trying multiple different ways, I found a working version:
function automatic int smaller_pow_3(input int x);
smaller_pow_3 = 1;
while(smaller_pow_3 * 3 < x) smaller_pow_3 *= 3;
endfunction
This has:
- while instead of for
- no local variables
- no return statement (return value assigned to function name)
Not working versions
Other technically correct, but not working versions (based on the working one):
1. local variable + (optional) return
function automatic int smaller_pow_3(input int x);
int p = 1;
while (p * 3 < x) p *= 3;
return p;
// or
// smaller_pow_3 = p;
endfunction
Using a separate local variable makes Quartus think that the loop does not terminate. It does not matter whether a return statement or assignment to function name is used - just using a variable breaks synthesis.
2. Equivalent for loop
function automatic int smaller_pow_3(input int x);
for (smaller_pow_3 = 1; smaller_pow_3 * 3 < x; smaller_pow_3 *= 3)
;
endfunction
According to Verilog-2001 standard (IEEE 1364-2001, section 9.6) this should behave exactly like the working version with while loop, but in Quartus it warns that the function may return Don't Care (10241) and exits with an error, because the returned value is not a constant expression (10192).
Conclusions
I'm not sure why the not working versions don't, work, but if someone else has this problem (in SystemVerilog or Verilog) try the following.
In Quartus Prime Standard/Lite:
- don't use for loops - try an equivalent while
- don't use local variables in functions
- if you need a variable to return, use assignment to function name (even as temporary variable)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you for sharing the solution. This will help other users who might come across a similar issue.
Regards,
Richard Tan

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page