Main Content

How Generated Code Exchanges Data with an Environment

To use the code that you generate from a model, you call the generated entry-point functions such as step and initialize. The environment in which you call these functions must provide input signal data and, depending on your application, scheduling information. The generated algorithm then calculates output data that the calling environment can use. The environment and the algorithm can exchange this data through global variables or through formal parameters (arguments).

The set of input and output data and the exchange mechanisms constitute the interfaces of the entry-point functions. When you understand the default interfaces and how to control them, you can:

  • Write code that calls the generated code.

  • Generate reusable (reentrant) code that you can call multiple times in a single application.

  • Integrate the generated code with other, external code in your application.

In a model, root-level Inport and Outport blocks represent the primary inputs and outputs of the block algorithm. By default, the code generator aggregates these blocks into standard structures that store input and output data.

For information about how the generated code stores internal data, which does not participate in the model interface, see How Generated Code Stores Internal Signal, State, and Parameter Data.

Data Interfaces in the Generated Code

This example shows how the generated code exchanges data with an environment.

Explore Example Model

Open the example model RollAxisAutopilot.

open_system('RollAxisAutopilot')

At the root level, the model has a number of Inport blocks and one Outport block.

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 (not reentrant).

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

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

Generate Nonreusable Code

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 9.9704s

Inspect the file RollAxisAutopilot.h. The file defines a structure type that represents input data and a type that represents output data.

file = fullfile('RollAxisAutopilot_grt_rtw','RollAxisAutopilot.h');
coder.example.extractLines(file,...
    '/* External inputs (root inport signals with default storage) */',...
    '} ExtY_RollAxisAutopilot_T;',1,1)
/* External inputs (root inport signals with default storage) */
typedef struct {
  real32_T Phi;                        /* '<Root>/Phi' */
  real32_T Psi;                        /* '<Root>/Psi' */
  real32_T Rate_FB;                    /* '<Root>/Rate_FB' */
  real32_T TAS;                        /* '<Root>/TAS' */
  boolean_T AP_Eng;                    /* '<Root>/AP_Eng' */
  boolean_T HDG_Mode;                  /* '<Root>/HDG_Mode' */
  real32_T HDG_Ref;                    /* '<Root>/HDG_Ref' */
  real32_T Turn_Knob;                  /* '<Root>/Turn_Knob' */
} ExtU_RollAxisAutopilot_T;

/* External outputs (root outports fed by signals with default storage) */
typedef struct {
  real32_T Ail_Cmd;                    /* '<Root>/Ail_Cmd' */
} ExtY_RollAxisAutopilot_T;

Each field corresponds to an Inport or Outport block at the root level of the model. The name of each field derives from the name of the block.

The file also defines a structure type, the real-time model data structure, whose single field stores 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 states (default storage) */',1,0)
/* Real-time Model Data Structure */
struct tag_RTM_RollAxisAutopilot_T {
  const char_T *errorStatus;
};

When you modify model configuration parameters to suit your application, this structure can also contain other data that pertain to the entire model, such as scheduling information.

For the structure type that represents the real-time model data structure, the file RollAxisAutopilot_types.h creates an alias (typedef) 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 input and output 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,...
    '/* External inputs (root inport signals with default storage) */',...
    '= &RollAxisAutopilot_M_;',1,1)
