| Contents | Index |
This chapter describes how to create S-functions that work seamlessly with Simulink Coder code generation. It begins with basic concepts and concludes with an example of how to create a highly optimized direct-index lookup table S-Function block.
This chapter assumes that you understand the following concepts:
Level 2 S-functions
Target Language Compiler (TLC) scripting
How the Simulink Coder software generates and builds C/C++ code
Note When this chapter refers to actions performed by the Target Language Compiler, including parsing, caching, creating buffers, and so on, the name Target Language Compiler is spelled out fully. When referring to code written in the Target Language Compiler syntax, this chapter uses the abbreviation TLC. |
Note The guidelines presented in this chapter are for Simulink Coder users. Even if you do not currently use the Simulink Coder code generator, you should follow the practices presented in this chapter when writing S-functions, especially if you are creating general-purpose S-functions. |
See the Target Language Compiler documentation and other Simulink Coder documentation for more information on the code generation process.
See Inlining S-Functions in the Target Language Compiler documentation for additional information on inlining S-functions.
S-functions help solve various kinds of problems you might face when working with the Simulink and Simulink Coder products. These problems include
Extending the set of algorithms (blocks) provided by the Simulink and Simulink Coder products
Interfacing legacy (hand-written) code with the Simulink and Simulink Coder products
Interfacing to hardware through device driver S-functions
Generating highly optimized code for embedded systems
Verifying code generated for a subsystem as part of a Simulink simulation
S-functions are written using an application program interface (API) that allows you to implement generic algorithms in the Simulink environment with a great deal of flexibility. This flexibility cannot always be maintained when you use S-functions with the Simulink Coder code generator. For example, it is not possible to access the MATLAB workspace from an S-function that is used with the code generator. However, using the techniques presented in this chapter, you can create S-functions for most applications that work with the Simulink Coder generated code.
Although S-functions provide a generic and flexible solution for implementing complex algorithms in a Simulink model, the underlying API incurs overhead in terms of memory and computation resources. Most often the additional resources are acceptable for real-time rapid prototyping systems. In many cases, though, additional resources are unavailable in real-time embedded applications. You can minimize memory and computational requirements by using the Target Language Compiler technology provided with the Simulink Coder product to inline your S-functions. If you are producing an S-function for existing code, consider using the Simulink Legacy Code Tool.
The implementation of S-functions changes based on your requirements. This chapter discusses the typical problems that you may face and how to create S-functions for applications that need to work with the Simulink and Simulink Coder products. These are some (informally defined) common situations:
"I'm not concerned with efficiency. I just want to write one version of my algorithm and have it work in the Simulink and Simulink Coder products automatically."
"I have a lot of hand-written code that I need to interface. I want to call my function from the Simulink and Simulink Coder products in an efficient manner."
or said another way:
"I want to create a block for my blockset that will be distributed throughout my organization. I'd like it to be very maintainable with efficient code. I'd like my algorithm to exist in one place but work with both the Simulink and Simulink Coder products."
"I want to implement a highly optimized algorithm in the Simulink and Simulink Coder products that looks like a built-in block and generates very efficient code."
MathWorks products have adopted terminology for these different requirements. Respectively, the situations described above map to this terminology:
Noninlined S-Functions. A noninlined S-function is a C or C++ MEX S-function that is treated identically by the Simulink engine and Simulink Coder generated code. In general, you implement your algorithm once according to the S-function API. The Simulink engine and Simulink Coder generated code call the S-function routines (for example, mdlOutputs) during model execution.
Additional memory and computation resources are required for each instance of a noninlined S-Function block. However, this routine of incorporating algorithms into Simulink models and Simulink Coder applications is typical during the prototyping phase of a project where efficiency is not important. The advantage gained by forgoing efficiency is the ability to change model parameters and structures rapidly.
Writing a noninlined S-function does not involve any TLC coding. Noninlined S-functions are the default case for the Simulink Coder build process in the sense that once you build a MEX S-function in your model, there is no additional preparation prior to clicking Build in the Code Generation pane of the Configuration Parameters dialog box for your model.
Some restrictions exist concerning the names and locations of noninlined S-function files when generating makefiles. See Write Noninlined S-Functions.
Wrapper S-Functions. A wrapper S-function is ideal for interfacing hand-written code or a large algorithm that is encapsulated within a few procedures. In this situation, usually the procedures reside in modules that are separate from the MEX S-function. The S-function module typically contains a few calls to your procedures. Because the S-function module does not contain any parts of your algorithm, but only calls your code, it is referred to as a wrapper S-function.
In addition to the MEX S-function wrapper, you need to create a TLC wrapper that complements your S-function. The TLC wrapper is similar to the S-function wrapper in that it contains calls to your algorithm.
Fully Inlined S-Functions. For S-functions to work in the Simulink environment, some overhead code is generated. When the Simulink Coder software generates code from models that contain S-functions (without sfunction.tlc files), it embeds some of this overhead code in the generated code. If you want to optimize your real-time code and eliminate some of the overhead code, you must inline (or embed) your S-functions. This involves writing a TLC (sfunction.tlc) file that eliminates all overhead code from the generated code. The Target Language Compiler processes sfunction.tlc files to define how to inline your S-function algorithm in the generated code.
Note The term inline should not be confused with the C++ inline keyword. In Simulink Coder terminology, inline means to specify a text string in place of the call to the general S-function API routines (for example, mdlOutputs). For example, when a TLC file is used to inline an S-function, the generated code contains the C/ C++ code that would normally appear within the S-function routines and the S-function itself has been removed from the build process. |
A fully inlined S-function builds your algorithm (block) into Simulink Coder generated code in a manner that is indistinguishable from a built-in block. Typically, a fully inlined S-function requires you to implement your algorithm twice: once for the Simulink model (C/C++ MEX S-function) and once for Simulink Coder code generation (TLC file). The complexity of the TLC file depends on the complexity of your algorithm and the level of efficiency you're trying to achieve in the generated code. TLC files vary from simple to complex in structure.
The Simulink Legacy Code Tool can automate the generation of a fully inlined S-function and a corresponding TLC file based on information that you register in a Legacy Code Tool data structure. For more information, see Integrating Existing C Functions into Simulink Models with the Legacy Code Tool in the Simulink Writing S-Functions documentation and Integrate External Code Using Legacy Code Tool.
This section briefly describes what files and functions you need to create noninlined, wrapper, and fully inlined S-functions.
Noninlined S-functions require the C or C++ MEX S-function source code (sfunction.c or sfunction.cpp).
Wrapper S-functions that inline a call to your algorithm (your C/C++ function) require an sfunction.tlc file.
Fully inlined S-functions also require an sfunction.tlc file. Fully inlined S-functions produce the optimal code for a parameterized S-function. This is an S-function that operates in a specific mode dependent upon fixed S-function parameters that do not change during model execution. For a given operating mode, the sfunction.tlc file specifies the exact code that is generated to implement the algorithm for that mode. For example, the direct-index lookup table S-function at the end of this chapter contains two operating modes — one for evenly spaced x-data and one for unevenly spaced x-data.
Fully inlined S-functions might require the placement of the mdlRTW routine in your S-function MEX-file sfunction.c or sfunction.cpp. The mdlRTW routine lets you place information in model.rtw, the record file that specifies a model, and which the Simulink Coder code generator invokes the Target Language Compiler to process prior to executing sfunction.tlc when generating code.
Including a mdlRTW routine is useful when you want to introduce nontunable parameters into your TLC file. Such parameters are generally used to determine which operating mode is active in a given instance of the S-function. Based on this information, the TLC file for the S-function can generate highly efficient, optimal code for that operating mode.
Use only C MEX S-functions with the Simulink Coder code generator. You cannot use Level-1 MATLAB language S-functions with Simulink Coder software.
To inline an S-function, use the Legacy Code Tool. The Legacy Code Tool automatically generates fully inlined C MEX S-functions for legacy or custom code. In addition, the tool generates other files for compiling and building the S-function for simulation and generate a masked S-function block configured to call existing external code. For more information, see Integrating Existing C Functions into Simulink Models with the Legacy Code Tool in the Simulink documentation and Integrate External Code Using Legacy Code Tool.
If you are rapid prototyping, you might not have to inline an S-function.. If you choose not to inline the C MEX S-function, write the S-function, include it directly in the model, and let the Simulink Coder software generate the code. For more information, see Write Noninlined S-Functions.
Noninlined S-functions are identified by the absence of an sfunction.tlc file for your S-function. The filename varies depending on your platform. For example, on a 32–bit Microsoft Windows system, the file name would be sfunction.mexw32. Type mexext in the MATLAB Command Window to see which extension your system uses.
The MEX-file cannot call MATLAB functions.
If the MEX-file uses functions in the MATLAB External Interface libraries, include the header file cg_sfun.h instead of mex.h or simulink.c. To handle this case, include the following lines at the end of your S-function:
#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
Use only MATLAB API function that the code generator supports, which include:
| mxGetEps |
| mxGetInf |
| mxGetM |
| mxGetN |
| mxGetNaN |
| mxGetPr |
| mxGetScalar |
| mxGetString |
| mxIsEmpty |
| mxIsFinite |
| mxIsInf |
MEX library calls are not supported in generated code. To use such calls in MEX-file and not in the generated code, conditionalize the code as follows:
#ifdef MATLAB_MEX_FILE #endif
Use only full matrices that contain only real data.
Do not specify a return value for calls to mxGetString . If you do specify a return value, the MEX-file will not compile. Instead, use the function's second input argument, which returns a pointer to a string.
Make sure that the #define s-function_name statement is correct. The S-function name that you specify must match the S-function's filename.
Use the data types real_T and int_T instead of double and int, if possible. The data types real_T and int_T are more generic and can be used in multiple environments.
Provide the Simulink Coder build process with the names of all modules used to build the S-function. You can do this by using the Simulink Coder template make file or the set_param function. For example, suppose you build your S-function with the following command:
mex sfun_main.c sfun_module1.c sfun_module2.c
You can then use the following call to set_param to include all the required modules:
set_param(sfun_block, "SFunctionModules","sfun_module1 sfun_module2')
Parameters to noninlined S-functions can be of the following types only:
Double precision
Characters in scalars, vectors, or 2-D matrices
For more flexibility in the type of parameters you can supply to S-functions or the operations in the S-function, inline your S-function and consider using an mdlRTW S-function routine.
Use of other functions from the MATLAB matrix.h API or other MATLAB APIs, such as mex.h and mat.h, is not supported. If you call unsupported APIs from an S-function source file, compiler errors occur. See the file matlabroot/rtw/c/src/rt_matrx.h(.c) for details on supported MATLAB API functions.
If you use mxGetPr on an empty matrix, the function does not return NULL; rather, it returns a random value. Therefore, you should protect calls to mxGetPr with mxIsEmpty.
This section describes how to create S-functions that work seamlessly with the Simulink and Simulink Coder 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 Simulink Coder 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 Simulink Coder 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 Simulink Coder 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')Code Overhead for Noninlined S-Functions. 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.
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 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 KB of memory usage.
Continuing the example of the previous section, you could eliminate the call to my_alg entirely by specifying the explicit code (that is, 2.0 * u) in wrapsfcn.tlc. This is referred to as a fully inlined S-function. While this can improve performance, if you are working with a large amount of C/C++ code, this can be a lengthy task. In addition, you now have to maintain your algorithm in two places, the C/C++ S-function itself and the corresponding TLC file. However, the performance gains might outweigh the disadvantages. To inline the algorithm used in this example, in the Outputs section of your wrapsfcn.tlc file, instead of writing
%<y> = my_alg(%<u>);
use
%<y> = 2.0 * %<u>;
This is the code produced in mdlOutputs:
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 = 2.0 * rtB.Sin; /* Explicit embedding of algorithm */
/* Outport Block: <Root>/Out */
rtY.Out = rtB.S_Function;
}
The Target Language Compiler has replaced the call to my_alg with the algorithm itself.
A more advanced multiport inlined S-function example is sfun_multiport.c and sfun_multiport.tlc. This S-function demonstrates how to create a fully inlined TLC file for an S-function that contains multiple ports. You might find that looking at this example helps you to understand fully inlined TLC files.
You can inline more complex S-functions that use the S-function mdlRTW routine. The purpose of the mdlRTW routine is to provide the code generation process with more information about how the S-function is to be inlined, by creating a parameter record of a nontunable parameter for use with a TLC file. The mdlRTW routine does this by placing information in the model.rtw file. The mdlRTW function is described in the text file matlabroot/simulink/src/sfuntmpl_doc.c.
As an example of how to use the mdlRTW function, this section discusses the steps you must take to create a direct-index lookup S-function. Lookup tables are collections of ordered data points of a function. Typically, these tables use some interpolation scheme to approximate values of the associated function between known data points. To incorporate the example lookup table algorithm into a Simulink model, the first step is to write an S-function that executes the algorithm in mdlOutputs. To produce the most efficient code, the next step is to create a corresponding TLC file to eliminate computational overhead and improve the performance of the lookup computations.
For your convenience, the Simulink product provides support for two general-purpose lookup 1-D and 2-D algorithms. You can use these algorithms as they are or create a custom lookup table S-function to fit your requirements. This section demonstrates how to create a 1-D lookup S-function, sfun_directlook.c, and its corresponding inlined sfun_directlook.tlc file. (See the Target Language Compiler documentation for more details on the Target Language Compiler.) This 1-D direct-index lookup table example demonstrates the following concepts that you need to know to create your own custom lookup tables:
Error checking of S-function parameters
Caching of information for the S-function that doesn't change during model execution
How to use the mdlRTW function to customize Simulink Coder generated code to produce the optimal code for a given set of block parameters
How to generate an inlined TLC file for an S-function in a combination of the fully inlined form and/or the wrapper form
There is a property of blocks called RTWdata, which can be used by the Target Language Compiler when inlining an S-function. RTWdata is a structure of strings that you can attach to a block. It is saved with the model and placed in the model.rtw file when generating code. For example, this set of MATLAB commands,
mydata.field1 = 'information for field1'; mydata.field2 = 'information for field2'; set_param(gcb,'RTWdata',mydata) get_param(gcb,'RTWdata')
produces this result:
ans =
field1: 'information for field1'
field2: 'information for field2'
Inside the model.rtw file for the associated S-Function block is this information.
Block {
Type "S-Function"
RTWdata {
field1 "information for field1"
field2 "information for field2"
}Note RTWdata is saved in the .mdl file for S-functions that are not linked to a library. However, RTWdata is not persistent for S-Function blocks that are linked to a library. |
The 1-D lookup table block provided in the Simulink library uses interpolation or extrapolation when computing outputs. This extra accuracy might not be required. In this example, you create a lookup table that directly indexes the output vector (y-data vector) based on the current input (x-data) point.
This direct 1-D lookup example computes an approximate solution p(x) to a partially known function f(x) at x=x0, given data point pairs (x,y) in the form of an x-data vector and a y-data vector. For a given data pair (for example, the i'th pair), y_i = f(x_i). It is assumed that the x-data values are monotonically increasing. If x0 is outside the range of the x-data vector, the first or last point is returned.
The parameters to the S-function are
XData, YData, XEvenlySpaced
XData and YData are double vectors of equal length representing the values of the unknown function. XDataEvenlySpaced is a scalar, 0.0 for false and 1.0 for true. If the XData vector is evenly spaced, XDataEvenlySpaced is 1.0 and more efficient code is generated.
The following graph shows how the parameters XData=[1:6]and YData=[1,2,7,4,5,9] are handled. For example, if the input (x-value) to the S-Function block is 3, the output (y-value) is 7.

This section shows how to improve the lookup table by inlining a direct-index S-function with a TLC file. This direct-index lookup table S-function does not require a TLC file. Here the example uses a TLC file for the direct-index lookup table S-function to reduce the code size and increase efficiency of the generated code.
Implementation of the direct-index algorithm with inlined TLC file requires the S-function main module, sfun_directlook.c, and a corresponding lookup_index.c module. The lookup_index.c module contains the GetDirectLookupIndex function that is used to locate the index in the XData for the current x input value when the XData is unevenly spaced. The GetDirectLookupIndex routine is called from both the S-function and the generated code. Here the example uses the wrapper concept for sharing C/C++ code between Simulink MEX-files and the generated code.
If the XData is evenly spaced, then both the S-function main module and the generated code contain the lookup algorithm (not a call to the algorithm) to compute the y-value of a given x-value, because the algorithm is short. This demonstrates the use of a fully inlined S-function for generating optimal code.
The inlined TLC file, which either performs a wrapper call or embeds the optimal C/C++ code, is sfun_directlook.tlc (see the example in mdlRTW Usage).
Error Handling. In this example, the mdlCheckParameters routine verifies that
The new parameter settings valid..
XData and YData are vectors of the same length containing real finite numbers.
XDataEvenlySpaced is a scalar.
The XData vector is a monotonically increasing vector and evenly spaced.
The mdlInitializeSizes function explicitly calls mdlCheckParameters after it verifies the number of parameters passed to the S-function. After the Simulink engine calls mdlInitializeSizes, it then calls mdlCheckParameters whenever you change the parameters or there is a need to reevaluate them.
User Data Caching. The mdlStart routine shows how to cache information that does not change during the simulation (or while the generated code is executing). The example caches the value of the XDataEvenlySpaced parameter in UserData, a field of the SimStruct. The following line in mdlInitializeSizes tells the Simulink engine to disallow changes to XDataEvenlySpaced.
ssSetSFcnParamTunable(S, iParam, SS_PRM_NOT_TUNABLE);
During execution, mdlOutputs accesses the value of XDataEvenlySpaced from UserData rather than calling the mxGetPr MATLAB API function. This increases performance.
mdlRTW Usage. The Simulink Coder code generator calls the mdlRTW routine while generating the model.rtw file. To produce optimal code for your Simulink model, you can add information to the model.rtw file about the mode in which your S-Function block is operating.
The following example adds parameter settings to the model.rtw file. The parameter settings do not change during execution. In this case, the XDataEvenlySpaced S-function parameter cannot change during execution (ssSetSFcnParamTunable was specified as false (0) for it in mdlInitializeSizes). The example writes it out as a parameter setting (XSpacing) using the function ssWriteRTWParamSettings.
Because xData and yData are registered as run-time parameters in mdlSetWorkWidths, the code generator handles writing to the model.rtw file automatically.
Before examining the S-function and the inlined TLC file, consider the generated code for the following model.

The model uses evenly spaced XData in the top S-Function block and unevenly spaced XData in the bottom S-Function block. When creating this model, you need to specify the following for each S-Function block.
set_param(`sfun_directlook_ex/S-Function','SFunctionModules','lookup_index') set_param(`sfun_directlook_ex/S-Function1','SFunctionModules','lookup_index')
This informs the Simulink Coder build process to use the module lookup_index.c when creating the executable.
When generating code for this model, the Simulink Coder software uses the S-function's mdlRTW method to generate a model.rtw file with the value EvenlySpaced for the XSpacing parameter for the top S-Function block, and the value UnEvenlySpaced for the XSpacing parameter for the bottom S-Function block. The TLC-file uses the value of XSpacing to determine what algorithm to include in the generated code. The generated code contains the lookup algorithm when the XData is evenly spaced, but calls the GetDirectLookupIndex routine when the XData is unevenly spaced. The generated model.c or model.cpp code for the lookup table example model is similar to the following:
/*
* sfun_directlook_ex.c
*
* Code generation for Simulink model
* "sfun_directlook_ex.mdl".
*
...
*/
#include "sfun_directlook_ex.h"
#include "sfun_directlook_ex_private.h"
/* External output (root outports fed by signals with auto storage) */
ExternalOutputs_sfun_directlook_ex sfun_directlook_ex_Y;
/* Real-time model */
rtModel_sfun_directlook_ex sfun_directlook_ex_M_;
rtModel_sfun_directlook_ex *sfun_directlook_ex_M = &sfun_directlook_ex_M_;
/* Model output function */
static void sfun_directlook_ex_output(int_T tid)
{
/* local block i/o variables */
real_T rtb_SFunction_h;
real_T rtb_temp1;
/* Sin: '<Root>/Sine Wave' */
rtb_temp1 = sfun_directlook_ex_P.SineWave_Amp *
sin(sfun_directlook_ex_P.SineWave_Freq * sfun_directlook_ex_M->Timing.t[0] +
sfun_directlook_ex_P.SineWave_Phase) + sfun_directlook_ex_P.SineWave_Bias;
/* Code that is inlined for the top S-function block in the
* sfun_directlook_ex model
*/
/* S-Function Block: <Root>/S-Function */
{
const real_T *xData = &sfun_directlook_ex_P.SFunction_XData[0];
const real_T *yData = &sfun_directlook_ex_P.SFunction_YData[0];
real_T spacing = xData[1] - xData[0];
if ( rtb_temp1 <= xData[0] ) {
rtb_SFunction_h = yData[0];
} else if ( rtb_temp1 >= yData[20] ) {
rtb_SFunction_h = yData[20];
} else {
int_T idx = (int_T)( ( rtb_temp1 - xData[0] ) / spacing );
rtb_SFunction_h = yData[idx];
}
}
/* Outport: '<Root>/Out1' */
sfun_directlook_ex_Y.Out1 = rtb_SFunction_h;
/* Code that is inlined for the bottom S-function block in the
* sfun_directlook_ex model
*/
/* S-Function Block: <Root>/S-Function1 */
{
const real_T *xData = &sfun_directlook_ex_P.SFunction1_XData[0];
const real_T *yData = &sfun_directlook_ex_P.SFunction1_YData[0];
int_T idx;
idx = GetDirectLookupIndex(xData, 5, rtb_temp1);
rtb_temp1 = yData[idx];
}
/* Outport: '<Root>/Out2' */
sfun_directlook_ex_Y.Out2 = rtb_temp1;
}
/* Model update function */
static void sfun_directlook_ex_update(int_T tid)
{
/* Update absolute time for base rate */
if(!(++sfun_directlook_ex_M->Timing.clockTick0))
++sfun_directlook_ex_M->Timing.clockTickH0;
sfun_directlook_ex_M->Timing.t[0] = sfun_directlook_ex_M->Timing.clockTick0 *
sfun_directlook_ex_M->Timing.stepSize0 +
sfun_directlook_ex_M->Timing.clockTickH0 *
sfun_directlook_ex_M->Timing.stepSize0 * 0x10000;
{
/* Update absolute timer for sample time: [0.1s, 0.0s] */
if(!(++sfun_directlook_ex_M->Timing.clockTick1))
++sfun_directlook_ex_M->Timing.clockTickH1;
sfun_directlook_ex_M->Timing.t[1] = sfun_directlook_ex_M->Timing.clockTick1
* sfun_directlook_ex_M->Timing.stepSize1 +
sfun_directlook_ex_M->Timing.clockTickH1 *
sfun_directlook_ex_M->Timing.stepSize1 * 0x10000;
}
}
...matlabroot/toolbox/simulink/simdemos/simfeatures/src/sfun_directlook.c.
/*
* File : sfun_directlook.c
* Abstract:
*
* Direct 1-D lookup. Here we are trying to compute an approximate
* solution, p(x) to an unknown function f(x) at x=x0, given data point
* pairs (x,y) in the form of a x data vector and a y data vector. For a
* given data pair (say the i'th pair), we have y_i = f(x_i). It is
* assumed that the x data values are monotonically increasing. If the
* x0 is outside of the range of the x data vector, then the first or
* last point will be returned.
*
* This function returns the "nearest" y0 point for a given x0. No
* interpolation is performed.
*
* The S-function parameters are:
* XData - double vector
* YData - double vector
* XDataEvenlySpacing - double scalar 0 (false) or 1 (true)
* The third parameter cannot be changed during simulation.
*
* To build:
* mex sfun_directlook.c lookup_index.c
*
* Copyright 1990-2004 The MathWorks, Inc.
* $Revision: 1.1.4.7 $
*/
#define S_FUNCTION_NAME sfun_directlook
#define S_FUNCTION_LEVEL 2
#include <math.h>
#include "simstruc.h"
#include <float.h>
/* use utility function IsRealVect() */
#if defined(MATLAB_MEX_FILE)
#include "sfun_slutils.h"
#endif
/*================*
* Build checking *
*================*/
#if !defined(MATLAB_MEX_FILE)
/*
* This file cannot be used directly with Simulink Coder. However,
* this S-function does work with Simulink Coder via
* the Target Language Compiler technology. See matlabroot/
* toolbox/simulink/simdemos/simfeatures/tlc_c/sfun_directlook.tlc
* for the C version
*/
# error This_file_can_be_used_only_during_simulation_inside_Simulink
#endif
/*=========*
* Defines *
*=========*/
#define XVECT_PIDX 0
#define YVECT_PIDX 1
#define XDATAEVENLYSPACED_PIDX 2
#define NUM_PARAMS 3
#define XVECT(S) ssGetSFcnParam(S,XVECT_PIDX)
#define YVECT(S) ssGetSFcnParam(S,YVECT_PIDX)
#define XDATAEVENLYSPACED(S) ssGetSFcnParam(S,XDATAEVENLYSPACED_PIDX)
/*==============*
* misc defines *
*==============*/
#if !defined(TRUE)
#define TRUE 1
#endif
#if !defined(FALSE)
#define FALSE 0
#endif
/*===========*
* typedef's *
*===========*/
typedef struct SFcnCache_tag {
boolean_T evenlySpaced;
} SFcnCache;
/*===================================================================*
* Prototype define for the function in separate file lookup_index.c *
*===================================================================*/
extern int_T GetDirectLookupIndex(const real_T *x, int_T xlen, real_T u);
/*====================*
* S-function methods *
*====================*/
#define MDL_CHECK_PARAMETERS /* Change to #undef to remove function */
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)
/* Function: mdlCheckParameters ================================================
* Abstract:
* This routine will be called after mdlInitializeSizes, whenever
* parameters change or get re-evaluated. The purpose of this routine is
* to verify the new parameter settings.
*
* You should add a call to this routine from mdlInitalizeSizes
* to check the parameters. After setting your sizes elements, you should:
* if (ssGetSFcnParamsCount(S) == ssGetNumSFcnParams(S)) {
* mdlCheckParameters(S);
* }
*/
static void mdlCheckParameters(SimStruct *S)
{
if (!IsRealVect(XVECT(S))) {
ssSetErrorStatus(S,"1st, X-vector parameter must be a real finite "
" vector");
return;
}
if (!IsRealVect(YVECT(S))) {
ssSetErrorStatus(S,"2nd, Y-vector parameter must be a real finite "
"vector");
return;
}
/*
* Verify that the dimensions of X and Y are the same.
*/
if (mxGetNumberOfElements(XVECT(S)) != mxGetNumberOfElements(YVECT(S)) ||
mxGetNumberOfElements(XVECT(S)) == 1) {
ssSetErrorStatus(S,"X and Y-vectors must be of the same dimension "
"and have at least two elements");
return;
}
/*
* Verify we have a valid XDataEvenlySpaced parameter.
*/
if ((!mxIsNumeric(XDATAEVENLYSPACED(S)) &&
!mxIsLogical(XDATAEVENLYSPACED(S))) ||
mxIsComplex(XDATAEVENLYSPACED(S)) ||
mxGetNumberOfElements(XDATAEVENLYSPACED(S)) != 1) {
ssSetErrorStatus(S,"3rd, X-evenly-spaced parameter must be logical
scalar");
return;
}
/*
* Verify x-data is correctly spaced.
*/
{
int_T i;
boolean_T spacingEqual;
real_T *xData = mxGetPr(XVECT(S));
int_T numEl = mxGetNumberOfElements(XVECT(S));
/*
* spacingEqual is TRUE if user XDataEvenlySpaced
*/
spacingEqual = (mxGetScalar(XDATAEVENLYSPACED(S)) != 0.0);
if (spacingEqual) { /* XData is 'evenly-spaced' */
boolean_T badSpacing = FALSE;
real_T spacing = xData[1] - xData[0];
real_T space;
if (spacing <= 0.0) {
badSpacing = TRUE;
} else {
real_T eps = DBL_EPSILON;
for (i = 2; i < numEl; i++) {
space = xData[i] - xData[i-1];
if (space <= 0.0 ||
fabs(space-spacing) >= 128.0*eps*spacing ){
badSpacing = TRUE;
break;
}
}
}
if (badSpacing) {
ssSetErrorStatus(S,"X-vector must be an evenly spaced "
"strictly monotonically increasing vector");
return;
}
} else { /* XData is 'unevenly-spaced' */
for (i = 1; i < numEl; i++) {
if (xData[i] <= xData[i-1]) {
ssSetErrorStatus(S,"X-vector must be a strictly "
"monotonically increasing vector");
return;
}
}
}
}
}
#endif /* MDL_CHECK_PARAMETERS */
/* Function: mdlInitializeSizes ================================================
* Abstract:
* The sizes information is used by Simulink to determine the S-function
* block's characteristics (number of inputs, outputs, states, and so on).
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, NUM_PARAMS); /* Number of expected parameters */
/*
* Check parameters passed in, providing the correct number was specified
* in the S-function dialog box. If an incorrect number of parameters
* was specified, Simulink will detect the error since ssGetNumSFcnParams
* and ssGetSFcnParamsCount will differ.
* ssGetNumSFcnParams - This sets the number of parameters your
* S-function expects.
* ssGetSFcnParamsCount - This is the number of parameters entered by
* the user in the Simulink S-function dialog box.
*/
#if defined(MATLAB_MEX_FILE)
if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) {
mdlCheckParameters(S);
if (ssGetErrorStatus(S) != NULL) {
return;
}
} else {
return; /* Parameter mismatch will be reported by Simulink */
}
#endif
{
int iParam = 0;
int nParam = ssGetNumSFcnParams(S);
for ( iParam = 0; iParam < nParam; iParam++ )
{
switch ( iParam )
{
case XDATAEVENLYSPACED_PIDX:
ssSetSFcnParamTunable( S, iParam, SS_PRM_NOT_TUNABLE );
break;
default:
ssSetSFcnParamTunable( S, iParam, SS_PRM_TUNABLE );
break;
}
}
}
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 0);
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
ssSetInputPortDirectFeedThrough(S, 0, 1);
ssSetInputPortOptimOpts(S, 0, SS_REUSABLE_AND_LOCAL);
ssSetInputPortOverWritable(S, 0, TRUE);
if (!ssSetNumOutputPorts(S, 1)) return;
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);
ssSetOutputPortOptimOpts(S, 0, SS_REUSABLE_AND_LOCAL);
ssSetNumSampleTimes(S, 1);
ssSetOptions(S,
SS_OPTION_WORKS_WITH_CODE_REUSE |
SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_USE_TLC_WITH_ACCELERATOR);
} /* mdlInitializeSizes */
/* Function: mdlInitializeSampleTimes ==========================================
* Abstract:
* The lookup inherits its sample time from the driving block.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
} /* end mdlInitializeSampleTimes */
/* Function: mdlSetWorkWidths ===============================================
* Abstract:
* Set up the [X,Y] data as run-time parameters
* that is, these values can be changed during execution.
*/
#define MDL_SET_WORK_WIDTHS
static void mdlSetWorkWidths(SimStruct *S)
{
const char_T *rtParamNames[] = {"XData","YData"};
ssRegAllTunableParamsAsRunTimeParams(S, rtParamNames);
}
#define MDL_START /* Change to #undef to remove function */
#if defined(MDL_START)
/* Function: mdlStart ==========================================================
* Abstract:
* Here we cache the state (true/false) of the XDATAEVENLYSPACED parameter.
* We do this primarily to illustrate how to "cache" parameter values (or
* information which is computed from parameter values) which do not change
* for the duration of the simulation (or in the generated code). In this
* case, rather than repeated calls to mxGetPr, we save the state once.
* This results in a slight increase in performance.
*/
static void mdlStart(SimStruct *S)
{
SFcnCache *cache = malloc(sizeof(SFcnCache));
if (cache == NULL) {
ssSetErrorStatus(S,"memory allocation error");
return;
}
ssSetUserData(S, cache);
if (mxGetScalar(XDATAEVENLYSPACED(S)) != 0.0){
cache->evenlySpaced = TRUE;
}else{
cache->evenlySpaced = FALSE;
}
}
#endif /* MDL_START */
/* Function: mdlOutputs ========================================================
* Abstract:
* In this function, you compute the outputs of your S-function
* block. Generally outputs are placed in the output vector, ssGetY(S).
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
SFcnCache *cache = ssGetUserData(S);
real_T *xData = mxGetPr(XVECT(S));
real_T *yData = mxGetPr(YVECT(S));
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
int_T ny = ssGetOutputPortWidth(S,0);
int_T xLen = mxGetNumberOfElements(XVECT(S));
int_T i;
/*
* When the XData is evenly spaced, we use the direct lookup algorithm
* to calculate the lookup
*/
if (cache->evenlySpaced) {
real_T spacing = xData[1] - xData[0];
for (i = 0; i < ny; i++) {
real_T u = *uPtrs[i];
if (u <= xData[0]) {
y[i] = yData[0];
} else if (u >= xData[xLen-1]) {
y[i] = yData[xLen-1];
} else {
int_T idx = (int_T)((u - xData[0])/spacing);
y[i] = yData[idx];
}
}
} else {
/*
* When the XData is unevenly spaced, we use a bisection search to
* locate the lookup index.
*/
for (i = 0; i < ny; i++) {
int_T idx = GetDirectLookupIndex(xData,xLen,*uPtrs[i]);
y[i] = yData[idx];
}
}
} /* end mdlOutputs */
/* Function: mdlTerminate ======================================================
* Abstract:
* Free the cache which was allocated in mdlStart.
*/
static void mdlTerminate(SimStruct *S)
{
SFcnCache *cache = ssGetUserData(S);
if (cache != NULL) {
free(cache);
}
} /* end mdlTerminate */
#define MDL_RTW /* Change to #undef to remove function */
#if defined(MDL_RTW) && (defined(MATLAB_MEX_FILE) || defined(NRT))
/* Function: mdlRTW ============================================================
* Abstract:
* This function is called when Simulink Coder is generating the
* model.rtw file. In this routine, you can call the following functions
* which add fields to the model.rtw file.
*
* Important! Since this S-function has this mdlRTW method, it is required
* to have a corresponding .tlc file so as to work with Simulink Coder. See the
* sfun_directlook.tlc in matlabroot/toolbox/simulink/simdemos/simfeatures/tlc_c/.
*/
static void mdlRTW(SimStruct *S)
{
/*
* Write out the spacing setting as a param setting, that is, this cannot be
* changed during execution.
*/
{
boolean_T even = (mxGetScalar(XDATAEVENLYSPACED(S)) != 0.0);
if (!ssWriteRTWParamSettings(S, 1,
SSWRITE_VALUE_QSTR,
"XSpacing",
even ? "EvenlySpaced" : "UnEvenlySpaced")){
return;/* An error occurred which will be reported by Simulink */
}
}
}
#endif /* MDL_RTW */
/*=============================*
* Required S-function trailer *
*=============================*/
#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
/* [EOF] sfun_directlook.c */matlabroot/toolbox/simulink/simdemos/simfeatures/src/lookup_index.c.
/* File : lookup_index.c
* Abstract:
*
* Contains a routine used by the S-function sfun_directlookup.c to
* compute the index in a vector for a given data value.
*
* Copyright 1990-2004 The MathWorks, Inc.
* $Revision: 1.1.4.7 $
*/
#include "tmwtypes.h"
/*
* Function: GetDirectLookupIndex ==============================================
* Abstract:
* Using a bisection search to locate the lookup index when the x-vector
* isn't evenly spaced.
*
* Inputs:
* *x : Pointer to table, x[0] ....x[xlen-1]
* xlen : Number of values in xtable
* u : input value to look up
*
* Output:
* idx : the index into the table such that:
* if u is negative
* x[idx] <= u < x[idx+1]
* else
* x[idx] < u <= x[idx+1]
*/
int_T GetDirectLookupIndex(const real_T *x, int_T xlen, real_T u)
{
int_T idx = 0;
int_T bottom = 0;
int_T top = xlen-1;
/*
* Deal with the extreme cases first:
*
* i] u <= x[bottom] then idx = bottom
* ii] u >= x[top] then idx = top-1
*
*/
if (u <= x[bottom]) {
return(bottom);
} else if (u >= x[top]) {
return(top);
}
/*
* We have: x[bottom] < u < x[top], onward
* with search for the index ...
*/
for (;;) {
idx = (bottom + top)/2;
if (u < x[idx]) {
top = idx;
} else if (u > x[idx+1]) {
bottom = idx + 1;
} else {
/*
* We have: x[idx] <= u <= x[idx+1], only need
* to do two more checks and we have the answer
*/
if (u < 0) {
/*
* We want right continuity, that is,
* if u == x[idx+1]
* then x[idx+1] <= u < x[idx+2]
* else x[idx ] <= u < x[idx+1]
*/
return( (u == x[idx+1]) ? (idx+1) : idx);
} else {
/*
* We want left continuity, that is,
* if u == x[idx]
* then x[idx-1] < u <= x[idx ]
* else x[idx ] < u <= x[idx+1]
*/
return( (u == x[idx]) ? (idx-1) : idx);
}
}
}
} /* end GetDirectLookupIndex */
/* [EOF] lookup_index.c */matlabroot/toolbox/simulink/simdemos/simfeatures/tlc_c/sfun_directlook.tlc.
%% File : sfun_directlook.tlc
%% Abstract:
%% Level-2 S-function sfun_directlook block target file.
%% It is using direct lookup algorithm without interpolation
%%
%% Copyright 1990-2004 The MathWorks, Inc.
%% $Revision: 1.1.4.7 $
%implements "sfun_directlook" "C"
%% Function: BlockTypeSetup ====================================================
%% Abstract:
%% Place include and function prototype in the model's header file.
%%
%function BlockTypeSetup(block, system) void
%% To add this external function's prototype in the header of the generated
%% file.
%%
%openfile buffer
extern int_T GetDirectLookupIndex(const real_T *x, int_T xlen, real_T u);
%closefile buffer
%<LibCacheFunctionPrototype(buffer)>
%endfunction
%% Function: mdlOutputs ========================================================
%% Abstract:
%% Direct 1-D lookup table S-function example.
%% Here we are trying to compute an approximate solution, p(x) to an
%% unknown function f(x) at x=x0, given data point pairs (x,y) in the
%% form of a x data vector and a y data vector. For a given data pair
%% (say the i'th pair), we have y_i = f(x_i). It is assumed that the x
%% data values are monotonically increasing. If the first or last x is
%% outside of the range of the x data vector, then the first or last
%% point will be returned.
%%
%% This function returns the "nearest" y0 point for a given x0.
%% No interpolation is performed.
%%
%% The S-function parameters are:
%% XData
%% YData
%% XEvenlySpaced: 0 or 1
%% The third parameter cannot be changed during execution and is
%% written to the model.rtw file in XSpacing filed of the SFcnParamSettings
%% record as "EvenlySpaced" or "UnEvenlySpaced". The first two parameters
%% can change during execution and show up in the parameter vector.
%%
%function Outputs(block, system) Output
/* %<Type> Block: %<Name> */
{
%assign rollVars = ["U", "Y"]
%%
%% Load XData and YData as local variables
%%
const real_T *xData = %<LibBlockParameterAddr(XData, "", "", 0)>;
const real_T *yData = %<LibBlockParameterAddr(YData, "", "", 0)>;
%assign xDataLen = SIZE(XData.Value, 1)
%%
%% When the XData is evenly spaced, we use the direct lookup algorithm
%% to locate the lookup index.
%%
%if SFcnParamSettings.XSpacing == "EvenlySpaced"
real_T spacing = xData[1] - xData[0];
%roll idx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars
%assign u = LibBlockInputSignal(0, "", lcv, idx)
%assign y = LibBlockOutputSignal(0, "", lcv, idx)
if ( %<u> <= xData[0] ) {
%<y> = yData[0];
} else if ( %<u> >= yData[%<xDataLen-1>] ) {
%<y> = yData[%<xDataLen-1>];
} else {
int_T idx = (int_T)( ( %<u> - xData[0] ) / spacing );
%<y> = yData[idx];
}
%%
%% Generate an empty line if we are not rolling,
%% so that it looks nice in the generated code.
%%
%if lcv == ""
%endif
%endroll
%else
%% When the XData is unevenly spaced, we use a bisection search to
%% locate the lookup index.
int_T idx;
%assign xDataAddr = LibBlockParameterAddr(XData, "", "", 0)
%roll idx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars
%assign u = LibBlockInputSignal(0, "", lcv, idx)
idx = GetDirectLookupIndex(xData, %<xDataLen>, %<u>);
%assign y = LibBlockOutputSignal(0, "", lcv, idx)
%<y> = yData[idx];
%%
%% Generate an empty line if we are not rolling,
%% so that it looks nice in the generated code.
%%
%if lcv == ""
%endif
%endroll
%endif
}
%endfunction
%% EOF: sfun_directlook.tlcConsider using the block property RTWdata (see S-Function RTWdata). This property is a structure of strings that you can associate with a block. The code generator saves the structure with the model in the model.rtw file and makes the .rtw file more readable. For example, suppose you enter the following commands in the MATLAB Command Window:
mydata.field1 = 'information for field1'; mydata.field2 = 'information for field2'; set_param(sfun_block, 'RTWdata', mydata);
The .rtw file that Simulink Coder generates for the block, will include the comments specified in the structure mydata.
Consider using the mdlRTW function to inline your C MEX S-function in the generated code. This is useful when you want to
Rename tunable parameters in the generated code
Introduce nontunable parameters into a TLC file
This section describes how you can take advantage of expression folding to increase the efficiency of code generated by your own inlined S-Function blocks, by calling macros provided in the S-Function API. This section assumes that you are familiar with:
Writing inlined S-functions (see Overview of S-Functions in the Simulink Writing S-Functions documentation).
The Target Language Compiler (see the Target Language Compiler documentation)
The S-Function API lets you specify whether a given S-Function block should nominally accept expressions at a given input port. A block should not always accept expressions. For example, if the address of the signal at the input is used, expressions should not be accepted at that input, because it is not possible to take the address of an expression.
The S-Function API also lets you specify whether an expression can represent the computations associated with a given output port. When you request an expression at a block's input or output port, the Simulink engine determines whether or not it can honor that request, given the block's context. For example, the engine might deny a block's request to output an expression if the destination block does not accept expressions at its input, if the destination block has an update function, or if multiple output destinations exist.
The decision to honor or deny a request to output an expression can also depend on the category of output expression the block uses (see Categories of Output Expressions).
The sections that follow explain
When and how you can request that a block accept expressions at an input port
When and how you can request that a block generate expressions at an outport
The conditions under which the Simulink engine will honor or deny such requests
To take advantage of expression folding in your S-functions, you should understand when to request acceptance and generation of expressions for specific blocks. You do not have to understand the algorithm by which the Simulink engine chooses to accept or deny these requests. However, if you want to trace between the model and the generated code, it is helpful to understand some of the more common situations that lead to denial of a request.
When you implement a C MEX S-function, you can specify whether the code corresponding to a block's output is to be generated as an expression. If the block generates an expression, you must specify that the expression is constant, trivial, or generic.
A constant output expression is a direct access to one of the block's parameters. For example, the output of a Constant block is defined as a constant expression because the output expression is simply a direct access to the block's Value parameter.
A trivial output expression is an expression that can be repeated, without any performance penalty, when the output port has multiple output destinations. For example, the output of a Unit Delay block is defined as a trivial expression because the output expression is simply a direct access to the block's state. Because the output expression involves no computations, it can be repeated more than once without degrading the performance of the generated code.
A generic output expression is an expression that should be assumed to have a performance penalty if repeated. As such, a generic output expression is not suitable for repeating when the output port has multiple output destinations. For instance, the output of a Sum block is a generic rather than a trivial expression because it is costly to recompute a Sum block output expression as an input to multiple blocks.
Examples of Trivial and Generic Output Expressions. Consider the following block diagram. The Delay block has multiple destinations, yet its output is designated as a trivial output expression, so that it can be used more than once without degrading the efficiency of the code.

The following code excerpt shows code generated from the Unit Delay block in this block diagram. The three root outputs are directly assigned from the state of the Unit Delay block, which is stored in a field of the global data structure rtDWork. Since the assignment is direct, involving no expressions, there is no performance penalty associated with using the trivial expression for multiple destinations.
void MdlOutputs(int_T tid)
{
...
/* Outport: <Root>/Out1 incorporates:
* UnitDelay: <Root>/Unit Delay */
rtY.Out1 = rtDWork.Unit_Delay_DSTATE;
/* Outport: <Root>/Out2 incorporates:
* UnitDelay: <Root>/Unit Delay */
rtY.Out2 = rtDWork.Unit_Delay_DSTATE;
/* Outport: <Root>/Out3 incorporates:
* UnitDelay: <Root>/Unit Delay */
rtY.Out3 = rtDWork.Unit_Delay_DSTATE;
...
}
On the other hand, consider the Sum blocks in the following model:

The upper Sum block in the preceding model generates the signal labeled non_triv. Computation of this output signal involves two multiplications and an addition. If the Sum block's output were permitted to generate an expression even when the block had multiple destinations, the block's operations would be duplicated in the generated code. In the case illustrated, the generated expressions would proliferate to four multiplications and two additions. This would degrade the efficiency of the program. Accordingly the output of the Sum block is not allowed to be an expression because it has multiple destinations
The code generated for the previous block diagram shows how code is generated for Sum blocks with single and multiple destinations.
The Simulink engine does not permit the output of the upper Sum block to be an expression because the signal non_triv is routed to two output destinations. Instead, the result of the multiplication and addition operations is stored in a temporary variable (rtb_non_triv) that is referenced twice in the statements that follow, as seen in the code excerpt below.
In contrast, the lower Sum block, which has only a single output destination (Out2), does generate an expression.
void MdlOutputs(int_T tid)
{
/* local block i/o variables */
real_T rtb_non_triv;
real_T rtb_Sine_Wave;
/* Sum: <Root>/Sum incorporates:
* Gain: <Root>/Gain
* Inport: <Root>/u1
* Gain: <Root>/Gain1
* Inport: <Root>/u2
*
* Regarding <Root>/Gain:
* Gain value: rtP.Gain_Gain
*
* Regarding <Root>/Gain1:
* Gain value: rtP.Gain1_Gain
*/
rtb_non_triv = (rtP.Gain_Gain * rtU.u1) + (rtP.Gain1_Gain *
rtU.u2);
/* Outport: <Root>/Out1 */
rtY.Out1 = rtb_non_triv;
/* Sin Block: <Root>/Sine Wave */
rtb_Sine_Wave = rtP.Sine_Wave_Amp *
sin(rtP.Sine_Wave_Freq * rtmGetT(rtM_model) +
rtP.Sine_Wave_Phase) + rtP.Sine_Wave_Bias;
/* Outport: <Root>/Out2 incorporates:
* Sum: <Root>/Sum1
*/
rtY.Out2 = (rtb_non_triv + rtb_Sine_Wave);
}
Specify the Category of an Output Expression. The S-Function API provides macros that let you declare whether an output of a block should be an expression, and if so, to specify the category of the expression. The following table specifies when to declare a block output to be a constant, trivial, or generic output expression.
Types of Output Expressions
Category of Expression | When to Use |
|---|---|
Constant | Use only if block output is a direct memory access to a block parameter. |
Trivial | Use only if block output is an expression that can appear multiple times in the code without reducing efficiency (for example, a direct memory access to a field of the DWork vector, or a literal). |
Generic | Use if output is an expression, but not constant or trivial. |
You must declare outputs as expressions in the mdlSetWorkWidths function using macros defined in the S-Function API. The macros have the following arguments:
SimStruct *S: pointer to the block's SimStruct.
int idx: zero-based index of the output port.
bool value: pass in TRUE if the port generates output expressions.
The following macros are available for setting an output to be a constant, trivial, or generic expression:
void ssSetOutputPortConstOutputExprInRTW(SimStruct *S, int idx, bool value)
void ssSetOutputPortTrivialOutputExprInRTW(SimStruct *S, int idx, bool value)
void ssSetOutputPortOutputExprInRTW(SimStruct *S, int idx, bool value)
The following macros are available for querying the status set by any prior calls to the macros above:
bool ssGetOutputPortConstOutputExprInRTW(SimStruct *S, int idx)
bool ssGetOutputPortTrivialOutputExprInRTW(SimStruct *S, int idx)
bool ssGetOutputPortOutputExprInRTW(SimStruct *S, int idx)
The set of generic expressions is a superset of the set of trivial expressions, and the set of trivial expressions is a superset of the set of constant expressions.
Therefore, when you query an output that has been set to be a constant expression with ssGetOutputPortTrivialOutputExprInRTW, it returns True. A constant expression is considered a trivial expression, because it is a direct memory access that can be repeated without degrading the efficiency of the generated code.
Similarly, an output that has been configured to be a constant or trivial expression returns True when queried for its status as a generic expression.
A block can request that its output be represented in code as an expression. Such a request can be denied if the destination block cannot accept expressions at its input port. Furthermore, conditions independent of the requesting block and its destination blocks can prevent acceptance of expressions.
This section discusses block-specific conditions under which requests for input expressions are denied. For information on other conditions that prevent acceptance of expressions, see Generic Conditions for Denial of Requests to Output Expressions.
A block should not be configured to accept expressions at its input port under the following conditions:
The block must take the address of its input data. It is not possible to take the address of most types of input expressions.
The code generated for the block references the input more than once (for example, the Abs or Max blocks). This would lead to duplication of a potentially complex expression and a subsequent degradation of code efficiency.
If a block refuses to accept expressions at an input port, then any block that is connected to that input port is not permitted to output a generic or trivial expression.
A request to output a constant expression is never denied, because there is no performance penalty for a constant expression, and it is always possible to take the parameter's address.
Use the S-Function API to Specify Input Expression Acceptance. The S-Function API provides macros that let you
Specify whether a block input should accept nonconstant expressions (that is, trivial or generic expressions)
Query whether a block input accepts nonconstant expressions
By default, block inputs do not accept nonconstant expressions.
You should call the macros in your mdlSetWorkWidths function. The macros have the following arguments:
SimStruct *S: pointer to the block's SimStruct.
int idx: zero-based index of the input port.
bool value: pass in TRUE if the port accepts input expressions; otherwise pass in FALSE.
The macro available for specifying whether or not a block input should accept a nonconstant expression is as follows:
void ssSetInputPortAcceptExprInRTW(SimStruct *S, int portIdx, bool value)
The corresponding macro available for querying the status set by any prior calls to ssSetInputPortAcceptExprInRTW is as follows:
bool ssGetInputPortAcceptExprInRTW(SimStruct *S, int portIdx)
Generic Conditions for Denial of Requests to Output Expressions. Even after a specific block requests that it be allowed to generate an output expression, that request can be denied, for generic reasons. These reasons include, but are not limited to
The output expression is nontrivial, and the output has multiple destinations.
The output expression is nonconstant, and the output is connected to at least one destination that does not accept expressions at its input port.
The output is a test point.
The output has been assigned an external storage class.
The output must be stored using global data (for example is an input to a merge block or a block with states).
The output signal is complex.
You do not need to consider these generic factors when deciding whether or not to utilize expression folding for a particular block. However, these rules can be helpful when you are examining generated code and analyzing cases where the expression folding optimization is suppressed.
To take advantage of expression folding, you must modify the TLC block implementation of an inlined S-Function such that it informs the Simulink engine whether it generates or accepts expressions at its
Input ports, as explained in Use the S-Function API to Specify Input Expression Acceptance.
Output ports, as explained in Categories of Output Expressions.
This section discusses required modifications to the TLC implementation.
Expression Folding Compliance. In the BlockInstanceSetup function of your S-function, register your block to be compliant with expression folding. Otherwise, any expression folding requested or allowed at the block's outputs or inputs will be disabled, and temporary variables will be used.
To register expression folding compliance, call the TLC library function LibBlockSetIsExpressionCompliant(block), which is defined in matlabroot/rtw/c/tlc/lib/utillib.tlc. For example:
%% Function: BlockInstanceSetup =========================================== %% %function BlockInstanceSetup(block, system) void %% %<LibBlockSetIsExpressionCompliant(block)> %% %endfunction
You can conditionally disable expression folding at the inputs and outputs of a block by making the call to this function conditionally.
If you have overridden one of the TLC block implementations provided by the Simulink Coder product with your own implementation, you should not make the preceding call until you have updated your implementation, as described by the guidelines for expression folding in the following sections.
Output Expressions. The BlockOutputSignal function is used to generate code for a scalar output expression or one element of a nonscalar output expression. If your block outputs an expression, you should add a BlockOutputSignal function. The prototype of the BlockOutputSignal is
%function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void
The arguments to BlockOutputSignal are as follows:
block: the record for the block for which an output expression is being generated
system: the record for the system containing the block
portIdx: zero-based index of the output port for which an expression is being generated
ucv: user control variable defining the output element for which code is being generated
lcv: loop control variable defining the output element for which code is being generated
idx: signal index defining the output element for which code is being generated
retType: string defining the type of signal access desired:
"Signal" specifies the contents or address of the output signal.
"SignalAddr" specifies the address of the output signal
The BlockOutputSignal function returns a text string for the output signal or address. The string should enforce the precedence of the expression by using opening and terminating parentheses, unless the expression consists of a function call. The address of an expression can only be returned for a constant expression; it is the address of the parameter whose memory is being accessed. The code implementing the BlockOutputSignal function for the Constant block is shown below.
%% Function: BlockOutputSignal =================================================
%% Abstract:
%% Return the reference to the parameter. This function *may*
%% be used by Simulink when optimizing the Block IO data structure.
%%
%function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void
%switch retType
%case "Signal"
%return LibBlockParameter(Value,ucv,lcv,idx)
%case "SignalAddr"
%return LibBlockParameterAddr(Value,ucv,lcv,idx)
%default
%assign errTxt = "Unsupported return type: %<retType>"
%<LibBlockReportError(block,errTxt)>
%endswitch
%endfunction
The code implementing the BlockOutputSignal function for the Relational Operator block is shown below.
%% Function: BlockOutputSignal ================================================= %% Abstract: %% Return an output expression. This function *may* %% be used by Simulink when optimizing the Block IO data structure. %% %function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void %switch retType %case "Signal" %assign logicOperator = ParamSettings.Operator %if ISEQUAL(logicOperator, "~=") %assign op = "!=" elseif ISEQUAL(logicOperator, "==") %assign op = "==" %else %assign op = logicOperator %endif %assign u0 = LibBlockInputSignal(0, ucv, lcv, idx) %assign u1 = LibBlockInputSignal(1, ucv, lcv, idx) %return "(%<u0> %<op> %<u1>)" %default %assign errTxt = "Unsupported return type: %<retType>" %<LibBlockReportError(block,errTxt)> %endswitch %endfunction
Expression Folding for Blocks with Multiple Outputs. When a block has a single output, the Outputs function in the block's TLC file is called only if the output port is not an expression. Otherwise, the BlockOutputSignal function is called.
If a block has multiple outputs, the Outputs function is called if any output port is not an expression. The Outputs function should guard against generating code for output ports that are expressions. This is achieved by guarding sections of code corresponding to individual output ports with calls to LibBlockOutputSignalIsExpr().
For example, consider an S-Function with two inputs and two outputs, where
The first output, y0, is equal to two times the first input.
The second output, y1, is equal to four times the second input.
The Outputs and BlockOutputSignal functions for the S-function are shown in the following code excerpt.
%% Function: BlockOutputSignal ================================================= %% Abstract: %% Return an output expression. This function *may* %% be used by Simulink when optimizing the Block IO data structure. %% %function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void %switch retType %case "Signal" %assign u = LibBlockInputSignal(portIdx, ucv, lcv, idx) %case "Signal" %if portIdx == 0 %return "(2 * %<u>)" %elseif portIdx == 1 %return "(4 * %<u>)" %endif %default %assign errTxt = "Unsupported return type: %<retType>" %<LibBlockReportError(block,errTxt)> %endswitch %endfunction %% %% Function: Outputs ================================================= %% Abstract: %% Compute output signals of block %% %function Outputs(block,system) Output %assign rollVars = ["U", "Y"] %roll sigIdx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars %assign u0 = LibBlockInputSignal(0, "", lcv, sigIdx) %assign u1 = LibBlockInputSignal(1, "", lcv, sigIdx) %assign y0 = LibBlockOutputSignal(0, "", lcv, sigIdx) %assign y1 = LibBlockOutputSignal(1, "", lcv, sigIdx) %if !LibBlockOutputSignalIsExpr(0) %<y0> = 2 * %<u0>; %endif %if !LibBlockOutputSignalIsExpr(1) %<y1> = 4 * %<u1>; %endif %endroll %endfunction
Comments for Blocks That Are Expression-Folding-Compliant. In the past, all blocks preceded their outputs code with comments of the form
/* %<Type> Block: %<Name> */
When a block is expression-folding-compliant, the initial line shown above is generated automatically. You should not include the comment as part of the block's TLC implementation. Additional information should be registered using the LibCacheBlockComment function.
The LibCacheBlockComment function takes a string as an input, defining the body of the comment, except for the opening header, the final newline of a single or multiline comment, and the closing trailer.
The following TLC code illustrates registering a block comment. Note the use of the function LibBlockParameterForComment, which returns a string, suitable for a block comment, specifying the value of the block parameter.
%openfile commentBuf $c(*) Gain value: %<LibBlockParameterForComment(Gain)> %closefile commentBuf %<LibCacheBlockComment(block, commentBuf)>
You can use the following SimStruct macros in the mdlInitializeSizes method to specify the scope and reusability of the memory used for your S-function's input and output ports:
ssSetInputPortOptimOpts: Specify the scope and reusability of the memory allocated to an S-function input port
ssSetOutputPortOptimOpts: Specify the scope and reusability of the memory allocated to an S-function output port
ssSetInputPortOverWritable: Specify whether one of your S-function's input ports can be overwritten by one of its output ports
ssSetOutputPortOverwritesInputPort: Specify whether an output port can share its memory buffer with an input port
You declare an input or output as local or global, and indicate its reusability, by passing one of the following four options to the ssSetInputPortOptimOpts and ssSetOutputPortOptimOpts macros:
SS_NOT_REUSABLE_AND_GLOBAL: Indicates that the input and output ports are stored in separate memory locations in the global block input and output structure
SS_NOT_REUSABLE_AND_LOCAL: Indicates that the Simulink Coder software can declare individual local variables for the input and output ports
SS_REUSABLE_AND_LOCAL: Indicates that the Simulink Coder software can reuse a single local variable for these input and output ports
SS_REUSABLE_AND_GLOBAL: Indicates that these input and output ports are stored in a single element in the global block input and output structure
Note Marking an input or output port as a local variable does not imply that the code generator uses a local variable in the generated code. If your S-function accesses the inputs and outputs only in its mdlOutputs routine, the code generator declares the inputs and outputs as local variables. However, if the inputs and outputs are used elsewhere in the S-function, the code generator includes them in the global block input and output structure. |
The reusability setting indicates if the memory associated with an input or output port can be overwritten. To reuse input and output port memory:
Indicate the ports are reusable using either the SS_REUSABLE_AND_LOCAL or SS_REUSABLE_AND_GLOBAL option in the ssSetInputPortOptimOpts and ssSetOutputPortOptimOpts macros
Indicate the input port memory is overwritable using ssSetInputPortOverWritable
If your S-function has multiple input and output ports, use ssSetOutputPortOverwritesInputPort to indicate which output and input ports share memory
The following example shows how different scope and reusability settings effect the generated code. The following model contains an S-function block pointing to the C MEX S-function matlabroot/toolbox/simulink/simdemos/simfeatures/src/sfun_directlook.c, which models a direct 1-D lookup table.

The S-function's mdlInitializeSizes method declares the input port as reusable, local, and overwritable and the output port as reusable and local, as follows:
static void mdlInitializeSizes(SimStruct *S)
{
/* snip */
ssSetInputPortOptimOpts(S, 0, SS_REUSABLE_AND_LOCAL);
ssSetInputPortOverWritable(S, 0, TRUE);
/* snip */
ssSetOutputPortOptimOpts(S, 0, SS_REUSABLE_AND_LOCAL);
/* snip */
}The generated code for this model stores the input and output signals in a single local variable rtb_SFunction, as shown in the following output function:
static void sl_directlook_output(int_T tid)
{
/* local block i/o variables */
real_T rtb_SFunction[2];
/* Sin: '<Root>/Sine Wave' */
rtb_SFunction[0] = sin(((real_T)sl_directlook_DWork.counter[0] +
sl_directlook_P.SineWave_Offset) * 2.0 * 3.1415926535897931E+000 /
sl_directlook_P.SineWave_NumSamp) * sl_directlook_P.SineWave_Amp[0] +
sl_directlook_P.SineWave_Bias;
rtb_SFunction[1] = sin(((real_T)sl_directlook_DWork.counter[1] +
sl_directlook_P.SineWave_Offset) * 2.0 * 3.1415926535897931E+000 /
sl_directlook_P.SineWave_NumSamp) * sl_directlook_P.SineWave_Amp[1] +
sl_directlook_P.SineWave_Bias;
/* S-Function Block: <Root>/S-Function */
{
const real_T *xData = &sl_directlook_P.SFunction_XData[0];
const real_T *yData = &sl_directlook_P.SFunction_YData [0];
real_T spacing = xData[1] - xData[0];
if (rtb_SFunction[0] <= xData[0] ) {
rtb_SFunction[0] = yData[0];
} else if (rtb_SFunction[0] >= yData[20] ) {
rtb_SFunction[0] = yData[20];
} else {
int_T idx = (int_T)( ( rtb_SFunction[0] - xData[0] ) / spacing );
rtb_SFunction[0] = yData[idx];
}
if (rtb_SFunction[1] <= xData[0] ) {
rtb_SFunction[1] = yData[0];
} else if (rtb_SFunction[1] >= yData[20] ) {
rtb_SFunction[1] = yData[20];
} else {
int_T idx = (int_T)( ( rtb_SFunction[1] - xData[0] ) / spacing );
rtb_SFunction[1] = yData[idx];
}
}
/* Outport: '<Root>/Out1' */
sl_directlook_Y.Out1[0] = rtb_SFunction[0];
sl_directlook_Y.Out1[1] = rtb_SFunction[1];
UNUSED_PARAMETER(tid);
}
The following table shows variations of the code generated for this model when using the generic real-time target (GRT). Each row explains a different setting for the scope and reusability of the S-function's input and output ports.
| Scope and reusability | S-function mdlInitializeSizes code | Generated code |
|---|---|---|
| Inputs: Local, reusable, overwritable Outputs: Local, reusable | ssSetInputPortOptimOpts(S, 0, SS_REUSABLE_AND_LOCAL); ssSetInputPortOverWritable(S, 0, TRUE); ssSetOutputPortOptimOpts(S, 0, SS_REUSABLE_AND_LOCAL); | The model.c file
declares a local variable in the output function./* local block i/o variables */ real_T rtb_SFunction[2]; |
| Inputs: Global, reusable,
overwritable
| ssSetInputPortOptimOpts(S, 0, SS_REUSABLE_AND_GLOBAL); ssSetInputPortOverWritable(S, 0, TRUE); ssSetOutputPortOptimOpts(S, 0, SS_REUSABLE_AND_GLOBAL); | The model.h file
defines a block signals structure with a single element to store the
S-function's input and output./* Block signals (auto storage) */
typedef struct {
real_T SFunction[2];
} BlockIO_sl_directlook;The model.c file
uses this element of the structure in calculations of the S-function's
input and output signals. /* Sin: '<Root>/Sine Wave' */
sl_directlook_B.SFunction[0] = sin ...
/* snip */
/*S-Function Block:<Root>/S-Function*/
{
const real_T *xData =
&sl_directlook_P.SFunction_XData[0] |
| Inputs: Local, not reusable
| ssSetInputPortOptimOpts(S, 0, SS_NOT_REUSABLE_AND_LOCAL); ssSetInputPortOverWritable(S, 0, FALSE); ssSetOutputPortOptimOpts(S, 0, SS_NOT_REUSABLE_AND_LOCAL); | The model.c file
declares local variables for the S-function's input and output in
the output function/* local block i/o variables */ real_T rtb_SineWave[2]; real_T rtb_SFunction[2]; |
| Inputs: Global, not reusable
| ssSetInputPortOptimOpts(S, 0, SS_NOT_REUSABLE_AND_GLOBAL); ssSetInputPortOverWritable(S, 0, FALSE); ssSetOutputPortOptimOpts(S, 0, SS_NOT_REUSABLE_AND_GLOBAL); | The model.h file
defines a block signal structure with individual elements to store
the S-function's input and output./* Block signals (auto storage) */
typedef struct {
real_T SineWave[2];
real_T SFunction[2];
} BlockIO_sl_directlook;The model.c file
uses the different elements in this structure when calculating the
S-function's input and output. /* Sin: '<Root>/Sine Wave' */
sl_directlook_B.SineWave[0] = sin ...
/* snip */
/*S-Function Block:<Root>/S-Function*/
{
const real_T *xData =
&sl_directlook_P.SFunction_XData[0] |
For the Simulink engine to determine whether a model can inherit a sample time, the S-functions in the model need to specify how they use sample times. You can specify this information by calling the macro ssSetModelReferenceSampleTimeInheritanceRule from mdlInitializeSizes or mdlSetWorkWidths. To use this macro:
Check whether the S-function calls any of the following macros:
ssGetSampleTime
ssGetInputPortSampleTime
ssGetOutputPortSampleTime
ssGetInputPortOffsetTime
ssGetOutputPortOffsetTime
ssGetSampleTimePtr
ssGetInputPortSampleTimeIndex
ssGetOutputPortSampleTimeIndex
ssGetSampleTimeTaskID
ssGetSampleTimeTaskIDPtr
Check for the following in your S-function TLC code:
LibBlockSampleTime
CompiledModel.SampleTime
LibBlockInputSignalSampleTime
LibBlockInputSignalOffsetTime
LibBlockOutputSignalSampleTime
LibBlockOutputSignalOffsetTime
Depending on your search results, use ssSetModelReferenceSampleTimeInheritanceRule as indicated in the following table.
| If... | Use... |
|---|---|
| None of the macros or functions are present, the S-function does not preclude the model from inheriting a sample time. | ssSetModelReferenceSampleTimeInheritanceRule
(S, USE_DEFAULT_FOR_DISCRETE_INHERITANCE)
|
Any of the macros or functions are used for
| ssSetModelReferenceSampleTimeInheritanceRule... (S,USE_DEFAULT_FOR_DISCRETE_INHERITANCE) |
| The S-function uses its sample time for computing parameters, outputs, and so on | ssSetModelReferenceSampleTimeInheritanceRule (S, DISALLOW_SAMPLE_TIME_INHERITANCE) |
Note If an S-function does not set the ssSetModelReferenceSampleTimeInheritanceRule macro, by default the Simulink engine assumes that the S-function does not preclude the model containing that S-function from inheriting a sample time. However, the engine issues a warning indicating that the model includes S-functions for which this macro is not set. |
You can use settings on the Diagnostics/Solver pane of the Configuration Parameters dialog box or Model Explorer to control how the Simulink engine responds when it encounters S-functions that have unspecified sample time inheritance rules. Toggle the Unspecified inheritability of sample time diagnostic to none, warning, or error. The default is warning.
The Simulink Coder code reuse feature generates code for a subsystem in the form of a single function that is invoked wherever the subsystem occurs in the model (see Subsystems). If a subsystem contains S-functions, the S-functions must be compatible with the code reuse feature. Otherwise, the code generator might not generate reusable code from the subsystem or might generate incorrect code.
If you want your S-function to support the subsystem code reuse feature, the S-function must meet the following requirements:
The S-function must be inlined.
Code generated from the S-function must not use static variables.
The S-function must initialize its pointer work vector in mdlStart and not before.
The S-function must not be a sink that logs data to the workspace.
The S-function must register its parameters as run-time parameters in mdlSetWorkWidths. (It must not use ssWriteRTWParameters in its mdlRTW function for this purpose.)
The S-function must not be a device driver.
In addition to meeting the preceding requirements, your S-function must set the SS_OPTION_WORKS_WITH_CODE_REUSE flag (see the description of ssSetOptions in the Simulink Writing S-Function documentation). This flag indicates that your S-function meets the requirements for subsystem code reuse.
S-functions can be used in models with multiple sample rates and deployed in multitasking target environments. Likewise, S-functions themselves can have multiple rates at which they operate. The Embedded Coder product generates code for multirate multitasking models using an approach called rate grouping. In code generated for ERT-based targets, rate grouping generates separate model_step functions for the base rate task and each subrate task in the model. Although rate grouping is a code generation feature found in ERT targets only, your S-functions can use it in other contexts when you code them as explained below.
To take advantage of rate grouping, you must inline your multirate S-functions if you have not done so. You need to follow certain Target Language Compiler protocols to exploit rate grouping. Coding TLC to exploit rate grouping does not prevent your inlined S-functions from functioning properly in GRT. Likewise, your inlined S-functions will still generate valid ERT code even if you do not make them rate-grouping-compliant. If you do so, however, they will generate more efficient code for multirate models.
For instructions and examples of Target Language Compiler code illustrating how to create and upgrade S-functions to generate rate-grouping-compliant code, see Rate Grouping Compliance and Compatibility Issues in the Embedded Coder documentation.
For each multirate S-function that is not rate grouping-compliant, the Simulink Coder software issues the following warning when you build:
Warning: Simulink Coder: Code of output function for multirate block '<Root>/S-Function' is guarded by sample hit checks rather than being rate grouped. This will generate the same code for all rates used by the block, possibly generating dead code. To avoid dead code, you must update the TLC file for the block.
You will also find a comment such as the following in code generated for each noncompliant S-function:
/* Because the output function of multirate block <Root>/S-Function is not rate grouped, the following code might contain unreachable blocks of code. To avoid this, you must update your block TLC file. */
The words "update function" are substituted for "output function" in these warnings.
The following instructions show how to support both data determinism and data integrity in multirate S-functions. They do not cover cases where there is no determinism nor integrity. Support for frame-based processing does not affect the requirements.
Note The slow rates must be multiples of the fastest rate. The instructions do not apply when two rates being interfaced are not multiples or when the rates are not periodic. |
Rules for Properly Handling Fast-to-Slow Transitions. The rules that multirate S-functions should observe for inputs are
The input should only be read at the rate that is associated with the input port sample time.
Generally, the input data is written to DWork, and the DWork can then be accessed at the slower (downstream) rate.
The input can be read at every sample hit of the input rate and written into DWork memory, but this DWork memory cannot then be directly accessed by the slower rate. Any DWork memory that will be read by the slow rate must only be written by the fast rate when there is a special sample hit. A special sample hit occurs when both this input port rate and rate to which it is interfacing have a hit. Depending on their requirements and design, algorithms can process the data in several locations.
The rules that multirate S-functions should observe for outputs are
The output should not be written by any rate other than the rate assigned to the output port, except in the optimized case described below.
The output should always be written when the sample rate of the output port has a hit.
If these conditions are met, the S-Function block can specify that the input port and output port can both be made local and reusable.
You can include an optimization when little or no processing needs to be done on the data. In such cases, the input rate code can directly write to the output (instead of by using DWork) when there is a special sample hit. If you do this, however, you must declare the outport port to be global and not reusable. This optimization results in one less memcpy but does introduce nonuniform processing requirements on the faster rate.
Whether you use this optimization or not, the most recent input data, as seen by the slower rate, is always the value when both the faster and slower rate had their hits (and possible earlier input data as well, depending on the algorithm). Any subsequent steps by the faster rate and the associated input data updates are not seen by the slower rate until the next hit for the slow rate occurs.
Pseudocode Examples of Fast-to-Slow Rate Transition. The pseudocode below abstracts how you should write your C MEX code to handle fast-to-slow transitions, illustrating with an input rate of 0.1 second driving an output rate of one second. A similar approach can be taken when inlining the code. The block has following characteristics:
File: sfun_multirate_zoh.c, Equation: y = u(tslow)
Input: local and reusable
Output: local and reusable
DirectFeedthrough: yes
OutputFcn
if (ssIsSampleHit(".1")) {
if (ssIsSepcialSampleHit("1")) {
DWork = u;
}
}
if (ssIsSampleHit("1")) {
y = DWork;
}
An alternative, slightly optimized approach for simple algorithms:
Input: local and reusable
Output: global and not reusable because it needs to persist between special sample hits
DirectFeedthrough: yes
OutputFcn
if (ssIsSampleHit(".1")) {
if (ssIsSpecialSampleHit("1")) {
y = u;
}
}
Example adding a simple algorithm:
File: sfun_multirate_avg.c; Equation: y = average(u)
Input: local and reusable
Output: local and reusable
DirectFeedthrough: yes
(Assume DWork[0:10] and DWork[mycounter] are initialized to zero)
OutputFcn
if (ssIsSampleHit(".1")) {
/* In general, processing on 'u' could be done here,
it runs on every hit of the fast rate. */
DWork[DWork[mycounter]++] = u;
if (ssIsSpecialSampleHit("1")) {
/* In general, processing on DWork[0:10] can be done
here, but it does cause the faster rate to have
nonuniform processing requirements (every 10th hit,
more code needs to be run).*/
DWork[10] = sum(DWork[0:9])/10;
DWork[mycounter] = 0;
}
}
if (ssIsSampleHit("1")) {
/* Processing on DWork[10] can be done here before
outputing. This code runs on every hit of the
slower task. */
y = DWork[10];
}
Rules for Properly Handling Slow-to-Fast Transitions. When output rates are faster than input rates, input should only be read at the rate that is associated with the input port sample time, observing the following rules:
Always read input from the update function.
Use no special sample hit checks when reading input.
Write the input to a DWork.
When there is a special sample hit between the rates, copy the DWork into a second DWork in the output function.
Write the second DWork to the output at every hit of the output sample rate.
The block can request that the input port be made local but it cannot be set to reusable. The output port can be set to local and reusable.
As in the fast-to-slow transition case, the input should not be read by any rate other than the one assigned to the input port. Similarly, the output should not be written to at any rate other than the rate assigned to the output port.
An optimization can be made when the algorithm being implemented is only required to run at the slow rate. In such cases, you use only one DWork. The input still writes to the DWork in the update function. When there is a special sample hit between the rates, the output function copies the same DWork directly to the output. You must set the output port to be global and not reusable in this case. This optimization results in one less memcpy operation per special sample hit.
In either case, the data that the fast rate computations operate on is always delayed, that is, the data is from the previous step of the slow rate code.
Pseudocode Examples of Slow-to-Fast Rate Transition. The pseudocode below abstracts what your S-function needs to do to handle slow-to-fast transitions, illustrating with an input rate of one second driving an output rate of 0.1 second. The block has following characteristics:
File: sfun_multirate_delay.c, Equation: y = u(tslow-1)
Input: Set to local, will be local if output/update are combined (ERT) otherwise will be global. Set to not reusable because input needs to be preserved until the update function runs.
Output: local and reusable
DirectFeedthrough: no
OutputFcn
if (ssIsSampleHit(".1") {
if (ssIsSpecialSampleHit("1") {
DWork[1] = DWork[0];
}
y = DWork[1];
}
UpdateFcn
if (ssIsSampleHit("1")) {
DWork[0] = u;
}
An alternative, optimized approach can be used by some algorithms:
Input: Set to local, will be local if output/update are combined (ERT) otherwise will be global. Set to not reusable because input needs to be preserved until the update function runs.
Output: global and not reusable because it needs to persist between special sample hits.
DirectFeedthrough: no
OutputFcn
if (ssIsSampleHit(".1") {
if (ssIsSpecialSampleHit("1") {
y = DWork;
}
}
UpdateFcn
if (ssIsSampleHit("1")) {
DWork = u;
}
Example adding a simple algorithm:
File: sfun_multirate_modulate.c, Equation: y = sin(tfast) + u(tslow-1)
Input: Set to local, will be local if output/update are combined (an ERT feature) otherwise will be global. Set to not reusable because input needs to be preserved until the update function runs.
Output: local and reusable
DirectFeedthrough: no
OutputFcn
if (ssIsSampleHit(".1") {
if (ssIsSpecialSampleHit("1") {
/* Processing not likely to be done here. It causes
* the faster rate to have nonuniform processing
* requirements (every 10th hit, more code needs to
* be run).*/
DWork[1] = DWork[0];
}
/* Processing done at fast rate */
y = sin(ssGetTaskTime(".1")) + DWork[1];
}
UpdateFcn
if (ssIsSampleHit("1")) {
/* Processing on 'u' can be done here. There is a delay of
one slow rate period before the fast rate sees it.*/
DWork[0] = u;}
User-written S-Function blocks provide a powerful way to incorporate legacy and custom code into the Simulink and Simulink Coder development environment. In most cases, you should use S-functions to integrate existing code with Simulink Coder generated code. Several approaches to writing S-functions are available as discussed in
S-functions also provide the most flexible and capable way of including build information for legacy and custom code files in the Simulink Coder build process.
This section discusses the different ways of adding S-functions to the Simulink Coder build process.
When building models with S-functions, the code generator automatically adds rules, include paths, and source filenames to the generated makefile. For this to occur, the source files (.h, .c, and .cpp) for the S-function must be in the same folder as the S-function MEX-file. The code generator propagates this information through the token expansion mechanism of converting a template makefile (TMF) to a makefile. The propagation requires the TMF to support the tokens.
Details of the implicit build support follow:
If the file sfcnname.h exists in the same folder as the S-function MEX-file (for example, sfcnname.mexext), the folder is added to the include path.
If the file sfcnname.c or sfcnname.cpp exists in the same folder as the S-function MEX-file, the Simulink Coder code generator adds a makefile rule for compiling files from that folder.
When an S-function is not inlined with a TLC file, the Simulink Coder code generator must compile the S-function's source file. To determine the name of the source file to add to the list of files to compile, the code generator searches for sfcnname.cpp on the MATLAB path. If the source file is found, the code generator adds the source filename to the makefile. If sfcnname.cpp is not found on the path, the code generator adds the filename sfcnname.c to the makefile, whether or not it is on the MATLAB path.
If your S-function has additional source file dependencies, you must add the names of the additional modules to the build process. You can do this by specifying the filenames
In the S-function modules field of the S-Function block parameter dialog box
With the SFunctionModules parameter in a call to the set_param function
For example, suppose you build your S-function with multiple modules, as in
mex sfun_main.c sfun_module1.c sfun_module2.c
You can then add the modules to the build process by doing one of the following:
Specifying sfun_main, sfun_module1, and sfun_module2 in the S-function modules field in the S-Function block dialog box
Entering the following command at the MATLAB command prompt:
set_param(sfun_block,'SFunctionModules','sfun_module1 sfun_module2')
Alternatively, you can define a variable to represent the parameter value.
modules = 'sfun_module1 sfun_module2'
set_param(sfun_block,'SFunctionModules', modules)
Note The S-function modules field and SFunctionsModules parameter do not support complete source file path specifications. To use the parameter, the Simulink Coder software must be able to find the additional source files when executing the makefile. For the Simulink Coder software to locate the additional files, place them in the same folder as the S-function MEX-file. This will enable you to leverage the implicit build support discussed in Implicit Build Support. |
For more complicated S-function file dependencies, such as specifying source files in other locations or specifying libraries or object files, use the rtwmakecfg.m API, as explained in Use rtwmakecfg.m API to Customize Generated Makefiles.
If you inline your S-function by writing a TLC file, you can add source filenames to the build process by using the TLC library function LibAddToModelSources. For details, see LibAddSourceFileCustomSection(file, builtInSection, newSection) in the Target Language Compiler documentation.
Note This function does not support complete source file path specifications and assumes the Simulink Coder software can find the additional source files when executing the makefile. |
Another useful TLC library function is LibAddToCommonIncludes. Use this function in a #include statement to include S-function header files in the generated model.h header file. For details, see LibAddToCommonIncludes(incFileName) in the Target Language Compiler documentation.
For more complicated S-function file dependencies, such as specifying source files in other locations or specifying libraries or object files, use the rtwmakecfg.m API, as explained in Use rtwmakecfg.m API to Customize Generated Makefiles.
Overview. Simulink Coder TMFs provide tokens that let you add the following items to generated makefiles:
Source folders
Include folders
Run-time library names
Run-time module objects
S-functions can add this information to the makefile by using an rtwmakecfg function. This function is particularly useful when building a model that contains one or more of your S-Function blocks, such as device driver blocks.
To add information pertaining to an S-function to the makefile,
Create the MATLAB language function rtwmakecfg in a file rtwmakecfg.m. The Simulink Coder software associates this file with your S-function based on its folder location. Create the rtwmakecfg Function discusses the requirements for the rtwmakecfg function and the data it should return.
Modify your target's TMF such that it supports macro expansion for the information returned by rtwmakecfg functions. Modify the Template Makefile discusses the required modifications.
After the TLC phase of the build process, when generating a makefile from the TMF, the Simulink Coder code generator searches for an rtwmakecfg.m file in the folder that contains the S-function component. If it finds the file, the code generator calls the rtwmakecfg function.
Create the rtwmakecfg Function. Create the rtwmakecfg.m file containing the rtwmakecfg function in the same folder as your S-function component (sfcname.mexext on a Microsoft Windows system and sfcname and a platform-specific extension on The Open Group UNIX system). The function must return a structured array that contains the following fields:
| Field | Description |
|---|---|
| makeInfo.includePath | A cell array that specifies additional include folder names, organized as a row vector. The Simulink Coder code generator expands the folder names into include instructions in the generated makefile. |
| makeInfo.sourcePath | A cell array that specifies additional source folder names, organized as a row vector. You must include the folder names of files entered into the S-function modules field on the S-Function Block Parameters dialog box or into the block's SFunctionModules parameter if they are not in the same folder as the S-function. The Simulink Coder code generator expands the folder names into make rules in the generated makefile. |
| makeInfo.sources | A cell array that specifies additional source filenames (C or C++), organized as a row vector. Do not include the name of the S-function or any files entered into the S-function modules field on the S-Function Block Parameters dialog box or into the block's SFunctionModules parameter. The Simulink Coder code generator expands the filenames into make variables that contain the source files. You should specify only filenames (with extension). Specify path information with the sourcePath field. |
| makeInfo.linkLibsObjs | A cell array that specifies additional, fully qualified paths to object or library files against which the Simulink Coder generated code should link. The Simulink Coder code generator does not compile the specified objects and libraries. However, it includes them when linking the final executable. This can be useful for incorporating libraries that you do not want the Simulink Coder code generator to recompile or for which the source files are not available. You might also use this element to incorporate source files from languages other than C and C++. This is possible if you first create a C compatible object file or library outside of the Simulink Coder build process. |
| makeInfo.precompile | A Boolean flag that indicates whether the libraries specified in the rtwmakecfg.m file exist in a specified location (precompile==1) or if the libraries need to be created in the build folder during the Simulink Coder build process (precompile==0). |
| makeInfo.library | A structure array that specifies additional run-time libraries and module objects, organized as a row vector. The Simulink Coder code generator expands the information into make rules in the generated makefile. See the next table for a list of the library fields. |
The makeInfo.library field consists of the following elements:
| Element | Description |
|---|---|
| makeInfo.library(n).Name | A character array that specifies the name of the library (without an extension). |
| makeInfo.library(n).Location | A character array that specifies the folder in which the library is located when precompiled. See the description of makeInfo.precompile in the preceding table for more information. A target can use the TargetPreCompLibLocation parameter to override this value. See Specify the Location of Precompiled Libraries for details. |
| makeInfo.library(n).Modules | A cell array that specifies the C or C++ source file base names (without an extension) that comprise the library. Do not include the file extension. The makefile appends the object extension. |
Note The makeInfo.library field must fully specify each library and how to build it. The modules list in the makeInfo.library(n).Modules element cannot be empty. If you need to specify a link-only library, use the makeInfo.linkLibsObjs field instead. |
Example:
disp(['Running rtwmakecfg from folder: ',pwd]);
makeInfo.includePath = { fullfile(pwd, 'somedir2') };
makeInfo.sourcePath = {fullfile(pwd, 'somedir2'), fullfile(pwd, 'somedir3')};
makeInfo.sources = { 'src1.c', 'src2.cpp'};
makeInfo.linkLibsObjs = { fullfile(pwd, 'somedir3', 'src3.object'),...
fullfile(pwd, 'somedir4', 'mylib.library')};
makeInfo.precompile = 1;
makeInfo.library(1).Name = 'myprecompiledlib';
makeInfo.library(1).Location = fullfile(pwd,'somdir2','lib');
makeInfo.library(1).Modules = {'srcfile1' 'srcfile2' 'srcfile3' };Note If a path that you specify in the rtwmakecfg.m API contains spaces, the Simulink Coder code generator does not automatically convert the path to its non-space equivalent. If the build environments you intend to support do not support spaces in paths, refer to Enabling the Simulink Coder Software to Build When Path Names Contain Spaces. |
Modify the Template Makefile. To expand the information generated by an rtwmakecfg function, you can modify the following sections of your target's TMF:
Include Path
C Flags and/or Additional Libraries
Rules
The TMF code examples below may not apply to your make utility. For additional examples, see the GRT or ERT TMFs located in matlabroot/rtw/c/grt/*.tmf or matlabroot/rtw/c/ert/*.tmf.
Example — Adding Folder Names to the Makefile Include Path.The following TMF code example adds folder names to the include path in the generated makefile:
ADD_INCLUDES = \ |>START_EXPAND_INCLUDES<| -I|>EXPAND_DIR_NAME<| \ |>END_EXPAND_INCLUDES<|
Additionally, the ADD_INCLUDES macro must be added to the INCLUDES line, as shown below.
INCLUDES = -I. -I.. $(MATLAB_INCLUDES) $(ADD_INCLUDES) $(USER_INCLUDES)Example — Adding Library Names to the Makefile.
The following TMF code example adds library names to the generated makefile.
LIBS = |>START_PRECOMP_LIBRARIES<| LIBS += |>EXPAND_LIBRARY_NAME<|.a |>END_PRECOMP_LIBRARIES<| |>START_EXPAND_LIBRARIES<| LIBS += |>EXPAND_LIBRARY_NAME<|.a |>END_EXPAND_LIBRARIES<|
For more information on how to use configuration parameters to control library names and location during the build process, see Control Library Location and Naming During the Build Process.
Example — Adding Rules to the Makefile.The following TMF code example adds rules to the generated makefile.
|>START_EXPAND_RULES<|
$(BLD)/%.o: |>EXPAND_DIR_NAME<|/%.c $(SRC)/$(MAKEFILE) rtw_proj.tmw
@$(BLANK)
@echo ### "|>EXPAND_DIR_NAME<|\$*.c"
$(CC) $(CFLAGS) $(APP_CFLAGS) -o $(BLD)$(DIRCHAR)$*.o \
|>EXPAND_DIR_NAME<|$(DIRCHAR)$*.c > $(BLD)$(DIRCHAR)$*.lst
|>END_EXPAND_RULES<|
|>START_EXPAND_LIBRARIES<|MODULES_|>EXPAND_LIBRARY_NAME<| = \
|>START_EXPAND_MODULES<| |>EXPAND_MODULE_NAME<|.o \
|>END_EXPAND_MODULES<|
|>EXPAND_LIBRARY_NAME<|.a : $(MAKEFILE) rtw_proj.tmw
$(MODULES_|>EXPAND_LIBRARY_NAME<|:%.o=$(BLD)/%.o)
@$(BLANK)
@echo ### Creating $@
$(AR) -r $@ $(MODULES_|>EXPAND_LIBRARY_NAME<|:%.o=$(BLD)/%.o)
|>END_EXPAND_LIBRARIES<|
|>START_PRECOMP_LIBRARIES<|MODULES_|>EXPAND_LIBRARY_NAME<| = \
|>START_EXPAND_MODULES<| |>EXPAND_MODULE_NAME<|.o \
|>END_EXPAND_MODULES<|
|>EXPAND_LIBRARY_NAME<|.a : $(MAKEFILE) rtw_proj.tmw
$(MODULES_|>EXPAND_LIBRARY_NAME<|:%.o=$(BLD)/%.o)
@$(BLANK)
@echo ### Creating $@
$(AR) -r $@ $(MODULES_|>EXPAND_LIBRARY_NAME<|:%.o=$(BLD)/%.o)
|>END_PRECOMP_LIBRARIES<|You can precompile new or updated S-function libraries (MEX-files) for a model by using the MATLAB language function rtw_precompile_libs. Using a specified model and a library build specification, this function builds and places the libraries in a precompiled library folder.
By precompiling S-function libraries, you can optimize system builds. Once your precompiled libraries exist, the Simulink Coder code generator can omit library compilation from subsequent builds. For models that use numerous libraries, the time savings for build processing can be significant.
To use rtw_precompile_libs,
Set the library file suffix, including the file type extension, based on the platform in use.
Set the precompiled library folder.
Define a build specification.
Issue a call to rtw_precompile_libs.
The following procedure explains these steps in more detail.
Set the library file suffix, including the file type extension, based on the platform in use.
Consider checking for the type of platform in use and then using the TargetLibSuffix parameter to set the library suffix accordingly. For example, you might set the suffix to .a for a UNIX platform and _vc.lib otherwise.
if isunix
suffix = '.a';
else
suffix = '_vc.lib';
end
set_param(my_model,'TargetLibSuffix', suffix);Set the precompiled library folder.
Use one of the following methods to set the precompiled library folder.
Set the TargetPreCompLibLocation parameter, as explained in Specify the Location of Precompiled Libraries.
Set the makeInfo.precompile field in an rtwmakecfg.m function file.
If you set both TargetPreCompLibLocation and makeInfo.precompile, the setting for TargetPreCompLibLocation takes precedence.
The following command sets the precompiled library folder for model my_model to folder lib under the current working folder.
set_param(my_model,'TargetPreCompLibLocation', fullfile(pwd,'lib'));
Define a build specification.
Set up a structure that defines a build specification. The following table describes fields you can define in the structure. All fields except rtwmakecfgDirs are optional.
The following commands set up build specification build_spec, which indicates that the files to be compiled are in folder src under the current working folder.
build_spec = [];
build_spec.rtwmakecfgDirs = {fullfile(pwd,'src')};
Issue a call to rtw_precompile_libs.
Issue a call to rtw_precompile_libs that specifies the model for which you want to build the precompiled libraries and the build specification. For example:
rtw_precompile_libs(my_model,build_spec);
![]() | Insert Custom Code Blocks | Run-Time Data Interface Extensions | ![]() |

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