2. QGen Code Generator

2.1. Using qgenc

Code generation is performed using the qgenc executable which can be invoked in three methods:

_images/qgen_toolset.png
  1. From the Simulink GUI

    Code generation can be launched directly from within a Simulink model in the MATLAB GUI. This method is the easiest and most straightforward, however it is less flexible than the other methods in terms of the configuration options it provides.

  2. From the MATLAB Command Line

    Code generation can be launched by calling the qgen_build function in the MATLAB Command Line. This method allows configuring the code generation through arguments passed to qgen_build.

  3. From the System Command Line

    Code generation can be launched by running the qgenc executable from the system command line. This method requires a preliminary execution of the qgen_export_types MATLAB function to extract relevant information from the Simulink model and serialize it in a decoration file as will be explained in Producing the Decoration File. Once the decoration file is generated, qgenc no longer requires the MATLAB/Simulink environment which makes this method better suited for nightly and regression testing.

2.2. Prerequisites

Before performing code generation with QGen, it is necessary to ensure that the model is consistent and simulation can be run in Simulink with no errors.

2.4. Generating Code from the MATLAB Command Line

QGen can be invoked from the MATLAB command line using the qgen_build function. This requires QGen to be correctly setup in MATLAB following the instructions in Setting up QGen in MATLAB.

The qgen_build function performs the following main steps:

  1. Load the given model in Simulink
  2. Produce a decoration file using the qgen_export_types function in a manner similar to Producing the Decoration File.
  3. Invoke the qgenc executable.

As a result of step 1, Simulink itself is launched. Do not worry if you see the Simulink model being loaded upon the invocation of qgen_build.

2.4.1. Models with Externally Defined Constants

QGen supports models that use parameter values defined outside the model itself. Such values are defined in variables declared either in the MATLAB workspace, or in a separate .m file. When using a separate .m file, only variable definitions are supported but not statements such as if and for constructs.

2.4.2. Quick Examples

Here are some quick examples on how to invoke the qgen_build function.

Given an arbitrary model mymodel.mdl, the following invocation generates Ada code in the directory generated_code/ for the model mymodel.mdl. The --clean argument instructs QGen to delete all contents of the output directory generated_code/ before generating code.

>> qgen_build ('./mymodel.mdl', 'ada', '-o', './generated_code', '--clean')

Let us take the example in the directory speedometer, located in the $QGEN_INSTALL/share/qgen/examples/ directory. This example requires some global variables which hare defined in a separate file.

The following generates Ada code in the folder ./generated_code for speedometer.mdl and by using the speedometer_def.m MATLAB file to define global variables used by the model.

>> qgen_build ('./speedometer.mdl', 'ada', '-o', './generated_code', '-m', './speedometer_def.m',
               '--clean')
_images/qgen_build.png

In the following invocation the default output directory speedometer.mdl_generated/ will be used since the -o argument was not specified. The --incremental argument instructs QGen to keep the current content of the output directory, and only overwrite files as they are generated.

>> qgen_build ('./speedometer.mdl', 'ada', '--incremental', '-m', './speedometer_def.m')

When running from within MATLAB, it is possible to use the -v switch to get the complete output of loading and analyzing the Simulink model. This is particularly useful to detect the use of call backs in models, blocks or port which may disrupt the use of QGen.

If global variables used by the model are defined in several m-files, it is possible to include them all as follows:

>> qgen_build
       ('./my_model.mdl', 'ada', '-m', {'./my_model_param.m',
        './my_model_def.m'}, '--incremental', '--export-ws')

The command above loads my_model_param.m and my_model_def.m into MATLAB workspace, export the contents of the workspace to file my_model_base_ws.m and executes qgenc with command line qgenc my_model.mdl --typing my_model_types.txt --matlab my_model_base_ws.m

2.4.3. qgen_build Arguments

The qgen_build script has two required inputs and a number of optional parameters:

qgen_build (<mdl path>, <lang> [, <options>]);
  • mdl_path

    Name of the mdl or slx file optionally with path (absolute or relative).

  • lang

    Name of the language. Accepted values are ada and c.

Options are all string values. Each option is either a single boolean switch or a switch followed by a value. Options can be passed in an arbitrary order.

Note

Several options correspond to identical or similar options of the qgenc executable. As a result, more details on options will be provided in Generating Code from the System Command Line.

Note

Options can be passed either as function arguments, or as a cell array of length 1 where the value of the first element is also a cell array containing the list of options.

