Main Content

Create Data Interface Configuration Programmatically

This example shows how to programmatically create a data interface configuration in an Embedded Coder Dictionary. To automate the creation of an Embedded Coder Dictionary in a script or to create a data interface configuration programmatically, use this programming interface.

In this example, you create a shared Embedded Coder Dictionary in an SLDD file so that multiple models can use the code definitions. In the data interface configuration, you create code definitions that control the naming and placement of global data and functions. You also use a memory section definition that includes a pragma to place the data and functions in fast memory. To verify the architecture that you set up through the code definitions, apply the definitions to a model, and then generate code.

Set Up Embedded Coder Dictionary

Create a Simulink data dictionary (SLDD file) to store the code definitions. Storing the definitions in a shared SLDD file enables you to use the definitions in multiple models.

dictionaryFile = Simulink.data.dictionary.create('codeDefinitions.sldd');

Create an Embedded Coder Dictionary in the SLDD file. When you create the dictionary, represent it with a coder.Dictionary object. Use the object to perform operations on the entire Embedded Coder Dictionary and to access sections of it.

codeDictionary = coder.dictionary.create('codeDefinitions.sldd');

The coder.Dictionary object contains three coder.dictionary.Section objects that represent the sections of an Embedded Coder Dictionary: Storage Classes, Memory Sections, and Function Customization Templates. A coder.dictionary.Section object contains coder.dictionary.Entry objects that represent the definitions in that section. To interact with a definition and access its properties, use the coder.dictionary.Entry object that represents it.

new.png

When you create an Embedded Coder Dictionary, the dictionary loads definitions from the Simulink package so that the Embedded Coder Dictionary then contains built-in definitions. If you have custom code definitions stored in a package, load that package to the dictionary. When you use the Embedded Coder Dictionary to configure the code interface for a model, you can apply definitions from packages that you loaded.

Memory Sections

To create memory section definitions, add entries to the Memory Sections section. Storage classes and function customization templates residing in the same dictionary can use these memory sections. For this example, add a memory section named FastMem, which allocates memory by using a pragma. When you apply the memory section to a storage class or function template, their generated definitions and declarations are in the memory section.

memorySections = getSection(codeDictionary,'MemorySections');
msFast = addEntry(memorySections,'FastMem');
set(msFast,'PreStatement','#pragma begin FAST');
set(msFast,'Comment','/*Fast onchip RAM*/');
set(msFast,'PostStatement','#pragma end FAST');

Storage Classes

For this example, create a storage class named ExportToPrivateHeader, which generates a global variable declaration in the header file $R_private.h and definition in $R_private.c. The token $R signifies the name of the root model for which you generate code. To use a variable in external code, apply this storage class to the data element and include the header file in the external code.

storageClasses = getSection(codeDictionary,'StorageClasses');
exportToPrivateH = addEntry(storageClasses,'ExportToPrivateHeader');
set(exportToPrivateH,'HeaderFile','$R_private.h','DataScope','Exported');

To apply the memory section FastMem to the storage class, use the coder.dictionary.Entry object that represents the memory section.

set(exportToPrivateH,'MemorySection',msFast);

Create another storage class definition named ImportFromHeader for data that external code defines. Because the storage class has 'DataScope' set to 'Imported', the generated code reads and writes to the variable defined by your external code.

importFromH = addEntry(storageClasses,'ImportFromHeader');
set(importFromH,'DataScope','Imported','HeaderFile','$R_input.h','DataInit','Dynamic');

Function Customization Templates

To create a function customization template, create another coder.dictionary.Section object that represents the Function Customization Templates section. Add an entry to this section, which represents a definition that controls the appearance of generated entry-point functions. Apply the memory section FastMem to the function template.

functionTemplates = getSection(codeDictionary,'FunctionCustomizationTemplates');
fcGlobal = addEntry(functionTemplates,'GlobalFunctions');
set(fcGlobal,'FunctionName','$R$N');
set(fcGlobal,'MemorySection',msFast);
saveChanges(dictionaryFile);

Apply Code Generation Definitions

Confirm the settings of your code definitions by applying them to a model and generating code. You can configure the model code interface to use the code definitions programmatically or by using the Code Mappings editor.

Open the model ConfigurationInterface and attach the data dictionary that you created to the model.

