Inline C MEX S-Functions
Inline S-Function Overview
When a Simulink® model contains an S-function and a corresponding TLC block target file exists for that S-function, the code generator inlines the S-function. Inlining an S-function can produce more efficient code by eliminating the S-function API layer from the generated code.
For S-functions that can perform a variety of tasks, inlining them gives you the opportunity to generate code only for the current mode of operation set for each instance of the block. As an example of this, if an S-function accepts an arbitrary signal width and loops through each element of the signal, you would want to generate inlined code that has loops when the signal has two or more elements, but generates a simple nonlooped calculation when the signal has just one element.
Level 1 C MEX S-functions (written to an older form of the S-function API) that are not inlined will cause the generated code to make calls to all of these functions even if the routine is empty for the particular S-function.
| Function | Purpose | 
|---|---|
| Initialize the sizes array | |
| Initialize the sample times array | |
| Initialize the states | |
| Compute the outputs | |
| Update discrete states | |
| Compute the derivatives of continuous states | |
| Clean up when the simulation terminates | 
Level 2 C MEX S-functions (i.e., those written to the current S-function API) that are not inlined make calls to the above functions, with the following exceptions:
- mdlInitializeConditionsis called only if- MDL_INITIALIZE_CONDITIONSis declared with- #define.
- mdlStartis called only if- MDL_STARTis declared with- #define.
- mdlUpdateis called only if- MDL_UPDATEis declared with- #define.
- mdlDerivativesis called only if- MDL_DERIVATIVESis declared with- #define.
By inlining an S-function, you can eliminate the calls to these possibly empty functions in the simulation loop. This can greatly improve the efficiency of the generated code.
To inline an S-function called sfunc_namesfunc_name.tlc.c file. The S-function
        target file “inlines” the S-function by directing the Target Language Compiler
        to insert only the statements defined in the target file.
In general, inlining an S-function is especially useful when
- The time required to execute the contents of the S-function is small in comparison to the overhead required to call the S-function. 
- Certain S-function routines are empty (e.g., - mdlUpdate).
- The behavior of the S-function changes between simulation and code generation. For example, device driver I/O S-functions might read from the MATLAB® workspace during simulation, but read from an actual hardware address in the generated code. 
S-Function Parameters
An S-function can write two different types of parameters into the
            model.rtw
- Parameter settings: These correspond to nontunable parameters (typically set from check boxes and menus on a masked S-function) that are written via the - mdlRTWmethod of the S-function using- ssWriteRTWParamSettings. The S-function TLC implementation file can then directly access the values of these parameter settings from the- SFcnParamSettingsrecord in the block.
- Tunable parameters: This class of parameters can be accessed when they are registered as run-time parameters within the S-function. Note that such tunable parameters are automatically written out to the - model.rtw- LibBlockParameterlibrary function and its variants.
For more information on how to create and use run-time parameters, see Create and Update S-Function Run-Time Parameters. Also see the example sfcndemo_runtime in the S-function
        examples for how to create and use the two classes of parameters. The example source files,
        which you can inspect and adapt, are
- toolbox/simulink/simdemos/simfeatures/src/sfun_runtime1.c
- toolbox/simulink/sfuntemplates/tlc_c/sfun_runtime1.tlc
- toolbox/simulink/simdemos/simfeatures/src/sfun_runtime2.c
- toolbox/simulink/sfuntemplates/tlc_c/sfun_runtime2.tlc
- toolbox/simulink/simdemos/simfeatures/src/sfun_runtime3.c
- toolbox/simulink/sfuntemplates/tlc_c/sfun_runtime3.tlc
Sample Code for S-Function
Suppose you have a simple S-function that mimics the Gain block, with one input, one
        output, and a scalar gain. That is, y = u * p. If the Simulink block’s name is foo and the name of the Level 2 S-function
        is foogain, the C MEX S-function must contain this code:
