Skip to Main Content Skip to Search
Product Documentation

Reusable Components

Reusable Function Option

The difference between functions and reusable functions is that the latter have data passed to them as arguments (enabling them to be reentrant), while the former communicate by using global data. Choosing the Reusable function option directs the Simulink Coder code generator to generate a single function (optionally in a separate file) for the subsystem, and to call that code for each identical subsystem in the model, if possible.

For a summary of code reuse limitations, see Code Reuse Limitations.

Reusable Code and Referenced Models

Models that employ model referencing might require special treatment when generating and using reusable code. The following sections identify general restrictions and discuss how reusable functions with inputs or outputs connected to a referenced model's root Inport or Outport blocks can affect code reuse.

General Considerations

You can generate code for subsystems that contain referenced models using the same procedures and options described in Subsystems. However, the following restrictions apply to such builds:

Code Reuse and Model Blocks with Root Inport or Outport Blocks

Reusable functions with inputs or outputs connected to a referenced model's root Inport or Outport block can affect code reuse. This means that code for certain atomic subsystems cannot be reused in a model reference context the same way it is reused in a standalone model.

For example, suppose you create the following subsystem and make the following changes to the subsystem's block parameters:

Suppose you then create the following model, which includes three instances of the preceding subsystem.

With the Inline parameters option enabled in this stand-alone model, the Simulink Coder code generator can optimize the code by generating a single copy of the function for the reused subsystem, as shown below.

void reuse_subsys1_Subsystem1(
                   real_T rtu_0,
                   rtB_reuse_subsys1_Subsystem1 *localB)
{

  /* Gain: '<S1>/Gain' */
  localB->Gain_k = rtu_0 * 3.0;
}

When generated as code for a Model block (into an slprj project folder), the subsystems have three different function signatures:

/* Output and update for atomic system: '<Root>/Subsystem1' */
void reuse_subsys1_Subsystem1(const real_T *rtu_0, 
rtB_reuse_subsys1_Subsystem1
 *localB)
{
  /* Gain: '<S1>/Gain' */
  localB->Gain_w = (*rtu_0) * 3.0;
}

/* Output and update for atomic system: '<Root>/Subsystem2' */
void reuse_subsys1_Subsystem2(real_T rtu_In1, 
rtB_reuse_subsys1_Subsystem2
 *localB)
{
  /* Gain: '<S2>/Gain' */
  localB->Gain_y = rtu_In1 * 3.0;
}

/* Output and update for atomic system: '<Root>/Subsystem3' */
void reuse_subsys1_Subsystem3(real_T rtu_In1, real_T *rty_0)
{
  /* Gain: '<S3>/Gain' */
  (*rty_0) = rtu_In1 * 3.0;
}

One way to make all the function signatures the same for code reuse, is to insert Signal Conversion blocks. Place one between the Inport and Subsystem1 and another between Subsystem3 and the Outport of the referenced model.

The result is a single reusable function:

void reuse_subsys2_Subsystem1(real_T rtu_In1,
                   rtB_reuse_subsys2_Subsystem1 *localB)
{

  /* Gain: '<S1>/Gain' */
  localB->Gain_g = rtu_In1 * 3.0;
}

You can achieve the same result (reusable code) with only one Signal Conversion block. You can omit the Signal Conversion block connected to the Inport block if you select the Pass fixed-size scalar root inputs by value check box at the bottom of the Model Referencing pane of the Configuration Parameters dialog box. When you do this, you still need to insert a Signal Conversion block before the Outport block.

Generate Reusable Code from Stateflow Charts

You can generate reusable code from a Stateflow chart, or from a subsystem containing a chart, except in the following cases:

Generate Reusable Code for Subsystems Containing S-Function Blocks

Regarding S-Function blocks, there are several requirements that need to be met in order for subsystems containing them to be reused. See Write S-Functions That Support Code Reuse for the list of requirements.

When you select the Reusable function option, two additional options are enabled, Function name options and File name options. See Function Option for descriptions of these options and fields. If you use these fields to enter a function name and/or a file name, you must specify exactly the same function name and file name for each instance of identical subsystems for the Simulink Coder software to be able to reuse the subsystem code.

Subsystem Reusable Function Code Generation Option

To request that the Simulink Coder software generate reusable subsystem code,

  1. Select the subsystem block. Then select Subsystem Parameters from the Simulink model editor Edit menu. The Block Parameters dialog box opens.

    Alternatively, you can open the Block Parameters dialog box by:

    • Shift-double-clicking the subsystem block

    • Right-clicking the subsystem block and selecting Subsystem parameters from the menu.

  2. If the subsystem is virtual, select Treat as atomic unit. On the Code Generation tab, the Function packaging menu becomes enabled.

    If the system is already nonvirtual, the Function packaging menu is already enabled.

  3. Go to the Code Generation tab and select Reusable function from the Function packaging menu as shown in Subsystem Reusable Function Code Generation Option.

  4. If you want to give the function a specific name, set the function name, using the Function name options parameter, as described in Function Name Options Menu.

    If you do not choose Auto for the Function name options parameter, and want code to be reused, you must assign exactly the same function name to all other subsystem blocks that you want to share this code.

  5. If you want to direct the generated code to a specific file, set the file name using any File name options parameter value other than Auto (options are described in File Name Options Menu).

    In order for code to be reused, you must repeat this step for all other subsystem blocks that you want to share this code, using the same file name.

  6. Click Apply and close the dialog box.

