Create S-functions that work seamlessly with the Simulink® and code generator products by using the wrapper concept. You can:
Interface your algorithms in Simulink models by writing MEX S-function wrappers
(
).sfunction
.mex
Direct the code generator to insert your algorithm into the generated code
by creating a TLC S-function wrapper
(
).sfunction
.tlc
Creating S-functions by using an S-function wrapper enables you to insert C/C++ code algorithms in Simulink models and the generated code with little or no change to your original C/C++ function. A MEX S-function wrapper is an S-function that calls code, which resides in another module.
Note
Use a MEX S-function wrapper only in the MATLAB® version in which you created the wrapper.
Suppose that you have an algorithm (that is, a C function) called
my_alg
that resides in the file my_alg.c
.
You can integrate my_alg
into a Simulink model by creating a MEX S-function wrapper (for example,
wrapsfcn.c
). A Simulink model can then call my_alg
from an S-Function
block. The Simulink S-function contains a set of empty functions that the Simulink engine requires for various API related purposes. For example,
although only mdlOutputs
calls my_alg
, the
engine calls mdlTerminate
, even though this S-function routine
performs no action.
You can embed the call to my_alg
in the generated code by
creating a TLC S-function wrapper (for example, wrapsfcn.tlc
).
You can eliminate the empty function calls. You can avoid the overhead of executing
the mdlOutputs
function and you can then eliminate the
my_alg
function.
Wrapper S-functions are useful when you are creating algorithms that are procedural or when you are integrating legacy code into a Simulink model. If you want to create code that is:
Interpretive in nature (that is, highly parameterized by operating modes)
Heavily optimized (that is, no extra tests to decide what mode the code is operating in)
then you must create a fully inlined TLC file for your S-function.
The next figure shows the wrapper S-function concept.
Using an S-function wrapper to import algorithms into your Simulink model means that the S-function serves as an interface that calls your
C/C++ algorithms from mdlOutputs
. You can quickly integrate
large standalone C/C++ programs into your model without having to change the
code.
This sample model includes an S-function wrapper.
Two files are associated with the wrapsfcn
block: the
S-function wrapper and the C/C++ code that contains the algorithm. The first three
statements:
Define the name of the S-function (what you enter in the Simulink S-Function block dialog box).
Specify that the S-function is using the level 2 format.
Provide access to the SimStruct
data structure.
SimStruct
contains pointers to data used during
simulation and code generation and defines macros that store data in and
retrieve data from the SimStruct
.
#define S_FUNCTION_NAME wrapsfcn #define S_FUNCTION_LEVEL 2 #include "simstruc.h" extern real_T my_alg(real_T u); /* Declare my_alg as extern */ /* * mdlInitializeSizes - initialize the sizes array */ static void mdlInitializeSizes(SimStruct *S) { ssSetNumSFcnParams( S, 0); /*number of input arguments*/ if (!ssSetNumInputPorts(S, 1)) return; ssSetInputPortWidth(S, 0, 1); ssSetInputPortDirectFeedThrough(S, 0, 1); if (!ssSetNumOutputPorts(S,1)) return; ssSetOutputPortWidth(S, 0, 1); ssSetNumSampleTimes( S, 1); } /* * mdlInitializeSampleTimes - indicate that this S-function runs * at the rate of the source (driving block) */ static void mdlInitializeSampleTimes(SimStruct *S) { ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME); ssSetOffsetTime(S, 0, 0.0); } /* * mdlOutputs - compute the outputs by calling my_alg, which * resides in another module, my_alg.c */ static void mdlOutputs(SimStruct *S, int_T tid) { InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); real_T *y = ssGetOutputPortRealSignal(S,0); *y = my_alg(*uPtrs[0]); /* Call my_alg in mdlOutputs */ } /* * mdlTerminate - called when the simulation is terminated. */ static void mdlTerminate(SimStruct *S) { } #ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */ #include "simulink.c" /* MEX-file interface mechanism */ #else #include "cg_sfun.h" /* Code generation registration function */ #endif
For more information, see Templates for C S-Functions.
The S-function routine mdlOutputs
contains a function call to
my_alg
, which is the C function containing the algorithm
that the S-function performs. For my_alg.c
, the code is:
#ifdef MATLAB_MEX_FILE #include "tmwtypes.h" #else #include "rtwtypes.h" #endif real_T my_alg(real_T u) { return(u * 2.0); }
For more information, see Manage Build Process File Dependencies.
The wrapper S-function wrapsfcn
calls
my_alg
, which computes u * 2.0
. To build
wrapsfcn.mex
, use this command:
mex wrapsfcn.c my_alg.c
A TLC S-function wrapper is a TLC file that specifies how the code generator calls
your code. For example, you can inline the call to my_alg
in
the mdlOutputs
section of the generated code. In the MEX S-Function Wrapper example, the call to my_alg
is embedded in the mdlOutputs
section as:
*y = my_alg(*uPtrs[0]);
When you are creating a TLC S-function wrapper, the goal is to embed the same type of call in the generated code.
Look at how the code generator executes S-functions that are not inlined. A
noninlined S-function is identified by the absence of the file
and the
existence of sfunction
.tlc
. When
generating code for a noninlined S-function, the code generator produces a call to
sfunction
.mexmdlOutputs
through a function pointer that, in this
example, then calls my_alg
.
The wrapper example contains one S-function, wrapsfcn.mex
. You
must compile and link an additional module, my_alg
, with the
generated code. At the MATLAB command prompt, enter:
set_param('wrapper/S-Function','SFunctionModules','my_alg')
The code generated when using grt.tlc
as the system target file
without
wrapsfcn.tlc
is:
<Generated code comments for wrapper model with noninlined wrapsfcn S-function> #include <math.h> #include <string.h> #include "wrapper.h" #include "wrapper.prm" /* Start the model */ void mdlStart(void) { /* (start code not required) */ } /* Compute block outputs */ void mdlOutputs(int_T tid) { /* Sin Block: <Root>/Sin */ rtB.Sin = rtP.Sin.Amplitude * sin(rtP.Sin.Frequency * ssGetT(rtS) + rtP.Sin.Phase); /* Level2 S-Function Block: <Root>/S-Function (wrapsfcn) */ { /* Noninlined S-functions create a SimStruct object and * generate a call to S-function routine mdlOutputs */ SimStruct *rts = ssGetSFunction(rtS, 0); sfcnOutputs(rts, tid); } /* Outport Block: <Root>/Out */ rtY.Out = rtB.S_Function; } /* Perform model update */ void mdlUpdate(int_T tid) { /* (update code not required) */ } /* Terminate function */ void mdlTerminate(void) { /* Level2 S-Function Block: <Root>/S-Function (wrapsfcn) */ { /* Noninlined S-functions require a SimStruct object and * the call to S-function routine mdlTerminate */ SimStruct *rts = ssGetSFunction(rtS, 0); sfcnTerminate(rts); } } #include "wrapper.reg" /* [EOF] wrapper.c */
The wrapper.reg
generated file contains the initialization of
the SimStruct
for the wrapper S-Function block. There is one
child SimStruct
for each S-Function block in your model. You can
significantly reduce this overhead by creating a TLC wrapper for the
S-function.
The generated code makes the call to your S-function,
wrapsfcn.c
, in mdlOutputs
by using this
code:
SimStruct *rts = ssGetSFunction(rtS, 0); sfcnOutputs(rts, tid);
This call has computational overhead associated with it. The Simulink engine creates a SimStruct
data structure for the
S-Function block. The code generator constructs a call through a function pointer to
execute mdlOutputs
, then mdlOutputs
calls
my_alg
. By inlining the call to your C/C++ algorithm,
my_alg
, you can eliminate both the
SimStruct
and the extra function call, thereby improving the
efficiency and reducing the size of the generated code.
Inlining a wrapper S-function requires an
file for the
S-function. The TLC file must contain the function call to
sfunction
.tlcmy_alg
. The figure shows the relationships between the
algorithm, the wrapper S-function, and the
file.sfunction
.tlc
To inline the call to my_alg
, place your function call in an
file with the
same name as the S-function (in this example, sfunction
.tlcwrapsfcn.tlc
). The
Target Language Compiler overrides the default method of placing calls to your
S-function in the generated code.
This code is the TLC file wrapsfcn.tlc
that inlines
wrapsfcn.c
:
%% File : wrapsfcn.tlc %% Abstract: %% Example inlined tlc file for S-function wrapsfcn.c %% %implements "wrapsfcn" "C" %% Function: BlockTypeSetup ==================================================== %% Abstract: %% Create function prototype in model.h as: %% "extern real_T my_alg(real_T u);" %% %function BlockTypeSetup(block, system) void %openfile buffer extern real_T my_alg(real_T u); /* This line is placed in wrapper.h */ %closefile buffer %<LibCacheFunctionPrototype(buffer)> %endfunction %% BlockTypeSetup %% Function: Outputs =========================================================== %% Abstract: %% y = my_alg( u ); %% %function Outputs(block, system) Output /* %<Type> Block: %<Name> */ %assign u = LibBlockInputSignal(0, "", "", 0) %assign y = LibBlockOutputSignal(0, "", "", 0) %% PROVIDE THE CALLING STATEMENT FOR "algorithm" %% The following line is expanded and placed in mdlOutputs within wrapper.c %<y> = my_alg(%<u>); %endfunction %% Outputs
The first section of this code inlines the wrapsfcn
S-Function
block and generates the code in C:
%implements "wrapsfcn" "C"
The next task is to inform the code generator that the routine
my_alg
must be declared as external in the generated
wrapper.h
file for any
wrapsfcn
S-Function blocks in the model. Do this declaration once
for all
wrapsfcn
S-Function blocks by using the
BlockTypeSetup
function. In this function, you direct the
Target Language Compiler to create a buffer and cache the my_alg
as extern
in the wrapper.h
generated header
file.
The final step is the inlining of the call to the function
my_alg
. The Outputs
function inlines
the call. In this function, you access the block input and output and place a direct
call to my_alg
. The call is embedded in
wrapper.c
.
The code generated when you inline your wrapper S-function is similar to the
default generated code. The mdlTerminate
function does not
contain a call to an empty function and the mdlOutputs
function
now directly calls my_alg
.
void mdlOutputs(int_T tid) { /* Sin Block: <Root>/Sin */ rtB.Sin = rtP.Sin.Amplitude * sin(rtP.Sin.Frequency * ssGetT(rtS) + rtP.Sin.Phase); /* S-Function Block: <Root>/S-Function */ rtB.S_Function = my_alg(rtB.Sin); /* Inlined call to my_alg */ /* Outport Block: <Root>/Out */ rtY.Out = rtB.S_Function; }
wrapper.reg
does not create a child
SimStruct
for the S-function because the generated code is
calling my_alg
directly, eliminating over 1 KB of memory
usage.