Files
powertrain-build/docs/target_link/enumerations.md
Henrik Wahlqvist 475c4aaa47 Try using data type from Simulink class for enumerations
Change-Id: I569d06f93faabcd96987b71140464d5149068262
2025-04-07 13:03:41 +02:00

14 KiB

TargetLink Enumerations


[TOC]

As of TargetLink version 4.3, there is support for using enumerations in Simulink models and generated code. This section describes how to use this new feature.

Defining an Enumeration

All enumerations must be defined as Matlab classes, inheriting from the Simulink.IntEnumType class. These files should be placed in the same folder, e.g. Models/Common/VcEnumDefinitions. This makes them easy to find and keep unique. Unique enumeration definitions are required. The name of the enumeration must match the name of the file.

Defining the Underlying Data Type

As of powertrain-build version 1.5.0, it is possible to specify the underlying data type. There was already a concern regarding the underlying data type in different compilers, for more informatin see Pitfalls.

The new version changes the default behaviour from calculating the smallest possible data type to using the one specified in the Simulink class definition file.

This is done by parsing the classdef SomeEnumName < Simulink.IntEnumType line, at the top of the Simulink enumeration definition file.

If calculating the underlying data type is desired, one must add a new class function called calculateUnderlyingDataType and set the return value to true.

Matlab enumeration template:

classdef SomeEnumName < Simulink.IntEnumType
    %SOMEENUMNAME SomeEnumName enumeration

    enumeration
        MemberOne(0)
        MemberTwo(1)
        MemberThree(2)
    end

    methods (Static)
        function retVal = getDescription()
            retVal = 'SomeEnumName';
        end
        function retVal = getDefaultValue()
            retVal = SomeEnumName.MemberOne;
        end
        function retVal = getDataScope()
            retVal = 'Exported';
        end
        function retVal = addClassNameToEnumNames()
            retVal = true;
        end
        function retVal = calculateUnderlyingDataType()
            retVal = false;
        end
    end
end

Using an Enumeration

When the enumeration is defined, it can be used in, for example, an inport or a constant block. Many TargetLink blocks accept enumeration type inputs, such as TL_MultiPortSwitch and TL_RelationalOperator.

To get the enumeration in the code, the data type IMPLICIT_ENUM must be chosen in the Type field, in the TargetLink block. Additionally, the name of the enumeration must be specified in the Unit field in the TargetLink block, together with the prefix "-,$". For example, "-,$DtElecThermSt". Signals must specify the quantity "_Enm_" in the name, e.g. sVcModelName_Enm_Dummy.

In- and Out-ports

TLEnumInport

Constants

This sections provides examples for constants with different variable classes.

Default

This class only works on constants on the top most layer of the model.

enum_constant

CVC_VAR

This class generates the same code as the default class does. There is no need for a name as the code produces pure string values.

enum_constant_var

A CVC_VAR constant compared with an input of the same enumeration:

/* Relational: VcModelName/VcModelName/6_ControllerRequestForSecondRowWhenNotFourZoneCont
   rol/Relational Operator2 */
SModelName10___ional_Operator2 = CLIMACFGFORNROFTSETG_NROFTSETG2 ==
 sVcModelNameTwo_Enm_CatcCfgForNrOfTSetg;

CVC_CONST

This class generates a constant. The constant values does not come from the Matlab workspace, it does not exist in any _par.m file. Instead, it is loaded in the memory as a Simulink class after running Init_PyBuild.m.

enum_constant_const

A CVC_CONST constant compared with an input of the same enumeration:

if (cEnm_ClimaCfgForNrOfTSetg1_NrOfTSetg1 == sVcModelNameTwo_Enm_CatcCfgForNrOfTSetg) {
   /* Switch: VcModelName/VcModelName/6_ControllerRequestForSecondRowWhenNotFourZoneCo
      ntrol/Switch5
      # combined # Switch: VcModelName/VcModelName/7_ManualOverrideOfActuatorRequest/S
      witch1 */
   SModelName11_Switch1 = SModelName9_Switch2[0];
}

Definition:

CVC_CONST ClimaCfgForNrOfTSetg cEnm_ClimaCfgForNrOfTSetg1_NrOfTSetg1 =
    CLIMACFGFORNROFTSETG_NROFTSETG1; /*
   Unit: -,$ClimaCfgForNrOfTSetg
   MIN/MAX: 0 .. 0 */

CVC_CAL

This class generates a calibratable constant.

enum_constant_cal

A CVC_CAL constant compared with an input of the same enumeration:

/* Relational: VcModelName/VcModelName/6_ControllerRequestForSecondRowWhenNotFourZoneCont
   rol/Relational Operator1 */
