Skip to content

Referenced Models

Danish Belal edited this page Dec 20, 2020 · 1 revision

Referenced Models

Referenced models exhibit special behaviour regarding C-API access. This document explains my findings.

Examples in this document assume the following hierarchy.

  • Controller
    • ControllerRef

ControllerRef is a (useless) model that is referenced by Controller. ControllerRef has an instance specific model parameter "SubmodelConfig" which is passed in by Controller (therefore Controller owns storage of the parameter and ControllerRef has only a non-owning reference to it).

Normal C-API Access without referenced models

Embedded Coder generates a ModelName_capi.c file that contains definitions like:

static rtwCAPI_BlockParameters rtBlockParameters[] = {
    /* addrMapIndex, blockPath, paramName, dataTypeIndex, dimIndex, fixPtIdx */
    {10, TARGET_STRING("Controller/Constant"), TARGET_STRING("Value"), 0, 1, 0 },
    {11, TARGET_STRING("Controller/Discrete-Time Integrator"), TARGET_STRING("gainval"), 0, 0, 0 },
    {12, TARGET_STRING("Controller/Discrete-Time Integrator"), TARGET_STRING("InitialCondition"), 0, 0, 0 },
    {13, TARGET_STRING("Controller/ConfigDDGain"), TARGET_STRING("Gain"), 0, 0, 0 },
    {14, TARGET_STRING("Controller/SuperDuperGainBlock"), TARGET_STRING("Gain"), 0, 0, 0 },
    {15, TARGET_STRING("Controller/Model"), TARGET_STRING("SubmodelConfig"), 1, 0, 0 },
    {16, TARGET_STRING("Controller/AlgebraicLoopBreaker"), TARGET_STRING("InitialCondition"), 0, 0, 0 },
    {0, (NULL), (NULL), 0, 0, 0 }
};

/* Tunable variable parameters */
static rtwCAPI_ModelParameters rtModelParameters[] = {
    /* addrMapIndex, varName, dataTypeIndex, dimIndex, fixPtIndex */
    {19, TARGET_STRING("ModelConfig"), 1, 0, 0 },
    {20, TARGET_STRING("mMatrix"), 0, 1, 0 },
    {21, TARGET_STRING("X3_DD"), 0, 2, 0 },
    {22, TARGET_STRING("X4_DD"), 0, 3, 0 },
    {0, (NULL), 0, 0, 0 }
};

/* ... */

These definitions describe the available C-API elements (Signals, Parameters, ...) and provide additional information about them. This information is required to access the underlying data and modify model behaviour.

Later on, this data is merged in an ModelMappingInfo:

static rtwCAPI_ModelMappingStaticInfo mmiStatic = {
  /* Signals: {signals,     numSignals,
   *           rootInputs,  numRootInputs,
   *           rootOutputs, numRootOutputs},
   * Params: {blockParameters, numBlockParameters,
   *          modelParameters, numModelParameters},
   * States: {states, numStates},
   * Maps:   {dataTypeMap, dimensionMap, fixPtMap,
   *          elementMap, sampleTimeMap, dimensionArray},
   * TargetType: targetType
   */
    {
        rtBlockSignals, 10,
        (NULL), 0,
        (NULL), 0 },

    { rtBlockParameters, 7,
        rtModelParameters, 4 },

    { rtBlockStates, 2 },

    { rtDataTypeMap, rtDimensionMap, rtFixPtMap,
        rtElementMap, rtSampleTimeMap, rtDimensionArray },
    "float",

    { 1252166140U,
        3602851096U,
        2959716157U,
        3429659910U },
    (NULL), 0,
    0
};

The ModelMappingInfo object is declared static and as such only available within the current compilation unit. So how do we access it from our code?

Easy. The model initialization function grabs a pointer to the ModelMappingInfo and puts it in the ModelStruct (RT_MODEL_Controller_T).

rtwCAPI_SetStaticMap(Controller_M->DataMapInfo.mmi, &mmiStatic);

This expression is located in Controller_InitializeDataMapInfo, which is called by Controller_initialize.

