Main Content

How Generated Code Stores Internal Signal, State, and Parameter Data

To calculate outputs from inputs, the generated code stores some internal data in global memory. A signal that does not connect to a root-level input or output (Inport or Outport block) is internal data.

Internal data can also include:

  • A block state such as the state of a Unit Delay block. The algorithm must retain the state value between execution cycles, so the generated code typically stores states in global memory (for example, as a global variable or a field of a global structure variable).

  • A block parameter, such as the Gain parameter of a Gain block, whose value the code generator cannot inline in the code. For example, the code generator cannot inline the value of a nonscalar parameter.

  • The status indicator of a conditionally executed subsystem such as an enabled subsystem.

For more efficient code, you can configure optimizations such as Configuration Parameters > Default parameter behavior and Configuration Parameters > Signal storage reuse that attempt to eliminate storage for internal data. However, the optimizations cannot eliminate storage for some data, which consume memory in the generated code.

When you understand the default format in which the generated code stores internal data, you can:

  • Make signals accessible and parameters tunable by default. You can then interact with and monitor the code during execution.

  • Generate efficient production code by eliminating storage for internal data and, depending on your hardware and build toolchain, controlling the placement in memory of data that the optimizations cannot eliminate.

  • Promote pieces of internal data to the model interface so that other components and systems can access that data.

For information about how the generated code exchanges data with a calling environment through interfaces, see How Generated Code Exchanges Data with an Environment.

Internal Data in the Generated Code

This example shows how the generated code stores internal data such as block states.

Explore Example Model

Open the example model RollAxisAutopilot.

open_system('RollAxisAutopilot')

The model contains internal signals that do not connect to root-level Inport or Outport blocks. Some of the signals have a name, such as the phiCmd signal.

The model also contains some blocks that maintain state data. For example, in the BasicRollMode subsystem, a Discrete-Time Integrator block labeled Integrator maintains a state.

In the model, set Configuration Parameters > Code Generation > System target file to grt.tlc.

set_param('RollAxisAutopilot','SystemTargetFile','grt.tlc')

Inspect the setting for Configuration Parameters > Code Generation > Interface > Code interface packaging. The setting Nonreusable function means that the generated code is not reusable (reentrant).

For this example, generate simpler code by clearing Configuration Parameters > Code Generation > Interface > Advanced parameters > Mat-file logging.

set_param('RollAxisAutopilot','MatFileLogging','off')

Generate Nonreusable Code

Set these configuration parameters:

  • Set Default parameter behavior to Tunable.

  • Clear Signal storage reuse.

set_param('RollAxisAutopilot','DefaultParameterBehavior','Tunable',...
    'OptimizeBlockIOStorage','off')

Generate code from the model.

slbuild('RollAxisAutopilot')
### Starting build procedure for: RollAxisAutopilot
### Successful completion of build procedure for: RollAxisAutopilot

Build Summary

Top model targets built:

Model              Action                        Rebuild Reason                                    
===================================================================================================
RollAxisAutopilot  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 10.757s

The file RollAxisAutopilot.h defines several structure types that represent internal data. For example, the block input and output structure defines one field for each internal signal in the model. Each field name derives from the name of the block that generates the signal or, if you specify a name for the signal, from the name of the signal.

file = fullfile('RollAxisAutopilot_grt_rtw','RollAxisAutopilot.h');
coder.example.extractLines(file,...
    '/* Block signals (default storage) */','} B_RollAxisAutopilot_T;',1,1)
/* Block signals (default storage) */
typedef struct {
  real32_T phiCmd;                     /* '<Root>/ModeSwitch' */
  real32_T hdgError;                   /* '<S2>/Sum' */
  real32_T DispGain;                   /* '<S2>/DispGain' */
  real32_T Product;                    /* '<S2>/Product' */
  real32_T Abs;                        /* '<S3>/Abs' */
  real32_T FixPtUnitDelay1;            /* '<S4>/FixPt Unit Delay1' */
  real32_T Xnew;                       /* '<S4>/Enable' */
  real32_T TKSwitch;                   /* '<S3>/TKSwitch' */
  real32_T RefSwitch;                  /* '<S3>/RefSwitch' */
  real32_T Integrator;                 /* '<S1>/Integrator' */
  real32_T DispLimit;                  /* '<S1>/DispLimit' */
  real32_T Sum;                        /* '<S1>/Sum' */
  real32_T DispGain_f;                 /* '<S1>/DispGain' */
  real32_T RateLimit;                  /* '<S1>/RateLimit' */
  real32_T Sum1;                       /* '<S1>/Sum1' */
  real32_T RateGain;                   /* '<S1>/RateGain' */
  real32_T Sum2;                       /* '<S1>/Sum2' */
  real32_T CmdLimit;                   /* '<S1>/CmdLimit' */
  real32_T IntGain;                    /* '<S1>/IntGain' */
  boolean_T NotEngaged;                /* '<S3>/NotEngaged' */
  boolean_T TKThreshold;               /* '<S3>/TKThreshold' */
  boolean_T RefThreshold2;             /* '<S3>/RefThreshold2' */
  boolean_T RefThreshold1;             /* '<S3>/RefThreshold1' */
  boolean_T Or;                        /* '<S3>/Or' */
  boolean_T NotEngaged_e;              /* '<S1>/NotEngaged' */
} B_RollAxisAutopilot_T;

The file defines a structure type, the DWork structure, to represent block states such as the state of the Discrete-Time Integrator block.

coder.example.extractLines(file,...
    '/* Block states (default storage) for system','} DW_RollAxisAutopilot_T;',1,1)
/* Block states (default storage) for system '<Root>' */
typedef struct {
  real32_T FixPtUnitDelay1_DSTATE;     /* '<S4>/FixPt Unit Delay1' */
  real32_T Integrator_DSTATE;          /* '<S1>/Integrator' */
  int8_T Integrator_PrevResetState;    /* '<S1>/Integrator' */
} DW_RollAxisAutopilot_T;

