Import Calls to External Code into Generated Code with Legacy Code Tool
Legacy Code Tool and Code Generation
You can use the Simulink® Legacy Code Tool to generate fully inlined C MEX S-functions for legacy or custom code. The S-functions are optimized for embedded components, such as device drivers and lookup tables, and they call existing C or C++ functions.
Note
The Legacy Code Tool can interface with C++ functions, but not C++ objects. To work around this issue so that the tool can interface with C++ objects, see Legacy Code Tool Limitations.
You can use the tool to:
If you want to include these types of S-functions in models for which you intend to generate code, use the tool to generate a TLC block file. The TLC block file specifies how the generated code for a model calls the existing C or C++ function.
If the S-function depends on files in folders other than the
folder containing the S-function dynamically loadable executable file,
use the tool to generate an sFunction
_makecfg.m
or rtwmakecfg.m
file
for the S-function. Generating the file maintains those dependencies
when you build a model that includes the S-function. For example,
for some applications, such as custom targets, you might want to locate
files in a target-specific location. The build process looks for sFunction
_makecfg.m
or rtwmakecfg.m
in
the same folder as the S-function dynamically loadable executable
and calls the function in the file.
For more information, see Integrate C Functions Using Legacy Code Tool.
Generate Inlined S-Function Files for Code Generation
Depending on the code generation requirements of your application, to generate code for a model that uses the S-function, do either of the following:
Generate one
.cpp
file for the inlined S-function. In the Legacy Code Tool data structure, set the value of theOptions.singleCPPMexFile
field totrue
before generating the S-function source file from your existing C function. For example:def.Options.singleCPPMexFile = true; legacy_code('sfcn_cmex_generate', def);
Generate a source file and a TLC block file for the inlined S-function. For example:
def.Options.singleCPPMexFile = false; legacy_code('sfcn_cmex_generate', def); legacy_code('sfcn_tlc_generate', def);
singleCPPMexFile Limitations
You cannot set the singleCPPMexFile
field
to true
if
Options.language='C++'
You use one of the following Simulink objects with the
IsAlias
property set totrue
:Simulink.Bus
Simulink.AliasType
Simulink.NumericType
The Legacy Code Tool function specification includes a
void*
orvoid**
to represent scalar work data for a state argumentHeaderFiles
field of the Legacy Code Tool structure specifies multiple header files
Apply Code Style Settings to Legacy Functions
To apply the model configuration parameters for code style to a legacy function:
Initialize the Legacy Code Tool data structure. For example:
def = legacy_code('initialize');
In the data structure, set the value of the
Options.singleCPPMexFile
field totrue
. For example:def.Options.singleCPPMexFile = true;
To check the setting, enter:
def.Options.singleCPPMexFile
Address Dependencies on Files in Different Locations
By default, the Legacy Code Tool assumes that files on which
an S-function depends reside in the same folder as the dynamically
loadable executable file for the S-function. If your S-function depends
on files that reside elsewhere and you are using the template makefile
build process, generate an sFunction
_makecfg.m
or rtwmakecfg.m
file for the S-function. For example, you might generate this
file if your Legacy Code Tool data structure defines compilation resources
as path names.
To generate the sFunction
_makecfg.m
or rtwmakecfg.m
file,
call the legacy_code
function
with 'sfcn_makecfg_generate'
or 'rtwmakecfg_generate'
as
the first argument, and the name of the Legacy Code Tool data structure
as the second argument. For example:
legacy_code('sfcn_makecfg_generate', lct_spec);
If you use multiple registration files in the same folder and generate an S-function for each
file with a single call to legacy_code
, the call to
legacy_code
that specifies
'sfcn_makecfg_generate'
or
'rtwmakecfg_generate'
must be common to
all registration files. For more information, see Handling Multiple Registration Files.
For example, if you define defs
as an array
of Legacy Code Tool structures, you call legacy_code
with 'sfcn_makecfg_generate'
once.
defs = [defs1(:);defs2(:);defs3(:)]; legacy_code('sfcn_makecfg_generate', defs);
For more information, see Build Support for S-Functions.
Deploy S-Functions for Simulation and Code Generation
You can deploy the S-functions that you generate with the Legacy Code Tool so that other people can use them. To deploy an S-function for simulation and code generation, share the following files:
Registration file
Compiled dynamically loadable executable
TLC block file
sFunction
_makecfg.m
orrtwmakecfg.m
fileHeader, source, and include files on which the generated S-function depends
When you use these deployed files:
Before using the deployed files in a Simulink model, add the folder that contains the S-function files to the MATLAB® path.
If the Legacy Code Tool data structure registers required files as absolute paths and the location of the files changes, regenerate the
sFunction
_makecfg.m
orrtwmakecfg.m
file.
Integrate External C++ Objects
The Legacy Code Tool can interface with C++ functions, but not C++ objects. Using the previous example as a starting point, here is an example of how you can work around this limitation.
Modify the class definition for
adder
in a new fileadder_cpp.hpp
. Add three new macros that dynamically allocate a newadder
object, invoke the methodadd_one()
, and free the memory allocated. Each macro takes a pointer to anadder
object. Because each function called by the Legacy Code Tool must have a C-like signature, the pointer is cached and passed as avoid*
. Then you must explicitly cast toadder*
in the macro. The new class definition foradder
:#ifndef _ADDER_CPP_ #define _ADDER_CPP_ class adder { private: int int_state; public: adder(): int_state(0) {}; int add_one(int increment); int get_val() {return int_state;}; }; // Method wrappers implemented as macros #define createAdder(work1) \ *(work1) = new adder #define deleteAdder(work1) \ delete(static_cast<adder*>(*(work1))) #define adderOutput(work1, u1) \ (static_cast<adder*> ((work1)))->add_one(u1) #endif /* _ADDER_CPP_ */
Update
adder_cpp.cpp
. With the class modification, instead of one global instance, each generated S-function manages its ownadder
object.#include "adder_cpp.hpp" int adder::add_one(int increment) { int_state += increment; return int_state; }
Update
adder_cpp.cpp
with the following changes:StartFcnSpec
calls the macro that allocates a newadder
object and caches the pointer.def.StartFcnSpec = 'createAdder(void **work1)';
OutputFcnSpec
calls the macro that invokes the methodadd_one()
and provides the S-function specificadder
pointer object.def.OutputFcnSpec = 'int32 y1 = adderOutput(void *work1, int32 u1)';
TerminateFcnSpec
calls the macro that frees the memory.def.TerminateFcnSpec = 'deleteAdder(void **work1)';