Apparently the single instance mmiStatic is shared across all model instances. This is not a problem, because it does not own any storage.

The RT_MODEL_Controller_T looks like this:

/* Real-time Model Data Structure */
struct tag_RTM_Controller_T
{
  const char_T * volatile errorStatus;
  BlockIO_Controller_T *blockIO;
  D_Work_Controller_T *dwork;

  /*
   * DataMapInfo:
   * The following substructure contains information regarding
   * structures generated in the model's C API.
   */
  struct
  {
    rtwCAPI_ModelMappingInfo mmi;
    void* dataAddress[23];
    int32_T* vardimsAddress[23];
    RTWLoggingFcnPtr loggingPtrs[23];
    rtwCAPI_ModelMappingInfo* childMMI[1];
  }
  DataMapInfo;

  InstP_Controller_T *Controller_InstP_ref;
};

Throwback

Until now, we can access all elements of the root Model (aka Controller). Controller's mmiStatic object does not know about elements in referenced submodels. How are they accessed?

Accessing elements in referenced models

The referenced model has the same pattern of definitions like its parent model does:

static rtwCAPI_BlockParameters rtBlockParameters[] =
{
  /* addrMapIndex, blockPath, paramName, dataTypeIndex, dimIndex, fixPtIdx */
  {2, TARGET_STRING("ControllerRef/NormalGain"), TARGET_STRING("Gain"), 0, 0, 0},
  {0, (NULL), (NULL), 0, 0, 0}
};

static rtwCAPI_ModelParameters rtModelParameters[] =
{
  /* addrMapIndex, varName, dataTypeIndex, dimIndex, fixPtIndex */
  {3, TARGET_STRING("SubmodelConfig"), 1, 0, 0},
  {0, (NULL), 0, 0, 0}
};

/* ... */

Later on, this data is merged into a larger structure.

static rtwCAPI_ModelMappingStaticInfo mmiStatic =
{
  /* Signals:{signals,     numSignals,
   *          rootInputs,  numRootInputs,
   *          rootOutputs, numRootOutputs},
   * Params: {blockParameters, numBlockParameters,
   *          modelParameters, numModelParameters},
   * States: {states, numStates},
   * Maps:   {dataTypeMap, dimensionMap, fixPtMap,
   *          elementMap, sampleTimeMap, dimensionArray},
   * TargetType: targetType
   */
  {
    rtBlockSignals, 2,
    (NULL), 0,
    (NULL), 0
  },

  {
    rtBlockParameters, 1,
    rtModelParameters, 0
  },

  {
    rtBlockStates, 0
  },

  {
    rtDataTypeMap, rtDimensionMap, rtFixPtMap,
    rtElementMap, rtSampleTimeMap, rtDimensionArray
  },
  "float",

  {
    855875857U,
    1953457339U,
    4263635231U,
    4058057694U
  },
  (NULL), 0,
  0
};

Therefore, we now have two mmiStatic objects containing information about their models. This information needs to be linked and we are now going to discuss how this is done.

Review the definition of Controllers model-struct:

/* Real-time Model Data Structure */
struct tag_RTM_Controller_T
{
  const char_T * volatile errorStatus;
  BlockIO_Controller_T *blockIO;
  D_Work_Controller_T *dwork;

  /*
   * DataMapInfo:
   * The following substructure contains information regarding
   * structures generated in the model's C API.
   */
  struct
  {
    rtwCAPI_ModelMappingInfo mmi;
    void* dataAddress[23];
    int32_T* vardimsAddress[23];
    RTWLoggingFcnPtr loggingPtrs[23];
    rtwCAPI_ModelMappingInfo* childMMI[1];
  }
  DataMapInfo;

  InstP_Controller_T *Controller_InstP_ref;
};

Not only does it contain our well known DataMapInfo.mmi element, but also an DataMapInfo.childMMI object. Note that it is a pointer and therefore non-owning. It is filled in by the initialization function of the referenced model.

This is how its called...

