3. QGen Code Generator

3.1. Using qgenc

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

_images/qgen_generation_methods.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.

3.2. Prerequisites

QGen supports models with data values and data types defined in objects that are not directly contained in the models themselves. Such objects can be defined in variables declared either in the MATLAB base workspace, model workspace or data dictionaries, or come from the enumeration classes contained in .m files visible in the MATLAB path.

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

3.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. This might take some time as the Simulink model and its referenced models are loaded upon the invocation of qgen_build.

3.4.1. 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 -l ada --incremental

3.4.2. 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), including extension.

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

  • --noreuse-flattening

    Flatten code for subsystems not marked as Reusable function.

  • --ref-flattening, --rf

    Flatten code for each model.

  • --full-flattening, --ff

    Flatten code for the entire model.

  • --wrap-io

    Wrap IO of top-level subsystem to structure.

  • --global-io

    Generate main subsystem’s IO as global variables.

  • --consts-as-vars

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

  • --debug

    Run in debug mode.

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

  • --remove-assertions

    Remove code for assertion blocks and their inputs.

  • --verbose, -v

    Print verbose output when exporting the model.

  • --wmisra

    Treat violations to MISRA Simulink as warnings.

  • --no-misra

    Accept violations to MISRA Simulink.

  • --eggshell

    Consider all root subsystems in “eggshell” models as separate root models when generating code. “Eggshell” models are Simulink models with no interface (Inport or Outport blocks), consisting only of subsystems (with interface or not). Using this pattern is not recommended.

  • --po-wrapper

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

  • --export-ws, -w

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

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

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

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

  • --export-dir, -e

    Specify the target directory for the XMI files. By default the files are placed in <modelname>_qgen_xmi.

  • --custom-header

    Specify the file to use as header for all the generated code.

  • --static-output

    Directory in which the static files provided by QGen are copied.

  • --unroll-threshold N

    Modifies the way binary operations on arrays are generated. When an array has less or the same number of elements than the specified threshold, binary operations on the array will be generated as a sequence of elementary binary operations on each element. Otherwise, the binary operations will be expanded to a loop statement iterating over the elements of the array. When not specified, QGen uses 6 as the default value.

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

    It is possible to use both --debug and --steps switches simultaneously. In that case, --steps should be either ‘psgxcd’ or any combination of those steps with ‘e’ (e.g. ‘psegxcede’). Using the --debug switch with the ‘o’ step is not allowed.

  • --trace

    Generate qdb files in the output directory to allow debugging at the model level using the QGen Debugger and GNAT Studio.

  • --with-gui

    Generate diagram files to allow debugging at the model level using the QGen Debugger and GNATStudio.

  • --with-gui-only

    Generate diagram files for the QGen Debugger without generating code.

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

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

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

3.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, including extension (.mdl, or .slx).

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:

  • --verbose, -v

    Print verbose output when exporting the model.

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

3.5.3. qgen_export_xmi Examples

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

qgen_export_xmi ('test.slx')

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')

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

3.5.4.1. Mandatory Arguments

For code generation, there are three mandatory arguments:

qgenc FILE [switches] --language ada|c
  • 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.

3.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 qdb files in the output directory to allow debugging at the model level using GNAT Studio.

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

3.5.4.4. Code Generation Options

  • --block-conf

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

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

  • --noreuse-flattening

    Use Simulink Function Packaging for subsystem code generation.

  • --ref-flattening, --rf

    Flatten code for each model.

  • --full-flattening, --ff

    Specifies to flatten all non-virtual subsystems.

  • --global-io

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

  • --wrap-io

    Encapsulate formal parameters in Ada record types or C structs.

  • --consts-as-vars

    Generates all constants defined in .m files as variables.

  • --remove-assertions

    Remove code for assertion blocks and their inputs.

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

  • --unroll-threshold N

    Modifies the way binary operations on arrays are generated. When an array has less or the same number of elements than the specified threshold, binary operations on the array will be generated as a sequence of elementary binary operations on each element. Otherwise, the binary operations will be expanded to a loop statement iterating over the elements of the array. When not specified, QGen uses 6 as the default value.

  • --verbose, -v

    Display debug level messages

  • --static-output

    Directory in which the static files provided by QGen are copied.

  • --custom-header

    Specify the file to use as header for all the generated code.

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

  • --no-jump

    Removes goto statements.

  • --prettyprint NUM, -pp NUM

    Format lines to be at most NUM characters long.

  • --remove-unused-enable

    Unused code generated for enable blocks will be removed from the generated code. This has the downside of no longer making the code generation modular and disables the capability to generate code incrementally from these sources.

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

  • --eggshell

    Consider all root subsystems in “eggshell” models as separate root models when generating code. “Eggshell” models are Simulink models with no interface (Inport or Outport blocks), consisting only of subsystems (with interface or not). Using this pattern is not recommended.

