Specify Buffer Reuse by Using Simulink.Signal Objects

If your model has the optimal parameter settings for removing data copies, you might be able to remove additional data copies by using Simulink.Signal objects to specify buffer reuse. After studying the generated code and the Static Code Metrics Report and identifying areas where you think buffer reuse is possible, you specify signal objects on signal lines.

You can specify buffer reuse on signals that include a pair of root inport and outport signals. You can also specify buffer reuse on just a pair of root inport and outport signals. This optimization reduces ROM and RAM consumption because there are less global variables and data copies in the generated code. Code execution speed also increases.

Example Model

The model rtwdemo_reusable_csc contains the nonreusable subsystem DeltaSubsystem and the MATLAB Function block Downsample. DeltaSubsystem contains the MATLAB Function blocks DeltaX and DeltaY.

model ='rtwdemo_reusable_csc';
open_system(model);

Specify a Simulink Signal Object for Reuse

  1. In the Apps gallery, under Code generation, click Embedded Coder . The C Code tab opens.

  2. To open the Code Mappings editor, click Code Interface > Individual Element Code Mappings .

  3. In the model, select the RCSC_REAL signal line. To view this signal or any signal in the Code Mappings editor, on the Signals/States tab, click the Add selected signals to code mappings button.

  4. For the row that represents the signal line, inspect the Storage Class column. The signal object uses the storage class Reusable, which means the object appears in the generated code as a global variable named RCSC_REAL.

  5. In the model, navigate into the DeltaSubsystem subsystem.

  6. Select the RCSC_REAL signal line in this subsystem. This signal also resolves to the signal object in the base workspace.

With the Reusable storage class, the generated code can store the output of the Complex to Real-Imag block (at the root level of the model) and the output of the DeltaX block (in the subsystem) in the RCSC_REAL global variable.

Generate Code

Build the model.

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

Build Summary

Top model targets built:

Model                 Action                       Rebuild Reason                                    
=====================================================================================================
rtwdemo_reusable_csc  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 16.142s

For buffer reuse, the rtwdemo_reusable_csc.c file contains these global variables:

  • static real_T RCSC_IMAG[1048576];

  • static real_T RCSC_IMAG2[262144];

  • static real_T RCSC_REAL[1048576];

  • static real_T RCSC_REAL2[262144];

The rtwdemo_reusable_csc.c file contains this code:

cfile = fullfile(cgDir,...
    'rtwdemo_reusable_csc_ert_rtw','rtwdemo_reusable_csc.c');
rtwdemodbtype(cfile,...
    '/* Output and update for atomic system: ''<Root>/DeltaSubsystem'' */',...
    '/* Output and update for atomic system: ''<Root>/Downsample'' */',1,0);
rtwdemodbtype(cfile,...
    '/* Model step function */','/* Model initialize function */',1,0);
/* Output and update for atomic system: '<Root>/DeltaSubsystem' */
static void DeltaSubsystem(void)
{
  /* SignalConversion generated from: '<S1>/DeltaX' incorporates:
   *  MATLAB Function: '<S1>/DeltaX'
   */
  DeltaX((&(RCSC_REAL2[0])), (&(RCSC_IMAG2[0])), (&(RCSC_REAL[0])),
         (&(RCSC_IMAG[0])));

  /* SignalConversion generated from: '<S1>/DeltaY' incorporates:
   *  MATLAB Function: '<S1>/DeltaY'
   */
  DeltaY((&(RCSC_REAL[0])), (&(RCSC_IMAG[0])), (&(RCSC_REAL2[0])),
         (&(RCSC_IMAG2[0])));
}

/* Model step function */
void rtwdemo_reusable_csc_step(void)
{
  int32_T i;

  /* ComplexToRealImag: '<Root>/Complex to Real-Imag' incorporates:
   *  Inport: '<Root>/ComplexData'
   */
  for (i = 0; i < 1048576; i++) {
    RCSC_REAL[i] = rtU.ComplexData[i].re;
    RCSC_IMAG[i] = rtU.ComplexData[i].im;
  }

  /* End of ComplexToRealImag: '<Root>/Complex to Real-Imag' */

  /* MATLAB Function: '<Root>/Downsample' */
  Downsample((&(RCSC_REAL[0])), (&(RCSC_IMAG[0])), (&(RCSC_REAL2[0])),
             (&(RCSC_IMAG2[0])));

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

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

  /* Outport: '<Root>/Out1' incorporates:
   *  RealImagToComplex: '<Root>/Real-Imag to Complex'
   *  SignalConversion generated from: '<S1>/DeltaY'
   * */
  for (i = 0; i < 261121; i++) {
    rtY.Out1[i].re = RCSC_REAL2[i];
    rtY.Out1[i].im = RCSC_IMAG2[i];
  }

  /* End of Outport: '<Root>/Out1' */
}

The variables RCSC_REAL and RCSC_IMAG hold the outputs of the Complex to Real-Image block and DeltaX. These variables hold the inputs to the DeltaY block. The variables RCSC_REAL2 and RCSC_IMAG2 hold the outputs of Downsample and DeltaY. These variables hold the inputs to the DeltaX block. By interleaving buffers in this way, you eliminate global variables in the generated code.

To remove the signal objects from the signal lines and regenerate code, in the MATLAB Command Window, enter these commands:

portHandles = get_param(...
    'rtwdemo_reusable_csc/Complex to Real-Imag','portHandles');
set_param(portHandles.Outport(1),'MustResolveToSignalObject','off');
set_param(portHandles.Outport(2),'MustResolveToSignalObject','off');

portHandles = get_param(...
    'rtwdemo_reusable_csc/Downsample','portHandles');
