Main Content

Data Stores in Generated Code

About Data Stores

A data store contains data that is accessible in a model hierarchy at or below the level in which the data store is defined. Data stores can allow subsystems and referenced models to share data without having to use I/O ports to pass the data from level to level. See Data Stores with Data Store Memory Blocks for information about data stores in Simulink®. This section provides additional information about data store code generation.

Generate Code for Data Store Memory Blocks

To control the code generated for a Data Store Memory block, apply a storage class to the data store. You can associate a Data Store Memory block with a signal object that you store in a workspace or data dictionary, and control code generation for the block by applying the storage class to the object:

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

  2. In the Model Data Editor, select the Data Stores tab.

  3. Begin editing the name of the target Data Store Memory block by clicking the corresponding row in the Name column.

  4. Next to the name, click the button Three vertical dots. and select Create and Resolve.

  5. In the Create New Data dialog box, set Value to Simulink.Signal. Optionally, use the Location drop-down list to choose a workspace for storing the resulting Simulink.Signal object.

  6. Click Create. The Simulink.Signal object, which has the same name as the data store, appears in the target workspace. Simulink selects the block parameter Data store name must resolve to Simulink signal object.

    When the property dialog box for the object opens, click OK.

  7. In the Simulink Coder or Embedded Coder app, open the Code Mappings editor. In the C Code tab, select Code Interface > Individual Element Code Mappings.

  8. On the Data Stores tab, apply the target storage class.

Note

When a Data Store Memory block is associated with a signal object, the mapping between the Data store name and the signal object name must be one-to-one. If two or more identically named entities map to the same signal object, the name conflict is flagged as an error at code generation time. See Resolve Conflicts in Configuration of Signal Objects for Code Generation for more information.

Storage Classes for Data Store Memory Blocks

You can control how Data Store Memory blocks in your model are stored and represented in the generated code by assigning storage classes and type qualifiers. You do this in almost exactly the same way you assign storage classes and type qualifiers for block states.

Data Store Memory blocks, like block states, have Auto storage class by default, and their memory is stored within the DWork vector. The symbolic name of the storage location is based on the data store name.

You can generate code from multiple Data Store Memory blocks that have the same data store name, subject to the following restriction: at most one of the identically named blocks can have a storage class other than Auto. An error is reported if this condition is not met.

For blocks with Auto storage class, the code generator produces a unique symbolic name for each block to avoid name clashes. For Data Store Memory blocks with storage classes other than Auto, the generated code uses the data store name as the symbol.

In the following model, a Data Store Write block writes to memory declared by the Data Store Memory block myData:

A model that contains a Data Store Memory block with a store named "myData". A Sine Wave block connects to a Data Store Write block that uses the "myData" store.

To control the storage declaration for a Data Store Memory block, in the coder app, use the Code Mappings editor. On the Data Stores tab, select a Storage Class for the block.

Data Store Memory blocks are nonvirtual because code is generated for their initialization in .c and .cpp files and their declarations in header files. The following table shows how the code generated for the Data Store Memory block in the preceding model differs for different storage classes. The table gives the variable declarations and MdlOutputs code generated for the myData block.

Storage Class

Declaration

Code

Auto or Model default (when Code Mapping Editor specifies Default storage class)

In model.h

typedef struct 
D_Work_tag 
{
  real_T myData;
} 
D_Work;

In model.c or model.cpp

/* Block states (auto storage) */
D_Work model_DWork;
model_DWork.myData = 
  rtb_SineWave;

ExportedGlobal

In model.c or model.cpp

/* Exported block states */
real_T myData; 

In model.h

extern real_T myData;
myData = rtb_SineWave;

ImportedExtern

In model_private.h

extern real_T myData;
myData = rtb_SineWave;

ImportedExternPointer

In model_private.h

extern real_T *myData;
(*myData) = rtb_SineWave;

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

For ERT models, you can preserve dimensions of multidimensional arrays in data stores. For more information, see Preserve Dimensions of Multidimensional Arrays in Generated Code (Embedded Coder).