qgen_build accepts the following boolean switches (no value):

  • --wmisra

    Treat violations to MISRA Simulink as warnings.

  • --no-misra

    Accept violations to MISRA Simulink.

  • --global-io

    Generate main subsystem’s IO as global variables.

  • --po-wrapper

    Generate a protected object wrapper (for SPARK/Ada only) to ensure mutual exclusion when changing input signals to execute a computation step.

  • --simulink-subsystem

    Use Simulink function packaging for subsystem code generation.

  • --consts-as-vars

    Always generate system parameters as variables without this switch constants are created.

  • --wrap-io

    Wrap IO of top-level subsystem to structure.

  • --wrap-state

    Wrap state of all subsystem to structure.

  • --full-flattening, --ff

    Flatten the entire model.

  • --ref-flattening, --rf

    Flatten code for each model.

  • --unroll-threshold

    Expand expressions as loops when more elements than the threshold.

  • --export-ws, -w

    Export workspace contents to an m-file, ignore MATLAB file passed in the function input.

  • -v

    Print verbose output when exporting the decoration file.

  • --prettyprint

    Wrap all lines to 80 characters.

  • --clean, -c

    Instructs QGen to delete all the content of the output directory before starting the code generation.

  • --incremental, -i

    Instructs QGen to leave the content of the output directory as is. Generated files will overwrite existing files with the same name, but other existing files will remain untouched.

  • --debug

    Run in debug mode.

Switch-value pairs shall be passed as two arguments. First is the key and the second is value. It is assumed that values never start with '-' to distinguish them from other switches

  • --trace

    Generate a mappings.json file in the output directory to allow debugging at model level using GPS.

  • --subsys

    Generate code for the subsystem identified by the name in argument (the name is /-separated path of subsystem names starting from the model root).

  • --tasking-mode

    Override tasking mode specified in the model. Accepted values are SINGLETASKING and MULTITASKING.

  • --arith-conf

    Specify a file containing the configuration for arithmetic operations. See Customizing Arithmetic Functions.

  • --block-conf

    Specify a file containing the configuration for block libraries. See Customizing Block Implementations.

  • --output, -o

    Specify the code generation target directory. By default the files are placed in <modelname>_generated.

  • --matlab, -m

    Provide a MATLAB file containing the constant, Bus and Signal definitions. Multiple files can be provided as a cell array.

  • --steps, -s

    Specify compilation steps (one or more characters from the following list):

    • p preprocessor
    • s sequencer
    • g code model generation
    • j remove goto statements (no jump)
    • o code model optimization
    • x code model expansion
    • c code model postprocessing
    • d printing to target language
    • e export to XMI

    For example, to execute the preprocessor and then dump the resulting model to xmi without generating code, use --steps pe.

    It is possible to use the same code generation step multiple times, for example to dump to xmi at every step, use: --steps pesegeoed.

    Executing qgenc without this switch is equivalent to --steps psgxcd (preprocessor, sequencing, code model generation, code model expansion, code model postprocessing and printing).

2.5. Generating Code from the System Command Line

qgenc is the core executable that performs code generation. It runs completely outside the MATLAB/Simulink environment but requires a decoration file generated from the model within MATLAB/Simulink.

2.5.1. Producing the Decoration File

A decoration file is produced from within Simulink by executing the qgen_export_types function which is available in MATLAB after setting up QGen in MATLAB as per the instructions in Setting up QGen in MATLAB.

_images/decoration_file.png

During generation of the decoration file warnings and errors will be displayed, they are not necessarily blocking QGen from executing properly.

2.5.2. qgen_export_types Arguments

The function has one mandatory argument, the name of the Simulink model, and several optional arguments to tune its behaviour:

qgen_export_types (<mdl_path>, [<output_file>] [, <options>])
  • mdl_path

    Name of the mdl or slx file optionally with an absolute or relative path. file extensions .mdl and .slx are added automatically when no extension is found.

  • output_file

    Optional name of the output file. When omitted (or empty) the filename will be <mdl_path>_types.txt.

Options are all string values. Each option is either a single boolean switch or a switch followed by a value. Options can be passed in an arbitrary order.

Note

Options can be passed either as function arguments, or as a cell array of length 1 where the value of the first element is also a cell array containing the list of options.

The accepted boolean switches are:

  • --export-ws, -w

    Export workspace contents to m-file, ignore MATLAB file passed in the function input.

  • -v

    Print verbose output when exporting the decoration file.

  • -i

    Ignore simulink version – try to export even if the current simulink version is lower than that in the file.

Switch-value pairs shall be passed as two arguments. First is the key and the second is value. It is assumed that the value does not start with '-' to distinguish it from other switches.

  • --output, -o

    Specify the code generation target directory. By default the files are placed in <modelname>_generated.

  • --matlab, -m

    Provide a MATLAB file containing the constant, Bus and Signal definitions. Multiple files can be provided as a cell array.

2.5.3. qgen_export_types Examples

Example 1: Exporting the decoration file of test.mdl with default parameters

qgen_export_types ('test')

Example 2: Exporting the decoration file of test1.mdl with constants defined in test1_param.m

qgen_export_types ('test1.mdl','-m','./test_param.m')

Example 3: Exporting the decoration file of test2.mdl with constants in test2_param.m and datastructure definitions in test2_def.m. M-files are imported silently. The output is put in file test2_decoration.txt

qgen_export_types ('./test2.mdl', '-o', 'test2_decoration.txt', '-m',
    {'./test2_param.m', './test2_def.m'}, '-v')

2.5.4. qgenc Arguments

qgenc is invoked on the system command line as follows:

qgenc FILE [switches]

FILE can be either a Simulink model file (.mdl or .slx) or a .xmi file compliant with the Geneauto metamodel as available in share/qgen/plugins/geneauto/geneauto.ecore.

