Configure Data Interface in the Generated Code

Specify signals, states, and parameters for inclusion in generated code.

Learn how to control these attributes of data in the generated code:

  • Name

  • Data type

  • Data storage class

For information about the example model, see Prepare a Control Algorithm Model for C Code Generation.

Open Example Model

Open the example model, rtwdemo_PCG_Eval_P2.

open_system('rtwdemo_PCG_Eval_P2')

Data Declaration

Most programming languages require that you declare data and functions before using them. The declaration specifies:

  • Scope: The region of the program that has access to the data

  • Duration: The period during which the data exist in memory

  • Data type: The amount of memory allocated for the data

  • Initialization: A value, a pointer to memory, or NULL

The combination of scope and duration is the storage class. If you do not provide an initial value, most compilers assign a zero value or a null pointer.

Supported data types include:

  • double: Double-precision floating point

  • single: Single-precision floating point

  • int8: Signed 8-bit integer

  • uint8: Unsigned 8-bit integer

  • int16: Signed 16-bit integer

  • uint16: Unsigned 16-bit integer

  • int32: Signed 32-bit integer

  • uint32: Unsigned 32-bit integer

  • Fixed Point: 8-, 16-, 32-bit word lengths

With storage classes you can:

  • Generate exported files, with custom names, that contain global variable declarations and definitions.

  • Import custom header files that contain global variable declarations.

  • Generate const or volatile type qualifiers in declarations.

  • Represent a parameter as a macro (#define or compiler option).

  • Package signals or parameters into flat structures or bit fields.

Control Data in Simulink® and Stateflow®

This example uses data objects to specify code generation settings for data. Alternatively, you can store the settings in the model by using dialog boxes. Both methods allow full control over the data type and storage class. You can use both methods in a single model.

This example focuses on these types of data objects:

  • Signal

  • Parameter

  • Bus

The code generator uses the objects from the MATLAB base workspace or a Simulink data dictionary. You can create and inspect the objects with commands at the command prompt or by using the Model Data Editor or the Model Explorer with the Code Mappings Editor.

As an example, inspect the definition of the Simulink.Signal object pos_cmd_one, which the model created in the base workspace:

  1. Open the Embedded Coder app.

  2. On the Modeling tab, click Model Data Editor.

  3. In the Model Data Editor, inspect the Signals tab.

  4. Click the Show/refresh additional information button.

  5. Next to the Filter contents box, toggle the Filter using selection button.

  6. In the model, select the pos_cmd_one signal line. Now, the data table contains two rows: One that corresponds to the signal line in the model and one that corresponds to the signal object in the base workspace. For the row that corresponds to the signal line, the Resolve check box is selected, which means the signal line acquires settings from the signal object.

  7. Optionally, to inspect other properties of the signal object, adjust the setting for the Change view drop-down list.

  8. In the model, select the pos_cmd_one signal line and pause on the ellipsis that appears above or below the signal line to open the action bar. Click the Add Signal button.

  9. Open the Code Mappings Editor, click the Signals tab. Inspect the pos_cmd_one signal storage class.

Return to the Model Data Editor, toggle the Filter using selection button. Now, the data table shows these signal lines and signal objects:

  • pos_cmd_one

  • pos_rqst

  • P_InErrMap

  • ThrotComm

ThrotComm is a bus signal, which is an instantiation of the Simulink.Bus object ThrottleCommands. If a bus signal is nonvirtual, the signal appears as a structure in the C code. Bus objects do not appear in the Model Data Editor. However, you can inspect the object in the Model Explorer (View > Model Explorer).

As in C, you can use a bus definition (ThrottleCommands) to create multiple instances of the structure. In a model diagram, a bus signal appears as a wide line with central dashes.

A data object has properties that you configure for simulation and for code generation, including:

  • DataType (numeric data type for storage in the generated code)

  • StorageClass (storage class for code generation)

  • Value (parameter value)

  • InitialValue (initial value for signal)

  • Alias (alternative name for the data, which the code generator uses)

  • Dimensions (size and number of dimensions of parameter or signal value)

  • Complexity (numeric complexity)

  • Unit (physical units such as cm)

  • Min (minimum value)

  • Max (maximum value)

Use the property Description to specify custom documentation for data objects.

Add New Data Objects

You can create data objects for named signals, states, and parameters. To associate a data object with a construct, the construct must have a name.

The Data Object Wizard tool finds constructs for which you can create data objects, and then creates the objects for you. The example model includes two signals that are not associated with data objects: fbk_1 and pos_cmd_two.

To find the signals and create data objects for them:

1. Open the Data Object Wizard.

dataobjectwizard('rtwdemo_PCG_Eval_P2')

2. Click Find to find candidate constructs.

3. Click Select All to select all candidates.

4. Click Create to create the data objects.

The Data Object Wizard performs the equivalent of the following commands:

fbk_1 = Simulink.Signal;
fbk_1.Dimensions = 1;
fbk_1.DataType = 'double';
outport = get_param('rtwdemo_PCG_Eval_P2/fbk_1','PortHandles');
outport = outport.Outport;
set_param(outport,'MustResolveToSignalObject','on')

pos_cmd_two = Simulink.Signal;
pos_cmd_two.Dimensions = 1;
pos_cmd_two.DataType = 'double';
outport = get_param('rtwdemo_PCG_Eval_P2/PI_ctrl_2','PortHandles');
outport = outport.Outport;
set_param(outport,'MustResolveToSignalObject','on')

Configure Data Objects

Set the data type and storage class for each data object. To configure data object properties, inspect the appropriate tab in the Model Data Editor. Use the Change view drop-down list to show design properties (such as data type) and code generation settings (such as storage class).

In the Model Data Editor, inspect the Signals tab and click the Show/refresh additional information button to display information for the signal objects that the Data Object Wizard created. Then, configure these properties for the objects:

  • fbk_1: Data type double, storage class ImportedExtern

  • pos_cmd_two: Data type double, storage class ExportedGlobal

Alternatively, you can use these commands at the command prompt to configure the objects:

fbk_1.DataType = 'double';
fbk_1.StorageClass = 'ImportedExtern';

pos_cmd_two.DataType = 'double';
pos_cmd_two.StorageClass = 'ExportedGlobal';

Control File Placement of Parameter Data

With Embedded Coder®, you can control the file placement of definitions for parameters and constants. The example model writes the parameter definitions to the file eval_data.c.

To change the placement of parameter and constant definitions, set the data placement options for the model configuration. In the Configuration Parameters dialog box, configure the options on the Code Generation > Code Placement pane.

In the example model, inspect the Code Placement pane in the Configuration Parameters dialog box. The model places data definitions in the file eval_data.c and declarations in the file eval_data.h.

Declare Block Parameters Tunable or Inlined

You can control the default tunability of block parameters in the generated code by setting model configuration parameter Default parameter behavior.

For the example model, the default parameter behavior is set to Inlined. By default, block parameters appear in the code as numeric literal values, not as variables stored in memory. You can use Simulink.Parameter objects to override inlining and preserve tunability for individual parameters.

Enable Signal Data Objects in Generated Code

Make sure that the signal data objects (Simulink.Signal) appear in the generated code. For individual signal lines in the model, select the option Signal name must resolve to Simulink signal object to explicitly resolve the signal name to a Simulink.Signal object in a workspace or data dictionary. In the Model Data Editor, set Change view to Code and select the check box in the Resolve column.

Alternatively, you can select the option for all signals in a model. At the command prompt, use the disableimplicitsignalresolution function.

View Data Objects in Generated Code

Generate code from the example model.

rtwbuild('rtwdemo_PCG_Eval_P2');
### Starting build procedure for: rtwdemo_PCG_Eval_P2
### Successful completion of code generation for: rtwdemo_PCG_Eval_P2

Build Summary

Top model targets built:

Model                Action          Rebuild Reason                                    
=======================================================================================
rtwdemo_PCG_Eval_P2  Code generated  Code generation information file does not exist.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 24.181s

Create a code generation report to more easily view the generated files. In the Simulink Editor, select model configuration parameter Open Model Report.

You can access these generated files:

  • rtwdemo_PCG_Eval_P2.c: Defines the step and initialization functions. Uses the defined data objects.

  • eval_data.c: Assigns initial values to the defined parameters. The model configuration specifically sets the file name.

  • eval_data.h: Provides extern declarations of parameter data. The model configuration specifically sets the file name.

  • ert_main.c: Defines scheduling functions.

  • rtwdemo_PCG_Eval_P2.h: Contains the type definitions of the default structures that store signal, state, and parameter data. Due to the data object settings, some data appear in eval_data.c instead.

  • PCG_Eval_p2_private.h: Declares private (local) data for the generated functions.

  • rtwdemo_PCG_Eval_P2_types.h: Declares the real-time model data structure.

  • rtwtypes.h: Provides mapping to the data types that Simulink® Coder™ defines (typedef). Used for integration with external systems.

For example, view the file eval_data.c, which allocates const memory for global variables that correspond to the Simulink.Parameter objects in the base workspace.

cfile = fullfile('rtwdemo_PCG_Eval_P2_ert_rtw','eval_data.c');
rtwdemodbtype(cfile,'/* Exported data definition */','* [EOF]',1,1)
/* Exported data definition */

/* Const memory section */
/* Definition for custom storage class: Const */
const real_T I_Gain = -0.03;
const real_T I_InErrMap[9] = { -1.0, -0.5, -0.25, -0.05, 0.0, 0.05, 0.25, 0.5,
  1.0 } ;

const real_T I_OutMap[9] = { 1.0, 0.75, 0.6, 0.0, 0.0, 0.0, 0.6, 0.75, 1.0 } ;

const real_T P_Gain = 0.74;
const real_T P_InErrMap[7] = { -1.0, -0.25, -0.01, 0.0, 0.01, 0.25, 1.0 } ;

const real_T P_OutMap[7] = { 1.0, 0.25, 0.0, 0.0, 0.0, 0.25, 1.0 } ;

/*
 * File trailer for generated code.
 *
 * [EOF]

View the code algorithm in the model step function in the file rtwdemo_PCG_Eval_P2.c. The algorithm uses the data object names directly.

cfile = fullfile('rtwdemo_PCG_Eval_P2_ert_rtw','rtwdemo_PCG_Eval_P2.c');
rtwdemodbtype(cfile,'/* Model step function */',...
    '/* Sum: ''<S2>/Sum3'' incorporates:',1,0);