The file defines a structure type to represent parameter data. Each tunable block parameter in the model, such as the Gain parameter of a Gain block, appears as a field of this structure. If a block parameter acquires its value from a MATLAB® variable or a Simulink.Parameter object, the variable or object appears as a field, not the block parameter.

The file also defines a structure type, the real-time model data structure, whose single field represents a run-time indication of whether the generated code has encountered an error during execution.

coder.example.extractLines(file,'/* Real-time Model Data Structure */',...
    '/* Block parameters (default storage) */',1,0)
/* Real-time Model Data Structure */
struct tag_RTM_RollAxisAutopilot_T {
  const char_T *errorStatus;
};

For the structure type that represents the real-time model data structure, the file RollAxisAutopilot_types.h creates an alias that the generated code later uses to allocate memory for the structure.

file = fullfile('RollAxisAutopilot_grt_rtw','RollAxisAutopilot_types.h');
coder.example.extractLines(file,'/* Forward declaration for rtModel */',...
    'RT_MODEL_RollAxisAutopilot_T;',1,1)
/* Forward declaration for rtModel */
typedef struct tag_RTM_RollAxisAutopilot_T RT_MODEL_RollAxisAutopilot_T;

Using these structure types, the file RollAxisAutopilot.c defines (allocates memory for) global structure variables that store internal data for the generated algorithm. The file also defines variables that represent the real-time model data structure and a pointer to the structure.

file = fullfile('RollAxisAutopilot_grt_rtw','RollAxisAutopilot.c');
coder.example.extractLines(file,'/* Block signals (default storage) */',...
    '= &RollAxisAutopilot_M_;',1,1)
/* Block signals (default storage) */
B_RollAxisAutopilot_T RollAxisAutopilot_B;

/* Block states (default storage) */
DW_RollAxisAutopilot_T RollAxisAutopilot_DW;

/* External inputs (root inport signals with default storage) */
ExtU_RollAxisAutopilot_T RollAxisAutopilot_U;

/* External outputs (root outports fed by signals with default storage) */
ExtY_RollAxisAutopilot_T RollAxisAutopilot_Y;

/* Real-time model */
static RT_MODEL_RollAxisAutopilot_T RollAxisAutopilot_M_;
RT_MODEL_RollAxisAutopilot_T *const RollAxisAutopilot_M = &RollAxisAutopilot_M_;

The model step function, which represents the primary model algorithm, uses a void void interface (with no arguments).

coder.example.extractLines(file,...
    '/* Model step function */','void RollAxisAutopilot_step(void)',1,1)
/* Model step function */
void RollAxisAutopilot_step(void)

In the function definition, the algorithm performs calculations and stores intermediate results in the signal and state structures by directly accessing the global variables. The algorithm also reads parameter data from the corresponding global variable. For example, in the BasicRollMode subsystem, the code generated for the Integrator block reads and writes signal, state, and parameter data from the structures.

coder.example.extractLines(file,'/* DiscreteIntegrator: ''<S1>/Integrator'' *',...
    '/* End of DiscreteIntegrator: ''<S1>/Integrator'' */',1,1)
  /* DiscreteIntegrator: '<S1>/Integrator' */
  if (RollAxisAutopilot_B.NotEngaged_e ||
      (RollAxisAutopilot_DW.Integrator_PrevResetState != 0)) {
    RollAxisAutopilot_DW.Integrator_DSTATE = RollAxisAutopilot_P.Integrator_IC;
  }

  /* DiscreteIntegrator: '<S1>/Integrator' */
  RollAxisAutopilot_B.Integrator = RollAxisAutopilot_DW.Integrator_DSTATE;

  /* Saturate: '<S1>/DispLimit' */
  u0 = RollAxisAutopilot_B.phiCmd;
  u1 = RollAxisAutopilot_P.DispLimit_LowerSat;
  u2 = RollAxisAutopilot_P.dispLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/DispLimit' */
    RollAxisAutopilot_B.DispLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/DispLimit' */
    RollAxisAutopilot_B.DispLimit = u1;
  } else {
    /* Saturate: '<S1>/DispLimit' */
    RollAxisAutopilot_B.DispLimit = u0;
  }

  /* End of Saturate: '<S1>/DispLimit' */

  /* Sum: '<S1>/Sum' incorporates:
   *  Inport: '<Root>/Phi'
   */
  RollAxisAutopilot_B.Sum = RollAxisAutopilot_B.DispLimit -
    RollAxisAutopilot_U.Phi;

  /* Gain: '<S1>/DispGain' */
  RollAxisAutopilot_B.DispGain_f = RollAxisAutopilot_P.dispGain *
    RollAxisAutopilot_B.Sum;

  /* Saturate: '<S1>/RateLimit' */
  u0 = RollAxisAutopilot_B.DispGain_f;
  u1 = RollAxisAutopilot_P.RateLimit_LowerSat;
  u2 = RollAxisAutopilot_P.rateLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/RateLimit' */
    RollAxisAutopilot_B.RateLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/RateLimit' */
    RollAxisAutopilot_B.RateLimit = u1;
  } else {
    /* Saturate: '<S1>/RateLimit' */
    RollAxisAutopilot_B.RateLimit = u0;
  }

  /* End of Saturate: '<S1>/RateLimit' */

  /* Sum: '<S1>/Sum1' incorporates:
   *  Inport: '<Root>/Rate_FB'
   */
  RollAxisAutopilot_B.Sum1 = RollAxisAutopilot_B.RateLimit -
    RollAxisAutopilot_U.Rate_FB;

  /* Gain: '<S1>/RateGain' */
  RollAxisAutopilot_B.RateGain = RollAxisAutopilot_P.rateGain *
    RollAxisAutopilot_B.Sum1;

  /* Sum: '<S1>/Sum2' */
  RollAxisAutopilot_B.Sum2 = RollAxisAutopilot_B.Integrator +
    RollAxisAutopilot_B.RateGain;

  /* Saturate: '<S1>/CmdLimit' */
  u0 = RollAxisAutopilot_B.Sum2;
  u1 = RollAxisAutopilot_P.CmdLimit_LowerSat;
  u2 = RollAxisAutopilot_P.cmdLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/CmdLimit' */
    RollAxisAutopilot_B.CmdLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/CmdLimit' */
    RollAxisAutopilot_B.CmdLimit = u1;
  } else {
    /* Saturate: '<S1>/CmdLimit' */
    RollAxisAutopilot_B.CmdLimit = u0;
  }

  /* End of Saturate: '<S1>/CmdLimit' */

  /* Gain: '<S1>/IntGain' */
  RollAxisAutopilot_B.IntGain = RollAxisAutopilot_P.intGain *
    RollAxisAutopilot_B.Sum1;

  /* Update for DiscreteIntegrator: '<S1>/Integrator' */
  RollAxisAutopilot_DW.Integrator_DSTATE +=
    RollAxisAutopilot_P.Integrator_gainval * RollAxisAutopilot_B.IntGain;
  if (RollAxisAutopilot_DW.Integrator_DSTATE > RollAxisAutopilot_P.intLim) {
    RollAxisAutopilot_DW.Integrator_DSTATE = RollAxisAutopilot_P.intLim;
  } else if (RollAxisAutopilot_DW.Integrator_DSTATE <
             RollAxisAutopilot_P.Integrator_LowerSat) {
    RollAxisAutopilot_DW.Integrator_DSTATE =
      RollAxisAutopilot_P.Integrator_LowerSat;
  }

  RollAxisAutopilot_DW.Integrator_PrevResetState = (int8_T)
    RollAxisAutopilot_B.NotEngaged_e;

  /* End of Update for DiscreteIntegrator: '<S1>/Integrator' */
  /* End of Outputs for SubSystem: '<Root>/BasicRollMode' */

  /* Switch: '<Root>/EngSwitch' incorporates:
   *  Inport: '<Root>/AP_Eng'
   */
  if (RollAxisAutopilot_U.AP_Eng) {
    /* Outport: '<Root>/Ail_Cmd' */
    RollAxisAutopilot_Y.Ail_Cmd = RollAxisAutopilot_B.CmdLimit;
  } else {
    /* Outport: '<Root>/Ail_Cmd' incorporates:
     *  Constant: '<Root>/Zero'
     */
    RollAxisAutopilot_Y.Ail_Cmd = RollAxisAutopilot_P.Zero_Value_c;
  }

  /* End of Switch: '<Root>/EngSwitch' */
}