The following command lists all command line arguments supported by qgenc:

qgenc --help

2.5.4.1. Mandatory Arguments

For code generation, there are three mandatory arguments:

qgenc FILE [switches] --language ada|c --typing DECORATION_FILE
  • FILE is the model file either in Simulink mdl format, Simulink slx format or xmi format. The tool distinguishes between the input file formats by looking at the file extension.

  • --language, -l VALUE

    VALUE can be either ada or c. If the --language switch is not provided, the tool will not produce any code.

  • --typing, -t DECORATION_FILE

    Specifies a decoration file containing typing information extracted from Simulink as explained in Producing the Decoration File. This argument is mandatory when the input file is in Simulink mdl or slx formats.

2.5.4.2. Input and Output Data

  • --output, -o DIR

    Specifies an output directory for the generated code. If this argument is not specified, code is generated in <FILE>_generated by default. If the output directory is not empty, then one of the switches --clean or --incremental must be provided.

  • --clean, -c

    Instructs QGen to delete all the content of the output directory before starting the code generation.

  • --incremental, -i

    Instructs QGen to leave the content of the output directory as is. Generated files will overwrite existing files with the same name, but other existing files will remain untouched.

When passing an .mdl or a .slx file, the following additional switches are available:

  • --matlab, -m FILE

    Specifies a MATLAB file containing variable definitions that are used by the Simulink model. The MATLAB file must only contain variable definitions and not statements such as if and for constructs.

  • --lib DIR

    Specifies directories where library models can be found. The switch can be used multiple times to specify multiple directories.

Note

The --lib switch is required only in case the relative location of the libraries is different than at the time of generating the decoration file. The new location given with the --lib switch will override the original location which is stored in the decoration file.

  • --trace

    Generate a mappings.json file in the output directory to allow debugging at model level using GPS.

2.5.4.3. MISRA Constraints Options

QGen automatically checks that the input model conforms to selected MISRA Simulink constraints. QGenc should not be used as a MISRA checker tool. The checks are limited to constructs which directly influence the code generation policy. For full check one should use a dedicated MISRA checker.

The following flags control the implemented MISRA checks:

  • --no-misra

    Disable checks for MISRA Simulink constraints.

  • --wmisra

    Treat violations of MISRA Simulink as warnings.

2.5.4.4. Code Generation Options

  • --all-variants

    Generate code for all Model Reference Variants and an If statement choosing which Variant to execute using the Variant Condition from Simulink.

  • --arith-conf

    Specifies a file with transformations for arithmetic functions. See Customizing Arithmetic Functions.

  • --consts-as-vars

    Generates all constants defined in .m files as variables.

  • --debug

    Generate non-optimized code

  • --from-simulink

    Indicates that qgenc was executed from the MATLAB command line. This instructs QGen to produce block references in error messages as Simulink hyperlinks.

  • --full-flattening, --ff

    Specifies to flatten all non-virtual subsystems.

  • --ref-flattening, --rf

    Flatten code for each model.

  • --global-io

    Generates the function parameters of the main module as global variables in a separate module.

  • --no-jump

    Removes goto statements.

  • --prettyprint NUM, -pp NUM

    Format lines to be at most NUM characters long.

  • --remove-assertions

    Remove code for assertion blocks and their inputs.

  • --simulink-subsystem

    Use Simulink Function Packaging for subsystem code generation.

  • --steps, -s VALUES

    Specifies the code generation steps, which are:

    • p preprocessor
    • s sequencing
    • g code model generation
    • o code model optimization
    • x code model expansion
    • j removal of goto statements
    • c code model postprocessing
    • d printing
    • e export to xmi

    For example, to execute the preprocessor and then dump the resulting model to xmi without generating code, use --steps pe.

    It is possible to use the same code generation step multiple times, for example to dump to xmi at every step, use: --steps pesegeoed.

    Executing qgenc without this switch is equivalent to --steps psgxcd (preprocessor, sequencing, code model generation, code model expansion, code model postprocessing and printing).

  • --subsys PATH

    Generate code for the subsystem whose fully qualified name is PATH. For example --subsys MyModel/MySubsystem/AnotherSubsystem.

Note

  • The selected block must be a Simulink subsystem. The option is not supported for Stateflow charts. However, the selected subsystem can contain Stateflow charts.
  • The selected Simulink subsystem can have a dedicated trigger or enable port (i.e. contain a Trigger or Enable block at the root level).
  • The selected Simulink subsystem must not have function call signals entering or leaving the subsystem’s boundary through other ports (i.e. input or output ports).
  • --wrap-state

    Make the code reusable by creating a separated data structure for each model.

  • --wrap-io

    Encapsulate formal parameters in Ada record types or C structs.

  • --unroll-threshold

    Expand expressions as loops when more elements than the threshold.

2.5.5. qgenc Examples

To incrementally generate C code for myModel.mdl inside myFolder and using full flattening:

qgenc myModel.mdl --incremental --typing myModel_types.txt --language c
--output myFolder --full-flattening

Note

The decoration file myModel_types.txt is generated by running the following in MATLAB prior to invoking qgenc:

qgen_export_types('./myModel.mdl')