#define S_FUNCTION_NAME foogain
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#define GAIN mxGetPr(ssGetSFcnParam(S,0))[0]
static void mdlInitializeSizes(SimStruct *S)
{
  ssSetNumContStates		(S, 0);
  ssSetNumDiscStates		(S, 0);
  
  if (!ssSetNumInputPorts(S, 1)) return;
  ssSetInputPortWidth            (S, 0, 1);
  ssSetInputPortDirectFeedThrough(S, 0, 1);
  
  if (!ssSetNumOutputPorts(S, 1)) return;
  ssSetOutputPortWidth          (S, 0, 1);
  
  ssSetNumSFcnParams		(S, 1);
  ssSetNumSampleTimes		(S, 0);  
  ssSetNumIWork		(S, 0);
  ssSetNumRWork		(S, 0);
  ssSetNumPWork		(S, 0);
}
 
static void
mdlOutputs(SimStruct *S, int_T tid)
{
  real_T *y = ssGetOutputPortRealSignal(S, 0);
  const InputRealPtrsType u = ssGetInputPortRealSignalPtrs(S, 0);
  y[0] = (*u)[0] * GAIN;
}
static void
mdlInitializeSampleTimes(SimStruct *S){}
static void
mdlTerminate(SimStruct *S) {}
#define MDL_RTW  /* Change to #undef to remove function */
#if defined(MDL_RTW)&&(defined(MATLAB_MEX_FILE)||defined(NRT))
static void
mdlRTW (SimStruct *S)
{
  if (!ssWriteRTWParameters(S, 1,SSWRITE_VALUE_VECT,"Gain","",
                            mxGetPr(ssGetSFcnParam(S,0)),1)) 
  {
    return;
  }
}
#endif
#ifdef  MATLAB_MEX_FILE
#include "simulink.c" 
#else
#include "cg_sfun.h"
#endifThe following two sections show the difference in the generated code for
            model.cfoogain. The model contains
        no other Simulink blocks.
For more information about these S-function related C library functions, see Configure C/C++ S-Function Features. For information about how to generate code, see Approaches for Building Code Generated from Simulink Models.
Comparison of Noninlined and Inlined Versions of model.c
Without a TLC file to define the S-function specifics, the code generator must call
          the MEX-file S-function through the S-function API. The following code is the
              model.c
Noninlined S-Function
/*
 * model.c
.
.
.
*/
real_T untitled_RGND = 0.0;             /* real_T ground */
/* Start the model */
void MdlStart(void)
{
  /* (no start code required) */
}
/* Compute block outputs */
void MdlOutputs(int_T tid)
{
  /* Level2 S-Function Block: <Root>/S-Function (foogain) */
  {
    SimStruct *rts = ssGetSFunction(rtS, 0);
    sfcnOutputs(rts, tid);
  }
}
/* Perform model update */
void MdlUpdate(int_T tid)
{
  /* (no update code required) */
}
/* Terminate function */
void MdlTerminate(void)
{
  /* Level2 S-Function Block: <Root>/S-Function (foogain) */
  {
    SimStruct *rts = ssGetSFunction(rtS, 0);
    sfcnTerminate(rts);
  }
}
#include "model_reg.h"
/* [EOF] model.c */Inlined S-Function.  This code is model.cfoogain S-function fully inlined:
/*
 * model.c
.
.
.
*/
/* Start the model */
void MdlStart(void)
{
  /* (no start code required) */
}
/* Compute block outputs */
void MdlOutputs(int_T tid)
  /* S-Function block: <Root>/S-Function */
  /* NOTE: There are no calls to the S-function API in the inlined 
     version of model.c. */
  rtB.S_Function = 0.0 * rtP.S_Function_Gain;
}
/* Perform model update */
void MdlUpdate(int_T tid)
{
  /* (no update code required) */
}
/* Terminate function */
void MdlTerminate(void)
{
  /* (no terminate code required) */
}
#include "model_reg.h"
/* [EOF] model.c */If you include this target file for this S-function block, the resulting
                model.c