set_param(portHandles.Outport(1),'MustResolveToSignalObject','off');
set_param(portHandles.Outport(2),'MustResolveToSignalObject','off');

portHandles = get_param(...
    'rtwdemo_reusable_csc/DeltaSubsystem/DeltaX','portHandles');
set_param(portHandles.Outport(1),'MustResolveToSignalObject','off');
set_param(portHandles.Outport(2),'MustResolveToSignalObject','off');

portHandles = get_param(...
    'rtwdemo_reusable_csc/DeltaSubsystem/DeltaY','portHandles');
set_param(portHandles.Outport(1),'MustResolveToSignalObject','off');
set_param(portHandles.Outport(2),'MustResolveToSignalObject','off');

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

Build Summary

Top model targets built:

Model                 Action                       Rebuild Reason                   
====================================================================================
rtwdemo_reusable_csc  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 11.631s

The rtwdemo_reusable_csc.c file now contains this code.

cfile = fullfile(cgDir,...
    'rtwdemo_reusable_csc_ert_rtw','rtwdemo_reusable_csc.c');
rtwdemodbtype(cfile,...
    '/* Output and update for atomic system: ''<Root>/DeltaSubsystem'' */',...
    '/* Output and update for atomic system: ''<Root>/Downsample'' */',1,0);
rtwdemodbtype(cfile,...
    '/* Model step function */','/* Model initialize function */',1,0);
/* Output and update for atomic system: '<Root>/DeltaSubsystem' */
static void DeltaSubsystem(void)
{
  /* MATLAB Function: '<S1>/DeltaX' */
  DeltaX(rtDWork.z2, rtDWork.z1, rtDWork.z1_m, rtDWork.z2_c);

  /* MATLAB Function: '<S1>/DeltaY' */
  DeltaY(rtDWork.z1_m, rtDWork.z2_c, &rtDWork.z1[0], &rtDWork.z2[0]);
}

/* Model step function */
void rtwdemo_reusable_csc_step(void)
{
  int32_T i;

  /* ComplexToRealImag: '<Root>/Complex to Real-Imag' incorporates:
   *  Inport: '<Root>/ComplexData'
   */
  for (i = 0; i < 1048576; i++) {
    rtDWork.RCSC_REAL[i] = rtU.ComplexData[i].re;
    rtDWork.RCSC_IMAG[i] = rtU.ComplexData[i].im;
  }

  /* End of ComplexToRealImag: '<Root>/Complex to Real-Imag' */

  /* MATLAB Function: '<Root>/Downsample' */
  Downsample(rtDWork.RCSC_REAL, rtDWork.RCSC_IMAG, rtDWork.z2, rtDWork.z1);

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

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

  /* Outport: '<Root>/Out1' incorporates:
   *  RealImagToComplex: '<Root>/Real-Imag to Complex'
   */
  for (i = 0; i < 261121; i++) {
    rtY.Out1[i].re = rtDWork.z1[i];
    rtY.Out1[i].im = rtDWork.z2[i];
  }

  /* End of Outport: '<Root>/Out1' */
}

The generated code contains two additional global variables for holding block inputs and outputs.

Note: You can specify buffer reuse on signals that the code generator cannot implement. For those cases, use two diagnostics to specify the message type that the model displays. In the Configuration Parameters dialog box, these diagnostics are Detect non-reused custom storage classes and Detect ambiguous custom storage class final values.

bdclose(model)
rtwdemoclean;
cd(currentDir)

Buffer Reuse for Unit Delay and Delay Blocks

To reuse the signal of a Unit Delay or Delay block, use the same reusable storage class specification for a pair of input and state arguments or a pair of output and state arguments of a Unit Delay or a Delay block. For Delay blocks, you must set the Delay length parameter to 1 and Initial condition > Source to Dialog. To access these parameters, in the model, open the Property Inspector and click the block in the model.

Limitations for Root Inport and Outport Signals

These limitations apply to a model in which you specify buffer reuse for a pair of root inport and outport signals:

  • The output ports cannot be conditional.

  • If the code generator cannot reuse the same buffer in a top model, the generated code contains additional buffers. If the top model is a reference model, the code generator reports an error. To resolve the error, remove the Simulink.signal specification from the signal that connects to the outport port.

  • When you run the executable that the code generator produces, and you reuse a pair of root inport and outport signals, when the root input value is zero, the root output value must also be zero. If the output value is nonzero and you reuse the signals, then the results from the simulation can differ from the results that the executable produces.

  • There might be a simulation versus code generation mismatch when you assign the same Reusable storage class to pair of root inport and root outport signals. This mismatch occurs when an Assignment block drives the Root Outport block, and the Assignment block does not assign a value to every element of the output signal because the Y0 port of the Assignment block is not active. This image shows a model in which the mismatch occurs:

During simulation, the unwritten Assignment block output values are zero. During code generation, the unwritten output values are the same as the input.

Limitations for the Model

These limitations apply to a model in which you specify buffer reuse for signals:

  • Signals that you specify for reuse must have the same data types and sampling rates.

  • For user-specified buffer reuse, blocks that modify a signal specified for reuse must execute before blocks that use the original signal value. Sometimes the code generator has to change the block operation order so that buffer reuse can occur. For models in which the code generator is unable to reorder block operations, buffer reuse does not occur.

  • For models in which the code generator reorders block operations so that Simulink.Signal reuse can occur, you can observe the difference in the sorted order. On the Debug tab, in the Diagnostics section select the Information Overlays drop-down arrow. In the Blocks section of the dialog, select Execution Order. To display the sorted execution order during simulation, on the Modeling tab, select Update Model . To display the execution order in the generated code, on the C Code tab, select Build Model.

  • You can specify buffer reuse for a chain of reusable subsystems as long as these subsystems do not get reused anywhere in the model.

Related Topics