SModelName10___ional_Operator1 = sVcModelNameTwo_Enm_CatcCfgForNrOfTSetg ==
 cVcModelName_Enm_ClimaCfgForNrOfTSetg1_NrOfTSetg3;
}

Definition:

CVC_CAL ClimaCfgForNrOfTSetg cVcModelName_Enm_ClimaCfgForNrOfTSetg1_NrOfTSetg3 =
    CLIMACFGFORNROFTSETG_NROFTSETG3; /* Unit: -,$ClimaCfgForNrOfTSetg */

Note that the block name in this example starts with "Enm_".

Generated Files

This section describes what happens when TargetLink generates source files.

Code

When TargetLink generates code, it creates a new file, e.g. udt_ModelName.h, where it defines all enumerations which the model uses. The text values of the enumeration members are used in the code.

Code snippets:

udt_ModelName.h

/**************************************************************************************************\
 ***
 *** Simulink model       : VcModelName_OPortMvd
 *** TargetLink subsystem : VcModelName_OPortMvd/VcModelName
 *** Codefile             : udt_ModelName.h
 ***
 *** Generation date: 2020-11-30 16:10:50
 ***
 *** TargetLink version      : 4.3p3 from 16-Oct-2018
 *** Code generator version  : Build Id 4.3.0.27 from 2018-09-24 17:55:03
\**************************************************************************************************/

#ifndef UDT_MODELNAME_H
#define UDT_MODELNAME_H

#include "tl_basetypes.h"

typedef enum DtElecThermSt_tag {
   DTELECTHERMST_DTELECCOOLG = 0,
   DTELECTHERMST_DTELECPASCOOLG = 1,
   DTELECTHERMST_DTELECHEATG = 2,
   DTELECTHERMST_DTELECPASHEATG = 3,
   DTELECTHERMST_DTELECACTVHEATG = 4,
   DTELECTHERMST_DTELECPASACTVHEATG = 5,
   DTELECTHERMST_DEGAS = 6,
   DTELECTHERMST_FAILSAFE = 7
} DtElecThermSt; /* Description: Enumeration type derived from Simulink type DtElecThermSt */

VcModelName.h

/**************************************************************************************************\
   CVC_EXT: CVC external interface input variables | Width: N.A.
\**************************************************************************************************/
CVC_EXT DtElecThermSt sVcModelNameThree_Enm_DtElecThermSt; /*
   Unit: -,$DtElecThermSt
   Description: Electric drive coolant circuit thermal status: 'DtElecPasCoolg', 'DtElecPasHeatg', '
   DtElecPasActvHeatg', 'DtElecPas' */
CVC_EXT HvBattThermSt sVcModelNameThree_Enm_HvBattThermSt; /*
   Unit: -,$HvBattThermSt
   Description: HV battery coolant circuit thermal status: 'HvBattOff', 'HvBattActvHeatgFromClima', 
   'HvBattBal', 'HvBattActvCoolg', 'HvBattPasCoolg', 'HvBattPasActvCoolg', 'HvBattPasHeatg', 'HvBatt
   PasActvHeatgFromClima', 'HvBattPasActvHeatgFromDtElec', 'HvBattPasActvHeatgFromDtElecAndClima' */

VcModelName.c

   /* Multiport switch: VcModelName/VcModelName/8_VlvCtrl/81_V1RadiatorBypassValveControl/
      MultiportSwitch3 */
   switch (sVcModelNameThree_Enm_DtElecThermSt) {
      case DTELECTHERMST_DTELECPASHEATG: {
         SModelName115_MultiportSwitch3 = 1;
         break;
      }
      case DTELECTHERMST_DTELECPASACTVHEATG: {
         SModelName115_MultiportSwitch3 = 1;
         break;
      }
      default: {
         SModelName115_MultiportSwitch3 = 0;
         break;
      }
   }

A2L Files

The unit A2L-files are updated automatically with enumeration definitions.

JSON Configuration Files

The data type of each enumeration type signal will be the name of the corresponding enumeration. Note that the data type is set to IMPLICIT_ENUM in the TargetLink block.

The default value of the enumeration will be inserted into the configuration file, based on the return value of getDefaultValue() in the enumeration definition m-file.

RTE Interface Enumerations

As of powertrain-build 1.2.1, it is possible to use enumerations from the RTE interface directly in TargetLink. Previously, this was not possible as TargetLink would define its own version of the enum, and implicit cast between enumerations and double declaration of an enum are not allowed.

To enable this feature set mapToRteEnums to true in the project config under CodeGenerationConfig, or in the project template in BaseConfig.

Define enumerations in the enum definition folder normally. Assign external enum signal to internal (and vice versa for outbound signals) in the definition of the RTE call function, just like for other external signals in Zone Controller/GPA.