/* Model initialize function */
void RollAxisAutopilot_initialize(void)
{
  /* Registration code */

  /* initialize error status */
  rtmSetErrorStatus(RollAxisAutopilot_M, (NULL));

  /* block I/O */
  (void) memset(((void *) &RollAxisAutopilot_B), 0,
                sizeof(B_RollAxisAutopilot_T));

  /* states (dwork) */
  (void) memset((void *)&RollAxisAutopilot_DW, 0,
                sizeof(DW_RollAxisAutopilot_T));

  /* external inputs */
  (void)memset(&RollAxisAutopilot_U, 0, sizeof(ExtU_RollAxisAutopilot_T));

  /* external outputs */
  RollAxisAutopilot_Y.Ail_Cmd = 0.0F;

  /* SystemInitialize for Atomic SubSystem: '<Root>/RollAngleReference' */
  /* InitializeConditions for UnitDelay: '<S4>/FixPt Unit Delay1' */
  RollAxisAutopilot_DW.FixPtUnitDelay1_DSTATE =
    RollAxisAutopilot_P.LatchPhi_vinit;

  /* End of SystemInitialize for SubSystem: '<Root>/RollAngleReference' */

  /* SystemInitialize for Atomic SubSystem: '<Root>/BasicRollMode' */
  /* InitializeConditions for DiscreteIntegrator: '<S1>/Integrator' */
  RollAxisAutopilot_DW.Integrator_DSTATE = RollAxisAutopilot_P.Integrator_IC;
  RollAxisAutopilot_DW.Integrator_PrevResetState = 0;

  /* End of SystemInitialize for SubSystem: '<Root>/BasicRollMode' */
}

/* Model terminate function */
void RollAxisAutopilot_terminate(void)
{
  /* (no terminate code required) */
}

Due to the void void interface and the direct data access, the function is not reentrant. If you call the function multiple times in an application, each call writes data to the global structure variables and the subsequent call can read that data, resulting in unintentional interference between the calls.

The model initialization function RollAxisAutopilot_initialize initializes all of the internal data to zero. The function also initializes the error status by calling a specialized macro function. The initialization function directly accesses the global variables, which means that the function is not reentrant.

coder.example.extractLines(file,'/* Model initialize function */',...
    'sizeof(DW_RollAxisAutopilot_T));',1,1)
/* Model initialize function */
void RollAxisAutopilot_initialize(void)
{
  /* Registration code */

  /* initialize error status */
  rtmSetErrorStatus(RollAxisAutopilot_M, (NULL));

  /* block I/O */
  (void) memset(((void *) &RollAxisAutopilot_B), 0,
                sizeof(B_RollAxisAutopilot_T));

  /* states (dwork) */
  (void) memset((void *)&RollAxisAutopilot_DW, 0,
                sizeof(DW_RollAxisAutopilot_T));

The function then initializes the block states in the DWork structure to the initial values that the block parameters in the model specify. Two of the three states in the model have tunable initial values, so the code initializes them by reading data from the parameters structure.

coder.example.extractLines(file,...
    '/* SystemInitialize for Atomic SubSystem: ''<Root>/RollAngleReference'' */',...
    '/* Model terminate function */',1,0)
  /* SystemInitialize for Atomic SubSystem: '<Root>/RollAngleReference' */
  /* InitializeConditions for UnitDelay: '<S4>/FixPt Unit Delay1' */
  RollAxisAutopilot_DW.FixPtUnitDelay1_DSTATE =
    RollAxisAutopilot_P.LatchPhi_vinit;

  /* End of SystemInitialize for SubSystem: '<Root>/RollAngleReference' */

  /* SystemInitialize for Atomic SubSystem: '<Root>/BasicRollMode' */
  /* InitializeConditions for DiscreteIntegrator: '<S1>/Integrator' */
  RollAxisAutopilot_DW.Integrator_DSTATE = RollAxisAutopilot_P.Integrator_IC;
  RollAxisAutopilot_DW.Integrator_PrevResetState = 0;

  /* End of SystemInitialize for SubSystem: '<Root>/BasicRollMode' */
}

