Main Content

Array Layouts for HLS Code Generation

Array layout can be important for integration, usability, and performance. The code generator produces code that uses column-major layout by default. However, many devices, sensors, and libraries use row-major array layout for their data. You can apply your code directly to this data by generating code that uses row-major layout. Array layout can also affect performance. Many algorithms perform memory access more efficiently for one specific array layout.

You can specify row-major array layout at the command line, with code generation configuration properties, or by using the HDL Coder™ app. You can also specify row-major layout or column-major layout for individual functions. The inputs and outputs of your entry-point (top-level) functions must all use the same array layout.

Note

By default, code generation uses column-major array layout.

Specify row-major layout using -rowmajor option

Consider this function addMatrix and test bench addMatrix_tb for adding two matrices. The algorithm performs the addition through explicit row and column traversal.

% MATLAB Code, save it as addMatrix
function [S] = addMatrix(A,B) 
    %#codegen
    S = zeros(size(A));
    for row = 1:size(A,1) 
       for col = 1:size(A,2)  
         S(row,col) = A(row,col) + B(row,col);
    end
end

% MATLAB Test Bench, save it as addMatrix_tb
a = randi(100, 5,3);
b = randi(100, 5,3);

out = addMatrix(a,b);

Generate HLS code for addMatrix by using the -rowmajor option. Specify the form of the input parameters by using the -args option and launch the code generation report.

hdlcfg = coder.config("hdl");
hdlcfg.TestBenchName = "addMatrix_tb";
hdlcfg.Workflow = "High Level Synthesis";

codegen -config hdlcfg addMatrix -rowmajor -report

Alternatively, configure your code for row-major layout by modifying the RowMajor parameter in the code generation configuration object.

hdlcfg = coder.config("hdl");
hdlcfg.TestBenchName = "addMatrix_tb";
hdlcfg.Workflow = "High Level Synthesis";
hdlcfg.RowMajor = true; 
codegen -config hdlcfg addMatrix -launchreport

Code generation results in this HLS code:

... 
/* generated code for addMatrix using row-major */
for (int32_T row = 0; row < 5; row = row + 1) {
  for (int32_T col = 0; col < 3; col = col + 1) {
    S[row][col] = A[row][col] + B[row][col];
  }
} 
...

To specify row-major layout using the HDL Coder app:

  1. Open the HDL Coder dialog box. Select the Code Generation Workflow as MATLAB to HLS.

  2. Go to the HLS Code Generation step.

  3. On the Advanced tab, set Array layout: Row-major.

To verify that your generated code uses row-major layout, compare the array indexing in your generated code with the array indexing in code that uses column-major layout. You can also generate code that uses upto 3-dimensional indexing. 3-dimensional indexing can make differences in array layout more apparent.

Specify Array Layout in Functions

You can specialize individual MATLAB® functions for row-major layout or column-major layout by inserting coder.rowMajor or coder.columnMajor calls into the function body. Using these function specializations, you can combine row-major data and column-major data in your generated code.

For the entry-point (top-level) functions, all inputs and outputs must use the same array layout. In the generated HLS code, the entry-point function interface accepts and returns data with the same array layout as the function array layout specification.

For an example of a specialized function, consider function addMatrixRM and test bench addMatrixRM_tb:

% MATLAB code, save it as addMatrixRM
function [S] = addMatrixRM(A,B)
    %#codegen
    S = zeros(size(A));
    coder.rowMajor; % specify row-major code
    for row = 1:size(A,1)
       for col = 1:size(A,2)
         S(row,col) = A(row,col) + B(row,col);
    end
end

% MATLAB test bench, save it as addMatrixRM_tb

a = randi(100, 5,3);
b = randi(100, 5,3);

out = addMatrixRM(a,b);
You can generate code for addMatrixRM by using these commands.
hdlcfg = coder.config("hdl");
hdlcfg.TestBenchName = "addMatrixRM_tb";
hdlcfg.Workflow = "High Level Synthesis";

codegen -config hdlcfg addMatrixRM -launchreport

Because of the coder.rowMajor call, the code generator produces code that uses data stored in row-major layout.