There is also an option to generate defines for all enumerations pointed at by "enumDefDir". To enable this feature set includeAllEnums to true in the project config under CodeGenerationConfig, or in the project template in BaseConfig.

{
  ...
  "CodeGenerationConfig": {
    "mapToRteEnums": true,
    "includeAllEnums": true
    ...
  },
  ...
}

Implementation and Risks

With this feature enabled, all instances of enum members in source code will be mapped to RTE counterparts if they are defined, using the "ifndef" pragma.

#ifndef ActrReq_Off

typedef enum ActrReq_tag {
   ACTRREQ_OFF = 0, 
   ACTRREQ_ON = 1, 
   ACTRREQ_AUTON = 2 
} ActrReq;

#else
#define ACTRREQ_OFF ActrReq_Off
#define ACTRREQ_ON ActrReq_On
#define ACTRREQ_AUTON ActrReq_AutOn
#endif /* ActrReq_Off */

RTE enumerations, in turn, are implemented as define pragmas mapping enum members to integers. This method is less rigorous and more susceptible to errors than using an enum class. TargetLink prevents type casts and comparisons between TargetLink enumerations and other data types. As long as developers are not trying to work around these safeguards, enabling this feature should not cause any issues.

For example, if the Simulink definition of an interface enum differs from its counterpart in the RTE layer (different member order) and a developer uses bit operations on a signal of that enum, the behavior would not be what is expected from reading the projects enum definition.

powertrain-build

When powertrain-build builds a project, see Detailed build options, it will parse all udt_VcModel.h and json configuration files in order to get information about used enumerations in the project.

This information is used to:

  1. Make sure that the same model does not define the same enumeration twice.
    1. This is probably unnecessary. Two enumerations with the same name cannot exist in Simulink at the same time.
  2. Make sure that two (or more) models does not define the same enumeration differently.
    1. This can occur if an enumeration definition is updated without updating generated files based on the old definition.
  3. Define missing enumeration type signals in VcDummy_spm.c/h.

Interface enumerations are also compared with the parsed project enumerations to make sure they are defined correctly.

Old default value extraction

Below method was used before the default values were inserted into the json configuration file, during code generation. It will stay as a fall back for a while, until all model-configuration files have been updated with the default value.

To be able to extract the default values for each enumeration powertrain-build must know the path to the common enumeration class folder. The path is set with the key "enumDefDir" in the ProjectCfg.json file.

{
  "ConfigFileVersion": "0.2.1",
  "BaseConfig" : "../../../ConfigDocuments/BaseConfig.json",
  "ProjectInfo" : {
    "yamlInterface": true,
    "projConfig" : "Dummy",
    "a2LFileName": "SPM.a2l",
    "ecuSupplier" : "Dummy",
    "ecuType" : "",
    "unitCfgDeliveryDir": "./output/UnitCfgs",
    "configDir": "ConfigDocuments",
    "prjCodeswitches": "Codeswitch_Setup*.csv",
    "commonSrcDir": "../../../Models/Common/pybuild_src",
    "prjUnitSrcDir": "../../../Models/*/Vc*/pybuild_src",
    "prjUnitCfgDir": "../../../Models/*/Vc*/pybuild_cfg",
    "prjUnitMdlDir": "../../../Models/*/Vc*",
    "enumDefDir": "../../../Models/Common/VcEnumDefinitions"
    .
    .
    .

Requirements

  • Enumerations must be defined as Matlab classes, inheriting from the Simulink.IntEnumType class.
  • Enumeration class files must have the same name as the enumeration it defines.
  • Enumeration class files must be placed in a common folder.
  • Enumerations are uniquely defined.
  • To get the enumeration in the code, the data type IMPLICIT_ENUM must be selected in the TargetLink block.
  • The name of the enumeration must be specified in the Unit field in the TargetLink block, prefixed with "-,$".
  • Signals must specify the quantity "_Enm_" in the name.
  • Interface enumerations must be defined in the interface_data_types.yml file, located in the conf.local project folder.

Pitfalls

Calculating the underlying data type turned out to be a problem for some compilers, hence the default behaviour was changed, see Defining the Underlying Data Type for more information.

History

When TargetLink generates the A2L-file, it must know the underlying data type to use. The underlying data type is calculated based on the number of enumeration members and their values. When powertrain-build parses the enumeration it does not know about the underlying data type. Therefore, it must calculate the underlying data type too. This is done in a separate function.

The underlying data type calculations are based on how the compiler would choose a fitting data type, given an enumeration. The smallest underlying data type which can be chosen is an 8 bit integer, two bytes. Some compilers may perform optimization using only one byte. If the software compiles enumerations using one byte, while the A2L-files states they are two bytes, what will happen when calibrating the software?

Summary

  • If one of the functions for calculating the underlying data type is changed, the other must be changed too.