Generate Reusable Code

You can configure the generated code as reentrant, which means you can call the entry-point functions multiple times in an application. With this configuration, instead of directly accessing global variables, the entry-point functions accept internal data through formal parameters (pointer arguments). With these pointer arguments, each call can maintain internal data in a set of separate global variables, preventing unintentional interaction between the calls.

In the model, set Configuration Parameters > Code Generation > Interface > Code interface packaging to Reusable function.

set_param('RollAxisAutopilot','CodeInterfacePackaging','Reusable function')

Generate code from the model.

slbuild('RollAxisAutopilot')
### Starting build procedure for: RollAxisAutopilot
### Successful completion of build procedure for: RollAxisAutopilot

Build Summary

Top model targets built:

Model              Action                        Rebuild Reason                   
==================================================================================
RollAxisAutopilot  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 9.3468s

Now, in RollAxisAutopilot.h, the real-time model data structure contains pointers to the error indication, the internal data, and primary input and output data in the form of ExtU and ExtY substructures (the fields of which represent Inport and Outport blocks at the root level of the model).

file = fullfile('RollAxisAutopilot_grt_rtw','RollAxisAutopilot.h');
coder.example.extractLines(file,'/* Real-time Model Data Structure */',...
    '/* External data declarations for dependent source files */',1,0)
/* Real-time Model Data Structure */
struct tag_RTM_RollAxisAutopilot_T {
  const char_T *errorStatus;
  B_RollAxisAutopilot_T *blockIO;
  ExtU_RollAxisAutopilot_T *inputs;
  ExtY_RollAxisAutopilot_T *outputs;
  DW_RollAxisAutopilot_T *dwork;
};

/* Block parameters (default storage) */
extern P_RollAxisAutopilot_T RollAxisAutopilot_P;

To call the generated code multiple times in an application, your code must allocate memory for a real-time model data structure per call. The file RollAxisAutopilot.c defines a specialized function that allocates memory for a new real-time model data structure and returns a pointer to the structure. The function also allocates memory for the substructures that the fields in the model data structure point to, such as the DWork structure.

file = fullfile('RollAxisAutopilot_grt_rtw','RollAxisAutopilot.c');
coder.example.extractLines(file,'/* Model data allocation function */',...
    'RT_MODEL_RollAxisAutopilot_T *RollAxisAutopilot(void)',1,1)
/* Model data allocation function */
RT_MODEL_RollAxisAutopilot_T *RollAxisAutopilot(void)

The model step function accepts an argument that represents the real-time model data structure.