Code Reuse Limitations

The Simulink Coder software uses a checksum to determine whether subsystems are identical. You cannot reuse subsystem code if:

Some of these situations can arise even when you copy and paste subsystems within or between models or you construct them manually such that they are identical. If you select Reusable function and the Simulink Coder software determines that code for a subsystem cannot be reused, it generates a separate function that is not reused. The code generation report can show that the separate function is reusable, even if it is used by only one subsystem. If you prefer that subsystem code be inlined in such circumstances rather than deployed as functions, you choose Auto for the Function packaging option.

Use of the following blocks in a subsystem can also prevent its code from being reused:

Determine Why Subsystem Code Is Not Reused

Due to the limitations noted in Code Reuse Limitations, the Simulink Coder software might not reuse generated code as you expect. To determine why code generated for a subsystem is not reused,

  1. Review the Subsystems section of the HTML code generation report

  2. If you cannot determine why based on the report, compare subsystem checksum data

Review the Subsystems Section of the HTML Code Generation Report

If you determine that the Simulink Coder code generator does not generate code for a subsystem as reusable code and you specified the subsystem as reusable, examine the Subsystems section of the HTML code generation report (see Generate an HTML Code Generation Report). The Subsystems section contains

In addition to diagnosing exceptions, the Subsections section also indicates the mapping of each noninlined subsystem in the model to functions or reused functions in the generated code. For an example, open and build the rtwdemo_atomic demo model.

Compare Subsystem Checksum Data

If the HTML code generation report indicates that no code reuse exceptions occurred and code for a subsystem you expect to be reused is not reused, you can determine why by accessing and comparing subsystem checksum data. The Simulink Coder software determines whether subsystems are identical by comparing subsystem checksums, as noted in Code Reuse Limitations.

Consider the demo model, rtwdemo_ssreuse.

SS1 and SS2 are instances of the same subsystem, and in both instances the subsystem parameter Function packaging is set to Reusable function.