Data Store Buffering in Generated Code

A Data Store Read block is a nonvirtual block that copies the value of the data store to its output buffer when it executes. Since the value is buffered, downstream blocks connected to the output of the data store read utilize the same value, even if a Data Store Write block updates the data store in between execution of two of the downstream blocks.

The model uses blocks whose priorities have been modified to achieve a particular order of execution. It has a Data Store Memory block with the store A, and two groups of blocks that use this memory store:

  • An input port connects to a Data Store Write block that writes to the store A.

  • A Data Store Read block that reads from the store A. The Data Store Read block is connected to the inputs of two Absolute Value blocks. Each Absolute Value block connects to an output port.

The code generated from the model.

The following execution order applies:

  1. The block Data Store Read buffers the current value of the data store A at its output.

  2. The block Abs1 uses the buffered output of Data Store Read.

  3. The block Data Store Write updates the data store.

  4. The block Abs uses the buffered output of Data Store Read.

Because the output of Data Store Read is a buffer, both Abs and Abs1 use the same value: the value of the data store at the time that Data Store Read executes.

This model contains a Data Store Memory block with the store A, and two groups of blocks that use this memory store:

  • A Constant block connects to a Data Store Write block that writes to the store A.

  • A Constant block that inputs to an Atomic Subsystem block. A Sum block adds the output of the Atomic Subsystem block to the output of a Data Store Read block that reads from the store A. The output of the Sum block connects to an output port.

The code generated from the model.

In this example, the following execution order applies:

  1. The block Data Store Read buffers the current value of the data store A at its output.

  2. Atomic Subsystem executes.

  3. The Sum block adds the output of Atomic Subsystem to the output of Data Store Read.

Simulink assumes that Atomic Subsystem might update the data store, so Simulink buffers the data store. Atomic Subsystem executes after Data Store Read buffers its output, and the buffer provides a way for the Sum block to use the value of the data store as it was when Data Store Read executed.

In some cases, the code generator determines that it can optimize away the output buffer for a Data Store Read block, and the generated code refers to the data store directly, rather than a buffered value of it. The model in this example demonstrates it.

This model also contains a Data Store Memory block with the store A, and two groups of blocks that use this memory store:

  • An input port connects to a Data Store Write block that writes to the store A.

  • A Data Store Read block reads from the store A and connects to an Absolute Value block that outputs to an output port.

The code generated from the model.

In the generated code, the argument of the fabs() function is the data store A rather than a buffered value.

Data Stores Shared by Instances of a Reusable Model

You can use a data store to share a piece of data between the instances of a reusable referenced model (see Share Data Among Referenced Model Instances) or a model that you configure to generate reentrant code (by setting the configuration parameter Code interface packaging to Reusable function). If you implement the data store as a Data Store Memory block and select the Share across model instances parameter:

  • By default, the data store appears in the generated code as a separate global symbol.

  • If you have Embedded Coder®, to restrict access such that only the code generated from the model can use the data store, configure the data store to appear as static by applying the storage class FileScope. For more information about FileScope and other storage classes, see Choose Storage Class for Controlling Data Representation in Generated Code (Embedded Coder).

Structures in Generated Code Using Data Stores

If you use more than one data store to provide global access to multiple signals in generated code, you can combine the signals into a single structure variable by using one data store. This combination of signal data can help you integrate the code generated from a model with other existing code that requires the data in a structure format.

This example shows how to store several model signals in a structure in generated code using a single data store. To store multiple signals in a data store, you configure the data store to accept a composite signal, such as a nonvirtual bus or an array of nonvirtual buses.

Explore Example Model

Open the example model BusStructInCode.

open_system('BusStructInCode')

The model contains three subsystems that perform calculations on the inputs from the top level of the model. In each subsystem, a Data Store Memory block stores an intermediate calculated signal.

Generate code with the model. In the code generation report, view the file BusStructInCode.c. The code defines a global variable for each data store.