Other functions called from a row-major function or column-major function inherit the same array layout. If a called function has its own distinct coder.rowMajor or coder.columnMajor call, the local call takes precedence.

You can mix column-major and row-major functions in the same code. The code generator inserts transpose or conversion operations when passing data between row-major and column-major functions. These conversion operations ensure that array elements are stored as required by functions with different array layout specifications. For example, the inputs to a column-major function, called from a row-major function, are converted to column-major layout before being passed to the column-major function.

Query Array Layout of a Function

To query the array layout of a function at compile time, use coder.isRowMajor or coder.isColumnMajor. This query can be useful for specializing your generated code when it involves row-major and column-major functions. For example, consider this function:

function [S] = addMatrixRouted(A,B)
  if coder.isRowMajor
    %execute this code if row-major
    S = addMatrixRM(A,B);
  elseif coder.isColumnMajor
    %execute this code if column-major
    S = addMatrix_OptimizedForColumnMajor(A,B);
end
This function behaves differently depending on whether it is row-major or column-major. When addMatrixRouted is row-major, it calls the addMatrixRM function, which has efficient memory access for row-major data. When the function is column-major, it calls a version of the addMatrixRM function optimized for column-major data.

For example, consider this function definition. The algorithm iterates through the columns in the outer loop and the rows in the inner loop, in contrast to the addMatrixRM function.

function [S] = addMatrix_OptimizedForColumnMajor(A,B)
  %#codegen
  S = zeros(size(A));
  for col = 1:size(A,2)
    for row = 1:size(A,1)
      S(row,col) = A(row,col) + B(row,col);
  end
end
Code generation for this function yields:
... 
/* column-major layout */
for (col = 0; col < 10; col++) {
  for (row = 0; row < 20; row++) {
     S[row + 20 * col] = A[row + 20 * col] + B[row + 20 * col];  
  }
}
...
The generated code has a stride length of only one element. Due to the specializing queries, the generated code for addMatrixRouted provides efficient memory access for either choice of array layout.

Specify Array Layout in External Function Calls

To call external C/C++ functions that expect data stored with a specific layout, use coder.ceval with the layout syntax. If you do not use this syntax, the external function inputs and outputs are assumed to use column-major layout by default.

Consider an external C function designed to use row-major layout called myCFunctionRM. To integrate this function into your code, call the function using the -layout:rowMajor or -row option. This option ensures that the input and output arrays are stored in row-major order. The code generator automatically inserts array layout conversions as needed.

coder.ceval('-layout:rowMajor','myCFunctionRM',coder.ref(in),coder.ref(out)) 

Within a MATLAB function that uses row-major layout, you may seek to call an external function designed to use column-major layout. In this case, use the '-layout:columnMajor' or '-col' option.

coder.ceval('-layout:columnMajor','myCFunctionCM',coder.ref(in),coder.ref(out)) 

You can perform row-major and column-major function calls in the same code. Consider the function myMixedFn1 as an example:

function [E] = myMixedFn1(x,y)
%#codegen
% specify type of return arguments for ceval calls
D = zeros(size(x)); 
E = zeros(size(x));

% include external C functions that use row-major & column-major
coder.cinclude('addMatrixRM.h'); 
coder.updateBuildInfo('addSourceFiles', 'addMatrixRM.c');
coder.cinclude('addMatrixCM.h'); 
coder.updateBuildInfo('addSourceFiles', 'addMatrixCM.c');

% call C function that uses row-major order
coder.ceval('-layout:rowMajor','addMatrixRM', ...
    coder.rref(x),coder.rref(y),coder.wref(D));

% call C function that uses column-major order
coder.ceval('-layout:columnMajor','addMatrixCM', ...
    coder.rref(x),coder.rref(D),coder.wref(E));
end

The external files are:

 addMatrixRM.h

 addMatrixRM.c

 addMatrixCM.h

 addMatrixCM.c

To generate code, enter:

hdlcfg = coder.config("hdl");
hdlcfg.Workflow = "High Level Synthesis";

codegen -config hdlcfg myMixedFn1 -args {ones(20,10),ones(20,10)} -rowmajor -launchreport

See Also

| | |

Topics