coder.example.extractLines(file,'/* Model step function */','void RollAxisAutopilot_step',1,1)
/* Model step function */
void RollAxisAutopilot_step(RT_MODEL_RollAxisAutopilot_T *const

In the function definition, the algorithm first extracts each pointer from the real-time model data structure into a local variable.

coder.example.extractLines(file,'*RollAxisAutopilot_B =','RollAxisAutopilot_M->outputs;',1,1)
  B_RollAxisAutopilot_T *RollAxisAutopilot_B = RollAxisAutopilot_M->blockIO;
  DW_RollAxisAutopilot_T *RollAxisAutopilot_DW = RollAxisAutopilot_M->dwork;
  ExtU_RollAxisAutopilot_T *RollAxisAutopilot_U = (ExtU_RollAxisAutopilot_T *)
    RollAxisAutopilot_M->inputs;
  ExtY_RollAxisAutopilot_T *RollAxisAutopilot_Y = (ExtY_RollAxisAutopilot_T *)
    RollAxisAutopilot_M->outputs;

Then, to access the internal data stored in global memory, the algorithm interacts with these local variables.

coder.example.extractLines(file,'/* DiscreteIntegrator: ''<S1>/Integrator'' */',...
    '/* End of DiscreteIntegrator: ''<S1>/Integrator'' */',1,1)
  /* DiscreteIntegrator: '<S1>/Integrator' */
  if (RollAxisAutopilot_B->NotEngaged_e ||
      (RollAxisAutopilot_DW->Integrator_PrevResetState != 0)) {
    RollAxisAutopilot_DW->Integrator_DSTATE = RollAxisAutopilot_P.Integrator_IC;
  }

  /* DiscreteIntegrator: '<S1>/Integrator' */
  RollAxisAutopilot_B->Integrator = RollAxisAutopilot_DW->Integrator_DSTATE;

  /* Saturate: '<S1>/DispLimit' */
  u0 = RollAxisAutopilot_B->phiCmd;
  u1 = RollAxisAutopilot_P.DispLimit_LowerSat;
  u2 = RollAxisAutopilot_P.dispLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/DispLimit' */
    RollAxisAutopilot_B->DispLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/DispLimit' */
    RollAxisAutopilot_B->DispLimit = u1;
  } else {
    /* Saturate: '<S1>/DispLimit' */
    RollAxisAutopilot_B->DispLimit = u0;
  }

  /* End of Saturate: '<S1>/DispLimit' */

  /* Sum: '<S1>/Sum' */
  RollAxisAutopilot_B->Sum = RollAxisAutopilot_B->DispLimit -
    RollAxisAutopilot_U->Phi;

  /* Gain: '<S1>/DispGain' */
  RollAxisAutopilot_B->DispGain_f = RollAxisAutopilot_P.dispGain *
    RollAxisAutopilot_B->Sum;

  /* Saturate: '<S1>/RateLimit' */
  u0 = RollAxisAutopilot_B->DispGain_f;
  u1 = RollAxisAutopilot_P.RateLimit_LowerSat;
  u2 = RollAxisAutopilot_P.rateLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/RateLimit' */
    RollAxisAutopilot_B->RateLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/RateLimit' */
    RollAxisAutopilot_B->RateLimit = u1;
  } else {
    /* Saturate: '<S1>/RateLimit' */
    RollAxisAutopilot_B->RateLimit = u0;
  }

  /* End of Saturate: '<S1>/RateLimit' */

  /* Sum: '<S1>/Sum1' */
  RollAxisAutopilot_B->Sum1 = RollAxisAutopilot_B->RateLimit -
    RollAxisAutopilot_U->Rate_FB;

  /* Gain: '<S1>/RateGain' */
  RollAxisAutopilot_B->RateGain = RollAxisAutopilot_P.rateGain *
    RollAxisAutopilot_B->Sum1;

  /* Sum: '<S1>/Sum2' */
  RollAxisAutopilot_B->Sum2 = RollAxisAutopilot_B->Integrator +
    RollAxisAutopilot_B->RateGain;

  /* Saturate: '<S1>/CmdLimit' */
  u0 = RollAxisAutopilot_B->Sum2;
  u1 = RollAxisAutopilot_P.CmdLimit_LowerSat;
  u2 = RollAxisAutopilot_P.cmdLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/CmdLimit' */
    RollAxisAutopilot_B->CmdLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/CmdLimit' */
    RollAxisAutopilot_B->CmdLimit = u1;
  } else {
    /* Saturate: '<S1>/CmdLimit' */
    RollAxisAutopilot_B->CmdLimit = u0;
  }

  /* End of Saturate: '<S1>/CmdLimit' */

  /* Gain: '<S1>/IntGain' */
  RollAxisAutopilot_B->IntGain = RollAxisAutopilot_P.intGain *
    RollAxisAutopilot_B->Sum1;

  /* Update for DiscreteIntegrator: '<S1>/Integrator' */
  RollAxisAutopilot_DW->Integrator_DSTATE +=
    RollAxisAutopilot_P.Integrator_gainval * RollAxisAutopilot_B->IntGain;
  if (RollAxisAutopilot_DW->Integrator_DSTATE > RollAxisAutopilot_P.intLim) {
    RollAxisAutopilot_DW->Integrator_DSTATE = RollAxisAutopilot_P.intLim;
  } else if (RollAxisAutopilot_DW->Integrator_DSTATE <
             RollAxisAutopilot_P.Integrator_LowerSat) {
    RollAxisAutopilot_DW->Integrator_DSTATE =
      RollAxisAutopilot_P.Integrator_LowerSat;
  }

  RollAxisAutopilot_DW->Integrator_PrevResetState = (int8_T)
    RollAxisAutopilot_B->NotEngaged_e;

  /* End of Update for DiscreteIntegrator: '<S1>/Integrator' */
  /* End of Outputs for SubSystem: '<Root>/BasicRollMode' */

  /* Switch: '<Root>/EngSwitch' */
  if (RollAxisAutopilot_U->AP_Eng) {
    /* Outport: '<Root>/Ail_Cmd' */
    RollAxisAutopilot_Y->Ail_Cmd = RollAxisAutopilot_B->CmdLimit;
  } else {
    /* Outport: '<Root>/Ail_Cmd' incorporates:
     *  Constant: '<Root>/Zero'
     */
    RollAxisAutopilot_Y->Ail_Cmd = RollAxisAutopilot_P.Zero_Value_c;
  }

  /* End of Switch: '<Root>/EngSwitch' */
}

/* Model initialize function */
void RollAxisAutopilot_initialize(RT_MODEL_RollAxisAutopilot_T *const
  RollAxisAutopilot_M)
{
  DW_RollAxisAutopilot_T *RollAxisAutopilot_DW = RollAxisAutopilot_M->dwork;

  /* SystemInitialize for Atomic SubSystem: '<Root>/RollAngleReference' */
  /* InitializeConditions for UnitDelay: '<S4>/FixPt Unit Delay1' */
  RollAxisAutopilot_DW->FixPtUnitDelay1_DSTATE =
    RollAxisAutopilot_P.LatchPhi_vinit;

  /* End of SystemInitialize for SubSystem: '<Root>/RollAngleReference' */

  /* SystemInitialize for Atomic SubSystem: '<Root>/BasicRollMode' */
  /* InitializeConditions for DiscreteIntegrator: '<S1>/Integrator' */
  RollAxisAutopilot_DW->Integrator_DSTATE = RollAxisAutopilot_P.Integrator_IC;
  RollAxisAutopilot_DW->Integrator_PrevResetState = 0;

  /* End of SystemInitialize for SubSystem: '<Root>/BasicRollMode' */
}

/* Model terminate function */
void RollAxisAutopilot_terminate(RT_MODEL_RollAxisAutopilot_T
  * RollAxisAutopilot_M)
{
  /* model code */
  rt_FREE(RollAxisAutopilot_M->blockIO);
  rt_FREE(RollAxisAutopilot_M->inputs);
  rt_FREE(RollAxisAutopilot_M->outputs);
  rt_FREE(RollAxisAutopilot_M->dwork);
  rt_FREE(RollAxisAutopilot_M);
}

