Main Content

S-Functions for Code Reuse

You can reuse the generated code for identical subsystems that occur in multiple instances within a model and across referenced models. For more information about code generation of subsystems for code reuse, see Control Generation of Functions for Subsystems.

S-Function Code Reuse Requirements

To use S-function for code reuse for a subsystem, the S-function must meet these 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 only 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. For this purpose, the S-function must not use ssWriteRTWParameters in its mdlRTW function.

  • The S-function must not be a device driver.

Additionally, your S-function must set the SS_OPTION_WORKS_WITH_CODE_REUSE flag in the ssSetOptions function. This flag indicates that your S-function meets the requirements for subsystem code reuse. If the flag is set and your S-function does not meet the requirements, the code generator does not generate a reusable function and you see a warning.

S-Functions Reused Across Models

You can place S-functions inside reusable library subsystems. In the S-function file, set both of these flags:

If you use the legacy_code tool to generate your S-function, the S-function option supportCodeReuseAcrossModels specifies code reuse across models for the subsystem that contains the S-function.

Configure the S-function for code reuse across models only if the S-function does not access any model-specific data structures. This configuration is necessary because the generated code in _sharedutils folder is compiled separately from the generated code in model_ert_rtw folder. The reusable library subsystem code generated in the _sharedutils folder does not have access to types and macros that are declared in model.h. The reusable library subsystem code must be independent. For more information, see Generate Reusable Code from Library Subsystems Shared Across Models.

If your S-function uses custom functions defined in your external header files, add the LibAddtoSystemCustomIncludes(system, incFileName) function in your S-function's TLC file. For example, when you add LibAddtoSystemCustomIncludes("company_math.h") in your .tlc file, the reusable library subsystem code in the _sharedutils folder includes the #company_math.h header file.

1. Create a new MATLAB working folder. In the MATLAB Command Window, copy these external code files to your current MATLAB working folder:

currentDir = pwd;
[~,cgDir] = rtwdemodir();
copyfile(fullfile(matlabroot,'toolbox','rtw','rtwdemos','doubleIt.c'));
copyfile(fullfile(matlabroot,'toolbox','rtw','rtwdemos','doubleIt.h'));

2. Open example model rtwdemo_sfcn_rls. This example model uses two instances of the reusable library subsystem rtwdemo_sfcn_rls_lib. The S-function sfun_mySrc inside the reusable library subsystem uses external files doubleIt.c and doubleIt.h.

rtwdemo_sfcn_rls;

3. Generate the S-function code by using legacy code tool. Use these commands in your MATLAB Command window:

def = legacy_code('initialize');
def.SFunctionName = 'sfun_mySrc';
def.SourceFiles = {'doubleIt.c'};
def.HeaderFiles = {'doubleIt.h'};
def.OutputFcnSpec = 'double y1 = doubleIt(double u1)';

4. Specify the S-function for code reuse:

def.Options.supportCodeReuseAcrossModels = true;

5. Generate the S-function code TLC block file. Then compile the S-function.

legacy_code('sfcn_tlc_generate',def);
legacy_code('sfcn_cmex_generate', def);
legacy_code('compile', def);
### Start Compiling sfun_mySrc
    mex('-I/tmp/Bdoc21a_1606923_214996/tp62a12c64_7066_4c00_b8f6_a5b1eb1173f2/coderdemo', '-c', '-outdir', '/tmp/Bdoc21a_1606923_214996/tp45176135_0ca2_4e3c_9b7a_90faa7c57ca2', '/tmp/Bdoc21a_1606923_214996/tp62a12c64_7066_4c00_b8f6_a5b1eb1173f2/coderdemo/doubleIt.c')
Building with 'gcc'.
MEX completed successfully.
    mex('sfun_mySrc.c', '-I/tmp/Bdoc21a_1606923_214996/tp62a12c64_7066_4c00_b8f6_a5b1eb1173f2/coderdemo', '/tmp/Bdoc21a_1606923_214996/tp45176135_0ca2_4e3c_9b7a_90faa7c57ca2/doubleIt.o')