real_T BioBTURate;
real_T CoalBTURate;
real_T GasBTURate;

Suppose that you want to integrate code generated from the example model with other existing code. Suppose also that the existing code requires access to data from the three data stores in a single structure variable. You can use a data store to assemble the target data in a structure in generated code.

Configure Data Store

Configure a data store to contain multiple signals by creating a bus type to use as the data type of the data store. Define the bus type using the same hierarchy of elements as the structure that you want to appear in generated code.

  1. Open the Type Editor. On the Modeling tab, in the Design gallery, click Type Editor.

  2. Define a new bus type Raw_BTU_Rate with one element for each of the three target signals. Name the elements BioBTU, GasBTU, and CoalBTU.

    The newly created bus with its element list expanded.

  3. At the top level of the example model, add a Data Store Memory block.

  4. While you have the Data Store Memory block selected, in the Type Editor, right-click the bus type Raw_BTU_Rate. Then, select Assign type to selected blocks and ports.

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

  6. In the Model Data Editor, inspect the Data Stores tab. The Data Type column shows that the data type of the new data store is Bus: Raw_BTU_Rate.

  7. For the new Data Store Memory block, use the Name column to set the data store name to Raw_BTU_Data.

  8. In the Code Mappings editor, on the Data Stores tab, apply the storage class ExportedGlobal to Raw_BTU_Data.

Write to Data Store Elements

To write to a specific element of a data store, use a Data Store Write block. On the Element Assignment tab in the dialog box, you can specify to write to a single element, a collection of elements, or the entire contents of a data store.

  1. Open the Biomass Calc subsystem.

  2. Delete the Data Store Memory block BioBTURate.

  3. In the block dialog box for the Data Store Write block, set Data store name to Raw_BTU_Data.

  4. On the Element Assignment tab, under Signals in the bus, expand the contents of the data store Raw_BTU_Data. Click the element BioBTU, and then click Select. Click OK.

    Sink Block Parameters dialog with the revised values.

  5. Modify the Gas Calc and Coal Calc subsystems similarly.

    • Delete the Data Store Memory block in each subsystem.

    • In each Data Store Write block dialog box, set Data store name to Raw_BTU_Data.

    • In the Gas Calc subsystem, use the Data Store Write block to write to the data store element GasBTU. In the Coal Calc subsystem, write to the element CoalBTU.

Generate Code with Data Store Structure

  1. Generate code for the example model.

  2. In the code generation report, view the file BusStructInCode_types.h. The code defines a structure that corresponds to the bus type Raw_BTU_Rate.

    typedef struct {
      real_T BioBTU;
      real_T GasBTU;
      real_T CoalBTU;
    } Raw_BTU_Rate;
    
  3. View the file BusStructInCode.c. The code represents the data store with a global variable Raw_BTU_Data of the structure type Raw_BTU_Rate. In the model step function, the code assigns the data from the calculated signals to the fields of the global variable Raw_BTU_Data.

Code Generation for Data Store References

You can configure a Data Store Memory block to define a reference to a Data Store Memory block in a higher level in the model hierarchy. For more information, see Access Data Store at Higher Level in Model Hierarchy.

The code generator implements a data store reference as a pointer that is an element of a structure. The ancestor model that contains the referenced Data Store Memory block initializes this pointer to the address of that referenced data store.

Guidelines for configuring data store references for code generation include:

  • The default storage class of a data store reference must be Default or a structured storage class defined in the Embedded Coder Dictionary.

  • The individual storage class of a data store reference must be Auto, Model default, or a structured storage class defined in the Embedded Coder Dictionary.

  • The individual or default storage class of a data store with a data store reference must be addressable.

  • If your model has the configuration parameter Code interface packaging set to C++ class, the visibility of internal data must be set to public if the model contains:

    • Data store references

    • Model blocks with data store references

    In the Code Mappings editor, on the Data tab, select public in the Data Visibility column for the Signals, states, and internal data category.

Related Topics