-
Notifications
You must be signed in to change notification settings - Fork 1
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).
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;
};
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?
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.
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.