Building with 'gcc'.
MEX completed successfully.
### Finish Compiling sfun_mySrc
### Exit

6. The code generator creates the S-function file sfun_mySrc.c and its TLC file sfun_mySrc.tlc in your MATLAB working folder. In the sfun_mySrc.c file, the code generator adds these specifications:

file = fullfile('sfun_mySrc.c');
rtwdemodbtype(file,'* All options have the form SS_OPTION_<name>', ...
    'ssSetSupportedForCodeReuseAcrossModels(S, 1);',1,1);
     * All options have the form SS_OPTION_<name> and are documented in
     * matlabroot/simulink/include/simstruc.h. The options should be
     * bitwise or'd together as in
     *    ssSetOptions(S, (SS_OPTION_name1 | SS_OPTION_name2))
     */
    ssSetOptions(S,
        SS_OPTION_USE_TLC_WITH_ACCELERATOR |
        SS_OPTION_CAN_BE_CALLED_CONDITIONALLY |
        SS_OPTION_EXCEPTION_FREE_CODE |
        SS_OPTION_WORKS_WITH_CODE_REUSE |
        SS_OPTION_SFUNCTION_INLINED_FOR_RTW |
        SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME
    );
    ssSetSupportedForCodeReuseAcrossModels(S, 1);

In the sfun_mySrc.c, the SS_OPTION_WORKS_WITH_CODE_REUSE flag is set in the ssSetOptions function and ssSetSupportedForCodeReuseAcrossModels is set to true.

7. On the Simulink toolstrip, open the Embedded Coder or Simulink Coder app.

8. Generate code for the model. On the C Code tab, click Build.

evalc('slbuild(''rtwdemo_sfcn_rls'')');

The code generator produces a reusable function that can be reused across models in the _sharedutils folder. Models in a model reference hierarchy share this _sharedutils folder to reuse code. For this example, the reusable function is generated in the slprj/grt/_sharedutils/sfunc.c file:

file = fullfile('slprj','grt','_sharedutils','sfunc.c');
rtwdemodbtype(file,'Output and update for atomic system', ...
    'rtb_sfun_mySrc',1,1);
 * Output and update for atomic system:
 *    'ReusableSubsystem' ('rtwdemo_sfcn_rls_lib:60')
 *    'ReusableSubsystem' ('rtwdemo_sfcn_rls_lib:60')
 */
