Inlining S-Functions
Inlining an S-function
To inline an S-function means to provide a TLC file for an S-Function block that will replace the C, C++, Fortran, or MATLAB® language version of the block that was used during simulation.
Noninlined S-Function
If an inlining TLC file is not provided, most targets support the block by recompiling
the C MEX S-function for the block. There is overhead in memory usage and speed when using a
C/C++ coded S-function and a limited subset of mx*
API calls supported
within the code generator context. If you want the most efficient generated code, you must
inline S-functions by writing a TLC file for them.
When the simulation needs to execute one of the functions for an S-function block, it calls the MEX-file for that function. When the code generator executes a noninlined S-function, it does so in a similar manner, as this diagram illustrates.
Types of Inlining
It is helpful to define two categories of inlining:
Fully inlined S-functions
Wrapper inlined S-functions
While both inline the S-function and remove the overhead of a noninlined S-function, the
two approaches are different. The first example below, using
timestwo.tlc
, is considered a fully inlined TLC file, where the full
implementation of the block is contained in the TLC file for the block.
The second example uses a wrapper TLC file. Instead of generating the algorithmic code in place, this example calls a C function that contains the body of code. There are several potential benefits for using the wrapper TLC file:
It provides a way for the C MEX S-function and the generated code to share the C code. You do not need to write the code twice.
The called C function is an optimized routine.
Several of the blocks might exist in the model, and it is more efficient in terms of code size to have them call a function, as opposed to each creating identical algorithmic code.
It provides a way to incorporate legacy C code seamlessly into generated code.
Fully Inlined S-Function Example
Inlining an S-function provides a mechanism to directly embed code for an S-function
block into the generated code for a model. Instead of calling into a separate source file
via function pointers and maintaining a separate data structure
(SimStruct
) for it, the code appears “inlined” as the
next figure shows.
The S-function timestwo.c
provides a simple example of a fully
inlined S-function. This block multiplies its input by 2 and outputs it. The C MEX version
of the block is in the file
, and the inlining TLC file for the block is
matlabroot
/toolbox/simulink/sfuntemplates/src/timestwo.c
.matlabroot
/toolbox/simulink/sfuntemplates/tlc_c/timestwo.tlc
timestwo.tlc
%implements "timestwo" "C" %% Function: Outputs ========================================== %% %function Outputs(block, system) Output /* %<Type> Block: %<Name> */ %% /* Multiply input by two */ %assign rollVars = ["U", "Y"] %roll idx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars %<LibBlockOutputSignal(0, "", lcv, idx)> = \ %<LibBlockInputSignal(0, "", lcv, idx)> * 2.0; %endroll %endfunction
TLC Block Analysis
The %implements
directive is required by TLC block files and is
used by the Target Language Compiler to verify the block type and language supported by
the block. The %function
directive starts a function declaration and
shows the name of the function, Outputs
, and the arguments passed to
it, block
and system
. These are the relevant records
from the
file for this instance of
the block.model
.rtw
The last piece of the prototype is Output
. This means that
any line that is not a TLC directive is output by the
function to the current file that is selected in TLC. So, nondirective lines in the
Outputs
function become generated code for the block.
The most complicated piece of this TLC block example is the %roll
directive. TLC uses this directive to provide automatic generation of
for
loops, depending on input/output widths and whether the inputs
are contiguous in memory. This example uses the typical form of accessing outputs and
inputs from within the body of the roll, using LibBlockOutputSignal
and
LibBlockInputSignal
to access the outputs and inputs and perform the
multiplication and assignment. Note that this TLC file supports
any valid signal width.
The only function used to implement this block is Outputs
. For more
complicated blocks, other functions are declared as well. You can find examples of more
complicated inlining TLC files in the folders
(open) and
matlabroot
/toolbox/simulink/sfuntemplates/tlc_c
(open), and by looking at the code for built-in blocks in the folder
matlabroot
/toolbox/simulink/blocks/tlc_c
(open).matlabroot/rtw/c/tlc/blocks
The timestwo Model
This simple model uses the timestwo
S-function and shows the
MdlOutputs
function from the generated
file, which contains the inlined
S-function code.model
.c
Model Outputs Code
/* Model output function */ static void timestwo_ex_output(int_T tid) { /* S-Function Block: <Root>/S-Function */ /* Multiply input by two */ timestwo_ex_B.timestwo_output = timestwo_ex_P.Constant_Value * 2.0; /* Outport: '<Root>/Out1' */ timestwo_ex_Y.Out1 = timestwo_ex_B.timestwo_output; }
Wrapper Inlined S-Function Example
The following diagram illustrates inlining an S-function as a wrapper. The algorithm is directly called from the generated model code, removing the S-function overhead but maintaining the user function.
This is the inlining TLC file for a wrapper version of the timestwo
block.
%implements "timestwo" "C" %% Function: BlockTypeSetup ================================== %% %function BlockTypeSetup(block, system) void %% Add function prototype to model's header file %<LibCacheFunctionPrototype... ("extern void mytimestwo(real_T* in,real_T* out,int_T els);")> %% Add file that contains "myfile" to list of files to be compiled %<LibAddToModelSources("myfile")> %endfunction %% Function: Outputs ========================================== %% %function Outputs(block, system) Output /* %<Type> Block: %<Name> */ %assign outPtr = LibBlockOutputSignalAddr(0, "", "", 0) %assign inPtr = LibBlockInputSignalAddr(0, "", "",0) %assign numEls = LibBlockOutputSignalWidth(0) /* Multiply input by two */ mytimestwo(%<inPtr>,%<outPtr>,%<numEls>); %endfunction
Analysis
The function BlockTypeSetup
is called once for each type of block
in a model; it doesn't produce output directly like the Outputs
function. Use BlockTypeSetup
to include a function prototype in the
file and to tell the build
process to compile an additional file, model
.hmyfile.c
.
Instead of performing the multiplication directly, the Outputs
function now calls the function mytimestwo
.
All instances of this block in the model call the same
function to perform the multiplication. The resulting model function,
MdlOutputs
, then becomes
static void timestwo_ex_output(int_T tid) { /* S-Function Block: <Root>/S-Function */ /* Multiply input by two */ mytimestwo(&model_B.Constant_Value,&model_B.S_Function,1); /* Outport Block: <Root>/Out1 */ model_Y.Out1 = model_B.S_Function; }