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 | ![]() |