Vitis Model Composer lets you import C or C++ functions to create a library of blocks. However it requires the code to be properly recognized and processed. You can define the function source in either a header file (.h), or in a C or C++ source file (.c, .cpp). The header file must include the function signature.
You can import functions with function arguments that are real or complex types of scalar, vectors, or matrices. You can also use all the data types supported by the Model Composer HLS Library, including fixed-point data types. Model Composer lets you define functions as templates. You can use input signals or customization parameters to set template variables. Set these parameters when you add the block to the model or before simulation.
Using function templates in your code lets you create a Model Composer block that supports different applications. This can increase the re-usability of your block library. Refer to Defining Blocks Using Function Templates for more information.
The xmcImportFunction command supports C++
functions using std::complex<T> or hls::x_complex<T> types. For more details, see the
explanation in Using Complex Types.
If the input of an imported function is a 1-D array, the tool can
automatically map the input signal to the function argument. For example, if the function
argument is a[10], then the connected signal in Model Composer
can be either of the following:
- a vector of size 10
- a row or column matrix of size 1x10 or 10x1
However, if all inputs and outputs of an imported function are scalar arguments, you can connect a vector or matrix signal to the input. In this case, the imported function processes each value of the vector, or matrix on the input signal as a separate value. It combines those values into the vector, or matrix on the output signal. For example, a vector of size 10 connected to a scalar input processes and returns each element of the vector to a vector of size 10 on the output signal.
y is the
output:#include <stdint.h>
#include <ap_fixed.h>
#pragma XMC OUTPORT y
#pragma XMC PARAMETER Limit
template <typename T>
void counter(T &y, int16_t Limit)
{
static T count = 0;
count++;
if (count > Limit)
count =0;
y = count;
}
SampleTime parameter is automatically added when
the block is created with xmcImportFunction command, as shown
in the Function declaration in the following image. The default value is -1 which means the
sample time inherits from the model. You can also explicitly specify the sample time by
customizing the block when it is added to a model, as shown below.The direction of ports for the function arguments can be determined
automatically by the xmcImportFunction command, or manually
specified by pragma with the function signature in the header file.
- Automatically determining input and output ports:
- The
returnvalue of the function is always defined as an output, unless the return value isvoid. - A formal function argument declared with the
constqualifier is defined as an input. - An argument declared with a reference, a pointer type, or an array
type without a
constqualifier is defined as an output. - Other arguments are defined as inputs by default (for example, scalar read-by-value).
- The
- Manually defining input and output ports:
- You can specify which function arguments are defined as inputs and outputs by adding the INPORT and OUTPORT pragmas into the header file immediately before the function declaration.
-
#pragma XMC INPORT <parameter_name> [, <parameter_name>...] -
#pragma XMC OUTPORT <parameter_name> [, <parameter_name>...]
In the following example in is automatically
defined as an input due to the presence of the const
qualifier, and out is defined as an output. The imported
block will also have a second output due to the integer return value of the function.
int func(const int in, int &out);
In the following function in is
automatically defined as an input, and out as an output,
however, there is no return value.
void func(const in[512], int out[512]);
The following example uses pragmas that were added to the source code before the function declaration to manually identify ports. This is the only modification to the original C++ code needed to import the function into Model Composer. In this example the pragmas specify which parameter is the input to the block and which parameter is the output of the block.
#pragma XMC INPORT din
#pragma XMC OUTPORT dout
void fir_sym (ap_fixed<17,3,AP_TRN,AP_WRAP> din[100],
ap_fixed<17,3,AP_TRN,AP_WRAP> dout[100]);
ap_fixed specifies a fixed-point number compatible with Vitis
HLS.Manually adding pragmas to the function signature in the header file to
define the input and output parameters of the function is useful when your code does not use
the const qualifier, and adding the const qualifier can require extensive editing of the source code when there is a
hierarchy of functions. It also makes the designation of the inputs and outputs explicit in
the code, which can make the relationship to the imported block more clear.
Some final things to consider when writing C or C++ code for importing into Model Composer:
- Developing your source code to be portable between 32-bit and 64-bit architectures.
- Your source code can use Vitis HLS pragmas for resource and performance optimization. Model Composer uses those pragmas but does not modify or add to them.
- If your code has static variables, the static variable is shared across
all instances of the blocks that import that function. If you do not want to share that
variable across all instances, copy and rename the function with the static variable and
import a new library block using the
xmcImportFunctioncommand. -
If you use C (.c) source files to model the library function (as opposed to C++ (.cpp) source files), the .h header file must include an
extern "C"declaration for the downstream tools (such as Vitis HLS) to work properly. An example of how to declare theextern "C"in the header files is as follows:// c_function.h: #ifdef __cplusplus extern 'C' { #endif void c_function(int in, int &out); #ifdef __cplusplus } #endif