Embedded Coder shall use an externally implemented nested struct for read and write access (in place) in it's generated code which is written in C.

2 views (last 30 days)
I want to use an existing nested struct which is originally implemented in an external software project written in C to be used by Simulink.
The Embedded Coder shall generate code that accesses internal elements of that struct through an external variable "myNestedStructVar" of that nested struct type.
The Embedded Coder shall not run the access (especially for write) through a copy of "myNestedStructVar" into a second struct but use the one and only "myNestedStructVar".
When the bus assignment is used the Embedded Coder generates a copy of the variable, writes into the element of interest and copies the whole content back to "myNestedStructVar". So no assignment in place.
The generated code is used by the external software.
I can't figure out how to solve that task.
  2 Comments
Sahas
Sahas on 23 Oct 2024
Can you please elaborate what do you mean by this part The Embedded Coder shall generate code that accesses internal elements of that struct
and this part,
When the bus assignment is used the Embedded Coder generates a copy of the variable, writes into the element of interest and copies the whole content back to "myNestedStructVar". So no assignment in place.
It will help me assist you better.
Michael
Michael on 23 Oct 2024
Hello Sahas. Thank you for answering.
Sorry for the long answer, in advanced...
Say I have a struct like :
typedef struct s_pfc_tag {
struct s_subStruct_A_tag {
volatile float32_t varA;
volatile float32_t varA1;
}s_subStruct_A;
struct s_subStruct_B_tag {
volatile float32_t varB;
}s_subStruct_B;
struct s_subStruct_C_tag {
volatile float32_t varC;
volatile float32_t varC1;
volatile float32_t varC2;
}s_subStruct_C;
struct s_subStruct_D_tag {
volatile float32_t varD;
}s_subStruct_D;
struct s_subStruct_E_tag{
volatile float32_t varE;
}s_subStruct_E;
}MAINSTRUCT_ts;
I converted this struct into a number of Simulink.Bus objects through
Simulink.importExternalCTypes(...);
Then I built a Simulink model and created a Data Store Memory block called "mainStructVar" of data type "Bus: MAINSTRUCT_ts" like
I don't want to go into more details about the structure of the subsystems, beside that subsystem Func1 has a function generator while the others don't. All subsystems have read and write access to the Data Store Memory called "mainStructVar".
The generated code is like :
/* Model output function for TID0 */
void DataStoreTest_output0(void) /* Sample time: [1.0E-5s, 0.0s] */
{
/* DataTypeConversion: '<S1>/Cast To Boolean' incorporates:
* DataStoreRead: '<S1>/ds_varE'
*/
DataStoreTest_DW.CastToBoolean = (mainStructVar.s_subStruct_E.varE != 0L);
/* S-Function (fcgen): '<S1>/Function-Call Generator1' */
/* ModelReference: '<S1>/Func1_run' incorporates:
* DataStoreRead: '<S1>/ds_varA'
* DataStoreRead: '<S1>/ds_varB'
*/
Func1_run(mainStructVar.s_subStruct_A.varA,
mainStructVar.s_subStruct_B.varB,
DataStoreTest_DW.CastToBoolean,
&DataStoreTest_DW.resultFunc1);
/* End of Outputs for S-Function (fcgen): '<S1>/Function-Call Generator1' */
/* DataStoreWrite: '<S1>/ds_varC' */
mainStructVar.s_subStruct_C.varC = DataStoreTest_DW.resultFunc1;
/* DataStoreWrite: '<S6>/ds_varD' incorporates:
* DataStoreRead: '<S6>/ds_varC1'
* ModelReference: '<S6>/Func2'
*/
Func2_call(mainStructVar.s_subStruct_C.varC1, &mainStructVar.s_subStruct_D.varD);
/* DataStoreWrite: '<S4>/ds_varB' incorporates:
* DataStoreRead: '<S4>/ds_varA1'
* DataStoreRead: '<S4>/ds_varC2'
* DataStoreRead: '<S4>/ds_varD'
* ModelReference: '<S4>/Func3'
*/
Func3_run(mainStructVar.s_subStruct_A.varA1, mainStructVar.s_subStruct_D.varD,
mainStructVar.s_subStruct_C.varC2,
&mainStructVar.s_subStruct_B.varB);
}
And here is the thing:
Functions "Func2_call" and "Func3_run" only uses variables from the former mentioned struct-variable "mainStructVar". But for "Func1_run" the Embedded coder introduces "DataStoreTest_DW.resultFunc1" to hold the result of the function call. The result is assigned to the corresponding element in "mainStructVar" in a second step. This is what I want to avoid. "Func1_run" shall use a pointer to "mainStructVar.s_subStruct_C.varC" instead of to "DataStoreTest_DW.resultFunc1".
This example is a placeholder for something that shall be called on the target system every 10µs. So it's time critical and therefore I want to avoid additional assignments which are likely not needed.
During the first approach I used "Bus Selector" and "Bus Assignment" -blocks to let the Embedded Coder generate code which accesses the elements within the struct-variable "mainStructVar". But when it comes to write accesses, which I found to be realized through a "Bus Assignment" - block, in the generated code the whole structure is copied into a "temp"- variable of the same data type like the structvar "mainStrucVar" at the beginning of the function body. Then, the related element in the "temp" -variable is updated and afterwords the element in the "temp"-variable is assigned to "mainStructVar". This is not suitable for function calles every 10 µs.
So I started to use Data Store blocks. This fixed the temp copy of the whole struct, at least.
But it seems that I missed something during the model configuration which prevents using "temp-variables" when using Function-Call generators.
The option in "Code Generation>Optimization>Details>Perform in-place updates for Assignment and Bus Assignment blocks" is enabled.
I hope this helps.
Thank you for your help.

