Generate Source and Header Files with a Custom File Processing (CFP) Template

This example shows you the process of generating a simple source (.c or .cpp) and header (.h) file using the example CFP template. Then, it examines the template and the code generated by the template.

The example CFP template, matlabroot/toolbox/rtw/targets/ecoder/example_file_process.tlc, demonstrates some of the capabilities of the code template API, including

  • Generation of simple source (.c or .cpp) and header (.h) files

  • Use of buffers to generate file sections for includes, functions, and so on

  • Generation of includes, defines, into the standard generated files (for example, model.h)

  • Generation of a main program module

Generate Code with a CFP Template

This section sets up a CFP template and configures a model to use the template in code generation. The template generates (in addition to the standard model files) a source file (timestwo.c or .cpp) and a header file (timestwo.h).

Follow the steps below to become acquainted with the use of CFP templates:

  1. Copy the example CFP template, matlabroot/toolbox/rtw/targets/ecoder/example_file_process.tlc, to a folder outside of the MATLAB® folder structure (that is, not under matlabroot). If the folder is not on the MATLAB path or the TLC path, then add it to the MATLAB path. It is good practice to locate the CFP template in the same folder as your system target file, which is on the TLC path.

  2. Rename the copied example_file_process.tlc to test_example_file_process.tlc.

  3. Open test_example_file_process.tlc into the MATLAB editor.

  4. Uncomment the following line:

    %%  %assign ERTCustomFileTest = TLC_TRUE

    It now reads:

      %assign ERTCustomFileTest = TLC_TRUE

    If ERTCustomFileTest is not assigned as shown, the CFP template is ignored in code generation.

  5. Save your changes to the file. Keep test_example_file_process.tlc open, so you can refer to it later.

  6. Open the rtwdemo_udt model.

  7. Open the Simulink® Model Explorer. Select the active configuration set of the model, and open the Code Generation pane of the active configuration set.

  8. On the Templates tab, in the File customization template field, specify test_example_file_process.tlc. This is the file you previously edited and is now the specified CFP template for your model.

  9. On the General tab, select the Generate code only check box.

  10. Click Apply.

  11. In the model window, press Ctrl+B. During code generation, notice the following message in the Diagnostic Viewer:

    Warning:  Overriding example ert_main.c!
    

    This message is displayed because test_example_file_process.tlc generates the main program module, overriding the default action of the ERT target. This is explained in greater detail below.

  12. The rtwdemo_udt model is configured to generate an HTML code generation report. After code generation is complete, view the report.

    Notice that the Generated Code list contains the following files:

    • Under Main file, ert_main.c.

    • Under Other files, timestwo.c and timestwo.h.

    The files were generated by the CFP template. The next section examines the template to learn how this was done.

  13. Keep the model, the code generation report, and the test_example_file_process.tlc file open so you can refer to them in the next section.

Analysis of the Example CFP Template and Generated Code

This section examines excerpts from test_example_file_process.tlc and some of the code it generates. Refer to the comments in matlabroot/rtw/c/tlc/mw/codetemplatelib.tlc while reading the following discussion.

Generating Code Files

Source (.c or .cpp) and header (.h) files are created by calling LibCreateSourceFile, as in the following excerpts:

%assign cFile = LibCreateSourceFile("Source", "Custom", "timestwo")
...
%assign hFile = LibCreateSourceFile("Header", "Custom", "timestwo")

Subsequent code refers to the files by the file reference returned from LibCreateSourceFile.

File Sections and Buffers

The code template API lets you partition the code generated to each file into sections, tagged as Definitions, Includes, Functions, Banner, and so on. You can append code to each section as many times as required. This technique gives you a great deal of flexibility in the formatting of your custom code files.

Subsections Defined for Built-In Sections describes the available file sections and their order in the generated file.

