Generate Reentrant Code from Simulink Function Blocks
If you are using Embedded Coder®, you can generate reusable, reentrant code by representing an algorithm as a scoped Simulink Function block. Examples of when to generate reentrant code from Simulink Function blocks are when a function shares state between function callers within a model or for client/server applications. You can generate code that is highly modularized by using multiple instances of a shared Simulink Function block in an export-function model. The code generator produces function code and associates each use or call to the function with instance-specific data. The scope of the function depends on whether you place the function at the root level of model or in a subsystem.
The code generator produces reentrant function code when you configure a:
Top model with model configuration parameter Code interface packaging set to
Reusable function
orC++ class
(C++ only).Referenced model with Total number of instances allowed per top model set to
Multiple
.
Call a function represented by a scoped Simulink Function block from one level above, at the same level, or from a level below the level of the function definition. You can scope a function in an atomic or nonvirtual subsystem, but function call accessibility is limited to the same level or below of the hierarchy. The function name does not have to be unique.
Identify Requirements
Before and while you design your model, consider:
How many instances of each function are required?
Do you need to restrict call sites for a function to the model containing the function definition?
Do you need for a function to interface with signals in the local environment, but keep those signals hidden from callers?
Do functions need to communicate directly with each other?
Do functions need to connect to external I/O?
Do you need to log function output?
Create Model
Use the example model component rtwdemo_comp
to see how to use
Simulink Function blocks to generate reentrant C code. Use example model
rtwdemo_comp_cpp
if you prefer to generate C++ code. Open the
model and examine the model hierarchy.
The top level of the model includes a function-call subsystem and two instances of a referenced model.
The referenced model, rtwdemo_func_dinteg
, consists of a Simulink
Function block that defines function multiinstfunc
and subsystem
subsys_calc
.
The subsystem consists of a Simulink Function block. This use of a Simulink Function
block shows how you can limit the scope of the function that the block defines to the
model that contains the subsystem. The code generator produces function code for
func_calc
and associates each call to the function with
instance-specific data. The data includes states, such as data stored in memory.
The Simulink Function block that defines function multiinstfunc
uses a Function Caller block to invoke function func_calc
. That
Simulink Function block also shows that it can interface to signals in the local
environment of the block through Inport and Outport blocks.
At the top level of model rtwdemo_comp
, the function-call subsystem
uses Function Caller blocks to invoke the two instances of function
multiinstfunc
. The code generator produces function code and
associates each call with instance-specific data.
Configure Model and Model Elements
Configure Simulink Function Blocks
Configure the Simulink Function blocks by setting parameters for the function Trigger Port block. For the code generator to produce reentrant code from Simulink Function blocks:
Configure block instances with the same function name.
Set block parameter Function visibility to
scoped
.
In the example, the function name for the Simulink Function blocks in the two
instances of the referenced model rtwdemo_func_dinteg
is
specified as multiinstfunc
.
Configure Function Callers
Configure blocks to call the Simulink Function block. You can call
a Simulink Function block from a Function Caller
block, Stateflow®
Chart block, or MATLAB Function block. For this
example, use Function Caller blocks. For each of the blocks, set a
value for the block parameter Function prototype. Start typing
a prototype. For example, type y
. Prototype options based on
function definitions in the model appear in a selection list. Select the prototype
that corresponds to each function call.
For this example, the prototypes are configured as follows:
In the function-call subsystem, the prototypes for the function callers are configured as
y = Instance1.multiinstfunc(u)
andy = Instance2.multiinstfunc(u)
. TheInstance
prefix identifies each function invocation uniquely and associates the invocation with its own data set.n
The function caller in function
multiinstfunc
is configured with the prototypey = subsys_calc.func_calc(u)
. The prefixsubsys_calc
identifies the subsystem that contains the function definition.
For the example, the input and output argument specifications and sample time retain default settings.
Subsystem Configuration
Configuration changes are not required for the subsystem in the example model. When you include a Simulink Function block in a subsystem, the code generator
Scopes the function to the model that includes the subsystem.
Treats the subsystem as an atomic unit.
Configure Referenced Model
Configure the referenced model that includes a Simulink Function block:
In the Block Parameters dialog box, set parameter Model name to the referenced model file name. For this example, the model name is
rtwdemo_func_dinteg.slx
.Set model configuration parameter Total number of instances allowed per top model to
Multiple
.To generate a C++ class interface for the reference model, set model configuration parameter Language to
C++
and parameter Code interface packaging toC++ class
.
Optionally, you can customize model entry-point function interfaces. You can specify entry-point function names. For the execution (step) entry-point function you can configure the function name and arguments. Custom entry-point function interfaces can minimize changes to existing external code that you integrate with the generated code. The example uses the default function interfaces. See Configure Entry-Point Function Interfaces for Simulink Function and Function Caller Blocks.
Configure Top Model
Configure the top model for a model component. If you want the model component
(that is, the top model) to be reusable, set model configuration parameter
Code interface packaging to Reusable
function
. If you are generating C++ code, you can set this
parameter to C++ class
. In either case, also:
Set model configuration parameter Multi-instance code error diagnostic to
Error
.Set model configuration parameter Pass root-level I/O as to
Part of model data structure
.
Optionally, you can customize model entry-point function interfaces. You can specify entry-point function names. For the execution (step) entry-point function you can configure the function name and arguments. Custom entry-point function interfaces can minimize changes to existing external code that you integrate with the generated code. The example uses the default function interfaces. See Configure Entry-Point Function Interfaces for Simulink Function and Function Caller Blocks.
Generate and Inspect C Code
Generate C code for the model.
Function code for multi-instance Simulink Function block
When you place a scoped Simulink Function block in a referenced model that you use multiple times in another model, the code generator places the function code in the
file for the referenced model. For this example, the code generator places function code formodel
.cmultiinstfunc
inslprj/ert/rtwdemo_func_dinteg/rtwdemo_func_dinteg.c
.real_T rtwdemo_func_dinteg_multiinstfunc(RT_MODEL_rtwdemo_func_dinteg_T * const rtwdemo_func_dinteg_M, const real_T rtu_u) { real_T rtb_TmpLatchAtInOutport1; real_T rtb_TmpLatchAtIn2Outport1; real_T rty_y_0; rtb_TmpLatchAtInOutport1 = *rtwdemo_func_dinteg_M->rtwdemo_func_dintegrtextInport.rtu_In1; rtb_TmpLatchAtIn2Outport1 = *rtwdemo_func_dinteg_M->rtwdemo_func_dintegrtextInport.rtu_In2; *rtwdemo_func_dinteg_M->rtwdemo_func_dintegrtextOutport.rty_Out2 = 2.0 * rtb_TmpLatchAtInOutport1; rtwdemo_func_dinteg_func_calc(rtwdemo_func_dinteg_M, rtb_TmpLatchAtIn2Outport1, rtwdemo_func_dinteg_M->rtwdemo_func_dintegrtextOutport.rty_Out1); rty_y_0 = rtwdemo_func_dinteg_M->dwork.DiscreteIntegrator_DSTATE; rtwdemo_func_dinteg_M->dwork.DiscreteIntegrator_DSTATE += 0.1 * rtu_u; return rty_y_0; }
Function code for a Simulink Function block defined in a subsystem
The code generator places the function code for a Simulink Function block that you define in a subsystem in the
file for the model that contains the subsystem. For this example, the code generator places the function code formodel
.cfunc_calc
inslprj/ert/rtwdemo_func_dinteg/rtwdemo_func_dinteg.c
.void rtwdemo_func_dinteg_func_calc(RT_MODEL_rtwdemo_func_dinteg_T * const rtwdemo_func_dinteg_M, real_T rtu_u, real_T *rty_y) { rtwdemo_func_dinteg_M->dwork.calcMem = rtwdemo_func_dinteg_M->dwork.UnitDelay_DSTATE; *rty_y = rtwdemo_func_dinteg_M->dwork.UnitDelay_DSTATE; rtwdemo_func_dinteg_M->dwork.UnitDelay_DSTATE = rtu_u * 0.07; }
Structure that stores multi-instance data for reusable functions
The code generator uses a structure similar to the real-time model (
RT_MODEL
) data structure to store the multi-instance data associated with a reusable function. The code generator defines the structure inslprj/ert/rtwdemo_func_dinteg/rtwdemo_func_dinteg.h
.typedef struct rtwdemo_func_dinteg_tag_RTM RT_MODEL_rtwdemo_func_dinteg_T;
Initialization code for multi-instance referenced model
For each instance of a referenced model that includes the same scoped Simulink Function block, the code generator produces initialization and startup function code. A single copy of the initialization code is defined in
slprj/ert/rtwdemo_func_dinteg/rtwdemo_func_dinteg.c
.void rtwdemo_func_dinteg_Start(RT_MODEL_rtwdemo_func_dinteg_T *const rtwdemo_func_dinteg_M, const real_T *rtu_In1, const real_T *rtu_In2, real_T *rty_Out2, real_T *rty_Out1) { rtwdemo_func_dinteg_M->rtwdemo_func_dintegrtextInport.rtu_In1 = rtu_In1; rtwdemo_func_dinteg_M->rtwdemo_func_dintegrtextInport.rtu_In2 = rtu_In2; rtwdemo_func_dinteg_M->rtwdemo_func_dintegrtextOutport.rty_Out2 = rty_Out2; rtwdemo_func_dinteg_M->rtwdemo_func_dintegrtextOutport.rty_Out1 = rty_Out1; } void rtwdemo_func_dinteg_initialize(const char_T **rt_errorStatus, RT_MODEL_rtwdemo_func_dinteg_T *const rtwdemo_func_dinteg_M) { { rtmSetErrorStatusPointer(rtwdemo_func_dinteg_M, rt_errorStatus); } }
The initialization code is called for each instance of a referenced model that contains the same Simulink Function block. That code is in file
rtwdemo_comp.c
in the build folder.. . . rtwdemo_func_dinteg_initialize(rtmGetErrorStatusPointer(rtwdemo_comp_M), (&(rtwdemo_comp_M->Instance1))); rtwdemo_func_dinteg_initialize(rtmGetErrorStatusPointer(rtwdemo_comp_M), (&(rtwdemo_comp_M->Instance2))); rtwdemo_func_dinteg_Start((&(rtwdemo_comp_M->Instance1)), &rtU->In2, &rtU->In3, &rtY->Out2, &rtY->Out3); rtwdemo_func_dinteg_Start((&(rtwdemo_comp_M->Instance2)), &rtU->In4, &rtU->In5, &rtY->Out4, &rtY->Out5); }
Top model entry-point function declarations
The model header file
rtwdemo_comp.h
includesextern
declarations for top model initialize, terminate, and execution (run) entry-point functions.extern void rtwdemo_comp_initialize(RT_MODEL_rtwdemo_comp_T *const rtwdemo_comp_M); extern void Run(RT_MODEL_rtwdemo_comp_T *const rtwdemo_comp_M);
Reference model entry-point function declarations
Header file
slprj/ert/rtwdemo_func_dinteg/rtwdemo_func_dinteg.h
includesextern
declarations for reference model entry-point functions.extern void rtwdemo_func_dinteg_initialize(const char_T **rt_errorStatus, RT_MODEL_rtwdemo_func_dinteg_T *const rtwdemo_func_dinteg_M); extern real_T rtwdemo_func_dinteg_multiinstfunc(RT_MODEL_rtwdemo_func_dinteg_T * const rtwdemo_func_dinteg_M, const real_T rtu_u); extern void rtwdemo_func_dinteg_Start(RT_MODEL_rtwdemo_func_dinteg_T *const rtwdemo_func_dinteg_M, const real_T *rtu_In1, const real_T *rtu_In2, real_T *rty_Out2, real_T *rty_Out1);
Generate and Inspect C++ Code
Generate C++ code for the model.
Function code for multi-instance Simulink Function block
When you place a scoped Simulink Function block in a referenced model that you use multiple times in another model, the code generator places the function code in the
file for the referenced model. For this example, the code generator places function code formodel
.cppmultiinstfunc
inslprj/ert/rtwdemo_func_dinteg/rtwdemo_func_dinteg_cpp.cpp
.real_T rtwdemo_func_dintegModelClass::multiinstfunc(const real_T rtu_u) { real_T rtb_TmpLatchAtInOutport1; real_T rtb_TmpLatchAtIn2Outport1; real_T rty_y_0; rtb_TmpLatchAtInOutport1 = *rtwdemo_func_dinteg_cprtrtu_In1; rtb_TmpLatchAtIn2Outport1 = *rtwdemo_func_dinteg_cprtrtu_In2; *rtwdemo_func_dinteg_crtrty_Out2 = 2.0 * rtb_TmpLatchAtInOutport1; rtwdemo_func_dinteg_c_func_calc(rtb_TmpLatchAtIn2Outport1, rtwdemo_func_dinteg_crtrty_Out1); rty_y_0 = rtwdemo_func_dinteg_cpprtDW.DiscreteIntegrator_DSTATE; rtwdemo_func_dinteg_cpprtDW.DiscreteIntegrator_DSTATE += 0.1 * rtu_u; return rty_y_0; }
Function code for a Simulink Function block defined in a subsystem
The code generator places the function code for a Simulink Function block that you define in a subsystem in
for the model that contains the subsystem. For this example, the code generator places the function code formodel
.cppfunc_calc
inslprj/ert/rtwdemo_func_dinteg/rtwdemo_func_dinteg_cpp.cpp
.void rtwdemo_func_dintegModelClass::rtwdemo_func_dinteg_c_func_calc(real_T rtu_u, real_T *rty_y) { rtwdemo_func_dinteg_cpprtDW.calcMem = rtwdemo_func_dinteg_cpprtDW.UnitDelay_DSTATE; *rty_y = rtwdemo_func_dinteg_cpprtDW.UnitDelay_DSTATE; rtwdemo_func_dinteg_cpprtDW.UnitDelay_DSTATE = rtu_u * 0.07; }
Structure that stores multi-instance data for reusable functions
The code generator uses a structure similar to the real-time model (
RT_MODEL
) data structure to store the multi-instance data associated with aReusable function
. The code generator defines the structure inslprj/ert/rtwdemo_func_dinteg/rtwdemo_func_dinteg_cpp.h
.typedef struct rtwdemo_func_dinteg_cpp_tag_RTM rtwdemo_func_dinteg_cp_RT_MODEL;
Initialization code for multi-instance referenced model
For each instance of a referenced model that includes the same scoped Simulink Function block, the code generator produces initialization and startup function code. A single copy of the initialization code is defined in
slprj/ert/rtwdemo_func_dinteg/rtwdemo_func_dinteg_cpp.cpp
.void rtwdemo_func_dintegModelClass::start(real_T *rtu_In1, real_T *rtu_In2, real_T *rty_Out2, real_T *rty_Out1) { rtwdemo_func_dinteg_cprtrtu_In1 = rtu_In1; rtwdemo_func_dinteg_cprtrtu_In2 = rtu_In2; rtwdemo_func_dinteg_crtrty_Out2 = rty_Out2; rtwdemo_func_dinteg_crtrty_Out1 = rty_Out1; } . . . void rtwdemo_func_dintegModelClass::initializeRTM() { (void) memset((void *)((&rtwdemo_func_dinteg_cpprtM)), 0, sizeof(rtwdemo_func_dinteg_cp_RT_MODEL)); }
The initialization code is called for each instance of a referenced model that contains the same Simulink Function block. That code is in file
rtwdemo_comp_cpp.cpp
in the build folder.. . . Instance1MDLOBJ0.initializeRTM(); Instance1MDLOBJ0.setErrorStatusPointer(rtmGetErrorStatusPointer((&rtM))); Instance1MDLOBJ0.initialize(); Instance2MDLOBJ1.initializeRTM(); Instance2MDLOBJ1.setErrorStatusPointer(rtmGetErrorStatusPointer((&rtM))); Instance1MDLOBJ0.start(&rtU.In2, &rtU.In3, &rtY.Out2, &rtY.Out3); Instance2MDLOBJ1.start(&rtwU.In4, &rtwU.In5, &rtY.Out4, &rtY.Out5); }
Top model class declaration
The model header file
rtwdemo_comp_cpp.h
includes the class declaration for the top model.class rtwdemo_compModelClass { public: ExtU rtU; ExtY rtY; void initialize(); void Run(); rtwdemo_compModelClass(); ~rtwdemo_compModelClass(); RT_MODEL * getRTM(); private: RT_MODEL rtM; rtwdemo_func_dintegModelClass Instance1MDLOBJ0; rtwdemo_func_dintegModelClass Instance2MDLOBJ1; };
Reference model class declaration
Header file
slprj/ert/rtwdemo_func_dinteg/rtwdemo_func_dinteg_cpp.h
includes the class declaration for the reference model.class rtwdemo_func_dintegModelClass { public: real_T multiinstfunc(const real_T rtu_u); void start(real_T *rtu_In1, real_T *rtu_In2, real_T *rty_Out2, real_T *rty_Out1); rtwdemo_func_dintegModelClass(); ~rtwdemo_func_dintegModelClass(); rtwdemo_func_dinteg_cp_RT_MODEL * getRTM(); void initializeRTM(); void setErrorStatusPointer(const char_T **rt_errorStatus); private: rtwdemo_func_dinteg_cpp_DW rtwdemo_func_dinteg_cpprtDW; const real_T *rtwdemo_func_dinteg_cprtrtu_In1; const real_T *rtwdemo_func_dinteg_cprtrtu_In2; real_T *rtwdemo_func_dinteg_crtrty_Out2; real_T *rtwdemo_func_dinteg_crtrty_Out1; rtwdemo_func_dinteg_cp_RT_MODEL rtwdemo_func_dinteg_cpprtM; void rtwdemo_func_dinteg_c_func_calc(real_T rtu_u, real_T *rty_y); };
Limitations
These code generation limitations apply to export-function models that include multiple instances of a Simulink Function block.
You must set the model configuration parameter Pass root-level I/O as to
Part of model data structure
.Generated code is compatible with single-threaded execution only. To avoid race conditions for shared signal data, invoke instances of the function code from the same execution thread.
You cannot:
Generate code if the export-function model includes model variants.
Enable the external mode data exchange interface.