The following example demonstrates how to use the method Simulink.SubSystem.getChecksum to get the checksum for a subsystem and compare the results to determine why code is not reused.

  1. Open the model rtwdemo_ssreuse and save a copy of the demo in a folder where you have write access.

  2. Select subsystem SS1 in the model window and in the command window enter

    SS1 = gcb;
  3. Select subsystem SS2 in the model window and in the command window enter

    SS2 = gcb;
  4. Use the method Simulink.SubSystem.getChecksum to get the checksum for each subsystem. This method returns two output values: the checksum value and details on the input used to compute the checksum.

    [chksum1, chksum1_details] = ...
    Simulink.SubSystem.getChecksum(SS1);
    [chksum2, chksum2_details] = ...
    Simulink.SubSystem.getChecksum(SS2);
  5. Compare the two checksum values. They should be equal based on the subsystem configurations.

    isequal(chksum1, chksum2)
    ans =
         1
  6. To see how you can use Simulink.SubSystem.getChecksum to determine why the checksums of two subsystems differ, change the data type mode of the output port of SS1 so that it differs from that of SS2.

    1. Look under the mask of SS1 by right-clicking the subsystem and selecting Look Under Mask in the context menu. A block diagram of the subsystem appears.

    2. Double-click the Lookup Table block to open the Block Parameters dialog box.

    3. Click Signal Attributes.

    4. Select int8 for Output data type and click OK.

  7. Get the checksum for SS1 again and compare the checksums for the two subsystems again. This time, the checksums should not be equal.

    [chksum1, chksum1_details] = ...
    Simulink.SubSystem.getChecksum(SS1);
    isequal(chksum1, chksum2)
    ans =
         0
  8. After you determine that the checksums are different, find out why. The Simulink engine uses information, such as signal data types, some block parameter values, and block connectivity information, to compute the checksums. To determine why checksums are different, you compare the data used to compute the checksum values. You can get this information from the second value returned by Simulink.SubSystem.getChecksum, which is a structure array with four fields.

    Look at the structure chksum1_details.

    chksum1_details
    
    chksum1_details = 
              ContentsChecksum: [1x1 struct]
             InterfaceChecksum: [1x1 struct]
         ContentsChecksumItems: [221x1 struct]
        InterfaceChecksumItems: [91x1 struct]

    ContentsChecksum and InterfaceChecksum are component checksums of the subsystem checksum. The remaining two fields ContentsChecksumItems and InterfaceChecksumItems contain the checksum details.

  9. Determine whether a difference exists in the subsystem contents, interface, or both. For example:

    isequal(chksum1_details.ContentsChecksum.Value,...
            chksum2_details.ContentsChecksum.Value)
    ans =
         0
    isequal(chksum1_details.InterfaceChecksum.Value,...
            chksum2_details.InterfaceChecksum.Value)
    ans =
         0

    In this case, differences exist in both the contents and interface.

  10. Write a script like the following to find the differences.

    idxForCDiffs=[];
    for idx = 1:length(chksum1_details.ContentsChecksumItems)
        if (~strcmp(chksum1_details.ContentsChecksumItems(idx).Identifier, ...
                    chksum2_details.ContentsChecksumItems(idx).Identifier))
                disp(['Identifiers different for contents item ', num2str(idx)]); 
                idxForCDiffs=[idxForCDiffs, idx];
        end
        if (ischar(chksum1_details.ContentsChecksumItems(idx).Value))
                if (~strcmp(chksum1_details.ContentsChecksumItems(idx).Value, ...
                            chksum2_details.ContentsChecksumItems(idx).Value))
                disp(['String values different for contents item ', num2str(idx)]); 
                idxForCDiffs=[idxForCDiffs, idx];
                end
        end
        if (isnumeric(chksum1_details.ContentsChecksumItems(idx).Value))
                if (chksum1_details.ContentsChecksumItems(idx).Value ~= ...
                            chksum2_details.ContentsChecksumItems(idx).Value)
                disp(['Numeric values different for contents item ', num2str(idx)]); 
                idxForCDiffs=[idxForCDiffs, idx];
                end
        end
    end
    
    idxForIDiffs=[];
    for idx = 1:length(chksum1_details.InterfaceChecksumItems)
        if (~strcmp(chksum1_details.InterfaceChecksumItems(idx).Identifier, ...
                    chksum2_details.InterfaceChecksumItems(idx).Identifier))
                disp(['Identifiers different for interface item ', num2str(idx)]); 
                idxForIDiffs=[idxForIDiffs, idx];
        end
        if (ischar(chksum1_details.InterfaceChecksumItems(idx).Value))
                if (~strcmp(chksum1_details.InterfaceChecksumItems(idx).Value, ...
                            chksum2_details.InterfaceChecksumItems(idx).Value))
                disp(['String values different for interface item ', num2str(idx)]); 
                idxForIDiffs=[idxForIDiffs, idx];
                end
        end
        if (isnumeric(chksum1_details.InterfaceChecksumItems(idx).Value))
                if (chksum1_details.InterfaceChecksumItems(idx).Value ~= ...
                            chksum2_details.InterfaceChecksumItems(idx).Value)
                disp(['Numeric values different for interface item ', num2str(idx)]); 
                idxForIDiffs=[idxForIDiffs, idx];
                end
        end
     end
  11. Run the script. The following example assumes you named the script check_details.

    check_details
    String values different for contents item 64
    String values different for contents item 75
    String values different for contents item 81
    String values different for interface item 46

    The results indicate that differences exist for index items 64, 75, and 81 in the subsystem contents and for item 46 in the subsystem interfaces.

  12. Use the returned index values to get the handle , identifier, and value details for each difference found.

    chksum1_details.ContentsChecksumItems(64)
    ans = 
            Handle: 'my_ssreuse/SS1/Lookup Table Output1'
        Identifier: 'CompiledPortAliasedThruDataType'
             Value: 'int8'
    chksum2_details.ContentsChecksumItems(64)
    ans = 
            Handle: 'my_ssreuse/SS2/Lookup Table Output1'
        Identifier: 'CompiledPortAliasedThruDataType'
             Value: 'double'
    chksum1_details.ContentsChecksumItems(75)
    ans = 
            Handle: 'my_ssreuse/SS1/Lookup Table'
        Identifier: 'RunTimeParameter{'OutputValues'}.DataType'
             Value: 'int8'
    chksum2_details.ContentsChecksumItems(75)
    ans = 
            Handle: 'my_ssreuse/SS2/Lookup Table'
        Identifier: 'RunTimeParameter{'OutputValues'}.DataType'
             Value: 'double'
    chksum1_details.ContentsChecksumItems(81)
    ans = 
            Handle: 'my_ssreuse/SS1/Lookup Table'
        Identifier: 'OutDataTypeMode'
             Value: 'int8'
    chksum2_details.ContentsChecksumItems(81)
    ans = 
            Handle: 'my_ssreuse/SS2/Lookup Table'
        Identifier: 'OutDataTypeMode'
             Value: 'Same as input'
    chksum1_details.InterfaceChecksumItems(46)
    ans = 
            Handle: 'my_ssreuse/SS1'
        Identifier: 'CanonicalParameter(1).DataType'
             Value: 'int8'
    chksum2_details.InterfaceChecksumItems(46)
    ans = 
            Handle: 'my_ssreuse/SS2'
        Identifier: 'CanonicalParameter(1).DataType'
             Value: 'double'

    As expected, the details identify the Lookup Table block and data type parameters as areas on which to focus for debugging a subsystem reuse issue.

  13. Fix the problem by changing the output data type mode for the subsystems such that they match.

  


Related Products & Applications

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