Question regarding the range of part select of a vector

Hi,
I have following system verilog code :


module test(
  input wire [31:0] udp_data_in, 
  output reg [7:0] data_out[3:0],
  input clk,
  input reset);
  
  always @ (posedge clk or posedge reset) begin
    if (reset == 1'b1) begin
      for (int i = 0; i < 4; i++) begin
        data_out[i] <= 8'b0;
      end
    end
    else begin
      for (int i = 0; i < 4; i++) begin
         data_out[i] <= udp_data_in[8*i + 7: 8*i];
     //   data_out[0] <= udp_data_in[7:0];
     //   data_out[1] <= udp_data_in[15:8];
     //   data_out[2] <= udp_data_in[23:16];
     //   data_out[3] <= udp_data_in[31:24]; 
      end 
    end
  end
endmodule


This gives following error :
"Error-[IRIPS] Illegal range in part select
design.sv, 16
The range of the part select is illegal:
Unknown range in part select.udp_data_in[((8 * i) + 7):(8 * i)]

Error-[TCF-CETE] Cannot evaluate the expression
design.sv, 16
“((8 * i) + 7)”
Cannot evaluate the expression in left slicing expression.
The expression must be compile time constant."
I have following questions with reference to above error :

1.if I uncomment 4 lines which are commented and manually expand the loop, this works. However, if I need to write similar logic for much bigger vector say udp_data_in[255:0], this will be tedious - need to write 32 lines of code and set the ranges manually. So how can I write this logic with a loop for much bigger vectors?

  1. The error message says “The expression must be compile time constant.” But 8*i + 7 expression is compile time constant. Compiler can determine its value while compiling since i is not a variable (an internal signal or signal on the port whose value is not known at compile time. Can someone explain this please.

  2. The error message also says " Unknown range in part select.udp_data_in[((8 * i) + 7):(8 * i)]". But the range is known and depends only on value of i, the loop variable. So I feel it is known. I am confused here. Can someone explain please.
    "

  3. One way to implement this logic will be with packed array. Is there any other way to implement above logic with a loop?

thanks,
-sunil

In reply to puranik.sunil@tcs.com:

You want to look at section 7.4.6 of the LRM:


module test(
  input wire [31:0] udp_data_in, 
  output reg [7:0] data_out[3:0],
  input clk,
  input reset);
 
  always @ (posedge clk or posedge reset) begin
    if (reset == 1'b1) begin
      for (int i = 0; i < 4; i++) begin
        data_out[i] <= 8'b0;
      end
    end
    else begin
      for (int i = 0; i < 4; i++) begin
         data_out[i] <= udp_data_in[8*i + 7 -: 8];
     //   data_out[0] <= udp_data_in[7:0];
     //   data_out[1] <= udp_data_in[15:8];
     //   data_out[2] <= udp_data_in[23:16];
     //   data_out[3] <= udp_data_in[31:24]; 
      end 
    end
  end
endmodule

In reply to cgales:

You can also do this without a for-loop using the streaming operator (11.4.14):

data_out <= {>>{data_in}};

Perhaps the next SystemVerilog standard (202x) will let you do that. It’s still being tracked.

Thank you Dave, Cgales and Sbellock for your immediate replies.
regards,
-sunil