Main Content

Call Custom HLS Code from the Generated HLS Code

From within your MATLAB® code, you can directly call external HLS code, also called custom code or legacy code. To call external functions, use coder.ceval. The code generator integrates your custom HLS code into the HLS code generated from MATLAB. Integrate code when there are external libraries or optimized code developed in HLS that you want to use with your generated code.

MATLAB to HLS workflow enables providing additional build information via coder.updateBuildInfo to compile the generated HLS code. This includes —

  • Source files

  • Include files

  • Source paths

  • Include paths

  • Non-build files

  • Compile flags

Note

Use coder.ceval only in MATLAB code intended for HLS code generation. coder.ceval generates an error in uncompiled MATLAB code. To determine if a MATLAB function is executing in MATLAB, use coder.target. If the function is executing in MATLAB, call the MATLAB version of the external HLS function.

Following are some of the primary workflows for external code integration. For more examples, see the coder.ceval.

Call C++ Code

This example shows how to integrate a simple HLS C++ function with MATLAB® code by using coder.ceval. Consider the MATLAB® function, mathAdd:

function [added, multed] = mathAdd(in1, in2)
  added = in1+in2;
  multed = in1*in2; 
end

For this example, suppose that you want to implement the addition operation by using external C++ code. Consider the C++ function, adder, implemented in the file adder.cpp:

#include "adder.h"

sc_int<10> adder(sc_int<9> a, sc_int<9> b) {

return a + b;

}

To integrate adder with your MATLAB® code, you need a header file that contains the function prototype. See the file adder.h:

#include <systemc.h>

sc_int<10> adder(sc_int<9> a, sc_int<9> b);

Use the coder.ceval command to call the C++ function in mathAddIntegrated.m. Specify the header file and source/include file information by using coder.cinclude and coder.updateBuildInfo respectively.

function [added, multed] = mathAddIntegrated(in1, in2)
    %#codegen
    % For code generation, preinitialize the output variable data type, size, and complexity 
    added = zeros(size(in1), 'like', in1+in2);
    multed = zeros(size(in1), 'like', in1*in2);
    if coder.target('HLS')
      % Generate an include in the HLS code
      coder.cinclude('adder.h');
      % Specify the external source/include file information
      coder.updateBuildInfo('addSourceFiles', 'adder.cpp');
      coder.updateBuildInfo('addIncludeFiles', 'adder.h');

      % Use absolute paths for the source files and include files
      coder.updateBuildInfo('addSourcePaths', '/systemc/Documentation/R2024b/src');
      coder.updateBuildInfo('addIncludePaths', '/systemc/Documentation/R2024b/inc');
      % Evaluate the external C++ function
      added = coder.ceval('adder', in1, in2);
      multed = in1*in2; 
    else
      % MATLAB simulation behavior
      [added, multed] = mathAdd(in1,in2);
    end
end

% mathAddIntegrated_tb.m
a = fi(27,1,9,0,hdlfimath);
b = fi(25,1,9,0,hdlfimath);
out = mathAddIntegrated(a,b);

To generate HLS code, use the codegen command as shown below.

cfg = coder.config('hdl');
cfg.Workflow = "High Level Synthesis";
cfg.TestBenchName = "mathAddIntegrated_tb";
codegen -config cfg mathAddIntegrated -report
### Begin HLS Code Generation
### Working on mathAddIntegratedClass.hpp as mathAddIntegratedClass.hpp.
### Working on mathAddIntegratedModule.hpp as mathAddIntegratedModule.hpp.
### Generating Resource Utilization Report resource_report.html.
### Generating HDL Conformance Report mathAddIntegrated_hdl_conformance_report.html.
### HDL Conformance check complete with 0 errors, 0 warnings, and 0 messages.
### Code generation successful: To view the report, open('codegen/mathAddIntegrated/hdlsrc/html/report.mldatx')

Call Library Function

This example shows how to call a library function inside MATLAB® code by using coder.ceval. Consider the MATLAB® function, mycosh where you would like to call the cmath library function for cosh. Specify the header file using coder.cinclude. Since cmath is a built-in library, there is no need to specify include paths using coder.updateBuildInfo.

function out = mycosh(in)
%#codegen
out = coder.nullcopy(zeros('like', cosh(in)));
if coder.target('HLS')
    coder.cinclude('cmath');
    out = coder.ceval("std::cosh", in);
else
    out = cosh(in);
end
end

% mycosh_tb
for i = 1:10
    out = mycosh(rand(1,1));
end

To generate HLS code, use the codegen command as shown below.