void sfunc(real_T rtu_In1, B_sfunc_T *localB, DW_sfunc_T *localDW)
{
  /* local block i/o variables */
  real_T rtb_sfun_mySrc;

S-Functions Reused Within a Model

By default, S-functions inside a subsystem are not configured for code reuse within a model. You can specify the S-functions inside a subsystem for code reuse within a model. In the S-function file, set the SS_OPTION_WORKS_WITH_CODE_REUSE flag in the ssSetOptions function. This flag indicates that your S-function meets the requirements for subsystem code reuse and is configured for code reuse. If the flag is set and your S-function does not meet the requirements, the code generator does not generate a reusable function and you see a warning.

1. Create a new MATLAB working folder and copy these external code files to your current MATLAB working folder:

currentDir = pwd;
[~,cgDir] = rtwdemodir();
copyfile(fullfile(matlabroot,'toolbox','rtw','rtwdemos','doubleIt.c'));
copyfile(fullfile(matlabroot,'toolbox','rtw','rtwdemos','doubleIt.h'));

2. For the example model rtwdemo_sfcn_rls, generate the S-function code by using legacy code tool. Do not include the S-function option supportCodeReuseAcrossModels. Use these commands in your MATLAB Command window:

def = legacy_code('initialize');
def.SFunctionName = 'sfun_mySrc';
def.SourceFiles = {'doubleIt.c'};
def.HeaderFiles = {'doubleIt.h'};
def.OutputFcnSpec = 'double y1 = doubleIt(double u1)';

3. Generate the S-function code TLC block file. Then compile the S-function.

legacy_code('sfcn_tlc_generate',def);
legacy_code('sfcn_cmex_generate', def);
legacy_code('compile', def);
### Start Compiling sfun_mySrc
    mex('-I/tmp/Bdoc21a_1606923_214996/tp1c2a2715_ab33_4721_8d46_ef0862776842/coderdemo', '-c', '-outdir', '/tmp/Bdoc21a_1606923_214996/tp62f7476e_b4c6_44bf_8b70_dc342bebb8ab', '/tmp/Bdoc21a_1606923_214996/tp1c2a2715_ab33_4721_8d46_ef0862776842/coderdemo/doubleIt.c')
Building with 'gcc'.
MEX completed successfully.
    mex('sfun_mySrc.c', '-I/tmp/Bdoc21a_1606923_214996/tp1c2a2715_ab33_4721_8d46_ef0862776842/coderdemo', '/tmp/Bdoc21a_1606923_214996/tp62f7476e_b4c6_44bf_8b70_dc342bebb8ab/doubleIt.o')
Building with 'gcc'.
MEX completed successfully.
### Finish Compiling sfun_mySrc
### Exit

4. The code generator creates the S-function file sfun_mySrc.c and its TLC file sfun_mySrc.tlc in your MATLAB working folder.

file = fullfile('sfun_mySrc.c');
rtwdemodbtype(file,'* All options have the form SS_OPTION_<name>', ...
    'SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME',1,1);
     * All options have the form SS_OPTION_<name> and are documented in
     * matlabroot/simulink/include/simstruc.h. The options should be
     * bitwise or'd together as in
     *    ssSetOptions(S, (SS_OPTION_name1 | SS_OPTION_name2))
     */
    ssSetOptions(S,
        SS_OPTION_USE_TLC_WITH_ACCELERATOR |
        SS_OPTION_CAN_BE_CALLED_CONDITIONALLY |
        SS_OPTION_EXCEPTION_FREE_CODE |
        SS_OPTION_WORKS_WITH_CODE_REUSE |
        SS_OPTION_SFUNCTION_INLINED_FOR_RTW |
        SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME

In the sfun_mySrc.c file, the code generator adds SS_OPTION_WORKS_WITH_CODE_REUSE flag in the ssSetOptions function by default but does not include the ssSetSupportedForCodeReuseAcrossModels specification that prevents the S-function from being reused across models.

5. On the Simulink toolstrip, open the Embedded Coder or Simulink Coder app.

6. Generate code for the model. On the C Code tab, click Build.

evalc('slbuild(''rtwdemo_sfcn_rls'')');

The code generator produces a reusable function for the subsystem in the rtwdemo_sfcn_rls.c file, which is outside the _sharedutils folder. This code can be reused within the model but not across the model reference hierarchy:

file = fullfile('rtwdemo_sfcn_rls_grt_rtw','rtwdemo_sfcn_rls.c');
rtwdemodbtype(file,'void sfunc', ...
    'localDW->UnitDelay_DSTATE = rtb_sfun_mySrc;',1,1);
void sfunc_Init(DW_sfunc_T *localDW)
{
  /* InitializeConditions for UnitDelay: '<S1>/Unit Delay' */
  localDW->UnitDelay_DSTATE = 0.0;
}

/*
 * Output and update for atomic system:
 *    '<Root>/ReusableSubsystem1'
 *    '<Root>/ReusableSubsystem2'
 */
void sfunc(real_T rtu_In1, B_sfunc_T *localB, DW_sfunc_T *localDW)
{
  /* local block i/o variables */
  real_T rtb_sfun_mySrc;

  /* UnitDelay: '<S1>/Unit Delay' */
  localB->UnitDelay = localDW->UnitDelay_DSTATE;

  /* S-Function (sfun_mySrc): '<S1>/sfun_mySrc' */
  rtb_sfun_mySrc = doubleIt(rtu_In1);

  /* Update for UnitDelay: '<S1>/Unit Delay' */
  localDW->UnitDelay_DSTATE = rtb_sfun_mySrc;

Related Topics