For each section of a generated file, use %openfile and %closefile to store the text for that section in temporary buffers. Then, to write (append) the buffer contents to a file section, call LibSetSourceFileSection, passing in the desired section tag and file reference. For example, the following code uses two buffers (typesBuf and tmpBuf) to generate two sections (tagged "Includes" and "Functions") of the source file timestwo.c or .cpp (referenced as cFile):

%openfile typesBuf

#include "rtwtypes.h"

%closefile typesBuf

%<LibSetSourceFileSection(cFile,"Includes",typesBuf)>

 %openfile tmpBuf

 /* Times two function */
 real_T timestwofcn(real_T input) {
   return (input * 2.0);
}

%closefile tmpBuf

%<LibSetSourceFileSection(cFile,"Functions",tmpBuf)>

These two sections generate the entire timestwo.c or .cpp file:

#include "rtwtypes.h"

/* Times two function */
FLOAT64 timestwofcn(FLOAT64 input)
{
  return (input * 2.0);
}

Adding Code to Standard Generated Files

The timestwo.c or .cpp file generated in the previous example was independent of the standard code files generated from a model (for example, model.c or .cpp, model.h, and so on). You can use similar techniques to generate custom code within the model files. The code template API includes functions to obtain the names of the standard models files and other model-related information. The following excerpt calls LibGetMdlPubHdrBaseName to obtain the name for the model.h file. It then obtains a file reference and generates a definition in the Defines section of model.h:

%% Add a #define to the model's public header file model.h

%assign pubName = LibGetMdlPubHdrBaseName()
%assign modelH  = LibCreateSourceFile("Header", "Simulink", pubName)

%openfile tmpBuf

 #define ACCELERATION 9.81

 %closefile tmpBuf

%<LibSetSourceFileSection(modelH,"Defines",tmpBuf)>

Examine the generated rtwdemo_udt.h file to see the generated #define directive.

Customizing Main Program Module Generation

Normally, the ERT target determines whether and how to generate an ert_main.c or .cpp module based on the settings of the Generate an example main program and Target operating system options on the Templates pane of the Configuration Parameters dialog box. You can use a CFP template to override the normal behavior and generate a main program module customized for your target environment.

To support generation of main program modules, two TLC files are provided:

  • bareboard_srmain.tlc: TLC code to generate an example single-rate main program module for a bareboard target environment. Code is generated by a single TLC function, FcnSingleTaskingMain.

  • bareboard_mrmain.tlc: TLC code to generate a multirate main program module for a bareboard target environment. Code is generated by a single TLC function, FcnMultiTaskingMain.

In the example CFP template file matlabroot/toolbox/rtw/targets/ecoder/example_file_process.tlc, the following code generates either a single- or multitasking ert_main.c or .cpp module. The logic depends on information obtained from the code template API calls LibIsSingleRateModel and LibIsSingleTasking:

%% Create a simple main.  Files are located in MATLAB/rtw/c/tlc/mw.

 %if LibIsSingleRateModel() || LibIsSingleTasking()
   %include "bareboard_srmain.tlc"
   %<FcnSingleTaskingMain()>
 %else
   %include "bareboard_mrmain.tlc"
   %<FcnMultiTaskingMain()>
 %endif

Note that bareboard_srmain.tlc and bareboard_mrmain.tlc use the code template API to generate ert_main.c or .cpp.

When generating your own main program module, you disable the default generation of ert_main.c or .cpp. The TLC variable GenerateSampleERTMain controls generation of ert_main.c or .cpp. You can directly force this variable to TLC_FALSE. The examples bareboard_mrmain.tlc and bareboard_srmain.tlc use this technique, as shown in the following excerpt from bareboard_srmain.tlc.

%if GenerateSampleERTMain
    %assign CompiledModel.GenerateSampleERTMain = TLC_FALSE
    %warning Overriding example ert_main.c!
%endif

Alternatively, you can implement a SelectCallback function for your target. A SelectCallback function is a MATLAB function that is triggered when you:

  • Load the model.

  • Update any configuration settings in the Configuration Parameters dialog box.

  • Build the model.