For more information on the generation of the decoration file see Producing the Decoration File.

To do the same, but dump the xmi after the import:

qgenc myModel.mdl --incremental --typing myModel_types.txt --language c
--output myFolder --steps epsgd --full-flattening

To just run the importer and dump the xmi

qgenc myModel.mdl --typing myModel_types.txt --output myFolder --steps e

The examples above are valid also when passing a .xmi file in input. In this case, the --typing and --matlab switches cannot be used.

2.6. Errors and Warnings

When investigating errors and warnings issued by the QGen toolset, first make sure that the model is consistent and that simulation can be run in Simulink. Often errors issued by QGen are due to inconsistencies in the model that should be fixed within Simulink before invoking QGen tools.

The detailed list of errors and warnings issued by the QGen tools is given in Appendix A: Errors and warnings.

2.7. User-specific Headers

It is possible to add user-specific headers by modifying the files header.ada_header and header.c_header in the directory share/qgen/library of your QGen installation.

2.8. Custom Data Types

QGen supports custom type definitions based on Simulink.Aliastype, Simulink.Bus and Simulink.IntEnumType object definitions. For signals, parameters, variables etc. in the Simulink / Stateflow model that have been defined to be of such custom types QGen shall use the corresponding type. By default, QGen shall generate respective type definitions. However, it is also possible to bind those Simulink objects with external type definitions in the target language (Ada or C). Then QGen shall use the respective external definitions in the generated code. Generally, this facilitates the integration of generated and external application code. See Custom Data Types Based on External Definitions for more.

Note

The custom type definitions must be loaded into the MATLAB workspace before running QGen (before the decoration generation step). The typing information required for code generation will then be automatically extracted into the decoration file, as needed. In case the custom type definitions are defined in a .m script, it should be a different .m file from the one defining parameters for QGen and should not be passed to qgenc.

Note

Structured parameters don’t require explicit type definitions and can also be given in a parameter file. See MATLAB Code for Parameters in .m Files and Block Parameters.

2.8.1. General Constraints for Custom Data Types

2.8.1.1. Array and Structure Types

Simulink doesn’t support defining custom array (vector, matrix, etc.) types. Array types are usually defined implicitly as needed based on the base type and size of elements in the model. However, custom arrays with fixed size can be defined within buses (records/structures) as bus elements.

Structures containing arrays and arrays containing structures are supported with a limitation that

  • when a structure is included in an array, it must have only scalar members and
  • when a structure member is an array, the elements of this array must not be structures.

2.8.1.2. Enumerated Types

Enumerated types are supported. The integer values that are associated with enumeration literals don’t have to be ordered or consecutive. However, when the chosen target language is Ada they must be distinct within the given enumerated type. This is a requirement in the Ada language. Such type definitions should be refined and the models updated accordingly. C code generation does not have this constraint. Duplicate values will map to the same value also in the corresponding C code.

2.8.2. QGen Attributes

For using custom data types with QGen some attributes must be specified in the Description field of the respective Simulink data objects. The Description can also contain other text. Only the keyword patterns indicated below will be processed by QGen.

Note

In older versions of Simulink the Description field is not available for BusElement objects (it is available for Bus objects only). This limitation does not affect Simulink versions 8.5 (R2015a) and above. For earlier versions the support of the Description field for BusElement objects should be checked. If it is not available, then the usage of custom types with QGen is more limited.

Description of the supported QGen attributes
Attribute Description
QGen.AdaSpecFile (mandatory for Ada generation) The name of the external file containing custom type definitions
QGen.RangeName (mandatory when the range does not start from 1) The name of the range type used for the array indices of a custom array type (represented as a bus)
QGen.ValueRange (optional) The allowed range of values for a numeric type
QGen.TypeName (optional) If specified, the name to be used for the type

Example:

% Alias to a standard numeric type in Simulink
MyReal = Simulink.AliasType;
MyReal.BaseType = 'double';
MyReal.Description = ...
   ['This field contains description about this data type ', ...
    'as well as QGen-specific attributes. ', ...
    'The latter must be separated from the rest of text by whitespace. ', ...
    'Hence, there is a space character after this sentence. ', ...
    'QGen.AdaSpecFile:mytypes.ads QGen.ValueRange:[0.0, 1000.0] ', ...
    ' More text follows after whitespace.'];

2.8.3. Value Range Checks

For aliases of numeric data types one can optionally specify also the expected numeric range of the data with the QGen.ValueRange attribute. For Ada code QGen will generate a cast that enforces the values to be in this range and will raise a run-time exception, if this is not the case. For C code such casts are not generated. However, this information can also be used by the QGen Model Verifier that performs static analysis of the model and can reveal certain design issues also statically.

Example:

% Alias to a standard numeric type in Simulink
MyReal = Simulink.AliasType;
MyReal.BaseType = 'double';
MyReal.Description = 'QGen.ValueRange:[0.0, 1000.0]';

2.8.4. Custom Data Types Based on External Definitions

QGen can use custom externally defined data types in the generated code instead of generating default type definitions. The functionality is slightly different when generating Ada or C code. The external type definitions are assumed to be in the same programming language as the code generation target language.

