Main Content

Generate Reentrant Code from Subsystems

You can reduce the amount of code that the code generator produces for identical atomic subsystems by configuring the subsystems as reusable functions that pass data as arguments (for example, rtB_* for block input and output, rtDW_* for continuous states, and rtP_* for parameters) . By default, the code generator produces subsystem code that communicates with other code by sharing access to global data structures that reside in shared memory. By passing data as arguments, the code can be reentrant. Each instance of the code maintains its own unique data.

To configure subsystems for reusability and reentrancy, configure mask and block parameters of the identical subsystems the same way. The code generator performs a checksum to determine whether subsystems are identical and whether the code is reusable. If the code is reusable, the code generator produces a single instance of reusable, reentrant function code.

To configure the subsystem block parameters:

  1. If a subsystem is virtual, select block parameter Treat as atomic unit to enable function packaging parameters.

  2. Set block parameter Function packaging to Reusable function. The code generator produces a separate function with arguments for each subsystem. Selecting Reusable function also enables additional parameters that you can use to control the names of the function and file that the code generator produces for the subsystem code.

  3. Select a value for block parameter Function name options. To generate reusable, reentrant code, specify the same setting for identical subsystems.

    • To let the code generator determine the function name, select Auto.

    • To use the subsystem name or, for a library block, the name of the library block, select Use subsystem name.

    • To display block parameter Function name and enter a unique, valid C/C++ function name, select User specified.

    If multiple instances of an identical subsystem exist within a model reference hierarchy, select Auto.

    If you are using Embedded Coder®, you can use identifier format controls. See Identifier Format Control (Embedded Coder).

  4. Set File name options. To generate reusable, reentrant code, specify the same setting for identical subsystems.

    • To let the code generator determine file naming select Auto.

    • To use the subsystem name or, for a library block, the name of the library block select Use subsystem name.

    • To use the function name, as specified by block parameter Function name options select Use function name.

    • To display block parameter File name (no extension) and enter a file name, excluding the extension (for example, .c or .cpp) select User specified.

    Other considerations:

    • If multiple instances of an identical subsystem exist within a model reference hierarchy, select Auto.

    • If the code generator does not generate a separate file for a subsystem, the function code is placed in the file generated from the subsystem's parent system. If the parent is the model itself, the code generator places the function code in model.c or model.cpp.

    • If your generated code is under source control, specify a value other than Auto. This specification prevents the generated file name from changing when you modify and rebuild the model.

    • If you select Use subsystem name, the code generator mangles the subsystem file name if the model contains Model blocks, or if a model reference target is being generated for the model. In these situations, the code generator uses the file name modelsubsystem.c.

If subsystem A has mask parameter b and K and subsystem B has mask parameters c and K, code reuse is not possible. The code generator produces separate functions for subsystems A and B. If you set block parameters for subsystems A and B differently, code reuse is not possible.

Function Reuse in Generated Code

This example shows how to configure an atomic subsystem for generating reusable code. To specify that the code generated for a subsystem execute as an atomic unit, on the Block Parameters dialog box, select the Treat as atomic unit parameter . That parameter enables the Function Packaging parameter on the Code Generation tab. The Function Packaging parameter has these four settings:

  • Inline: Inline the subsystem code

  • Nonreusable function: Function with I/O passed as global data

  • Reusable function: Function with I/O passed as function arguments

  • Auto: Let Simulink Coder optimize based on context

The Reusable function and Auto settings permit the code generator to reuse subsystem code. The Reusable function and Nonreusable function settings enable the Function name options, Function name, and File name options parameters.

If you have an Embedded Coder license, you can configure a nonreusable subsystem to accept arguments.

Example Model

The rtwdemo_ssreuse model contains two identical subsystems, SS1 and SS2. For these subsystems, the Function packaging parameter is set to Reusable function, and the Function name parameter is myfun. The subsystems are parameterized masked subsystems. To see the contents of the masked subsystems, right-click the subsystem blocks and select Mask > Look Under Mask.

model = 'rtwdemo_ssreuse';
open_system(model);

Generate and Inspect Code

Create a temporary folder for the build and inspection process. Open the Simulink Coder or Embedded Coder app. Then, generate and inspect the code.

currentDir=pwd;
[~,cgDir]=rtwdemodir();
rtwbuild(model)
### Starting build procedure for: rtwdemo_ssreuse
### Successful completion of build procedure for: rtwdemo_ssreuse

Build Summary

Top model targets built:

Model            Action                       Rebuild Reason                                    
================================================================================================
rtwdemo_ssreuse  Code generated and compiled  Code generation information file does not exist.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 21.374s
cfile=fullfile(cgDir, 'rtwdemo_ssreuse_grt_rtw', 'rtwdemo_ssreuse.c');
rtwdemodbtype(cfile, '/* Model step', '/* Model initialize', 1, 0);
/* Model step function */
void rtwdemo_ssreuse_step(void)
{
  /* Outputs for Atomic SubSystem: '<Root>/SS1' */

  /* Inport: '<Root>/In1' incorporates:
   *  Inport: '<Root>/In2'
   */
  myfun(rtwdemo_ssreuse_U.In1, rtwdemo_ssreuse_U.In2, &rtwdemo_ssreuse_B.SS1,
        rtwdemo_ssreuse_P.T1Data, rtwdemo_ssreuse_P.T1Break);

  /* End of Outputs for SubSystem: '<Root>/SS1' */

  /* Outport: '<Root>/Out1' */
  rtwdemo_ssreuse_Y.Out1 = rtwdemo_ssreuse_B.SS1.LookupTable;

  /* Outputs for Atomic SubSystem: '<Root>/SS2' */

  /* Inport: '<Root>/In1' incorporates:
   *  Inport: '<Root>/In2'
   */
  myfun(rtwdemo_ssreuse_U.In1, rtwdemo_ssreuse_U.In2, &rtwdemo_ssreuse_B.SS2,
        rtwdemo_ssreuse_P.T2Data, rtwdemo_ssreuse_P.T2Break);

  /* End of Outputs for SubSystem: '<Root>/SS2' */

  /* Outport: '<Root>/Out2' */
  rtwdemo_ssreuse_Y.Out2 = rtwdemo_ssreuse_B.SS2.LookupTable;
}

In the model step function, there are two calls to the reusable function, myfun. The mask parameters, T1Break, T1Data, T2Break, and T2Data, are function arguments.

Change the Function Packaging parameter to Inline.

set_param('rtwdemo_ssreuse/SS1','RTWSystemCode','Inline')
set_param('rtwdemo_ssreuse/SS2','RTWSystemCode','Inline')

Generate and inspect the code.

rtwbuild(model)
### Starting build procedure for: rtwdemo_ssreuse
### Successful completion of build procedure for: rtwdemo_ssreuse

Build Summary

Top model targets built:

Model            Action                       Rebuild Reason                   
===============================================================================
rtwdemo_ssreuse  Code generated and compiled  Generated code was out of date.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 12.011s
cfile=fullfile(cgDir, 'rtwdemo_ssreuse_grt_rtw', 'rtwdemo_ssreuse.c');
rtwdemodbtype(cfile, '/* Model step', '/* Model initialize', 1, 0);
/* Model step function */
void rtwdemo_ssreuse_step(void)
{
  real_T Out1_tmp;

  /* Outputs for Atomic SubSystem: '<Root>/SS2' */
  /* Outputs for Atomic SubSystem: '<Root>/SS1' */
  /* Sum: '<S1>/Sum' incorporates:
   *  Inport: '<Root>/In1'
   *  Inport: '<Root>/In2'
   *  Sum: '<S2>/Sum'
   */
  Out1_tmp = rtwdemo_ssreuse_U.In1 + rtwdemo_ssreuse_U.In2;

  /* End of Outputs for SubSystem: '<Root>/SS2' */

  /* Outport: '<Root>/Out1' incorporates:
   *  Lookup_n-D: '<S1>/Lookup Table'
   *  Sum: '<S1>/Sum'
   */
  rtwdemo_ssreuse_Y.Out1 = look1_binlx(Out1_tmp, rtwdemo_ssreuse_P.T1Break,
    rtwdemo_ssreuse_P.T1Data, 10U);

  /* End of Outputs for SubSystem: '<Root>/SS1' */

  /* Outputs for Atomic SubSystem: '<Root>/SS2' */
  /* Outport: '<Root>/Out2' incorporates:
   *  Lookup_n-D: '<S2>/Lookup Table'
   */
  rtwdemo_ssreuse_Y.Out2 = look1_binlx(Out1_tmp, rtwdemo_ssreuse_P.T2Break,
    rtwdemo_ssreuse_P.T2Data, 10U);

  /* End of Outputs for SubSystem: '<Root>/SS2' */
}

In the model step function, the subsystem code is inlined.

Change the Function Packaging parameter to Nonreusable function. For SS2 , change the Function name parameter to myfun2.

set_param('rtwdemo_ssreuse/SS1','RTWSystemCode','Nonreusable function')
set_param('rtwdemo_ssreuse/SS2','RTWSystemCode','Nonreusable function')
set_param('rtwdemo_ssreuse/SS2','RTWFcnName','myfun2')

Generate and inspect the code.

rtwbuild(model)
### Starting build procedure for: rtwdemo_ssreuse
### Successful completion of build procedure for: rtwdemo_ssreuse

Build Summary

Top model targets built:

Model            Action                       Rebuild Reason                   
===============================================================================
rtwdemo_ssreuse  Code generated and compiled  Generated code was out of date.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 10.283s
cfile=fullfile(cgDir, 'rtwdemo_ssreuse_grt_rtw', 'rtwdemo_ssreuse.c');
rtwdemodbtype(cfile, '/* Model step', '/* Model initialize', 1, 0);
/* Model step function */
void rtwdemo_ssreuse_step(void)
{
  /* Outputs for Atomic SubSystem: '<Root>/SS1' */
  myfun();

  /* End of Outputs for SubSystem: '<Root>/SS1' */

  /* Outputs for Atomic SubSystem: '<Root>/SS2' */
  myfun2();

  /* End of Outputs for SubSystem: '<Root>/SS2' */
}

The model step function contains calls to the functions myfun and myfun2. These functions have a void-void interface.

Change the Function Packaging parameter to Auto.

set_param('rtwdemo_ssreuse/SS1','RTWSystemCode','Auto')
set_param('rtwdemo_ssreuse/SS2','RTWSystemCode','Auto')

For the auto setting, Simulink Coder chooses the optimal format. For this model, the optimal format is a reusable function.

Close the model and clean up.

bdclose(model)
rtwdemoclean;
cd(currentDir)

Limitations

  • The code generator uses a checksum to determine whether subsystems are identical and reusable. Subsystem code is not reused if:

    • In blocks and data objects, you use symbols to specify dimensions.

    • A port has different sample times, data types, complexity, frame status, or dimensions across subsystems.

    • The output of a subsystem is marked as a global signal.

    • Subsystems contain identical blocks with different names or parameter settings.

    • The output of a subsystem is connected to a Merge block, and the output of the Merge block is configured with a custom storage class that is implemented in the C code as nonaddressable memory (for example, BitField).

    • The input of a subsystem is nonscalar and is configured with a custom storage class that is implemented in the C code as nonaddressable memory.

    • A masked subsystem has a parameter that is nonscalar and is configured with a custom storage class that is implemented in the C code as nonaddressable memory.

    • A function-call subsystem uses mask parameters when you set the model configuration parameter Default parameter behavior to Tunable. To reuse the masked function-call subsystem, place it inside a new atomic subsystem without a mask. Then move the Trigger block from the masked subsystem into the atomic subsystem.

    • A block in a subsystem uses a partially tunable expression. Some partially tunable expressions disable code reuse.

      Partially tunable expressions are expressions that contain one or more tunable variables and an expression that is not tunable. For example, suppose that you create the tunable variable K with value 15.23 and the tunable variable P with value [5;7;9]. The expression K+P' is a partially tunable expression because the expression P' is not tunable. For more information about tunable expression limitations, see Tunable Expression Limitations.

  • For subsystems that contain S-function blocks that are reusable, the blocks must meet the requirements listed in S-Functions That Support Code Reuse (Embedded Coder).

  • If you select Reusable function, and the code generator determines that you cannot reuse the code for a subsystem, it generates a separate function that is not reused. The code generation report might show that the separate function is reusable, even if only one subsystem uses it. If you prefer that the code generator inline subsystem code in such cases (rather than deployed as functions), set Function packaging to Auto.

  • If a reusable subsystem uses a shared local data store and you configure default mapping for model data elements, leave the default storage class mapping for category Shared local data stores set to Default.

  • Use of these blocks in a subsystem can prevent the subsystem code from being reused:

    • Scope blocks (with data logging enabled)

    • S-Function blocks that fail to meet certain criteria (see S-Functions That Support Code Reuse (Embedded Coder))

    • To File blocks (with data logging enabled)

    • To Workspace blocks (with data logging enabled)

  • For reusable library subsystems (subsystems shared across reference models), the code generator uses a checksum to determine whether subsystems are identical. The code generator places the reusable library subsystem code in the shared utilities folder, and the reusable code is independent of the generated code of the top model or the reference model. For example, the reusable library subsystem code does not include model.h or model_types.h.

    Reusable code that the code generator places in the shared utilities folder and is dependent on the model code does not compile. If the code generator determines that the reusable library subsystem code is dependent on the model code, the reusable subsystem code is not placed in the shared utilities folder. The code generator produces code that is dependent on the model code when the reusable library subsystem:

    • Contains a block that uses time-related functionality, such as a Step block, or continuous time or multirate blocks.

    • Contains one or more Model blocks.

    • Contains a subsystem that is not inlined or a reusable library subsystem.

    • Contains a signal that is not configured with storage class Auto. The code generator places variables that are configured with a non-Auto storage class in model.h.

    • Contains a parameter that is not configure with storage class Auto.

    • Contains a user-defined storage class like Enumeration, Simulink.Signal, Simulink.Parameter, and so on where Data Scope is not set to Exported. The code generator might place the type definition in model_types.h.

    • Is a variant subsystem that generates preprocessor conditionals. The code generator places preprocessor directives that define the variant objects in model_types.h

The above limitations also apply for library-based code generation. For more information, see Library-Based Code Generation for Reusable Library Subsystems (Embedded Coder).

Related Topics