cfg = coder.config('hdl');
cfg.Workflow = "High Level Synthesis";
cfg.TestBenchName = "mycosh_tb";
codegen -config cfg mycosh -report
### Begin HLS Code Generation
### Working on mycoshClass.hpp as mycoshClass.hpp.
### Working on mycoshModule.hpp as mycoshModule.hpp.
### Generating Resource Utilization Report resource_report.html.
### Generating HDL Conformance Report mycosh_hdl_conformance_report.html.
### HDL Conformance check complete with 0 errors, 0 warnings, and 0 messages.
### Code generation successful: To view the report, open('codegen/mycosh/hdlsrc/html/report.mldatx')

Return Multiple Values from a C++ Function

The C language restricts functions from returning multiple outputs. Instead, they return only a single, scalar value. The MATLAB functions coder.ref, coder.rref and coder.wref allow you to return multiple outputs from an external HLS function.

For example, suppose you write a MATLAB function foo that takes two inputs x and y and returns three outputs a, b, and c. In MATLAB, you call this function as follows:

[a,b,c] = foo(x,y)

If you rewrite foo as a C++ function, you cannot return three separate values a, b, and c through a return statement. Instead, create a C++ function with multiple pointer type arguments and pass the output parameters by reference. For example:

void foo(sc_int<10> x, sc_int<10> y, sc_int<10> *a, sc_int<10> *b, sc_int<10> *c)

Then you can call the C++ function from a MATLAB function by using the coder.ceval function.

coder.ceval('foo',x,y,coder.ref(a),coder.ref(b),coder.ref(c));

If your external C function only writes to or only reads from the memory that is passed by reference, you can use the coder.wref or coder.rref functions instead of coder.ref. Under certain circumstances, these functions can enable further optimization of the generated code.

Note

When you use coder.wref(arg) to pass arg by reference, your external C++ function must fully initialize the memory referenced by arg.

Pass Data by Reference

This example shows how to pass data by reference to and from an external C++ function.

Pass by reference is an important technique for C++ code integration. When you pass data by reference, the program does not need to copy data from one function to another. With pass by value, C code can return only a single scalar variable. With pass by reference, C code can return multiple variables, including arrays.

Consider the MATLAB function adderRef. This function uses external C++ code to add two arrays. The coder.rref and coder.wref commands instruct the code generator to pass pointers to the arrays, rather than copy them.

function out = adderRef(a,b)
    %#codegen
    out = fi(zeros(size(a)),1,10,0,hdlfimath);
    if coder.target('HLS')
        coder.cinclude('hlsAdd.h');
        coder.updateBuildInfo('addSourceFiles', 'hlsAdd.cpp');
        coder.updateBuildInfo('addIncludeFiles', 'hlsAdd.h');
        coder.updateBuildInfo('addSourcePaths', '/mathworks/devel/sandbox/psikakol/work/systemc/Documentation/R2024b/src');
        coder.updateBuildInfo('addIncludePaths', '/mathworks/devel/sandbox/psikakol/work/systemc/Documentation/R2024b/inc');
        % Pass a, b and out by reference.
        coder.ceval('hlsAdd', coder.rref(a), coder.rref(b), coder.wref(out), int32(numel(a)));
    else
        out = a + b;
    end
end

% adderRef_tb
a = fi(1:10,1,9,0,hdlfimath);
b = fi(11:20,1,9,0,hdlfimath);
out = adderRef(a,b);

The C++ code, hlsAdd.cpp, uses linear indexing to access the elements of the arrays:

#include "hlsAdd.h"

void hlsAdd(const sc_int<9>* in1, const sc_int<9>* in2, sc_int<10>* out, int numel)

{

for (int i=0; i<numel; i++) {

out[i] = in1[i] + in2[i]; }

}

To build the HLS code you must provide a header file, hlsAdd.h, with the function signature:

#include<systemc.h>

void hlsAdd(const sc_int<9>* in1, const sc_int<9>* in2, sc_int<10>* out, int numel);

To generate the HLS code, use these codegen commands.

cfg = coder.config('hdl');
cfg.Workflow = "High Level Synthesis";
cfg.TestBenchName = "adderRef_tb";
codegen -config cfg adderRef -report
### Begin HLS Code Generation
### Working on adderRefClass.hpp as adderRefClass.hpp.
### Working on adderRefModule.hpp as adderRefModule.hpp.
### Generating Resource Utilization Report resource_report.html.
### Generating HDL Conformance Report adderRef_hdl_conformance_report.html.
### HDL Conformance check complete with 0 errors, 0 warnings, and 0 messages.
### Code generation successful: To view the report, open('codegen/adderRef/hdlsrc/html/report.mldatx')