rtB.S_Function = 0.0 * rtP.S_Function_Gain;
Including a TLC file drastically decreased the code size and increased the execution efficiency of the generated code. These notes highlight some information about the TLC code and the generated output:
- The TLC directive - %implementsis required by block target files, and must be the first executable statement in the block target file. This directive prevents the Target Language Compiler from executing an inappropriate target file for S-function- foogain.
- The input to - foois- rtGROUND(a Simulink Coder™ global equal to 0.0) because- foois the only block in the model and its input is unconnected.
- Including a TLC file for - foogaineliminates the need for an S-function registration segment for- foogain. This significantly reduces code size.
- The TLC code inlines the - gainparameter when the build process is configured to inline parameter values. For example, if the S-function parameter is specified as 2.5 in the S-function dialog box, the TLC- Outputsfunction generates- rtB.foo = input * 2.5; 
- Use the - %generatefiledirective if your operating system has a filename size restriction and the name of the S-function is- foosfunction(that exceeds the limit). In this case, you would include the following statement in the system target file (anywhere prior to a reference to this S-function block target file).- %generatefile foosfunction "foosfunc.tlc" - This statement tells the Target Language Compiler to open - foosfunc.tlcinstead of- foosfunction.tlc.
Comparison of Noninlined and Inlined Versions of model_reg.h
Inlining a Level 2 S-function significantly reduces the size of the
              model_reg.hmodel_reg.h
/*
 * model_reg.h
 *
*/
/* Normal model initialization code independent of 
      S-functions  */
/* child S-Function registration */
  ssSetNumSFunctions(rtS, 1);
  /* register each child */
  {
    static SimStruct childSFunctions[1];
    static SimStruct *childSFunctionPtrs[1];
    (void)memset((char_T *)&childSFunctions[0], 0, 
                  sizeof(childSFunctions));
    ssSetSFunctions(rtS, &childSFunctionPtrs[0]);
    {
      int_T i;
      for(i = 0; i < 1; i++) {
        ssSetSFunction(rtS, i, &childSFunctions[i]);
      }
    }
    /* Level2 S-Function Block: untitled/<Root>/S-Function 
       (foogain) */
    {
      extern void foogain(SimStruct *rts);
      SimStruct *rts = ssGetSFunction(rtS, 0);
      /* timing info */
      static time_T sfcnPeriod[1];
      static time_T sfcnOffset[1];
      static int_T sfcnTsMap[1];
      {
        int_T i;
        for(i = 0; i < 1; i++) {
          sfcnPeriod[i] = sfcnOffset[i] = 0.0;
        }
      }
      ssSetSampleTimePtr(rts, &sfcnPeriod[0]);
      ssSetOffsetTimePtr(rts, &sfcnOffset[0]);
      ssSetSampleTimeTaskIDPtr(rts, sfcnTsMap);
      ssSetMdlInfoPtr(rts, ssGetMdlInfoPtr(rtS));
      /* inputs */
      {
        static struct _ssPortInputs inputPortInfo[1];
        _ssSetNumInputPorts(rts, 1);
        ssSetPortInfoForInputs(rts, &inputPortInfo[0]);
        /* port 0 */
        {
          static real_T const *sfcnUPtrs[1];
          sfcnUPtrs[0] = &untitled_RGND;
          ssSetInputPortWidth(rts, 0, 1);
          ssSetInputPortSignalPtrs(rts, 0, 
              (InputPtrsType)&sfcnUPtrs[0]);
        }
      }
      /* outputs */
      {
        static struct _ssPortOutputs outputPortInfo[1];
        _ssSetNumOutputPorts(rts, 1);
        ssSetPortInfoForOutputs(rts, &outputPortInfo[0]);
        ssSetOutputPortWidth(rts, 0, 1);
        ssSetOutputPortSignal(rts, 0, &rtB.S_Function);
      }
      /* path info */
      ssSetModelName(rts, "S-Function");
      ssSetPath(rts, "untitled/S-Function");
      ssSetParentSS(rts, rtS);
      ssSetRootSS(rts, ssGetRootSS(rtS));
      ssSetVersion(rts, SIMSTRUCT_VERSION_LEVEL2);
      /* parameters */
      {
        static mxArray const *sfcnParams[1];
        ssSetSFcnParamsCount(rts, 1);
        ssSetSFcnParamsPtr(rts, &sfcnParams[0]);
        ssSetSFcnParam(rts, 0, &rtP.S_Function_P1Size[0]);
      }
      /* registration */
      foogain(rts);
      sfcnInitializeSizes(rts);
      sfcnInitializeSampleTimes(rts);
      /* adjust sample time */
      ssSetSampleTime(rts, 0, 0.2);
      ssSetOffsetTime(rts, 0, 0.0);
      sfcnTsMap[0] = 0;
      /* Update the InputPortReusable and BufferDstPort flags for 
        each input port */
      ssSetInputPortReusable(rts, 0, 0);
      ssSetInputPortBufferDstPort(rts, 0, -1);
      /* Update the OutputPortReusable flag of each output port */
    }
  }A TLC File to Inline S-Function foogain
