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:
On the Modeling tab, click Model Data Editor.
In the Model Data Editor, select the Data Stores tab.
Begin editing the name of the target Data Store Memory block by clicking the corresponding row in the Name column.
Next to the name, click the button and select Create and Resolve.
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 resultingSimulink.Signal
object.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.
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.
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
:
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 |
---|---|---|
| In
typedef struct D_Work_tag { real_T myData; } D_Work; In
/* Block states (auto storage) */ D_Work model_DWork; |
model_DWork.myData = rtb_SineWave; |
| In /* Exported block states */ real_T myData; In
extern real_T myData; |
myData = rtb_SineWave; |
| In
extern real_T myData; |
myData = rtb_SineWave; |
| In
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.
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 following execution order applies:
The block Data Store Read buffers the current value of the data store
A
at its output.The block Abs1 uses the buffered output of Data Store Read.
The block Data Store Write updates the data store.
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.
In this example, the following execution order applies:
The block Data Store Read buffers the current value of the data store
A
at its output.Atomic Subsystem executes.
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.
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 classFileScope
. For more information aboutFileScope
and other storage classes, see Choose Storage Class for Controlling Data Representation in Generated Code.
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.
Open the Type Editor. On the Modeling tab, in the Design gallery, click Type Editor.
Define a new bus type
Raw_BTU_Rate
with one element for each of the three target signals. Name the elementsBioBTU
,GasBTU
, andCoalBTU
.At the top level of the example model, add a Data Store Memory block.
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.On the Modeling tab, click Model Data Editor.
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
.For the new Data Store Memory block, use the Name column to set the data store name to
Raw_BTU_Data
.In the Code Mappings editor, on the Data Stores tab, apply the storage class
ExportedGlobal
toRaw_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.
Open the Biomass Calc subsystem.
Delete the Data Store Memory block
BioBTURate
.In the block dialog box for the Data Store Write block, set Data store name to
Raw_BTU_Data
.On the Element Assignment tab, under Signals in the bus, expand the contents of the data store
Raw_BTU_Data
. Click the elementBioBTU
, and then click Select. Click OK.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 elementCoalBTU
.
Generate Code with Data Store Structure
Generate code for the example model.
In the code generation report, view the file
BusStructInCode_types.h
. The code defines a structure that corresponds to the bus typeRaw_BTU_Rate
.typedef struct { real_T BioBTU; real_T GasBTU; real_T CoalBTU; } Raw_BTU_Rate;
View the file
BusStructInCode.c
. The code represents the data store with a global variableRaw_BTU_Data
of the structure typeRaw_BTU_Rate
. In the model step function, the code assigns the data from the calculated signals to the fields of the global variableRaw_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.