Create Code Definitions Programmatically

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

In this example, you create an Embedded Coder Dictionary in a Simulink data dictionary so that multiple models can share the code definitions. 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 to store the code definitions. Storing the definitions in a data dictionary enables you to use the definitions in multiple models by linking each model to the data dictionary as shown in Apply Code Generation Definitions.

dataDictionary = Simulink.data.dictionary.create('dataDictionary.sldd');

Create an Embedded Coder Dictionary in the data dictionary. 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('dataDictionary.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.

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 loaded packages.

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(dataDictionary);

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 rtwdemo_advsc and link the model to the data dictionary that you created.

rtwdemo_advsc
set_param('rtwdemo_advsc','DataDictionary','dataDictionary.sldd');

The model contains four inports that you configure to read data from external code. The model also contains state data for the Delay block and for the Stateflow chart 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, create a code mapping environment for the model.

coder.mapping.create('rtwdemo_advsc');

Use the code mapping to configure data to use your storage class definitions. For the default mapping of the internal data category, specify the storage class ExportToPrivateHeader.

coder.mapping.defaults.set('rtwdemo_advsc','InternalData','StorageClass','ExportToPrivateHeader');

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

coder.mapping.defaults.set('rtwdemo_advsc','Inports','StorageClass','ImportFromHeader');
set_param('rtwdemo_advsc','CustomSource','rtwdemo_advsc_input.c');

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

coder.mapping.defaults.set('rtwdemo_advsc','Execution','FunctionCustomizationTemplate','GlobalFunctions');

Verify that the code definitions reflect your specifications.

  1. In the Simulink Editor, open the Embedded Coder app. In the Code Mappings editor, on the Data Defaults tab, the Inports category shows the storage class ImportFromHeader. The Internal Data category shows the storage class ExportToPrivateHeader. On the Function Defaults tab, the Execution category shows the template GlobalFunctions.

  2. Open the 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.

evalc('rtwbuild(''rtwdemo_advsc'')');

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

file = fullfile('rtwdemo_advsc_ert_rtw','rtwdemo_advsc_private.h');
rtwdemodbtype(file,'/* Storage class', ...
    '#endif');
/* Storage class 'ExportToPrivateHeader' */

/*Fast onchip RAM*/

#pragma begin FAST

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

#pragma end FAST

Because the inports use the storage class ImportFromHeader, the generated header files do not define inport data. Instead, the generated file rtwdemo_advsc.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 rtwdemo_advsc_inputs.c.

In the generated source file rtwdemo_advsc.c, the execution 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('rtwdemo_advsc_ert_rtw','rtwdemo_advsc.c');
rtwdemodbtype(file2,'/* Model step function */', ...
    '/* Chart:');
/* Model step function */

/*Fast onchip RAM*/
#pragma begin FAST

void rtwdemo_advsc_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.

delete dataDictionary.sldd

See Also

| | | |

Related Topics