open_system('ConfigurationInterface')
set_param('ConfigurationInterface','EmbeddedCoderDictionary','codeDefinitions.sldd');

model_rtwdemo_configinterface.png

The model contains four inports. In1 is configured to use the storage class ImportFromFile. For this example, you configure the other inports to read data from external code by default. The model also contains state data for the Delay block that you configure to be accessible by external code. When you apply the storage classes ImportFromHeader and ExportToPrivateHeader to the model elements, the generated code conforms to this architecture.

To configure the model with the code generation definitions, get the code mapping for the model.

cm = coder.mapping.api.get('ConfigurationInterface');

Use the code mapping to configure data to use your storage class definitions. For the state data in the Delay block, specify the storage class ExportToPrivateHeader.

setState(cm,'ConfigurationInterface/Delay','StorageClass','ExportToPrivateHeader');

Specify the default storage class ImportFromHeader for root inport data. For inports of the model, by default, the generated code uses data that external code defines in the file ConfigurationInterface_input.c. To compile and link this external file when you build the generated code, set the configuration parameter CustomSource to ConfigurationInterface_input.c.

setDataDefault(cm,'Inports','StorageClass','ImportFromHeader');
set_param('ConfigurationInterface','CustomSource','ConfigurationInterface_input.c');

Specify the function customization template GlobalFunctions for the default code generation of execution functions.

setFunctionDefault(cm,'Execution','FunctionCustomizationTemplate','GlobalFunctions');

Verify that the code definitions reflect your specifications.

  1. Open the Code Mappings editor. In the Simulink Editor, open the Embedded Coder app. On the C Code tab, select Code Interface > Default Code Mappings.

  2. In the Code Mappings editor, on the Data Defaults tab, expand the Inports and Outports section. The Inports category shows the storage class ImportFromHeader. On the Inports tab, the inports In2, In3, and In4 use the model default storage class ImportFromHeader. The inport In1 has a different storage class specified, ImportFromFile. This setting overrides the default storage class so that In1 reads data from a different external file.

  3. On the Signals/States tab, the state X shows the storage class ExportToPrivateHeader.

  4. On the Function Defaults tab, the Execution category shows the template GlobalFunctions.

  5. Open the Embedded Coder Dictionary. On the C Code tab, select Code Interface > Embedded Coder Dictionary. The code definitions that you added to the dictionary appear in the tabs.

Generate and Verify the Code

Generate code for the model.

slbuild('ConfigurationInterface', 'generateCodeOnly', true);
### Starting build procedure for: ConfigurationInterface
### Successful completion of code generation for: ConfigurationInterface

Build Summary

Top model targets:

Model                   Build Reason                                         Status           Build Duration
============================================================================================================
ConfigurationInterface  Information cache folder or artifacts were missing.  Code generated.  0h 0m 15.662s 

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

The header file ConfigurationInterface_private.h defines the Delay block state data according to the storage class ExportToPrivateHeader. External code can access this state data.

file = fullfile('ConfigurationInterface_ert_rtw','ConfigurationInterface_private.h');
coder.example.extractLines(file,'/* Storage class', ...
   '#endif');
/* Storage class 'ExportToPrivateHeader' */

/*Fast onchip RAM*/

#pragma begin FAST

extern MYTYPE X;                       /* '<Root>/Delay' */

#pragma end FAST

Because the inports use the storage class specifications, the generated header files do not define inport data. Instead, the generated file ConfigurationInterface.h includes the external header file that you specified in the storage class definition. The generated code reads inport data from the variables defined in the external file ConfigurationInterface_inputs.c.

In the generated source file ConfigurationInterface.c, the execution (step) function reflects the settings of the function customization template GlobalFunctions. Because the template GlobalFunctions uses the memory section FastMem, the execution function is stored in the memory section and reflects the prestatement, poststatement, and comments that you set in the FastMem definition.

file2 = fullfile('ConfigurationInterface_ert_rtw','ConfigurationInterface.c');
coder.example.extractLines(file2,'/* Model step function */', ...
   '/* Logic:');
/* Model step function */

/*Fast onchip RAM*/
#pragma begin FAST

void ConfigurationInterface_step(void)
{

After you configure the Embedded Coder Dictionary, you can share the dictionary with your team or organization. You and other users can apply your definitions to multiple models so that the models use the same software architecture.

See Also

| | | | |

Related Topics