| Writing S-Functions | ![]() |
The TLC S-Function Wrapper
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 creating a TLC S-function wrapper, the goal is to have the Real-Time Workshop embed the same type of call in the generated code.
It is instructive to look at how the Real-Time Workshop 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 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) */
{
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) */
{
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. This overhead can be significantly reduced by creating a TLC wrapper for the S-function.
How to Inline
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 a significant amount of computational overhead associated with it. First, Simulink creates a SimStruct data structure for the S-function block. Second, the Real-Time Workshop constructs a call through a function pointer to execute MdlOutputs, and then MdlOutputs calls my_alg. By inlining the call to your 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; this file must contain the function call to my_alg. This picture shows the relationships between the algorithm, the wrapper S-function, and the sfunction.tlc file.

Figure 8-2: Inlining an Algorithm by Using a TLC File
To inline this call, you have to place your function call into 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 bufferextern real_T my_alg(real_T u); %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"
%<y> = my_alg(%<u>); %endfunction %% Outputs
The first section of this code directs the Real-Time Workshop to inline the wrapsfcn S-function block and generate the code in C:
%implements "wrapsfcn" "C"
The next task is to tell the Real-Time Workshop 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 actual inlining of the call to the function my_alg. This is done by the Outputs function. In this function, you load the input and output and call place a direct call to my_alg. The call is embedded in wrapper.c.
| The MEX S-Function Wrapper | The Inlined Code | ![]() |