In both cases the external data types must also be defined in Simulink using Simulink.Aliastype, Simulink.Bus or Simulink.IntEnumType objects. For signals, parameters, variables etc. in the Simulink / Stateflow model that have been defined to be of such custom types QGen shall use the corresponding target language types as well. For elements defined using Simulink predefined types QGen shall generate default type definitions.

QGen also generates the respective with and use clauses (Ada) and #include statements (C) for the external type definitions. It is the user’s task to ensure that the external files are accessible at compile time.

2.8.4.1. External Data Types for Ada Code Generation

The names of the Ada types and reference to the external spec (.ads) file containing the definitions for the target Ada code should be indicated in the Description field of Simulink data objects as shown below using the QGen.AdaSpecFile attribute.

Definition from external spec files may use standard Ada naming convention for packages with a hyphen as package delimiter, such as: mytypes.ads, mytypes-subpackage.ads, mytypes-subpackage-basictypes.ads. Generated files will then include them as:

with mytypes; use mytypes;
with mytypes.subpackage; use mytypes.subpackage;
with mytypes.subpackage.basictypes; use mytypes.subpackage.basictypes;

Simulink doesn’t support defining custom array types. However, for the custom array types used within buses (records/structures) one can optionally also provide the name of the range type used for the array indices with the QGen attribute QGen.RangeName. Providing the range type is mandatory, when the range does not start from 1.

2.8.4.1.1. Examples

This MATLAB script demonstrates how to set up custom Ada types for using with Simulink and Stateflow. This example assumes that the external type definitions are in the file mytypes.ads. The content of the latter is provided further below.

MATLAB code for defining external Ada-based custom data types
% Alias to a standard numeric type in Simulink 
MyReal = Simulink.AliasType;
MyReal.BaseType = 'double';
MyReal.Description = ...
   ['This field contains description about this data type ', ... 
    'as well as QGen-specific attributes. ', ... 
    'The latter must be separated from the rest of text by whitespace. ', ... 
    'Hence, there is a space character after this sentence. ', ... 
    'QGen.AdaSpecFile:mytypes.ads QGen.ValueRange:[0.0, 1000.0] ', ...
    ' More text follows after whitespace.'];

% Alias to a standard numeric type in Simulink 
MyInt16 = Simulink.AliasType;
MyInt16.BaseType = 'int16';
MyInt16.Description = 'QGen.AdaSpecFile:mytypes.ads';

% Alias to a standard numeric type in Simulink 
MyBool = Simulink.AliasType;
MyBool.BaseType = 'boolean';
MyBool.Description = 'QGen.AdaSpecFile:mytypes.ads';

% EnumType (defined in this script)
Simulink.defineIntEnumType('MyColors', ...
    {'UNDEFINED', 'RED', 'GREEN', 'BLUE'}, ...
    [0;1;2;3], ...
    'DefaultValue', 'UNDEFINED', ...
    'DataScope', 'Imported', ...
    'AddClassNameToEnumNames', false, ...
    'Description', 'QGen.AdaSpecFile:mytypes.ads Some more description ...');

% Bus/Struct Element data for MyStruct. 2 elements of numeric type MyReal.
MyStruct_member1=Simulink.BusElement;
MyStruct_member1.Name='arr1';
MyStruct_member1.DataType='MyReal';
MyStruct_member1.Dimensions=2;
MyStruct_member1.Description = ...
    ['QGen.AdaSpecFile:mytypes.ads QGen.TypeName:MyReal_2_array ', ...
    ' QGen.RangeName:MyReal_2_index'];

% Bus/Struct Element data for MyStruct. 3 elements of numeric type MyInt16.
MyStruct_member2=Simulink.BusElement;
MyStruct_member2.Name='arr2';
MyStruct_member2.DataType='MyInt16';
MyStruct_member2.Dimensions=3;
MyStruct_member2.Description = ...
    ['QGen.AdaSpecFile:mytypes.ads QGen.TypeName:MyInt16_3_array ', ...
    ' QGen.RangeName:MyInt16_3_index'];

% Bus/Struct Element data for MyStruct. 1 element of enumerated type MyColors.
MyStruct_member3=Simulink.BusElement;
MyStruct_member3.Name='enum1';
MyStruct_member3.DataType='Enum: MyColors';
MyStruct_member3.Dimensions=1;
MyStruct_member3.Description = ...
    ['QGen.AdaSpecFile:mytypes.ads QGen.TypeName:MyColors'];

% Bus/Struct Element data for MyStruct. 2 elements of enumerated type MyColors.
MyStruct_member4=Simulink.BusElement;
MyStruct_member4.Name='enum1Arr';
MyStruct_member4.DataType='Enum: MyColors';
MyStruct_member4.Dimensions=2;
MyStruct_member4.Description = ...
    ['QGen.AdaSpecFile:mytypes.ads QGen.TypeName:MyColors2_array ', ...
    ' QGen.RangeName:MyColors2_index'];