/* Model data allocation function */
RT_MODEL_RollAxisAutopilot_T *RollAxisAutopilot(void)
{
  RT_MODEL_RollAxisAutopilot_T *RollAxisAutopilot_M;
  RollAxisAutopilot_M = (RT_MODEL_RollAxisAutopilot_T *) malloc(sizeof
    (RT_MODEL_RollAxisAutopilot_T));
  if (RollAxisAutopilot_M == (NULL)) {
    return (NULL);
  }

  (void) memset((char *)RollAxisAutopilot_M, 0,
                sizeof(RT_MODEL_RollAxisAutopilot_T));

  /* block I/O */
  {
    B_RollAxisAutopilot_T *b = (B_RollAxisAutopilot_T *) malloc(sizeof
      (B_RollAxisAutopilot_T));
    rt_VALIDATE_MEMORY(RollAxisAutopilot_M,b);
    RollAxisAutopilot_M->blockIO = (b);
  }

  /* states (dwork) */
  {
    DW_RollAxisAutopilot_T *dwork = (DW_RollAxisAutopilot_T *) malloc(sizeof
      (DW_RollAxisAutopilot_T));
    rt_VALIDATE_MEMORY(RollAxisAutopilot_M,dwork);
    RollAxisAutopilot_M->dwork = (dwork);
  }

  /* external inputs */
  {
    ExtU_RollAxisAutopilot_T *RollAxisAutopilot_U = (ExtU_RollAxisAutopilot_T *)
      malloc(sizeof(ExtU_RollAxisAutopilot_T));
    rt_VALIDATE_MEMORY(RollAxisAutopilot_M,RollAxisAutopilot_U);
    RollAxisAutopilot_M->inputs = (((ExtU_RollAxisAutopilot_T *)
      RollAxisAutopilot_U));
  }

  /* external outputs */
  {
    ExtY_RollAxisAutopilot_T *RollAxisAutopilot_Y = (ExtY_RollAxisAutopilot_T *)
      malloc(sizeof(ExtY_RollAxisAutopilot_T));
    rt_VALIDATE_MEMORY(RollAxisAutopilot_M,RollAxisAutopilot_Y);
    RollAxisAutopilot_M->outputs = (RollAxisAutopilot_Y);
  }

  {
    B_RollAxisAutopilot_T *RollAxisAutopilot_B = RollAxisAutopilot_M->blockIO;
    DW_RollAxisAutopilot_T *RollAxisAutopilot_DW = RollAxisAutopilot_M->dwork;
    ExtU_RollAxisAutopilot_T *RollAxisAutopilot_U = (ExtU_RollAxisAutopilot_T *)
      RollAxisAutopilot_M->inputs;
    ExtY_RollAxisAutopilot_T *RollAxisAutopilot_Y = (ExtY_RollAxisAutopilot_T *)
      RollAxisAutopilot_M->outputs;

    /* block I/O */
    (void) memset(((void *) RollAxisAutopilot_B), 0,
                  sizeof(B_RollAxisAutopilot_T));

    /* states (dwork) */
    (void) memset((void *)RollAxisAutopilot_DW, 0,
                  sizeof(DW_RollAxisAutopilot_T));

    /* external inputs */
    (void)memset(RollAxisAutopilot_U, 0, sizeof(ExtU_RollAxisAutopilot_T));

    /* external outputs */
    RollAxisAutopilot_Y->Ail_Cmd = 0.0F;
  }

  return RollAxisAutopilot_M;
}

Similarly, the model initialization function accepts the real-time model data structure as an argument.

coder.example.extractLines(file,...
    '/* Model initialize function */','void RollAxisAutopilot_initialize',1,1)