3.5.5. qgenc Examples

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

qgenc myModel/qgen_xmi/myModel.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 --language c --output myFolder --steps epsgd --full-flattening

To just run the importer and dump the xmi

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

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

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

3.7.1. Default interface

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;

3.7.2. Wrap IO

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;

3.7.3. Global IO

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;

3.7.4. Protected object wrapper

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;

3.7.5. Passing Model Arguments

In Simulink, users may parameterize block parameter values in different instances of a referenced model by defining model arguments in that model’s model workspace. Such arguments are typically defined as MATLAB variables and used in block parameters of blocks contained in the model. Users may then customize the model arguments values when referencing the model through a ModelReference block.

There are two cases to consider for the code generated by QGen:

  1. Model arguments defined in referenced models

When model arguments are defined in models referenced at any level, but different from the top-level input model, QGen is able to generate code that will initialize the model argument variables to the specified values for each instance and reference them in the block parameters where they are used in the referenced model. There is no action required from the user to integrate the generated code.

  1. Model arguments defined for the top-level input model

When model arguments are defined in the top-level input model, QGen generates code that references the model argument variables. However, they are not initialized, since it is not possible to know their values in the context where the code is being integrated. The user must initialize all its model argument variables before calling the init function, or preferably, the qgen_entry_[modelname]_init function.

For example, consider the following input model defining myArray and myStruct variables as model arguments:

_images/model_args.png

where the model arguments are defined in the model workspace as:

_images/model_args_explorer.png

The generated init function without the --gen-entrypoint option is shown below.

void input_model_initStates
  (GAREAL const myArray[3],
  myStruct_struct const* const myStruct,
  input_model_State* const State)
{
  GAUINT8 i;

  for (i = 0; i <= 2; i++) {
      State->myArray[i] = myArray[i];
  }
  State->myStruct = *myStruct;
}

The model arguments are generated as formal parameters of the init function and then assigned to the state structure containing memories for the model.

When using the --gen-entrypoint option, QGen also generates formal parameters for the model arguments of the qgen_entry_input_model_init, which passes them to the init function.

void qgen_entry_input_model_init
  (GAREAL const myArray[3],
  myStruct_struct const* const myStruct)
{
  input_model_initStates (
      myArray,
      myStruct,
      &input_model_memory);
}

When integrating the generated code, the user must then ensure that model arguments are completely and correctly initialized before calling the above function:

#include "qgen_entry_input_model.h"

  typedef struct {
    GAREAL field1;
    GAREAL field2;
  } myStruct_struct;

  myStruct_struct myStruct_val;
  const GAREAL myArray_val[3] = {1.01E+02, 2.01E+02, 3.01E+02};

  myStruct_val.field1 = 1.1;
  myStruct_val.field2 = 2.1;

  qgen_entry_input_model_init (
      myArray_val,
      &myStruct_val);

3.8. Multirate models

QGen supports code generation for multirate models in multitasking mode i.e. the models where the flag “Treat each discrete rate as a separate task” is set.

_images/multirate_param.png

For such a model, QGen groups blocks with the same period and offset and teats each of these groups as a root-level subsystem (also referred to as “task subsystem” in this document) with name:

<model name>_T<period>[_O<offset>]

Each of these new task subsystems will be converted to a target language module (a task module). For instance the model below with two unique rates

_images/multirate_model.png

will be converted to two modules as follows:

_images/multirate_files.png

Note, that the period and offset are encoded in scientific notation where commas are replaced with underscores, “-” with “m” and exponent “0” is dropped. In this example

  • “T2_0em01” corresponds to “T=2.0e-01”

  • “T1_2” corresponds to “T=1.2e00”

If the exponent was positive e.g for period T=20 the corresponding notation in module name would be “T2_0e01”.

The patterns for generating module functions and entrypoint modules are the same as with the single root subsystem of a single-rate model:

_images/multirate_t1.2.png

Also, an entrypoint module is generated for each of the task modules in case the –gen-entrypoint switch is used.

_images/multirate_entry_t1.2.png