/* 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 file RollAxisAutopilot.h declares these structure variables. Your external code can include (#include) this file, whose general name is model.h where model is the name of the model, to access data that participate in the model interface.

In RollAxisAutopilot.c, the model step function, which represents the primary model algorithm, uses a void void interface (without 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 reads input data and writes output data by directly accessing the global structure variables. For example, near the end of the algorithm, the code for the EngSwitch block reads the value of the AP_Eng field (which represents an input) and, based on that value, conditionally writes a constant value to the Ail_Cmd field (which represents an output).

coder.example.extractLines(file,'if (RollAxisAutopilot_U.AP_Eng) {','      }',1,1)
  if (RollAxisAutopilot_U.AP_Eng) {
    /* Outputs for Atomic SubSystem: '<Root>/BasicRollMode' */
    /* Saturate: '<S1>/CmdLimit' */
    if (RollAxisAutopilot_Y.Ail_Cmd > 15.0F) {
      /* Sum: '<S1>/Sum2' incorporates:
       *  Outport: '<Root>/Ail_Cmd'
       */
      RollAxisAutopilot_Y.Ail_Cmd = 15.0F;
    } else if (RollAxisAutopilot_Y.Ail_Cmd < -15.0F) {
      /* Sum: '<S1>/Sum2' incorporates:
       *  Outport: '<Root>/Ail_Cmd'
       */
      RollAxisAutopilot_Y.Ail_Cmd = -15.0F;
    }

    /* End of Outputs for SubSystem: '<Root>/BasicRollMode' */
  } else {
    /* Sum: '<S1>/Sum2' incorporates:
     *  Constant: '<Root>/Zero'
     *  Outport: '<Root>/Ail_Cmd'
     *  Saturate: '<S1>/CmdLimit'
     */
    RollAxisAutopilot_Y.Ail_Cmd = 0.0F;
  }

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

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

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

  /* 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 = 0.0F;

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

  /* SystemInitialize for Atomic SubSystem: '<Root>/BasicRollMode' */
  /* InitializeConditions for DiscreteIntegrator: '<S1>/Integrator' */
  RollAxisAutopilot_DW.Integrator_DSTATE = 0.0F;
  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 reads and writes input and output data to the same global structure variables, resulting in data corruption and unintentional interaction between the calls.

The model initialization function, RollAxisAutopilot_initialize, initializes input and output data to zero. The function also initializes the error status. The function directly accesses the global variables, which means the function is not reentrant.

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

  /* initialize error status */
  rtmSetErrorStatus(RollAxisAutopilot_M, (NULL));
coder.example.extractLines(file,'  /* external inputs */',...
    '/* SystemInitialize for Atomic SubSystem:',1,0)
  /* external inputs */
  (void)memset(&RollAxisAutopilot_U, 0, sizeof(ExtU_RollAxisAutopilot_T));

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

Generate Reusable Code

You can configure the generated code as reentrant, which means that 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 exchange input, output, and other model data through formal parameters (pointer arguments). With these pointer arguments, each call can read inputs and write outputs to 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 8.1214s

Now, in RollAxisAutopilot.h, the real-time model data structure contains pointers to the error indication, the input data, the output data, and additional data in the form of a DWork substructure (which stores, for example, block states such as the state of a Discrete-Time Integrator block).

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;
  ExtU_RollAxisAutopilot_T *inputs;
  ExtY_RollAxisAutopilot_T *outputs;
  DW_RollAxisAutopilot_T *dwork;
};

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 input and output structures.

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)

In RollAxisAutopilot.c, the model step function accepts an argument that represents the real-time model data structure.

file = fullfile('RollAxisAutopilot_grt_rtw','RollAxisAutopilot.c');
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_DW =','RollAxisAutopilot_M->outputs;',1,1)
  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 input and output data stored in global memory, the algorithm interacts with these local variables.

coder.example.extractLines(file,'  if (RollAxisAutopilot_U->AP_Eng) {',...
    '  /* End of Switch: ''<Root>/EngSwitch'' */',1,1)
  if (RollAxisAutopilot_U->AP_Eng) {
    /* Outputs for Atomic SubSystem: '<Root>/BasicRollMode' */
    /* Saturate: '<S1>/CmdLimit' */
    if (RollAxisAutopilot_Y->Ail_Cmd > 15.0F) {
      /* Sum: '<S1>/Sum2' incorporates:
       *  Outport: '<Root>/Ail_Cmd'
       */
      RollAxisAutopilot_Y->Ail_Cmd = 15.0F;
    } else if (RollAxisAutopilot_Y->Ail_Cmd < -15.0F) {
      /* Sum: '<S1>/Sum2' incorporates:
       *  Outport: '<Root>/Ail_Cmd'
       */
      RollAxisAutopilot_Y->Ail_Cmd = -15.0F;
    }

    /* End of Outputs for SubSystem: '<Root>/BasicRollMode' */
  } else {
    /* Sum: '<S1>/Sum2' incorporates:
     *  Constant: '<Root>/Zero'
     *  Outport: '<Root>/Ail_Cmd'
     *  Saturate: '<S1>/CmdLimit'
     */
    RollAxisAutopilot_Y->Ail_Cmd = 0.0F;
  }

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.

Configure Data Interface

To control the characteristics of the data interface in the generated code, see Control Data and Function Interface in Generated Code.

Related Topics