Sign in to comment.

Answers (1)

Mark McBroom
Mark McBroom on 2 Nov 2024
A couple of ideas.
  1. It sounds like the functional call sub-system is set to be reusable. Try to make it non-reusable. Follow these steps except instead of selecting "inline" select "non-reuslable". https://www.mathworks.com/help/rtw/ug/inline-subsystem-code-ecoder.html#
  2. You could also use data store read/write blocks inside the function call subsystem.
  1 Comment
Michael
Michael on 5 Nov 2024
Hello Mark,
thank you for your ideas.
Regarding idea 1 I'm already using the configuration setting "non-reusalbe".
Indead idea 2 works but now my simulation model which uses the referenced models has to be redesigned, too. But more important than that is the fact that all function bodies will include the global variable structs element names now. This is not what I want to accomplish.
I'm fine with using the global struct element names as arguments to all the functions designed within the referenced models. All the functions are called from a single place like function "main" or at least a single file like "main.c". But I don't want to use the global variable structs element names within function bodies.
In fact the Embedded Coder generates exactly what I want - for models which are not called by a Function-call Generator.
But as soon as a model uses a Function-call Generator, local variables are used. Every argument in a function's argument list is a local (pointer) variable. None of the global variable struct element is used directly.
It looks like this:
modelname_DW.var1 = globalVar.innerStruct1.stVar11;
modelname_DW.var2 = globalVar.innerStruct2.stVar21;
modelname_DW.var3 = globalVar.innerStruct3.stVar31;
/* S-Function (fcgen): '<S1>/Function-Call Generator1' */
/* ModelReference: '<S1>/A_Model_Name' */
A_Model_Name_run(modelname_DW.var1, modelname_DW.var2, modelname_DW.var3,
&modelname_DW.var4);
globalVar.innerStruct4.stVar41 = modelname_DW.var4;
When a referenced model has no Function-Call Generator in use, the Embedded Coder generates code like that:
/* ModelReference: '<S4>/B_Model_Name'*/
B_Model_Name(globalVar.innerStruct5.stVar51, &globalVar.innerStruct6.stVar61);
Again, thanks for your ideas.

Sign in to comment.

Categories

Find more on Deployment, Integration, and Supported Hardware in Help Center and File Exchange

Products


Release

R2024b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!