/* Model initialize function */
void RollAxisAutopilot_initialize(RT_MODEL_RollAxisAutopilot_T *const

Because each call that you make to an entry-point function interacts with a separate real-time model data structure, you avoid unintentional interaction between the calls.

Eliminate Internal Data with Code Generation Optimizations

For more efficient code that consumes less memory, select the optimizations, such as Default parameter behavior, that you cleared earlier.

set_param('RollAxisAutopilot','DefaultParameterBehavior','Inlined',...
    'OptimizeBlockIOStorage','on',...
    'LocalBlockOutputs','on')

In this example, for simpler code, set Code interface packaging to Nonreusable function.

set_param('RollAxisAutopilot','CodeInterfacePackaging','Nonreusable function')

Generate code from the model.

slbuild('RollAxisAutopilot')
### Starting build procedure for: RollAxisAutopilot
### Successful completion of build procedure for: RollAxisAutopilot

Build Summary

Top model targets built:

Model              Action                        Rebuild Reason                   
==================================================================================
RollAxisAutopilot  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 9.6174s

Now, RollAxisAutopilot.h does not define a structure for block inputs and outputs. For all of the internal signals in the model, the optimizations either eliminated storage or created local function variables instead of global structure fields.

The optimizations were not able to eliminate storage for the three block states, so the file continues to define the DWork structure type.

file = fullfile('RollAxisAutopilot_grt_rtw','RollAxisAutopilot.h');
coder.example.extractLines(file,...
    '/* Block states (default storage) for system','} DW_RollAxisAutopilot_T;',1,1)
/* Block states (default storage) for system '<Root>' */
typedef struct {
  real32_T FixPtUnitDelay1_DSTATE;     /* '<S4>/FixPt Unit Delay1' */
  real32_T Integrator_DSTATE;          /* '<S1>/Integrator' */
  int8_T Integrator_PrevResetState;    /* '<S1>/Integrator' */
} DW_RollAxisAutopilot_T;

The code generated for the Discrete-Time Integrator block now stores state and output data only in the DWork structure.

file = fullfile('RollAxisAutopilot_grt_rtw','RollAxisAutopilot.c');
coder.example.extractLines(file,'/* Update for DiscreteIntegrator: ''<S1>/Integrator''',...
    '/* End of Update for DiscreteIntegrator: ''<S1>/Integrator'' */',1,1)
  /* Update for DiscreteIntegrator: '<S1>/Integrator' incorporates:
   *  Gain: '<S1>/IntGain'
   */
  RollAxisAutopilot_DW.Integrator_DSTATE += 0.5F * rtb_TKSwitch * 0.025F;
  if (RollAxisAutopilot_DW.Integrator_DSTATE > 5.0F) {
    RollAxisAutopilot_DW.Integrator_DSTATE = 5.0F;
  } else if (RollAxisAutopilot_DW.Integrator_DSTATE < -5.0F) {
    RollAxisAutopilot_DW.Integrator_DSTATE = -5.0F;
  }

  RollAxisAutopilot_DW.Integrator_PrevResetState = (int8_T)tmp;

The optimizations also eliminated storage for the block parameters in the model. For example, in the Discrete-Time Integrator block, the Upper saturation limit and Lower saturation limit parameters are set to intLim and -intLim. intLim is a Simulink.Parameter object that stores the value 5. In the code generated for the Discrete-Time Integrator, these block parameters and intLim appear as inlined literal numbers 5.0F and -5.0F.

If a model contains a parameter that the code generator cannot inline directly (for example, an array parameter), the code defines a structure type that represents the data. This constant parameters structure uses the const storage type qualifier, so some build toolchains can optimize the assembly code further.

The declaration of the structure, ConstParam_model, is in model.h.

/* Constant parameters (auto storage) */
typedef struct {
   /* Expression: [1 2 3 4 5 6 7]
    * Referenced by: '<Root>/Constant'
    */
   real_T Constant_Value[7];
   
   /* Expression: [7 6 5 4 3 2 1]
    * Referenced by: '<Root>/Gain'
    */
   real_T Gain_Gain[7];
 } ConstParam_model;

The declaration of the constant parameters, model_ConstP, is in model_data.c by default.

/* Constant parameters (auto storage) */
const ConstParam_model model_ConstP = {
   /* Expression: [1 2 3 4 5 6 7]
    * Referenced by: '<Root>/Constant'
    */
   { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0 },

   /* Expression: [7 6 5 4 3 2 1]
    * Referenced by: '<Root>/Gain'
    */
   { 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0 }
};

model_ConstP is passed as an argument to referenced models.

Local Variables in the Generated Code

When you select the optimization Configuration Parameters > Enable local block outputs, the code generator attempts to yield more efficient code by representing internal signals as local variables instead of fields of a global structure. If the memory consumed by local variables risks exceeding the stack space available on your target hardware, consider indicating the maximum stack size by setting Configuration Parameters > Maximum stack size (bytes). For more information, see Maximum stack size (bytes).

Appearance of Test Points in the Generated Code

A test point is a signal that is stored in a unique memory location. For information about including test points in your model, see Configure Signals as Test Points.

When you generate code for models that include test points, the build process allocates a separate memory buffer for each test point. By default, test points are stored as members of a standard data structure such as model_B.

If you have Embedded Coder®:

  • You can control the default representation of test points by specifying code generation settings for the Internal data category of data in the Code Mapping Editor (see Configure Default Code Generation for Data (Embedded Coder)).

  • You can specify that the build process ignore test points in the model, allowing optimal buffer allocation, by using the Ignore test point signals (Embedded Coder) parameter. Ignoring test points facilitates transitioning from prototyping to deployment and avoids accidental degradation of generated code due to workflow artifacts. See Ignore test point signals (Embedded Coder).

Virtual buses do not appear in generated code, even when associated with a test point. To display a bus in generated code, use a nonvirtual bus or a virtual bus converted to a nonvirtual bus by a Signal Conversion block.

Appearance of Workspace Variables in the Generated Code

Workspace variables are variables that you use to specify block parameter values in a model. Workspace variables include numeric MATLAB® variables and Simulink.Parameter objects that you store in a workspace, such as the base workspace, or in a data dictionary.

When you set Default parameter behavior to Tunable, by default, workspace variables appear in the generated code as tunable fields of the global parameters structure. If you use such a variable to specify multiple block parameter values, the variable appears as a single field of the global parameters structure. The code does not create multiple fields to represent the block parameters. Therefore, tuning the field value during code execution changes the mathematical behavior of the model in the same way as tuning the value of the MATLAB variable or parameter object during simulation.

If you have Embedded Coder, you can control the default representation of workspace variables by specifying code generation settings for categories of parameter data in the Code Mapping Editor (see Configure Default Code Generation for Data (Embedded Coder)).

  • The Model parameters category applies to variables that you store in a model workspace.

  • The External parameters category applies to variables that you store in the base workspace or a data dictionary.

Promote Internal Data to the Interface

By default, the code generator assumes that other systems and components in your application do not need to access internal data. For example, internal data are subject to optimizations that can eliminate them from the generated code. For prototyping and testing purposes, you can access internal data by clearing the optimizations or by configuring test points and applying storage classes (see Preserve Variables in Generated Code). For optimized production code, configure individual data items to appear in the generated code as part of the model interface.

Data That You Can Promote

Depending on the reentrancy of the generated code, that is, the setting that you choose for Code interface packaging, you can configure each data item in a model to participate in the interface by appearing in the code as one of these entities:

  • A global symbol, such as a global variable or a call to a specialized function

  • A formal parameter (argument) of the generated entry-point functions

The table shows the mechanisms that each category of data can use to participate in the interface.

Category of DataAppear as Global SymbolAppear as Argument of Entry-Point Function
Root-level Inport or Outport blockOnly for a nonreentrant model.Yes.
Signal connecting two blocksOnly for a nonreentrant model.

Only for a reentrant model and only as a field of a structure.

Alternatively, connect the signal to a root-level Outport block.

Block stateOnly for a nonreentrant model.Only for a reentrant model and only as a field of a structure.
Data store such as a Data Store Memory blockYes.Only for a reentrant model and only as a field of a structure.
Block parameter or parameter object such as Simulink.ParameterYes.Only as a field of a structure.

Single-Instance Algorithm

For a single-instance algorithm (you set Code interface packaging to Nonreusable function), apply storage classes directly to individual data items by using the Model Data Editor or the Property Inspector. With a directly applied storage class, a data item appears in the code as a global symbol such as a global variable. The storage class also prevents optimizations from eliminating storage for the data item.

You can apply storage classes to signals, block states, and block parameters. (For block parameters, you apply storage classes indirectly through parameter objects such as Simulink.Parameter). However, for a signal, consider connecting the signal to an Outport block at the root level of the model. Then, optionally, you can apply a storage class to the block. In the block diagram, the Outport block shows that the signal represents a system output.

For more information about storage classes, see C Data Code Interface Configuration for Model Interface Elements.

Reentrant Algorithm

For a reentrant algorithm (you set Code interface packaging to Reusable function), use different techniques to configure data items to appear in the code as formal parameters (arguments) of the generated entry-point functions.

  • For an internal signal, directly apply the storage class Model default (see C Data Code Interface Configuration for Model Interface Elements). If you have Embedded Coder, in the Code Mapping Editor, for the Internal data category, set the default storage class to Default or to a structured storage class that you define in an Embedded Coder Dictionary (see Create Code Definitions for Use in the Code Mappings Editor (Embedded Coder)). Alternatively, configure the signal as a test point (see Appearance of Test Points in the Generated Code). By default, the signal appears as a field of one of the standard data structures (see How Generated Code Stores Internal Signal, State, and Parameter Data). If you do not want the signal to appear in production code, use a test point so that you can later select the model configuration parameter Ignore test point signals.

    Alternatively, connect the signal to an Outport block at the root level of the model. Connecting the signal to a root-level Outport block prevents optimizations from eliminating the signal from the code. To help with signal routing in a large model, use Goto and From blocks.

  • For a block parameter, create a parameter object such as Simulink.Parameter and directly apply a storage class other than Auto to the object. The storage class prevents optimizations from inlining the parameter value in the code.

    With the storage class, the parameter is shared between instances of the model, which are calls to the entry-point functions. The functions access the parameter data directly, as a global symbol, not an argument. To enable each instance of the model to use a different value for the parameter, configure the parameter as a model argument. For more information, see Specify Instance-Specific Parameter Values for Reusable Referenced Model.

    For information about applying storage classes, see C Data Code Interface Configuration for Model Interface Elements.

Control Default Representation of Internal Data (Embedded Coder)

By default, the code generator aggregates internal data that optimizations cannot eliminate, such as most state data, into standard structures such as the DWork structure. With Embedded Coder, you can control how the generated code stores this data.

Control Placement of Data in Memory by Inserting Pragmas

Use the Code Mapping Editor to specify a default memory section for each category of data such as states and signals (Internal data). In the generated code, your custom pragmas or other decorations surround the data definitions and declarations.

You can also partition the structures according to atomic subsystems in your model so that you can specify different default memory sections for the data of subroutines and other algorithmic subcomponents.

For more information, see Control Data and Function Placement in Memory by Inserting Pragmas (Embedded Coder).

Control Names of Types, Fields, and Global Variables for Standard Data Structures

You can control some characteristics of the standard data structures. For more information, see Manage Replacement of Simulink Data Types in Generated Code (Embedded Coder).

For additional control over structure characteristics, such as placement in generated code files, create your own structured storage class by using the Embedded Coder Dictionary. Then, apply the storage class to categories of data by using the Code Mapping Editor. The storage class removes the data from the standard structures, creating other structures that you can more finely control. For more information about applying default storage classes to categories of data, see Configure Default Code Generation for Data (Embedded Coder). For more information about creating a storage class, see Define Service Interfaces, Storage Classes, Memory Sections, and Function Templates for Software Architecture (Embedded Coder).

Organize Data into Structures According to Subcomponents

  • In the standard data structures, to create substructures that contain the data for a single-instance (nonreentrant) subroutine or subcomponent, use an atomic subsystem to encapsulate the corresponding blocks. In the subsystem parameters, set Function packaging to Reusable function. For more information, see Generate Modular Function Code for Nonvirtual Subsystems (Embedded Coder).

    Alternatively, encapsulate the blocks in a model and use a Model block. In the referenced model, set Configuration Parameters > Model Referencing > Total number of instances allowed per top model to Multiple. For more information, see Generate Code for Model Reference Hierarchy.

  • To create separate, standalone structures that contain the data for a multi-instance (reentrant) subroutine or subcomponent, in the model, use an atomic subsystem to encapsulate the corresponding blocks. In the subsystem parameters, set Function packaging to Nonreusable function and select Function with separate data. For more information, see Generate Modular Function Code for Nonvirtual Subsystems (Embedded Coder).

    Alternatively, encapsulate the blocks in a model and use a Model block. In the referenced model, choose one of these techniques:

Organize Signal and Parameter Data into Meaningful, Custom Structures and Substructures

To organize arbitrary signals and parameters into custom structures and substructures, create nonvirtual bus signals and parameter structures. Optionally, to prevent optimizations from eliminating the data from the code, set the storage class of a bus signal or parameter structure to a value other than Auto (the default setting).

As you add blocks to the model, you must explicitly place each new signal and parameter into a bus or a structure.

For more information, see Organize Data into Structures in Generated Code.

Create Separate Global Variables Instead of Structure Fields

To make a category of internal data appear in the generated code as separate, unstructured global variables instead of fields of a standard data structure, apply an unstructured storage class to the data category by using the Code Mapping Editor. For example, apply the storage class ExportedGlobal. However, if you generate multi-instance, reentrant code by setting the configuration parameter Code interface packaging to a value other than Nonreusable function, you cannot use this technique for some categories of data (see Storage Classes and Reentrant, Multi-Instance Models and Components).

To apply default storage classes to categories of data by using the Code Mapping Editor, see Configure Default Code Generation for Data (Embedded Coder). To choose a storage class, see Choose Storage Class for Controlling Data Representation in Generated Code.

Related Topics