2. QGen Code Generator¶
2.1. Using qgenc
¶
Code generation is performed using the qgenc
executable which can be invoked
in three methods:
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.
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 toqgen_build
.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 theqgen_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.3. Generating Code from the Simulink GUI¶
QGen can be invoked from the Simulink user interface. This requires QGen to be correctly setup in MATLAB following the instructions in Setting up QGen in MATLAB.
A QGen menu item is available in the menu bar of any Simulink model.
Clicking on Verify and Generate
shows the following menu.
Clicking on the gear icon allows to select the code generation options of QGen.
Code generation options will be explained in
Generating Code from the MATLAB Command Line and Generating Code from the System Command Line.
Note
This code generation method only proposes a few code generation options. More code generation options are available with the other invocation methods in Generating Code from the MATLAB Command Line and Generating Code from the System Command Line.
It is also possible to right-click on a subsystem and execute QGen on a specific subsystem only.
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:
- Load the given model in Simulink
- Produce a decoration file using the
qgen_export_types
function in a manner similar to Producing the Decoration File. - 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')
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
andc
.
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
preprocessors
sequencerg
code model generationj
remove goto statements (no jump)o
code model optimizationx
code model expansionc
code model postprocessingd
printing to target languagee
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.
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 eitherada
orc
. 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
andfor
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
preprocessors
sequencingg
code model generationo
code model optimizationx
code model expansionj
removal of goto statementsc
code model postprocessingd
printinge
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.
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.
% 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.
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.
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.
% 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.
#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:
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.