ControllerRef_initialize(rtmGetErrorStatusPointer(Controller_M),
                &(Controller_DWork->Model_InstanceData.rtm),
                &(Controller_DWork->Model_InstanceData.rtb),
                &(Controller_M->DataMapInfo.mmi),
                "Controller/Model",
                0,
                -1);

... and this is its definition.

/* Model initialize function */
void ControllerRef_initialize(const char_T** rt_errorStatus,
    RT_MODEL_ControllerRef_T* const ControllerRef_M,
    rtB_ControllerRef_T* localB,
    rtwCAPI_ModelMappingInfo* rt_ParentMMI,
    const char_T* rt_ChildPath,
    int_T rt_ChildMMIIdx,
    int_T rt_CSTATEIdx)
{
    InstP_ControllerRef_T* ControllerRef_InstP_arg = ((InstP_ControllerRef_T*)ControllerRef_M->ControllerRef_InstP_ref);

    (void)memset((void*)ControllerRef_M, 0, sizeof(RT_MODEL_ControllerRef_T));
    ControllerRef_M->ControllerRef_InstP_ref = ControllerRef_InstP_arg;
    rtmSetErrorStatusPointer(ControllerRef_M, rt_errorStatus);
    (void)memset(((void*)localB), 0, sizeof(rtB_ControllerRef_T));
    /* Initialize DataMapInfo substructure containing ModelMap for C API */
    {
        ControllerRef_InitializeDataMapInfo(ControllerRef_M, localB);
    }

    /* Initialize Parent model MMI */
    if ((rt_ParentMMI != (NULL)) && (rt_ChildPath != (NULL)))
    {
        rtwCAPI_SetChildMMI(*rt_ParentMMI, rt_ChildMMIIdx, &(ControllerRef_M->DataMapInfo.mmi));
        rtwCAPI_SetPath(ControllerRef_M->DataMapInfo.mmi, rt_ChildPath);
        rtwCAPI_MMISetContStateStartIndex(ControllerRef_M->DataMapInfo.mmi, rt_CSTATEIdx);
    }
}

Have a look at the if statement in ControllerRef_initialize:

/* Initialize Parent model MMI */
if ((rt_ParentMMI != (NULL)) && (rt_ChildPath != (NULL)))
{
    rtwCAPI_SetChildMMI(*rt_ParentMMI, rt_ChildMMIIdx, &(ControllerRef_M->DataMapInfo.mmi));
    rtwCAPI_SetPath(ControllerRef_M->DataMapInfo.mmi, rt_ChildPath);
    rtwCAPI_MMISetContStateStartIndex(ControllerRef_M->DataMapInfo.mmi, rt_CSTATEIdx);
}

This is where it gets interesting: if the model is instantiated as a referened submodel, rt_ParentMMI and rt_ChildPath will be non-null.

In the call above, rt_ParentMMI is set to the address of Controllers own mmi, and rt_ChildPath is set to "Controller/Model"- this is the blockpath from within Controller to the Block that referenced ControllerRef.

ControllerRef_initialize will do two things:

  • link its own MMI into its parents DataMapInfo.childMMI
  • link its model path into its own MMI.

So, what?

All referenced submodels link its information into its parents DataMapInfo. I will once again repeat its definition:

/* Real-time Model Data Structure */
struct tag_RTM_Controller_T
{
  const char_T * volatile errorStatus;
  BlockIO_Controller_T *blockIO;
  D_Work_Controller_T *dwork;

  /*
   * DataMapInfo:
   * The following substructure contains information regarding
   * structures generated in the model's C API.
   */
  struct
  {
    rtwCAPI_ModelMappingInfo mmi;
    void* dataAddress[23];
    int32_T* vardimsAddress[23];
    RTWLoggingFcnPtr loggingPtrs[23];
    rtwCAPI_ModelMappingInfo* childMMI[1];
  }
  DataMapInfo;

  InstP_Controller_T *Controller_InstP_ref;
};
  • All information about elements of Controller are stored in DataMapInfo.mmi
  • Information about referenced submodels in linked through DataMapInfo.childMMI

Therefore, we need to recursively iterate through all MMI's to detect all information of all contained models. This is the quitessence of this document.