To avoid unnecessary calls to the S-function and to generate the minimum code required
          for the S-function, the following TLC file, foogain.tlc, is provided as
          an example.
%implements "foogain" "C" %function Outputs (block, system) Output /* %<Type> block: %<Name> */ %% %assign y = LibBlockOutputSignal (0, "", "", 0) %assign u = LibBlockInputSignal (0, "", "", 0) %assign p = LibBlockParameter (Gain, "", "", 0) %<y> = %<u> * %<p>; %endfunction
Managing Block Instance Data with an Eye Toward Code Generation
Instance data is extra data or working memory that is unique to each instance of a block in a Simulink model. This does not include parameter or state data (which is stored in the model parameter and state vectors, respectively), but rather is used to cache intermediate results or derived representations of parameters and modes. One example of instance data is the buffer used by a transport delay block.
Allocating and using memory on an instance-by-instance basis can be done several ways
          in a Level 2 S-function: via ssSetUserData, work vectors (e.g.,
            ssSetRWorkValue, ssSetIWorkValue), or data-typed
          work vectors known as DWork vectors. For the smallest effort in writing
          the S-function and block target file and for automatic conformance to both static and
            malloc instance data on targets such as grt, use
          data-typed work vectors when writing S-functions with instance data.
The advantages are twofold. In the first place, writing the S-function is more
          straightforward, in that memory allocations and frees are handled for you by Simulink. Secondly, the DWork vectors are written to the
              model.rtwDWork name, data type, and size. This makes writing the
          block target file easier, because you do not have to write TLC code for allocating and
          freeing the DWork memory.
Additionally, if you want to bundle groups of DWork vectors into
          structures for passing to functions, you can populate the structure with pointers to
            DWork arrays in both your S-function mdlStart
          function and the block target file’s Start method, achieving
          consistency between the S-function and the generated code’s handling of data.
Finally, using a DWork makes it straightforward to create a
          specific version of code (data types, scalar vs. vectorized, etc.) for each block instance
          that matches the implementation in the S-function. Both implementations use
            DWork in the same way so that the inlined code can be used with the
            Simulink accelerator software without changes to the C MEX S-function or the block
          target file.
Using Inlined Code with the Simulink Accelerator Software
By default, the Simulink accelerator software calls your C MEX S-function as part of an accelerated
          model simulation. If you prefer to have the accelerator inline your S-function before
          running the accelerated model, tell the accelerator to use your block target file to
          inline the S-function with the SS_OPTION_USE_TLC_WITH_ACCELERATOR flag
          in the call to ssSetOptions() in the
            mdlInitializeSizes function of that S-function.
Note that memory and work vector size and usage must be the same for the TLC generated
          code and the C MEX S-function, or the Simulink accelerator software cannot execute the inlined code properly. This is
          because the C MEX S-function is called to initialize the block and its work vectors,
          calling the mdlInitializeSizes,
            mdlInitializeConditions, mdlCheckParameters,
            mdlProcessParameters, and mdlStart functions.
          In the case of constant signal propagation, mdlOutputs is called from
          the C MEX S-function during the initialization phase of model execution.
During the time-stepping phase of accelerated model execution, the code generated by
          the Output and Update block TLC methods will
          execute, plus the Derivatives and zero-crossing methods if they
          exist. The Start method of the block target file is not used in
          generating code for an accelerated model.