Search code examples
bit-manipulationverilogbitwise-operatorsbitwise-and

Bit wise 'AND' an array of registers in Verilog


I have an array of registers/buses and a single result bus defined as follows.

wire [BW-1:0] bus_array[NUM-1:0];
reg  [BW-1:0] and_result;

where

parameter BW  = 4;
parameter NUM = 8;

I wish to perform a BW-bit AND operation on the elements of the array and assign the result to the register and_result.

I try doing this as follows.

integer l;

generate 
genvar m;
for (m=0; m<BW; m=m+1)
begin : BW_LOOP

    always @ (*)
    begin
      and_result[m] = 1'b1;
      for (l=0; l<NUM; l=l+1)
        and_result[m] = and_result[m] & bus_array[l][m];
    end

end
endgenerate

However when I simulate this in Modelsim 10.1e, I get the following error.

Error: (vsim-3601) Iteration limit reached at time 2 ns

If I do not use the generate loop, and instead have BW instances of the always @ (*) block, the simulation works okay.

I can infer from the error message, that there is a problem with the generate for loop, but I am not able to resolve the problem.


Solution

  • Most likely a bug with ModelSim. Reproducible on EDAplayground with ModelSim10.1d. Works fine with Riviera2014 (After localizing l inside the generate loop). I'm guessing that and_result[m] is somehow in the @(*) sensitivity list, which it shouldn't be.

    l needs to be localized or it will be accessed in parallel with the generated always blocks; creating a potential raise condition.

    One workaround is to use SystemVerilog and use always_comb instead of always @(*).

    A backwarnd compatable solution is to change and_result[m] = and_result[m] & bus_array[l][m]; to if (bus_array[l][m]==1'b0) and_result[m] = 1'b0; which is equivalent code. This keeps and_result[m] only on as a left hand expression so it cannot be in the sensitivity list.

    genvar m;
    for (m=0; m<BW; m=m+1)
    begin : BW_LOOP
        integer l; // <== 'l' is local to this generate loop
    
        always @ (*)
        begin
          and_result[m] = 1'b1;
          for (l=0; l<NUM; l=l+1) begin
            if (bus_array[l][m]==1'b0) begin 
              and_result[m] = 1'b0;
            end
        end
    
    end
    

    Working code here