Data Transfer Size

The syntax for this pragma is:
#pragma SDS data copy|zero_copy(ArrayName[offset:length])

This pragma must be specified immediately preceding a function declaration, or immediately preceding other #pragma SDS bound to the function declaration. This pragma applies to all the callers of the bound function.

Some notes about the syntax:
  • The copy implies that data is explicitly copied from the processor memory to the hardware function. A suitable data mover as described in Improving System Performance performs the data transfer. The zero_copy means that the hardware function accesses the data directly from shared memory through an AXI4 bus interface. If no copy or zero_copy pragma is specified to an array argument, the SDSoC compiler assumes the copy semantics.
  • The [offset:length] part is optional. When this part is not specified, this pragma is only used to select between copying the memory to/from the accelerator versus directly accessing the memory by the accelerator. For the array size, the SDSoC compiler first analyzes the callers to the accelerator function to determine the transfer size based on the memory allocation APIs for the array (for example, malloc or sds_alloc etc.). If the analysis fails, it checks the argument type to see if the argument type has a compile-time array size and use that size as the data transfer size. If no data transfer size can be determined, the compiler generates an error message so that the user can specify this pragma. If the data size is different between the caller and callee, or different between multiple callers, the compiler also generates an error message so that the user can correct the source code or use this pragma to override the compiler analysis.
  • For a multi-dimensional array, each dimension should be specified. For example, for a 2-dimensional array, use ArrayName[offset_dim1:length_dim1][offset_dim2:length2_dim2]
  • Multiple arrays can be specified in the same pragma, separated by a comma(,). For example, use copy(ArrayName1[offset1:length1], ArrayName2[offset2:length2])
  • ArrayName must be one of the formal parameters of the function definition, that is, not from the prototype (where parameter names are optional) but from the function definition.
  • offset is the number of elements from the first element in the corresponding dimension. It must be a compile-time constant. This is currently ignored.
  • length is the number of elements transferred for that dimension. It can be an arbitrary expression as long as the expression can be resolved at runtime inside the function.

Example 1

The following code snippet shows an example of applying the "copy" pragma to the "A" and "B" arguments of an accelerator function "foo" right before the function declaration:
#pragma SDS data copy(A[0:size*size], B[0:size*size])
void foo(int *A, int *B, int size)
The SDSoC system compiler will replace the body of the function "foo" with accelertor control, data transfer, and data synchronization code. The following code snippet shows the data transfer part:
void _p0_foo_0(int *A, int *B, int size)
{
    ...
    cf_send_i(&(_p0_swinst_foo_0.A), A, (size*size) * 4, &_p0_request_0);
    cf_receive_i(&(_p0_swinst_foo_0.B), B, (size*size) * 4, &_p0_request_1);
    ...
}

As shown above, the pragma value "size*size" is used to tell the SDSoC runtime the number of elements of array "A" and "B". The cf_send_i and cf_receive_i require the number of bytes, so the compiler will multiply the "size*size" with the number of bytes for each element (4 in this case). As shown in the example above, length need not be a compile-time constant; it can be a C arithmetic expression involving other scalar arguments of the same function.

Example 2

The following code snippet shows an example of applying the "zero_copy" pragma instead of the "copy" pragma above:
#pragma SDS data zero_copy(A[0:size*size], B[0:size*size])
void foo(int *A, int *B, int size)
The data transfer part of the replaced function body becomes:
    cf_send_ref_i(&(_p0_swinst_foo_0.A), A, (size*size) * 4, &_p0_request_0);
    cf_receive_ref_i(&(_p0_swinst_foo_0.B), B, (size*size) * 4, &_p0_request_1);

The cf_send_ref_i and cf_receive_ref_i mean only transfer the reference or pointer of the array to the accelerator, and the accelerator will access the memory directly.

Example 3

The following code snippet illustrates a common mistake—using an argument name in the function declaration that is different from the function definition:
"foo.h"
#pragma SDS data copy(in_A[0:1024])
void foo(int *in_A, int *out_B)

"foo.cpp"
#include "foo.h"
void foo(int *A, int *B)
{
...
}
This code will go through gcc without any problems. Actually, any C/C++ compiler will ignore the argument name in the function declaration, because the C/C++ standard makes the argument name in the function declaration optional. Only the argument name in the function definition is used by the compiler. In case of SDSoC, it will issue a warning later:
WARNING: [SDSoC 0-0] Cannot find argument in_A in accelerator function foo(int *A, int *B)