% Bus/Struct Element data for MyStruct. 1 element of enumerated type
% OtherColors, which has been defined in a dedicated .m file.
MyStruct_member5=Simulink.BusElement;
MyStruct_member5.Name='enum2';
MyStruct_member5.DataType='Enum: OtherColors';
MyStruct_member5.Dimensions=1;
MyStruct_member5.Description = ...
    ['QGen.AdaSpecFile:mytypes.ads QGen.TypeName:OtherColors'];

% Bus/Struct Element data for MyStruct. 2 elements of enumerated type
% OtherColors, which has been defined in a dedicated .m file.
MyStruct_member6=Simulink.BusElement;
MyStruct_member6.Name='enum2Arr';
MyStruct_member6.DataType='Enum: OtherColors';
MyStruct_member6.Dimensions=2;
MyStruct_member6.Description = ...
    ['QGen.AdaSpecFile:mytypes.ads QGen.TypeName:OtherColors2_array ', ...
    ' QGen.RangeName:OtherColors2_index'];

% Bus/Struct Type that uses the previous type definitions.
MyStruct=Simulink.Bus;
MyStruct.Elements = ...
    [MyStruct_member1, MyStruct_member2, MyStruct_member3, ...
     MyStruct_member4, MyStruct_member5, MyStruct_member6];
MyStruct.Description = ...
    ['QGen.AdaSpecFile:mytypes.ads QGen.TypeName:MyStruct'];

The file mytypes.ads has the following contents.

Ada type definitions to be used in the target code.
package mytypes
is

   --  MyReal  --
   subtype MyReal is Standard.Long_Float;

   --  MyReal_2_array  --
   subtype MyReal_2_index is Integer range 1 .. 2;
   type MyReal_2_array is array (MyReal_2_index) of MyReal;

   --  MyInt16  --
   subtype MyInt16 is Integer range -32768 .. 32767;

   --  MyInt16_3_array  --
   subtype MyInt16_3_index is Integer range 1 .. 3;
   type MyInt16_3_array is array (MyInt16_3_index) of MyInt16;

   --  MyBool  --
   subtype MyBool is Boolean;

   --  MyColors  --
   type MyColors is (UNDEFINED, RED, GREEN, BLUE);
   for MyColors use (UNDEFINED => 0, RED => 1, GREEN => 2, BLUE => 3);

   --  MyColors_array  --
   subtype MyColors2_index is Integer range 1 .. 2;
   type MyColors2_array is array (MyColors2_index) of MyColors;

   --  OtherColors  --
   type OtherColors is (CYAN, MAGENTA);
   for OtherColors use (CYAN => 5, MAGENTA => 7);

   --  OtherColors_array  --
   subtype OtherColors2_index is Integer range 1 .. 2;
   type OtherColors2_array is array (OtherColors2_index) of OtherColors;

   --  MyStruct  --
   type MyStruct is record
      arr1     : MyReal_2_array;
      arr2     : MyInt16_3_array;
      enum1    : MyColors;
      enum1Arr : MyColors2_array;
      enum2    : OtherColors;
      enum2Arr : OtherColors2_array;
   end record;

end mytypes;

2.8.4.2. External Data Types for C Code Generation

Reference to the external header (.h) file containing the type definitions for the target C code should be indicated in the HeaderFile field of Simulink data objects as shown below.

2.8.4.2.1. Configuring the Custom Code Section for Simulation

Note

If the model contains also Stateflow charts, then before simulating you must also configure Simulink to read the data from this header file. For this you should open the Configuration Parameters dialog box Simulation Target pane and enter the custom header file and directory as needed. See also the corresponding figure.

Tip: The data in these fields is only used by Simulink and Stateflow. QGen does not read them. Also, one should separately make sure that the file is available also when compiling and linking the generated code.

_images/CustomTypesExample_SL-C_config.png

Configuring Simulink to use a custom C header file for simulation.

2.8.4.2.2. Examples

The MATLAB script below demonstrates how to set up custom C types for using with Simulink and Stateflow. This example assumes that the external type definitions are in the file mytypes.h. The content of the latter is provided further below.

MATLAB code for defining external C-based custom data types.
% Alias to a standard numeric type in Simulink 
MyReal = Simulink.AliasType;
MyReal.BaseType = 'double';
MyReal.HeaderFile = 'mytypes.h';
MyReal.Description = ...
   ['This field contains description about this data type ', ... 
    'as well as QGen-specific attributes. ', ... 
    'The latter must be separated from the rest of text by whitespace. ', ... 
    'Hence, there is a space character after this sentence. ', ... 
    'QGen.ValueRange:[0.0, 1000.0] ', ...
    ' More text follows after whitespace.'];

% Alias to a standard numeric type in Simulink 
MyInt16 = Simulink.AliasType;
MyInt16.BaseType = 'int16';
MyInt16.HeaderFile = 'mytypes.h';

% Alias to a standard numeric type in Simulink 
MyBool = Simulink.AliasType;
MyBool.BaseType = 'boolean';
MyBool.HeaderFile = 'mytypes.h';

% EnumType (defined in this script)
Simulink.defineIntEnumType('MyColors', ...
    {'UNDEFINED', 'RED', 'GREEN', 'BLUE'}, ...
    [0;1;2;3], ...
    'DefaultValue', 'UNDEFINED', ...
    'DataScope', 'Imported', ...
    'AddClassNameToEnumNames', false, ...
    'HeaderFile', 'mytypes.h', ...
    'Description', 'Some description ...');

