Passing array as an argument (packed vs unpacked, dynamic vs fixed)

Hi there,
I have an issue with passing an array as an argument to a task.
Objective: I have a test that creates a scenario and has an internal small checker that needs to check values between actual and expected results. both results are poured to arrays which sizes can vary - therefore fixed size arrays cannot be used as an input to the case. the size of the input themselves is known, meaning:


//start of code
class test1 extends test_base;
bit array1 [2:0] [32:0];
bit array2 [5:0] [32:0];
bit array3 [8:0] [32:0];
bit array4 [14:0] [32:0];
extern task check_values(input bit array1 [][], input bit array2 [][]); //internal small checker, using dynamic arrays because I don't know the size
//..
//..
//somewhere later
check_values(array1, array2) // here I know the size
check_values(array3, array4) // here I know the size
//continue of code
endclass

task check_values (input bit actual_array [][], input bit expected_array [][]);
//implementation code
endtask


NOTE: don’t mind the array sizes it is not a 1-1 ratio and it is just for the sake of argument and example/explanation.
so a few issues/questions:

  1. is it ok to pass any size of array I want without any need to allocate using “new” method? (the compiler didn’t have any issue but the code itself didn’t manage to compile yet, for other reasons, so I don’t know for sure that everything is ok)
  2. I tried using ref (changed the task to automatic accordingly) instead of input but then I received: Illegal assignment: Cannot assign an unpacked type to a packed type (I cannot change all the fixed size array to unpacked because I use an internal function in our environment which can use only packed arrays)
  3. another issue I encountered and which I was able to fix but wanted clarification:
    when I declare an 2D array outside of the task and don’t pass it as an argument, when I use the following: array[i][31:0] inside a for loop everything is fine, when I pass it as an argument I receive: illegal range in part select. I solved by changing the slicing into [i][31 -: 0], why this solution works?

overall if any guidance and rule of thumbs can help on how to work with arrays while passing them as arguments (“when in situation A it is preferred to this but in B otherwise”) would be appreciated to clear the thought process a bit.

Thank you in advance for helping in the issue.

In reply to gabi0307:

You can always copy equivalently typed unpacked arrays (fixed-sized, dynamic, or queues) from one kind to another as a whole array (associative arrays can only be copied to the same kind as a whole). The target array automatically gets correctly sized to size of the source array. If the target array is fixed-sized, you get an run-time error if the source array is not the exact same size as the target.

Passing an argument by reference(ref) requires exact matching types making it much more restrictive. The main use of ref arguments is when you have a time consuming task and need the argument values updated during the life of the task. Input arguments copy their value on entry to the task, and output arguments copy their value on exit of the task. You would not see any other intermediate value changes. Another use of ref arguments is if you have very large arrays and only plan to access a few of the elements from inside the task/function. This prevents the overhead of trying to copy the entire array.

You will have to post more code for help on your 3rd question.

In reply to dave_59:

Hi Dave, thanks for the quick reply. here is main code for said issue:



class test1 extends base_test;
string array2 [3];
bit [3:0] [31:0] array1;
bit [1:0] [31:0] array3;
bit [15:0] [127:0] data_array_concatenated;

event ev, ev1, ev2, ev3, ev4;

extern task check_values (int num, input bit actual_result_array[][], input string reg_name_array[], input bit expected_result_array[][]);

//somecode


check_values(2, array1, array2, array3)

endclass

task test1::check_values (int num, input bit actual_result_array[][], input string reg_name_array[], input bit expected_result_array[][]);

   //actual_result_array              = new[SIZE1];
   //reg_name_array            = new[SIZE2];
   //expected_result_array      = new[SIZE3];
   //data_array_concatenated    = new[15][127];

   /*
   foreach (actual_result_array[i]) begin
      actual_result_array[i] = new[31];
   end
   foreach (reg_name_array[i]) begin
      reg_name_array[i] = new[31];
   end
   foreach (expected_result_array[i]) begin
      expected_result_array[i] = new[31];
   end
   foreach (data_array_concatenated[i]) begin
      data_array_concatenated[i] = new[127];
   end
   */
   @(negedge internal_vif.done_something)
   for (int i = 0; i < num; i = i + 1) begin // { 
      -> ev;
      //we check if the address is only FF
      if (data_array_concatenated[i][31:0] == 32'hffffffff) 
      begin // {
         writing_flag_addr0 = 0;
      end // }
      if (data_array_concatenated[i][95:64] == 32'hffffffff) 
      begin // {
         writing_flag_addr1 = 0;
      end // }

      reg_name_array[2*i] = api.get_reg_name(data_array_concatenated[i][31:0]);
      functions_vif.read_reg(.reg_name(reg_name_array[2*i]), .reg_rd_data(actual_result_array[2*i]));

      reg_name_array[2*i+1] = api.get_reg_name(data_array_concatenated[i][95:64]);
      functions_vif.read_reg(.reg_name(reg_name_array[2*i+1]), .reg_rd_data(actual_result_array[2*i+1]));
      #1ns
      if (writing_flag_addr0) begin // {
         if (actual_result_array[2*i] != expected_result_array[i][63 -: 32]) begin // {
            `uvm_error(m_name, $sformatf("Mismatch between expected and actual. actual data: %x, expected data: %x", 
            actual_result_array[2*i], expected_result_array[i][63 -: 32]));
            -> ev_1;
         end // }
      end // }
      else begin // {
         if (actual_result_array[2*i] == expected_result_array[i][63 -: 32]) begin // {
            `uvm_error(m_name, $sformatf("Mismatch between expected and actual. actual data: %x, expected data: %x", 
            actual_result_array[2*i], expected_result_array[i][63 -: 32]));
            -> ev_2;
         end // }
      end // }
      if (writing_flag_addr1) begin // {
         if (actual_result_array[2*i+1] != expected_result_array[i][127 -: 96]) begin // {
            `uvm_error(m_name, $sformatf("Mismatch between expected and actual. actual data: %x, expected data: %x", 
            actual_result_array[2*i+1], expected_result_array[i][127 -: 32]));
            -> ev_3;
         end // }
      end // }
      else begin // {
         if (actual_result_array[2*i+1] == expected_result_array[i][127 -: 96]) begin // {
            `uvm_error(m_name, $sformatf("Mismatch between expected and actual. actual data: %x, expected data: %x", 
            actual_result_array[2*i+1], expected_result_array[i][127 -: 32]));
            -> ev_4;
         end // }
      end // }
   end // }
endtask

Short explanation:
we read a slice of data_array_concatenated to get address which we then use to find the register name using an api. we then read the data from that register after a point in time and and after that we compare to the expected array which was created elsewhere.
Error Message: ‘actual_result_array’ of ‘check_values’: Cannot assign a packed type ‘bit[31:0][3:0]’ to an unpacked type ‘bit $’.
*NOTE: you can see also what I was referring to in question 3.

In reply to gabi0307:
Your original question showed unpacked arrays

bit array1 [2:0] [32:0];
bit array3 [8:0] [32:0];

But your latest post shows packed arrays.

bit [3:0] [31:0] array1;
bit [1:0] [31:0] array3;

You need to bitstream cast your packed arrays to unpacked.