Main Content

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:

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) and y = Instance2.multiinstfunc(u). The Instancen prefix identifies each function invocation uniquely and associates the invocation with its own data set.

  • The function caller in function multiinstfunc is configured with the prototype y = subsys_calc.func_calc(u). The prefix subsys_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 to C++ 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 model.c file for the referenced model. For this example, the code generator places function code for multiinstfunc in slprj/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 model.c file for the model that contains the subsystem. For this example, the code generator places the function code for func_calc in slprj/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 in slprj/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 includes extern 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 includes extern 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 model.cpp file for the referenced model. For this example, the code generator places function code for multiinstfunc in slprj/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 model.cpp for the model that contains the subsystem. For this example, the code generator places the function code for func_calc in slprj/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 a Reusable function. The code generator defines the structure in slprj/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.

Related Topics