I am trying to write synthesizable code which would allow me to index certain bits from a vector for further manipulation; to be more specific, a wide data vector would be defined as the input of a module:
parameter WIDTH = 4096
input [WIDTH - 1:0] i_dat
then, multiple “masks” are defined to be assigned with the desired bits:
logic [$clog2(WIDTH) - 1:0][WIDTH/2 - 1:0] dat_masks;
In order to make this module parameterizable, the mask extraction is defined as follows:
always_comb
begin
for(int i = 0; i < $clog2(WIDTH); i++)
begin
for(int j = (2**i), k = 0; j < $clog2(WIDTH); j = j + (2**(i + 1)), k = k + (2**i))
begin
dat_masks[i][k +: pwrs_of_2[i]] = i_dat[j +: pwrs_of_2[i]];
end
end
end
As you can see in the snippet above, each mask requires a different width_expr to generate the desired pattern. Because width_expr must be constant, I had defined earlier in the code the following list of constant values:
localparam int pwrs_of_2[10] = '{32'd1, 32'd2, 32'd4, 32'd8, 32'd16, 32'd32, 32'd64, 32'd128, 32'd256, 32'd512};
This way, I was hoping that pwrs_of_2[i] could be interpreted as a constant value in the loop; that doesn’t seem to be the case however. My simulator tool indicates an error saying that “Range width must be constant expression”. My synthesis tool, on the other hand, doesn’t produce any error, but produces the wrong result: pwrs_of_2[i] is always 1.
Does anybody know why pwrs_of_2[i] is not considered a constant value? Any ideas on how to have these different width values referenced by the respective loop iteration?
By the way, to further comment on this: by no means I am a SystemVerilog expert, but, in my opinion, I found this concept of a constant width_expr a bit too restrictive as it is (at least based on my experience trying to get around it). What I mean by that is: yes, width_expr should be constant indeed, but it should have to be constatnt “in the scope in which it is being used”. In other words, one should be able to write the previous code like:
always_comb
begin
for(int i = 0; i < $clog2(WIDTH); i++)
begin
for(int j = (2**i), k = 0; j < $clog2(WIDTH); j = j + (2**(i + 1)), k = k + (2**i))
begin
dat_masks[i][k +: 2**i] = i_dat[j +: 2**i];
end
end
end
The reason why I think so is because if one considers a for loop as simply an expression being unfolded into multiple “instances” of it, 2**i could be certainly seen as constant in each iteration of the outermost for loop.
Any feedback on that would be appreciated.