Products & Services Solutions Academia Support User Community Company

Learn more about Real-Time Workshop   

Creating 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 Real-Time Workshop 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 Creating 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 Real-Time Workshop 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 directory), 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 — and therefore assure 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 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.

Generating 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:

Generating 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 Writing S-Functions That Support Code Reuse for the list of requirements.

When you select the Reusable function option, two additional options are enabled, Real-Time Workshop function name options and Real-Time Workshop file name options. See the explanation of 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 Real-Time Workshop software to be able to reuse the subsystem code.

Subsystem Reusable Function Code Generation Option

To request that the Real-Time Workshop 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 Block parameters from the menu.

  2. If the subsystem is virtual, select Treat as atomic unit. The Real-Time Workshop system code menu becomes enabled.

    If the system is already nonvirtual, the Real-Time Workshop system code menu is already enabled.

  3. Select Reusable function from the Real-Time Workshop system code 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 Real-Time Workshop function name options described in Real-Time Workshop Function Name Options Menu.

    If you do not choose the Real-Time Workshop function name Auto option, 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 Real-Time Workshop file name option other than Auto (options are described in Real-Time Workshop 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 Real-Time Workshop 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 Real-Time Workshop 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 Real-Time Workshop system code option.

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

Determining Why Subsystem Code Is Not Reused

Due to the limitations noted in Code Reuse Limitations, the Real-Time Workshop 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

Reviewing the Subsystems Section of the HTML Code Generation Report

If you determine that the Real-Time Workshop 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 Generating a 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.

Comparing 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 Real-Time Workshop 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 Real-Time Workshop system code 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 directory 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. Correct 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-2009- The MathWorks, Inc.    -   Site Help   -   Patents   -   Trademarks   -   Privacy Policy   -   Preventing Piracy   -   RSS