| Products & Services | Solutions | Academia | Support | User Community | Company |
| Download Product Updates | | | Get Pricing | | | Trial Software |
| Documentation → Real-Time Workshop |
| Contents | Index |
| Learn more about Real-Time Workshop |
| On this page… |
|---|
This section describes how to create S-functions that work seamlessly with the Simulink and Real-Time Workshop products using the wrapper concept. This section begins by describing how to interface your algorithms in Simulink models by writing MEX S-function wrappers (sfunction.mex). It finishes with a description of how to direct the code generator to insert your algorithm into the generated code by creating a TLC S-function wrapper (sfunction.tlc).
Creating S-functions using an S-function wrapper allows you to insert C/C++ code algorithms in Simulink models and Real-Time Workshop 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 that resides in another module. A TLC S-function wrapper is a TLC file that specifies how the code generator should call your code (the same code that was called from the C MEX S-function wrapper).
Note A MEX S-function wrapper must only be used in the MATLAB version in which the wrapper is created. |
Suppose 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). Once this is done, a Simulink model can call my_alg from an S-Function block. However, 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 as well, even though this S-function routine performs no action.
You can integrate my_alg into generated code (that is, embed the call to my_alg in the generated code) by creating a TLC S-function wrapper (for example, wrapsfcn.tlc). The advantage of creating a TLC S-function wrapper is that the empty function calls can be eliminated and the overhead of executing the mdlOutputs function and then the my_alg function can be eliminated.
Wrapper S-functions are useful when you are creating new algorithms that are procedural in nature or when you are integrating legacy code into a Simulink model. However, 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 in your Simulink model means that the S-function serves as an interface that calls your C/C++ algorithms from mdlOutputs. S-function wrappers have the advantage that you can quickly integrate large standalone C /C++ programs into your model without having to make changes to the code.
The following sample model includes an S-function wrapper.

There are two files associated with the wrapsfcn block, the S-function wrapper and the C/C++ code that contains the algorithm. The S-function wrapper code for wrapsfcn.c appears below. The first three statements do the following:
Defines the name of the S-function (what you enter in the Simulink S-Function block dialog).
Specifies that the S-function is using the level 2 format.
Provides access to the SimStruct data structure, which contains pointers to data used during simulation and code generation and defines macros that store data in and retrieve data from the SimStruct.
For more information, see Templates for C S-Functions in the Simulink documentation.
#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
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. This is the code for my_alg.c:
#ifdef MATLAB_MEX_FILE
#include "tmwtypes.h"
#else
#include "rtwtypes.h"
#endif
real_T my_alg(real_T u)
{
return(u * 2.0);
}
See the section Header Dependencies When Interfacing Legacy/Custom Code with Generated Code in the Real-Time Workshop documentation for more information.
The wrapper S-function wrapsfcn calls my_alg, which computes u * 2.0. To build wrapsfcn.mex, use the following command:
mex wrapsfcn.c my_alg.c
This section describes how to inline the call to my_alg in the mdlOutputs section of the generated code. In the above 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.
It is instructive to 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 sfunction.tlc and the existence of sfunction.mex. When generating code for a noninlined S-function, the Real-Time Workshop software generates a call to mdlOutputs 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. To do this, specify
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)
{
/* (no start code 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)
{
/* (no update code 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 */
In addition to the overhead outlined above, 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. First, the Simulink engine creates a SimStruct data structure for the S-Function block. Second, 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 sfunction.tlc file for the S-function (see the Target Language Compiler documentation for details). The TLC file must contain the function call to my_alg. The following figure shows the relationships between the algorithm, the wrapper S-function, and the sfunction.tlc file.

To inline this call, you have to place your function call in an sfunction.tlc file with the same name as the S-function (in this example, wrapsfcn.tlc). This causes the Target Language Compiler to override the default method of placing calls to your S-function in the generated code.
This is the wrapsfcn.tlc file 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 tell the code generator that the routine my_alg needs to be declared external in the generated wrapper.h file for any wrapsfcn S-Function blocks in the model. You only need to do this once for all wrapsfcn S-Function blocks, so use the BlockTypeSetup function. In this function, you tell 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. This is done by the Outputs function. In this function, you access the block's 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 no longer contains 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;
}
In addition, wrapper.reg no longer creates a child SimStruct for the S-function because the generated code is calling my_alg directly. This eliminates over 1 K of memory usage.
![]() | Writing Noninlined S-Functions | Writing Fully Inlined S-Functions | ![]() |

Learn more about Simulink through this collection of videos, articles, technical literature and the Getting Started with Simulink Guide.
| © 1984-2009- The MathWorks, Inc. - Site Help - Patents - Trademarks - Privacy Policy - Preventing Piracy - RSS |