Your SelectCallback function should deselect and disable the Generate an example main program option. This prevents the TLC variable GenerateSampleERTMain from being set to TLC_TRUE.

See the rtwgensettings Structure section for information on creating a SelectCallback function.

The following code illustrates how to deselect and disable the Generate an example main program option in the context of a SelectCallback function.

slConfigUISetVal(hDlg, hSrc, 'GenerateSampleERTMain', 'off');
slConfigUISetEnabled(hDlg, hSrc, 'GenerateSampleERTMain',0);
hSrc.refreshDialog;

Note

Creation of a main program for your target environment requires some customization; for example, in a bareboard environment you need to attach rt_OneStep to a timer interrupt. It is expected that you will customize either the generated code, the generating TLC code, or both. See Guidelines for Modifying the Main Program and Guidelines for Modifying rt_OneStep for further information.

Generate a Custom Section

You can define custom tokens in a CGT file and direct generated code into an associated built-in section. This feature gives you additional control over the formatting of code within each built-in section. For example, you could add subsections to built-in sections that do not already define subsections. Custom sections must be associated with one of the built-in sections: Includes, Defines, Types, Enums, Definitions, Declarations, or Functions. To create custom sections, you must

  • Add a custom token to the code insertion section of your CGT file.

  • In your CFP file:

    • Assemble code to be generated to the custom section into a buffer.

    • Declare an association between the custom section and a built-in section, with the code template API function LibAddSourceFileCustomSection.

    • Emit code to the custom section with the code template API function LibSetSourceFileCustomSection.

The following code examples illustrate the addition of a custom token, Myincludes, to a CGT file, and the subsequent association of the custom section Myincludes with the built-in section Includes in a CFP file.

Note

If you have not already created custom CGT and CFP files for your model, copy the default template files matlabroot/toolbox/rtw/targets/ecoder/ert_code_template.cgt and matlabroot/toolbox/rtw/targets/ecoder/example_file_process.tlc to a work folder that is outside the MATLAB folder structure but on the MATLAB or TLC path, rename them (for example, add the prefix test_ to each file), and update the Templates pane of the Configuration Parameters dialog box to reference them.

First, add the token Myincludes to the code insertion section of your CGT file. For example:

%<Includes>
%<Myincludes>
%<Defines>
%<Types>
%<Enums>
%<Definitions>
%<Declarations>
%<Functions>

Next, in the CFP file, add code to generate include directives into a buffer. For example, in your copy of the example CFP file, you could insert the following section between the Includes section and the Create a simple main section:

%% Add a custom section to the model's C file model.c

%openfile tmpBuf
#include "moretables1.h"
#include "moretables2.h"
%closefile tmpBuf

%<LibAddSourceFileCustomSection(modelC,"Includes","Myincludes")>
%<LibSetSourceFileCustomSection(modelC,"Myincludes",tmpBuf)>

The LibAddSourceFileCustomSection function call declares an association between the built-in section Includes and the custom section Myincludes. Myincludes is a subsection of Includes. The LibSetSourceFileCustomSection function call directs the code in the tmpBuf buffer to the Myincludes section of the generated file. LibSetSourceFileCustomSection is syntactically identical to LibSetSourceFileSection.

In the generated code, the include directives generated to the custom section appear after other code directed to Includes.

#include "rtwdemo_udt.h"
#include "rtwdemo_udt_private.h"

/* #include "mytables.h" */
#include "moretables1.h"
#include "moretables2.h"

Note

The placement of the custom token in this example CGT file is arbitrary. By locating %<Myincludes> after %<Includes>, the CGT file specifies only that the Myincludes code appears after Includes code.

Custom Tokens

Custom tokens are automatically translated to TLC syntax as a part of the build process. To escape a token, that is to prepare it for normal TLC expansion, use the '!' character. For example, the token %<!TokenName> is expanded to %<TokenName> by the template conversion program. You can specify valid TLC code, including TLC function calls: %<!MyTLCFcn()>.