/* Model step function */
void rtwdemo_PCG_Eval_P2_step(void)
{
  real_T Discrete_Time_Integrator1;
  real_T Discrete_Time_Integrator1_i;
  real_T rtb_IntegralGainShape;

  /* Sum: '<S2>/Sum2' incorporates:
   *  Inport: '<Root>/fbk_1'
   *  Inport: '<Root>/pos_rqst'
   */
  rtb_IntegralGainShape = *pos_rqst - fbk_1;

  /* DiscreteIntegrator: '<S2>/Discrete_Time_Integrator1' */
  if (rtwdemo_PCG_Eval_P2_DWork.Discrete_Time_Integrator1_SYSTE != 0) {
    /* DiscreteIntegrator: '<S2>/Discrete_Time_Integrator1' */
    Discrete_Time_Integrator1 =
      rtwdemo_PCG_Eval_P2_DWork.Discrete_Time_Integrator1_DSTAT;
  } else {
    /* DiscreteIntegrator: '<S2>/Discrete_Time_Integrator1' incorporates:
     *  Gain: '<S2>/Int Gain1'
     *  Lookup_n-D: '<S2>/Integral  Gain Shape'
     *  Lookup_n-D: '<S3>/Integral  Gain Shape'
     *  Product: '<S2>/Product3'
     */
    Discrete_Time_Integrator1 = I_Gain * look1_binlx(rtb_IntegralGainShape,
      (&(I_InErrMap[0])), (&(I_OutMap[0])), 8U) * rtb_IntegralGainShape * 0.001
      + rtwdemo_PCG_Eval_P2_DWork.Discrete_Time_Integrator1_DSTAT;
  }

  /* End of DiscreteIntegrator: '<S2>/Discrete_Time_Integrator1' */

Without storage classes and other code generation settings that the data objects specify, the generated code:

  • Inlines invariant signals and block parameters when you select these optimizations in the model configuration parameters

  • Places model input and output signals, block states, and tunable parameters in global data structures

  • Does not create global variables that correspond to MATLAB® variables

In contrast, the example code shows that most of the default data structures have been replaced with user-defined data objects. For example, these signals and parameters appear as global variables in the code:

  • pos_rqst

  • fbk_1

  • I_Gain

  • I_OutMap

  • I_InErrMap

However, the local variable rtb_IntegralGainShape and the state variable rtwdemo_PCG_Eval_P2_DWork.Discrete_Time_Integrator1_DSTAT still use code generator data structures. Data objects do not exist for these entities.

Store Model Data in Data Dictionary

When you end your MATLAB session, variables that you create in the base workspace do not persist. To permanently store data objects and bus objects, consider linking the model to a data dictionary.

  1. In the example model, on the Modeling tab, under Design, click Link to Data Dictionary.

  2. In the Model Properties dialog box, click New.

  3. In the Create a New Data Dictionary dialog box, set File name to rtwdemo_PCG_Eval_dict and click Save.

  4. In the Model Properties dialog box, click Apply.

  5. Click Migrate data.

  6. Click Migrate in response to message about copying referenced variables.

  7. Click OK.

Alternatively, to manually migrate the objects into a data dictionary, you can use programmatic commands:

% Create a list of the variables and objects that the target
% model uses.
usedVars = {Simulink.findVars('rtwdemo_PCG_Eval_P2').Name};
% Create a new data dictionary in your current folder. Link the model to
% this new dictionary.
dictObj = Simulink.data.dictionary.create('rtwdemo_PCG_Eval_dict.sldd');
set_param('rtwdemo_PCG_Eval_P2','DataDictionary','rtwdemo_PCG_Eval_dict.sldd')
% Import only the target variables from the base workspace to the data
% dictionary.
importFromBaseWorkspace(dictObj,'clearWorkspaceVars',true,'varList',usedVars);
saveChanges(dictObj);

The data dictionary permanently stores the objects that the model uses. To view the contents of the dictionary, click the model data badge in the lower-left corner of the model, then click the Data Dictionary link. In the Model Explorer Model Hierarchy pane, select the Design Data node.

The separation of data from the model provides these benefits:

One model, multiple data sets

  • Use of different data types to change the targeted hardware (for example, for floating-point and fixed-point targets)

  • Use of different parameter values to change the behavior of the control algorithm (for example, for reusable components with different calibration values)

Multiple models, one data set

  • Sharing of data between Simulink® models in a system

  • Sharing of data between projects (for example, transmission, engine, and wheel controllers might all use the same CAN message data set)

Related Topics