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_xmi MATLAB function to extract relevant information from the Simulink model and serialize it in multiple XMI files, one per model and one for the base workspace, as explained in Exporting the model information. Once the files are 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. Export the model information using the qgen_export_xmi function in a manner similar to Exporting the model information.
  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 are defined in a separate file.

The following exports XMI files into the qgen_xmi_dir directory and then generates Ada code in the folder ./generated_code for speedometer.mdl and, by using the speedometer_def.m MATLAB file, defines global variables used by the model.

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

In the following invocation the default output directory speedometer.xmi_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 to only regenerate for the models that changed this the last generation in that directory.

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

When running from within MATLAB, it is possible to use the -v switch in the exporter_options 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', '-v', '--incremental')

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.xmi --pre-process-xmi -l ada --incremental

2.4.3. qgen_build Arguments

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

qgen_build (<mdl path>, <lang>, <exporter_options> [, <qgen_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.

The exporter options switches should be provided as a single string each value being separated by a comma.

If no exporter_options nor qgen_options are specified exporter_options -o <modelname>_qgen_xmi is used by default.

All exporter options are documented in qgen_export_xmi Arguments.

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.

The allowed qgen_options are described below.

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.

  • --gen-entrypoint

    Generate an entry module for the top-level model with init and comp functions. This module declares all necessary states variables and provides a simple interface to call from hand-written code. See Calling the generated code.

  • --eggshell

    Generate root subsystems in “eggshell” models as root models. “Eggshell” models are Simulink models with no interface (Inport or Outport blocks), consisting only of subsystems (with interface or not).

  • --po-wrapper

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

  • --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.

  • --noreuse-flattening

    Flatten code for subsystems not marked as Reusable function.

  • --full-flattening, --ff

    Flatten code for the entire model.

  • --ref-flattening, --rf

    Flatten code for each model.

  • --unroll-threshold

    Expand expressions as loops when more elements than the threshold.

  • --coverage-check

    Detect conditions within if statements that are predefined by conditions that are part of enclosing if statements, leading to uncoverable code.

  • --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 model.

  • --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. If the input file is an XMI file generated from Simulink, this option performs an incremental code generation that only regenerates the sources that originate from models that changed since the last code generation.

  • --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 GNAT Studio.

  • --subsys

    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).
  • --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.

  • --blocks-signatures

    Generate a file named qgen_<root_model>_blocks_signatures.txt to use as a basis for block configuration. Customizing Block Implementations.

  • --output, -o

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

  • --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 simplification
    • x code model expansion
    • c code model postprocessing
    • d printing to target language
    • e export the result of previous step to XMI

    The –steps switch determines the transformations that shall be executed by QGenc. The order of switches above corresponds to the order of transformations executed by the tool (except for ‘e’). This order is hard-coded and can not be modified by the –steps switch.

    Fully optional steps are ‘j’ and ‘o’ which can be left out at any time and ‘e’ which has a specific usage explained below. The other steps can be omitted only in a workflow where either it is desired to stop code generation at certain point or an external tool takes care of omitted intermediate transformations.

    Step ‘e’ is useful in conjunction with another step only – it dumps the model produced by the step preceding it in the list. 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 ‘e’ key multiple times, for example to dump to xmi at every step, use: --steps pesegeoed.

    Executing qgenc without the –steps switch is equivalent to --steps psgoxcd (preprocessor, sequencing, code model generation, code model simplification, 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 representation of the model in a set of XMI files generated from the model within MATLAB/Simulink.

2.5.1. Exporting the model information

A set of XMI files is produced from within Simulink by executing the qgen_export_xmi function which is available in MATLAB after setting up QGen in MATLAB as per the instructions in Setting up QGen in MATLAB. One file is generated for the base workspace, called QGen_Base_Workspace.xmi and one file will be generated per model referenced (including the root).

_images/export_phase.png

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

Note

The export phase will by default only re-export models that changed since the last export in the given output directory. This means that the first export can be long but if the model is architectured with multiple model references and only some are modified then subsequent incremental exports will be faster.

2.5.2. qgen_export_xmi Arguments

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

qgen_export_xmi (<mdl_path> [, <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.

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:

  • --force, -f

    Re-export the entire model discarding previously generated XMI files in the output directory.

  • --fast-export, -fe

    Only look at the model version to determine whether a model has to be re-exported or not. Export will be faster but this can cause faulty code generation when non-modified model have datatypes that depend on other modified models.

  • --no-simulink-sequencing, -ns

    Disable the use of Simulink Debugger to retrieve the block sequencing, see more detail on the next option below. This option will provide a faster export process at the risk of a deviation between the generated code and the Simulink sequencing.

  • --force-simulink-sequencing, -fs

    Force the use of Simulink Debugger to retrieve block sequencing, this will open each referenced model in Simulink and actively focus it. Without those options QGen uses the Debugger by default on subsystems that contain DataStores or Function-Call Susbsystem that can cause complex sequencing patterns. The default option is the best compromise between a fast export and accurate sequencing data.

  • --no-workspace-export, -nw

    Do not export the workspace.

  • --export-unsupported, -eu

    Force the export of blocks not supported by QGen. This option should not be used when code generation is intended. It is provided for translation of XMI to other languages and for advanced compatibility checking.

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.

  • -o, --output

    Specify the output directory where generated XMI files are stored. By default files are place in <modelname>_qgen_xmi.

2.5.3. qgen_export_xmi Examples

Example 1: Exporting the XMI files of test.mdl with default parameters

qgen_export_xmi ('test')
Example 2: Exporting the XMI files of test2.mdl in the XMI_test2 folder
without exporting the workspace.
qgen_export_xmi ('./test2.mdl', '-o', 'XMI_test2', '-nw')

2.5.4. qgenc Arguments

qgenc is invoked on the system command line as follows:

qgenc FILE [switches]

FILE is an .xmi file exported within MATLAB 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 --pre-process-xmi
  • FILE is the root model file exported in xmi format.

  • --language, -l VALUE

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

  • --pre-process-xmi

    Mandatory argument to treat the root XMI file as one generated from MATLAB/Simulink Exporting the model information. QGenc accepts other XMI files as input that are a result of previous executions and do not require this argument.

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:

  • --trace

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

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

  • --gen-entrypoint

    Generate an entry module for the top-level model with init and comp functions. This module declares all necessary states variables and provides a simple interface to call from hand-written code. See Calling the generated 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.

  • --noreuse-flattening

    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-io

    Encapsulate formal parameters in Ada record types or C structs.

  • --unroll-threshold

    Expand expressions as loops when more elements than the threshold.

  • --eggshell

    Generate root subsystems in “eggshell” models as root models. “Eggshell” models are Simulink models with no interface (Inport or Outport blocks), consisting only of subsystems (with interface or not).

2.5.5. qgenc Examples

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

qgenc myModel/qgen_xmi/myModel.xmi --pre-process-xmi --incremental --language c
--output myFolder --full-flattening

Note

The XMI file myModel.xmi is generated by running the following in MATLAB prior to invoking qgenc:

qgen_export_xmi('./myModel.mdl')

For more information on the generation of XMI files see Exporting the model information.

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

qgenc myModel.xmi --incremental --pre-process-xmi --language c
--output myFolder --steps epsgd --full-flattening

To just run the importer and dump the xmi

qgenc myModel.xmi --pre-process-xmi --output myFolder --steps e

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. Calling the generated code

To call the code generated from hand-written code you should use the --gen-entrypoint switch to generate a module called qgen_entry_[modelname].

This module contains two exported functions init and comp (prefixed by the model name in C) that you should call in that order.

The init function corresponds to the Simulink initialization phase and initializes the values for static variables across all the code, including the code coming from all referenced models.

The comp function corresponds to the execution of one Simulink iteration step and is typically called inside a loop.

There are several ways how data can be passed to and from the comp when executing it:

  • By default an input argument is generated for each input port and output argument for each output port in comp function.
extern void qgen_entry_CodeOrder_init (void);
extern void qgen_entry_CodeOrder_comp
  (GAREAL const In1,
   GAREAL const In2,
   GAREAL* const Out1,
   GAREAL* const Out2);
package qgen_entry_CodeOrder is

   procedure init;

   procedure comp
      (In1 : Long_Float;
       In2 : Long_Float;
       Out1 : out Long_Float;
       Out2 : out Long_Float);

end qgen_entry_CodeOrder;
  • the --wrap-io switch tells qgen to encapsulate parameters corresponding to input and output ports into a datastructure. The list of IO parameters of the comp function is replaced with a single input-output argument.
typedef struct {
   GAREAL In1;
   GAREAL In2;
} qgen_entry_CodeOrder_comp_Input;
typedef struct {
   GAREAL Out1;
   GAREAL Out2;
} qgen_entry_CodeOrder_comp_Output;

extern void qgen_entry_CodeOrder_init (void);
extern void qgen_entry_CodeOrder_comp
  (qgen_entry_CodeOrder_comp_Input const* const Input,
   qgen_entry_CodeOrder_comp_Output* const Output);
package qgen_type_wrap_io_qgen_entry_CodeOrder is

   type qgen_entry_CodeOrder_comp_Input is record
      In1 : Long_Float;
      In2 : Long_Float;
   end record;

   type qgen_entry_CodeOrder_comp_Output is record
      Out1 : Long_Float;
      Out2 : Long_Float;
   end record;

end qgen_type_wrap_io_qgen_entry_CodeOrder;

package qgen_entry_CodeOrder is

   procedure init;

   procedure comp
      (Input : qgen_entry_CodeOrder_comp_Input;
       Output : out qgen_entry_CodeOrder_comp_Output);

end qgen_entry_CodeOrder;
  • when calling qgen with --global-io switch the parameters are replaced with global variables. Input values are expected to be written to these variables before calling the comp function and the output values are written to global variables corresponding to output ports. Each of the generated IO variables can be replaced with a variable imported from an external module. See Importing variables and constants defined in external modules for a detailed description of this scenario.
CodeOrder_State CodeOrder_memory;
GAREAL qgen_entry_CodeOrder_comp_In1;
GAREAL qgen_entry_CodeOrder_comp_In2;
GAREAL qgen_entry_CodeOrder_comp_Out1;
GAREAL qgen_entry_CodeOrder_comp_Out2;

extern void qgen_entry_CodeOrder_init (void);
extern void qgen_entry_CodeOrder_comp (void);
package qgen_entry_CodeOrder_io is
   CodeOrder_memory : CodeOrder_State;
   qgen_entry_CodeOrder_comp_In1 : Long_Float;
   qgen_entry_CodeOrder_comp_In2 : Long_Float;
   qgen_entry_CodeOrder_comp_Out1 : Long_Float;
   qgen_entry_CodeOrder_comp_Out2 : Long_Float;

end qgen_entry_CodeOrder_io;

package qgen_entry_CodeOrder is

   procedure init;

   procedure comp;

end qgen_entry_CodeOrder;
  • the --po-wrapper switch works in conjunction with the --global-io and instructs QGen to put the global IO variables within the private part of an Ada protected object. This switch has no effect when generating code for C.

    Access to the input/output values is granted through protected procedures as setters for the different input signals, and protected functions as getters for the output signals.

    The input signals are set asynchronously by the setter procedures, and then the comp procedure uses the latest values for the input signals to compute the new output signals, that can be asynchronously read by the getter functions.

package qgen_entry_CodeOrder is

   procedure init;

   procedure comp
      (Out1 : out Long_Float;
       Out2 : out Long_Float);

   protected po is

      procedure comp
         (Out1 : out Long_Float;
          Out2 : out Long_Float);

      procedure set_In1 (In1 : Long_Float);

      procedure set_In2 (In2 : Long_Float);

      function get_Out1 return Long_Float;

      function get_Out2 return Long_Float;

   private
      qgen_entry_CodeOrder_comp_In1 : Long_Float;
      qgen_entry_CodeOrder_comp_In2 : Long_Float;
      qgen_entry_CodeOrder_comp_Out1 : Long_Float;
      qgen_entry_CodeOrder_comp_Out2 : Long_Float;
   end po;

end qgen_entry_CodeOrder;

2.8. 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.9. Bus Signals

Bus is a concept for composing composite signals in Simulink. Each signal in a bus is referred as bus element. There are two types of buses – virtual and non-virtual.

Virtual buses refer to a collection of several grouped signals. Grouping does not imply an underlying composite datastructure. However, depending on bus contents a datastructure may be defined implicitly. Also the user may choose to define an explicit datastructure.

Homogeneous virtual buses are buses where all the leaf elements have the same datatype. Typically Simulink treats this signal as an array of the common base type.

Non-homogeneous virtual buses are buses with mixed leaf element datatype. Elements of these buses can be accessed inside of bus-capable blocks (see below), but one should not assume a specific datastructure.

Non-virtual buses must correspond to a pre-defined structure type. These structure type definitions are stored as Bus Objects in Simulink and every non-virtual bus must reference a bus object.

Bus elements may salars or arrays of native Simulink datatypes or other buses. We refer to bus signals containing one or more elements with bus datatype as nested buses.

2.9.1. Bus capable blocks

Simulink supports bus signals for the following blocks:

Block Input Output
Assignment(nonvirtual buses) OK OK
Bus Assignment OK OK
Bus Creator OK OK
Bus Selector OK OK
Constant (nonvirtual buses)   OK
Data Store Memory (nonvirtual buses) Has no ports, can store bus signals
Data Store Read (nonvirtual buses)   OK
Data Store Write (nonvirtual buses) OK  
Delay OK OK
Interpolation Using Prelookup OK  
Memory OK OK
Merge OK  
Multiport Switch OK  
Prelookup OK  
Rate Transition   OK
Signal Conversion OK OK
Signal Specification OK OK
Switch OK OK
Unit Delay OK OK
Vector Concatenate(nonvirtual buses) OK OK
Zero-Order Hold OK OK

2.9.2. QGen conversion for bus datatypes

  • Virtual homogeneous buses are converted to arrays with base type equal to that of bus leaf elements and the length corresponding to the total number of bus elements. In case of a nested bus, the elements are flattened in depth-first order.
  • Virtual non-homogeneous buses are in general split into n variables with n being the total number of bus elements. The one exception occurs when the datatype of the virtual bus is explicitly defined in the associated Inport or Outport block, in which case the generated function parameters are structures of the given bus type.
  • Non-virtual buses use the datatype defined in Matlab workspace.

2.10. 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 XMI export phase). The typing information required for code generation will then be automatically extracted into the XMI files, as needed.

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.10.1. General Constraints for Custom Data Types

2.10.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.10.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.10.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.10.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.10.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.10.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.10.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.10.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.10.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.10.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.11. Naming Convention

When generating code, QGen uses the names of model elements as much as possible. The elements that explicitly reference external code or another model are expected to have a unique name that is always preserved during code generation. Such elements are referred as fixed identifiers.

Generated identifiers with unchanged model element names
Model element Code element
Data type Data type
Workspace variable Module-level variable
Model name Module name

Other elements have a generated name which is derived from corresponding model element name but may be decorated to end up with a unique identifier. The table below lists named elements from the input model and corresponding rules for using their name in generated code. We refer to these identifiers as derived identifiers

Generated identifiers derived from model element names
Model element Code element
Subsystem name (atomic subsystem) Module name
  Prefix of initialization (_initStates, _initOutputs), compute (_comp) and memory update (_up), enable (_enable), disable (_disable) functions
   
Subsystem port (atomic subsystem) Argument of initialization (_initStates, _initOutputs), compute (_comp) and memory update (_up), enable (_enable), disable (_disable) functions (the default mode)
  Structure element of IO structure when generating with --wrap-io switch
  Suffix of global variable corresponding to a port when generating with --global-io switch
Block name Prefix of output variables generated from this block unless the outgoing signal has a name
  Prefix of memory variables generated from this block
Port name Suffix of output variables generated from this block unless the outgoing signal has a name
Signal name Name of the block output variable corresponding to this signal. In case there are several named signals starting from the same port, QGen uses the first name it finds.

In the third group, referred as generated identifiers there are identifiers that are created during code generation process and do not correspond directly to a single model element.

Generated identifiers with no corresponding name in the model
Code element Naming rule
Array data type (Ada only) <type name>_Array_<dimension1 length>[_<dimension2 length>]
Array range (Ada only) <array type name>_Range_<dimension number>

Identifiers derived from model element names are first cleaned of illegal characters (such as whitespace, umlauts etc). The illegal characters are replaced by underscores, two subsequent underscores are merged to one. In case an identifier starts with a number, it is prefixed with “z” in C or “u” in Ada.

Finally the overlapping names are checked for uniqueness. In case of conflicts with each other the name is suffixed with underscore and a number. When generated name overlaps with a reserved word it is prefixed with “z” in C or duplicated first letter in Ada.

The rules for modifying the generated identifiers are as follows:

  • the names of fixed identifiers and generated identifiers are not modified during the code generation process
  • names of functions and types linked to a library instance are suffixed by the first 13 characters of the checksum associated to that library.
  • illegal characters in fixed names are not checked and shall be discovered by the target compiler
  • when there is a naming conflict between two fixed identifiers or between a fixed identifier and generated identifier the code generation stops with error
  • name conflict between two generated identifiers can not occur — in case the generated element (e.g. data type) gets a name that already exists, the existing element is used instead.
  • generated names are mangled as described above. In case of a conflict between two generated names the one discovered later by code generator is usually mangled except in case one of them is function argument. In case of a conflict between function argument an identifier inside of that function the latter is modified.

2.11.1. Examples

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;

If subsystem Dynamics contained signals named

  • Distance
  • else
  • 1_speed
  • name_____with_ underscores

then the generated variable names would be:

  • Distance_1 — there was a function argument named Distance
  • zelse for C or eelse for Ada — “else” is a reserved keyword
  • z1_speed for C or u1_speed for Ada — derived from 1_speed. The identifier cannot start with a number
  • name_with_underscores — space is replaced with underscore, subsequent underscores are merged

2.12. 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.13. 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.

Generating code with the --blocks-signatures on a given model switch will generate a file called “qgen_<model_name>_blocks_signatures.txt. This file will contain the signatures of all blocks present in the model. These signatures can be used as a basis to create custom configuration files.