Composing a scheduler that executes the generated tasks with correct rate is the responsibility of the user. The scheduler shall:

  • Execute once the *_init functions of all generated modules (here and in the following bullet points, “all generated modules” refer to entrypoint modules when –gen-entrypoint is used, modules generated from individual subsystems otherwise)

  • Execute each of the task module *_comp (and *_up if –gen-entrypoint was not used) at the frequency noted in the name of the module.

An example of a simple interleaving scheduler for the given example would be:

_images/multirate_scheduler.png

The function Run_Init_Step calls all the init functions from the entrypoint modules:

  • qgen_entry_RT_init – initializes the workspace variables

  • qgen_entry_RT_T1_2_init – state variables for the blocks running at rate 1.2

  • qgen_entry_RT_T2_0Em01_init – state variables for the blocks running at rate 0.2

The function Simulation_Run_Comp_Step shall be executed at the base rate of the model (0.2 in this case). The global variable tick controls the execution of each individual task:

  • qgen_entry_RT_T1_2_comp – shall be executed at rate 1.2 i.e on every 6th step.

  • qgen_entry_RT_T2_0Em01_comp – shall be executed at each step.

Warning

Importantly, to achieve the simulation results that match with Simulink simulation at each step of execution, the tasks processing inputs should be executed before those processing outputs.

3.9. 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/config of your QGen installation.

It is also possible to specify a file to be used as header using the –custom-header command line option or Custom Header File field.

3.10. 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 base type. Typically Simulink treats this signal as an array of the common base type.

Non-homogeneous virtual buses are buses with mixed leaf element data types, or homogeneous base types but with some leaf elements that are non-virtual buses. 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 data types or other buses. We refer to bus signals containing one or more elements with bus data type as nested buses.

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

3.10.2. QGen conversion for bus data types

  • 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 data type 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 data type defined in Matlab workspace.

3.11. 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 Workspace Containers and Block Parameters.

QGen will generate structures differently depending on the type of bus used. Considering the model below, that uses all the types of buses that QGen supports:

_images/qgen_bus_gen_1.png

QGen will generate the following types (example is in C, but the equivalent Ada types would be used when generating Ada) :

_images/qgen_bus_gen_2.png
  • 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.

  • Non-virtual buses use the datatype defined in the MATLAB workspace.

_images/qgen_bus_gen_3.png
  • 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.

_images/qgen_bus_gen_4.png

3.11.1. General Constraints for Custom Data Types

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

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

3.11.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.'];

3.11.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]';

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

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

3. 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;

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

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

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

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

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

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

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

3.15. Detailed Code Generation Options

3.15.1. Flattening

The following options are available and influence the generated code packaging, in the form of Ada package or C body/header pair.

  • Flatten non reusable subsystems: Atomic subsystems do not create their own package if they are not marked as reusable. Use Simulink Function Packaging in the Code Generation options of the Subsystem to specify that property.

  • Flatten all: Specifies to flatten all subsystems and models in a single package.

  • Flatten references: Default. Each model is flatten in its own package, but library instances are still factorized.

  • No flattening: Maximum amount of packages created. Only virtual subsystems do not end up in their own package.

_images/qgen_no_flat.png

3.15.2. Coding Rules and Formatting

3.15.2.1. Remove unused enable

Off by default. When set to on, unused code generated for enable blocks will be removed from the generated code. This has the downside of no longer making the code generation modular and disables the capability to generate code incrementally from these sources.

3.15.2.2. Consts as vars

Off by default. QGen will generate constants as variables allowing the generated code to be tuned at runtime.

3.15.2.3. Remove goto

Off by default. Removes goto statement, only relevant for Stateflow Charts code.

3.15.2.4. Remove assertions

Remove code for Assertion blocks and their inputs.

Example:

_images/remove_assertion.png

Without the “Remove assertion” option:

void example_remove_assertion_comp
 (GAREAL const In2,
  GAREAL* const Out1,
  GAREAL* const Out2,
  example_remove_assertion_State* const State)
{
  /* Block 'example_remove_assertion/Unit Delay' */
  /* Block 'example_remove_assertion/Out1' */
  *Out1 = State->Unit_Delay_memory;
  /* End Block 'example_remove_assertion/Out1' */
  /* End Block 'example_remove_assertion/Unit Delay' */

  /* Block 'example_remove_assertion/In2' */
  /* Block 'example_remove_assertion/Out2' */
  *Out2 = In2;
  /* End Block 'example_remove_assertion/Out2' */
  /* End Block 'example_remove_assertion/In2' */

  /* Block 'example_remove_assertion/Equal' */
  /* Block 'example_remove_assertion/In2' */
  /* Block 'example_remove_assertion/Unit Delay' */
  /* Block 'example_remove_assertion/Assertion' */
  assert (State->Unit_Delay_memory == In2);
  /* End Block 'example_remove_assertion/Assertion' */
  /* End Block 'example_remove_assertion/Unit Delay' */
  /* End Block 'example_remove_assertion/In2' */
  /* End Block 'example_remove_assertion/Equal' */

}