% Bus/Struct Element data for MyStruct. 2 elements of numeric type MyReal.
MyStruct_member1=Simulink.BusElement;
MyStruct_member1.Name='arr1';
MyStruct_member1.DataType='MyReal';
MyStruct_member1.Dimensions=2;

% Bus/Struct Element data for MyStruct. 3 elements of numeric type MyInt16.
MyStruct_member2=Simulink.BusElement;
MyStruct_member2.Name='arr2';
MyStruct_member2.DataType='MyInt16';
MyStruct_member2.Dimensions=3;

% Bus/Struct Element data for MyStruct. 1 element of enumerated type MyColors.
MyStruct_member3=Simulink.BusElement;
MyStruct_member3.Name='enum1';
MyStruct_member3.DataType='Enum: MyColors';
MyStruct_member3.Dimensions=1;

% Bus/Struct Element data for MyStruct. 2 elements of enumerated type MyColors.
MyStruct_member4=Simulink.BusElement;
MyStruct_member4.Name='enum1Arr';
MyStruct_member4.DataType='Enum: MyColors';
MyStruct_member4.Dimensions=2;

% Bus/Struct Element data for MyStruct. 1 element of enumerated type
% OtherColors, which has been defined in a dedicated .m file.
MyStruct_member5=Simulink.BusElement;
MyStruct_member5.Name='enum2';
MyStruct_member5.DataType='Enum: OtherColors';
MyStruct_member5.Dimensions=1;

% Bus/Struct Element data for MyStruct. 2 elements of enumerated type
% OtherColors, which has been defined in a dedicated .m file.
MyStruct_member6=Simulink.BusElement;
MyStruct_member6.Name='enum2Arr';
MyStruct_member6.DataType='Enum: OtherColors';
MyStruct_member6.Dimensions=2;

% Bus/Struct Type that uses the previous type definitions.
MyStruct=Simulink.Bus;
MyStruct.Elements = ...
    [MyStruct_member1, MyStruct_member2, MyStruct_member3, ...
     MyStruct_member4, MyStruct_member5, MyStruct_member6];
MyStruct.HeaderFile = 'mytypes.h';

The file mytypes.h has the following contents.

C type definitions to be used in the target code.
#ifndef __MYTYPES__
#define __MYTYPES__

typedef double MyReal;

typedef short MyInt16;

typedef unsigned char MyBool;

typedef enum {
   UNDEFINED = 0,
   RED = 1,
   GREEN = 2,
   BLUE = 3
} MyColors;

typedef enum {
   CYAN = 5,
   MAGENTA = 7
} OtherColors;

typedef struct {
   MyReal arr1[2];
   MyInt16 arr2[3];
   MyColors enum1;
   MyColors enum1Arr[2];
   OtherColors enum2;
   OtherColors enum2Arr[2];
} MyStruct;

#endif

2.9. Naming Convention

There is a simple means to control the names of variables, parameters and subprograms in the code generated by QGen. The names of ports and subsystems are those used for the corresponding input/output parameters or variables and subprograms.

In a model like the following:

_images/naming_convention.png

the name of the atomic subsystem ‘Dynamics’ determines the name of the compute function (‘Dynamics_comp’), and the names of the input and output ports are exactly the same as the name of the parameters:

void Dynamics_comp
  (GAREAL const Distance,
   GAREAL* const Speed,
   GAREAL* const Acceleration)

Note that when using the --global-io switch to generate main subsystem’s IO as global variables, the names of the variables for the previous model contain both the names of the subsystem and the ports:

GAREAL Dynamics_comp_Distance;
GAREAL Dynamics_comp_Speed;
GAREAL Dynamics_comp_Acceleration;

2.10. Customizing Arithmetic Functions

Thanks to the --arith-conf switch, it is possible to customize the call for arithmetic functions on both scalar and matrices. The switch take in input a textual file containing the specification of the transformation in the following format:

TRANS ::= ARITY : SATURATION : SIGNATURE : MODULE

where the arity of the arithmetic operation expressed as:

ARITY ::= TYPE([]([])?)? = TYPE([]([])?)? OPERATOR TYPE([]([])?)?

TYPE  ::= double|single|int32|int16|int8|uint32|uint16|uint8|boolean

OPERATOR ::= +|-|*|/|#

where the # operator is the element-wise multiplication for matrices.

SATURATION is a boolean value ‘true’ or ‘false’, where ‘true’ indicates that the custom call replaces a saturated operator.

SIGNATURE is the signature of the function to be called, using the special MATLAB variables uN, yN and the size function.

Finally, MODULE is the name of the .h/.ads file where the external functions are located.

For example, the following text:

double = double+double : false : double y1 = my_add(double u1, double u2) :
my_lib.h

means that, whenever the ‘+’ operator between double is found, it should be replaced by a call to function my_add declared in file my_lib.h. In this case, u1 represents the first operand, while u2 represents the second. With the transformation above, the code:

double a, b, c;
c = a + b;

is transformed into:

#include "my_lib.h"
...
double a, b, c;
c = my_add (a, b);

When it comes to matrices, it is typical to call a void function, for example:

double[][] = double[][]+double[][] : false : my_element_wise_sum
               (double u1[][], double u2[][],
                int32 size(u2, 1), int32 size(u2, 2)) : my_lib.h

In this case, the matrix multiplication is replaced by a call to the my_matrix_element_wise function. Since the function signature does not contain a return value, an implicit parameters is added ad the end of the call. With the transformation above, the code:

double[][] a, b, c;
...
for (i = 0; i < size_1; i++){
   for (j = 0; j < size_2; j++){
      c[i][j] = a[i][j] + b[i][j];
   }
}

is transformed into:

#include "my_lib.h"
...
double[][] a, b, c;
my_element_wise_sum (a, b, size_1, size_2, c);

Since C does not have a proper syntax to pass two dimensional arrays as arguments, matrices are passed as serialized row-major vectors:

void my_element_wise_sum (double a[], double b[], int size_1, int size_2,
                          double c[])
{
  int i, j;
  for (i = 0; i < size_1; ++i)
  {
     for (j = 0; j < size_2; ++j)
       c[i * size_2 + j] = ...; /* instead of c[i][j] */
  }
}

An example of a configuration file is available in share/qgen/examples/customization/arith.

2.11. Customizing Block Implementations

With the --block-conf switch, it is possible to replace the implementation of supported blocks with functions calls. The switch takes as parameter a textual file containing the specifications for block replacement:

TRANS ::= BLOCK_TYPE : ARITY : (PARAMETER_FILTERS)?
                     : (INIT_SIGNATURE)? : (COMPUTE_SIGNATURE)?
                     : (UPDATE_SIGNATURE)? : MODULE

Where the arity of the block is expressed as:

ARITY ::= TYPE([]([])?)?(,TYPE([]([])?)?)* <- TYPE([]([])?)?(,TYPE([]([])?)?)*

TYPE  ::= double|single|int32|int16|int8|uint32|uint16|uint8|boolean

Where the types on the left of ‘<-‘ represent the types of the output ports of the block and the types on the right are the input ports of the block.

PARAMETER_FILTERS is a list of parameter names associated to a value:

PARAMETER_FILTERS ::= (PARAM_NAME => VALUE (# PARAM_NAME => VALUE)*)?

INIT_SIGNATURE is the signature of the function to call for the initialization step of the block.

COMPUTE_SIGNATURE is the signature of the function called at the computation steps of the block.

UPDATE_SIGNATURE is the signature of the function called after each computation steps to update internal data.

The three signatures use the MATLAB special variables uN, yN and the size function. The feature also provides special variables mN to designate persistent memory variables and allows references to block parameters by using the parameter’s name.

Finally, MODULE is the name of the .h/.ads file where the external functions are located.

For example, the following line:

Gain : double <- double : : : double y1 = gain_comp(double u1) : : libgain.h

tells that all Gain blocks whose input and output are of type double should be replaced by a call to function gain_comp declared in file libgain.h. No init update function are needed here since there is no persistent memory at stake.

Another example with parameter filtering:

Abs : double<-double : SaturateOnIntegerOverflow => off : \
    : double y1 = abs_comp(double u1) : : libabs.h

This tells to replace only Abs blocks whose parameter SaturateOnIntegerOverflow is off by a call to abs_comp. As is pattern matching, you can define a “default” case for block of the same type and arity but didn’t match the parameter filters:

Abs : double<-double : : : double y1 = abs_sat_comp(double u1) : : libabs.h

This line in combination with the one above will make QGen replace Abs blocks using saturation.

Here’s another example with using memory variables an parameter references:

UnitDelay : double[]<-double[] : \
          : ud_init (double InitialValue[], uint32 size(InitialValue,1), \
                     double m1[size(InitialValue,1)], uint32 size(m1,1)) \
          : ud_comp (double y1[], uint32 size(y1,1), double m1[], \
                     uint32 size(m1,1)) \
          : ud_up (double u1[], uint32 size(u1,1), double m1[], \
                   uint32 size(m1,1)) \
          : libunitd.h

This line provides the three function signatures to replace UnitDelay blocks:

  • ud_init: This function is called for initialization, “double m1[size(InitialValue,1)]” will at the same time define a new memory persistent variable and specify that the created variable shall be passed to the function. The lengths of the arrays can also be provided as arguments to the function with the size() function (often needed when interfacing with C code).

    Note

    When variables mN are of type array, the size of each dimension must be provided in the INIT_SIGNATURE. The numbering of mN variables shall start from one and the variables should appear in INIT_SIGNATURE in increasing order according to their numbering (no index shall be omitted).

  • ud_comp: For computation, the unit delay needs to set its output y1 with its memory variable m1. Since the variable is already allocated, there is no need to provide its size. However, as in the above case, the function might still need to have the lengths of arrays as arguments.

  • ud_up: Finally, the update function updates the m1 variable with the input of the unit delay block.

An example of configuration file is available in share/qgen/examples/customization/blocks.