With the “Remove assertion” option:

void example_remove_assertion_comp
 (GAREAL const In2,
  GAREAL* const Out1,
  GAREAL* const Out2,
  example_remove_assertion_State* const State)
{
  /* Block 'example_remove_assertion/Unit Delay' */
  /* Block 'example_remove_assertion/Out1' */
  *Out1 = State->Unit_Delay_memory;
  /* End Block 'example_remove_assertion/Out1' */
  /* End Block 'example_remove_assertion/Unit Delay' */

  /* Block 'example_remove_assertion/In2' */
  /* Block 'example_remove_assertion/Out2' */
  *Out2 = In2;
  /* End Block 'example_remove_assertion/Out2' */
  /* End Block 'example_remove_assertion/In2' */

}

3.15.2.5. Unroll threshold

Modifies the way binary operations on arrays are generated.

When an array has less or the same number of elements than the specified threshold, binary operations on the array will be generated as a sequence of elementary binary operations on each element. Otherwise, the binary operations will be expanded to a loop statement iterating over the elements of the array. No value by default, in which case QGen uses 6 as the default value.

This code generation option mainly applies to blocks that perform arithmetic operations on a single, non-scalar input, e.g.:

  • Product

  • Sum

  • Logic

  • BitwiseOperator

Example with a threshold of 7, where only the Product3 block has a vector input with more than 7 elements:

_images/unroll_threshold_7.png

As can be seen in the generated code (example for C), the product operations for blocks Product, Product1 and Product2 are generated as a sequence of product operations. However, for Product4, a loop statement iterating over all elements of the input is generated.

void unroll_threshold_example_comp
 (GAREAL const In1[5],
  GAREAL const In3[6],
  GAREAL const In4[7],
  GAREAL const In2[8],
  GAREAL* const Out1,
  GAREAL* const Out3,
  GAREAL* const Out4,
  GAREAL* const Out2)
{
  GAUINT8 i;

  /* Block 'unroll_threshold_example/Product' */
  /* Block 'unroll_threshold_example/In1' */
  /* Block 'unroll_threshold_example/Out1' */
  *Out1 = 1.0E+00 * In1[0] * In1[1] * In1[2] * In1[3] * In1[4];
  /* End Block 'unroll_threshold_example/Out1' */
  /* End Block 'unroll_threshold_example/In1' */
  /* End Block 'unroll_threshold_example/Product' */

  /* Block 'unroll_threshold_example/Product1' */
  /* Block 'unroll_threshold_example/In3' */
  /* Block 'unroll_threshold_example/Out3' */
  *Out3 = 1.0E+00 * In3[0] * In3[1] * In3[2] * In3[3] * In3[4] * In3[5];
  /* End Block 'unroll_threshold_example/Out3' */
  /* End Block 'unroll_threshold_example/In3' */
  /* End Block 'unroll_threshold_example/Product1' */

  /* Block 'unroll_threshold_example/Product2' */
  /* Block 'unroll_threshold_example/In4' */
  /* Block 'unroll_threshold_example/Out4' */
  *Out4 = 1.0E+00 * In4[0] * In4[1] * In4[2] * In4[3] * In4[4] * In4[5] * In4[6];
  /* End Block 'unroll_threshold_example/Out4' */
  /* End Block 'unroll_threshold_example/In4' */
  /* End Block 'unroll_threshold_example/Product2' */

  /* Block 'unroll_threshold_example/Product3' */
  /* Block 'unroll_threshold_example/In2' */
  /* Block 'unroll_threshold_example/Out2' */
  *Out2 = 1.0E+00;
  for (i = 0; i <= 7; i++) {
     *Out2 = *Out2 * In2[i];
  }
  /* End Block 'unroll_threshold_example/Out2' */
  /* End Block 'unroll_threshold_example/In2' */
  /* End Block 'unroll_threshold_example/Product3' */

}