Simulink Function Blocks and Code Generation

Why Generate Code from Simulink Function Blocks and Function Callers?

Simulink Function blocks provide a mechanism for generating C or C++ code for modeling components that represent shared resources. You define the logic as a resource in a Simulink Function block, which separates the function interface (name and arguments) from the implementation of the logic. Function callers (Function Caller blocks, MATLAB Function blocks, and Stateflow® charts) can then reuse the function logic at different levels of the model hierarchy.

Simulink Function blocks provide an alternative to reusable subsystems. For example, a consideration for using a Simulink Function block instead of a subsystem block is that a Simulink Function block shares states between function callers. The code generator produces one function. If the Simulink Function block contains blocks that have states, such as a delay or memory, the states persistent between function callers. The order of the function calls is an important consideration.

Reusable functions that the code generator produces from subsystems do not share states. The code generator produces one function for multiple instances of the subsystem as an optimization. If that subsystem contains blocks that have states, the code generator produces one function, but passes a different state variable to each instance. The instances do not share states.

Other uses of Simulink Function blocks and callers include:

  • Nesting calls to a function.

  • Calling a function defined in one modeling component from another modeling component.

  • Generating functions that are globally accessible or scoped.

  • Producing code for a client and server application.

Implementation Options

Choose how to implement Simulink® functions and function callers based on your code generation requirements. Considerations include:

  • How you represent a function and function callers in a model

  • Scope of a function

  • Whether to export a function from a model (requires Embedded Coder®)

  • Function code interface customizations (requires Embedded Coder)

  • Code generation requirements

  • Code generation limitations

Choose a Modeling Pattern

This table shows C code for a function that multiplies an input value times two and a Simulink Function block that can represent that function in a Simulink model.

Function DefinitionModeling Element

codegen-folder/subsystem.c

#include "timestwo_sf.h"

#include "ex_slfunc_comp_sf.h"
#include "ex_slfunc_comp_sf_private.h"

void timestwo_sf(real_T rtu_x, real_T *rty_y)
{
  *rty_y = 2.0 * rtu_x;
}

Simulink Function block

A Simulink function caller invokes a function defined with a Simulink Function block. From anywhere in a model or chart hierarchy, you can call a function defined with a Simulink Function block by using one of these modeling elements:

Function CallModeling Element

codegen-folder/model.c

void ex_slfunc_comp_sf_step(void)
{
  real_T rtb_FunctionCaller1;

timestwo_sf(ex_slfunc_comp_sf_U.In1, &rtb_FunctionCaller1);

ex_slfunc_comp_sf_Y.Out1 = rtb_FunctionCaller1;
.
.
.

Function Caller block

codegen-folder/model.c

void ex_slfunc_comp_gf_step(void)
{
  real_T rtb_y1_l;

  timestwo_gf(ex_slfunc_comp_gf_U.In4, &rtb_y1_l);

  ex_slfunc_comp_gf_Y.Out4 = rtb_y1_l;  
  .
  .
  .

Stateflow chart transition

codegen-folder/model.c

void ex_slfunc_comp_mf_step(void)
{ 
  real_T rtb_y;
  
  timestwo_mf(ex_slfunc_comp_mf_U.In3, &rtb_y);

  ex_slfunc_comp_mf_Y.Out3 = rtb_y;
  .
  .
  .

MATLAB Function block

For more information about modeling choices, see Simulink Functions Overview.

Specify Function Scope

A function that you define with a Simulink Function block can be global or scoped.

  • Global---The code generator places code for a global function in source and header files that are separate from model code files (for example, function.c and function.h) . The separate files make the function code available for sharing between function callers.

  • Scoped---The code generator places code for a scoped function in model code files (model.c and model.h). To call a scoped function in the context of a model, the function caller must be at the same level as the function in the model hierarchy, or one or more levels below.

    To create a library of functions that are accessible from anywhere in the generated model code, set up each function as a scoped Simulink Function block. Place each scoped function within a virtual subsystem at the root level of a model.

For more information, see and Scoped and Global Simulink Function Blocks Overview.

Decide Whether to Generate Export Function Code

Although you can use Simulink Function blocks in a single top-model design, function code is more reusable when you generate it as standalone, atomic components. You do that by designing the functions in the context of export-function models.

For information, see Generate Component Source Code for Export to External Code Base and Export-Function Models Overview.

Specify Function Code Interface Customizations

With Embedded Coder, simplify integration of generated code with external code by customizing generated function code interfaces for Simulink Function and Function Caller blocks. You can customize the function interfaces for:

  • Global Simulink Function blocks

  • Scoped Simulink Function blocks that are at the root level of a model

For more information, see Configure Entry-Point Function Interfaces for Simulink Function and Function Caller Blocks.

Uncalled Simulink Function Blocks

If you use a Simulink Function block in a rate-based model and do not call that function, the code generator treats the Simulink Function block as a constant and does not produce function code. For example, this can occur during model development when you are ready to define a function, but are not ready to identify a caller.

To identify such blocks in a rate-based model, display sample time colors during simulation. By default, Constant blocks appear magenta. Because the code generator considers uncalled Simulink Function blocks constants during simulation, they appear magenta.

Requirements

  • Within a model hierarchy, function names are unique. If the code generator finds multiple functions with the same name, it issues an error. Change the name of one of the functions and delete the slprj folder.

  • The signature (for example, the arguments and argument data types) for a function and function callers must match.

    • If the code generator finds the function first and the signature of a function caller does not match, the code generator issues an error. Change the function caller signature to match the signature of the Simulink Function block or delete the slprj folder.

    • If the code generator finds a function caller first and the signature of the function does not match, the code generator issues a warning message. Change the signature of the function or function caller so that the signatures match.

  • In a Simulink Function block definition, do not define input and output signals for Argument Inport and Argument Outport blocks with a storage class.

  • Do not specify Argument Inport and Argument Outport blocks as test points.

  • If you specify the data type of input and output signals for Argument Inport and Argument Outport blocks as a Simulink.IntEnumType, Simulink.AliasType, or Simulink.Bus, set the DataScope property to Imported or Exported.

  • A function interface and function callers must agree in data type, complexity, dimension, and number of arguments.

Limitations

  • To generate code from a model that includes scoped Simulink functions, the model must be an export-function model. The code generator does not support rate-based models that include scoped Simulink functions.

  • You can use a Simulink Function block to define a scoped function in a referenced model. However, you cannot generate code for an export-function model that uses a Function Caller block in an atomic subsystem to invoke that function.

  • You can invoke a C++ function that the code generator produces from a Simulink Function block with code generated from a Stateflow chart. Due to current scope limitations for generated C++ functions, you must invoke those functions with code generated from a Function Caller block.

  • Simulink functions and function callers do not honor the MaxStackSize parameter.

  • Code generation for a C++ class interface supports scoped Simulink functions only.

Generate and Call Reusable Function Code

This example shows how to use the Simulink Function and Function Caller blocks to generate reusable function code. The code generator produces a global function, which complies with code requirements so that existing external code can call the function and a local function. The code generator also produces calls to global and local functions. The call to the global function shows that the global function is reused (shared).

Code requirements for the global function are:

  • Function names start with prefix func_.

  • Names of input arguments are of the form xn, where n is a unique integer value.

  • Names of output arguments are of the form yn, where n is a unique integer value.

  • Input and output arguments are integers (int) and are passed by reference. The native integer size of the target hardware is 32 bits.

You create a function that calls the generated reusable function code. Then, you construct and configure a model to match the code requirements.

Some of the features used in this example, such as data type aliasing and replacement, require Embedded Coder software.

Inspect External Code That Calls Reusable Function

In your code generation root folder, create the files call_times2.h and call_times2.c. If you prefer, you can copy the files from matlabroot\help\toolbox\ecoder\examples.

call_times2.h

typedef int my_int;

call_times2.c

#include "call_times2.h"

void call_times2(void)
{
  int times2result;

  func_times2(x1, &y1);

  printf('Times 2 Value:', y1);
}

This C code calls reusable function func_times2. The function multiplies an integer input value x1 by 2 and returns the result as y1.

Create Model

Open the example model ex_slfunc_comp, which is available in the folder matlabroot\help\toolbox\ecoder\examples. The model includes two Simulink functions modeled as Simulink Function blocks, func_times2 and func_times3, and a call to each function. As indicated in the model, the visibility of the Simulink Function block for func_times2 is set to global. That visibility setting makes the function code accessible to other code, including external code that you want to integrate with the generated code. The visibility for func_times3 is set to scoped.

If you configure the model with the GRT system target file or the ERT system target file with File packaging format set to Modular, the code generator produces function code for the model.

For more information, see Generate Code for a Simulink Function and Function Caller.

Configure Generated Code to Reuse Custom Data Type

The example assumes that the generated code runs on target hardware with a native integer size of 32 bits. The external code represents integers with data type my_int, which is an alias of int. Configure the code generator to use my_int in place of the data type that the code generator uses by default, which is int32_T.

  1. Create a Simulink.AliasType object to represent the custom data type my_int.

    my_int = Simulink.AliasType
    
    my_int =
    
      AliasType with properties:
    
        Description: ''
          DataScope: 'Auto'
         HeaderFile: ''
           BaseType: 'double'
  2. Set the alias type properties. Enter a description, set the scope to Imported, specify the header file that includes the type definition, and associate the alias type with the Simulink base type int32.

    my_int.Description='Custom 32-bit int representation';
    my_int.DataScope='Imported';
    my_int.HeaderFile='call_times2.h';
    my_int.BaseType='int32';
    
    my_int
      AliasType with properties:
    
        Description: 'Custom 32-bit int representation'
          DataScope: 'Imported'
         HeaderFile: 'call_times2.h'
           BaseType: 'int32'
    
  3. Configure the code generator to replace instances of type int32_T with my_int. In the Configuration Parameters dialog box, open the Code GenerationData Type Replacement pane.

    • Select Replace data type names in the generated code.

    • In the Data type names table, enter my_int for the replacement name for int32.

Configure Reusable, Global Function

For external code to call a global function, configure the corresponding Simulink Function block to have global visibility and an interface that matches what is expected by the external callers.

  1. Open the block that represents the times2 function.

  2. Configure the function name and visibility by setting Trigger Port block parameters. For example, the code requirements specify that the function name start with the prefix func_.

    • Set Function name to func_times2.

    • Set Function visibility to global.

  3. Configure the function input and output arguments by setting Argument Inport and Argument Outport block parameters. For example, the code requirements specify that argument names be of the form xn and yn. The requirements also specify that arguments be type my_int.

    • On the Main tab, set Argument name to x1 (input) and y1 (output).

    • On the Signal Attributes tab, set Data type to int32. With the data type replacement that you specified previously, int32 appears in the generated code as myint.

  4. Configure the Simulink Function block code interface. At the top level of the model, right-click the block representing global function func_times2. From the context menu, select C/C++ CodeConfigure C/C++ Function Interface.

    • Set C/C++ function name to func_times2.

    • Set C/C++ return argument to void.

    • Set C/C++ Identifier Name for argument x1 to x1.

    • Set C/C++ Identifier Name for argument y1 to y1.

    If the dialog box lists output argument y1 before input argument x1, reorder the arguments by dragging the row for x1 above the row for y1.

Configure Local Function

Configure a Simulink Function block that represents a local function with scoped visibility. Based on code requirements, you might also have to configure the function name, input and output argument names and types, and the function interface.

  1. Open the block that represents the times3 function.

  2. Configure the function name and visibility by setting Trigger Port block parameters. For example, the code requirements specify that the function name start with the prefix func_.

    • Set Function name to func_times3.

    • Set Function visibility to scoped.

  3. Configure the function input and output arguments by setting Argument Inport and Argument Outport block parameters. For example, the code requirements specify that argument names be of the form xn and yn. The requirements also specify that arguments be type my_int.

    • On the Main tab, set Argument name to x1 (input) and y1 (output).

    • On the Signal Attributes tab, set Data type to int32. With the data type replacement that you specified previously, int32 appears in the generated code as my_int.

  4. Configure the Simulink Function block code interface. At the top level of the model, right-click the scoped function func_times3. From the menu, select C/C++ CodeConfigure C/C++ Function Interface. In this case, the code generator controls naming the function and arguments. The function name combines the name of the root model and the function name that you specify for the trigger port of the Simulink Function block. In this example, the name is ex_slfunc_comp_func_times3.

    You can set C/C++ return argument to the argument name that you specify for the Argument Outport block or void. For this example, set it to void.

    For arguments, the code generator prepends rtu_ (input) or rty_ (output) to the argument name that you specify for the Argument Inport block.

    If the dialog box lists output argument y1 before input argument x1, reorder the arguments by dragging the row for x1 above the row for y1.

Configure Function Callers

For each function caller, configure Function Caller block parameters:

  • Set Function prototype to y1 = func_times2(x1).

  • Set Input argument specifications to int32(1).

  • Set Output argument specifications to int32(1).

Generate and Inspect Code

Generate code for the model.

  • Global function code

    Source code for the global, reusable function func_times2 is in the build folder in subsystem file, func_times2.c.

    #include "func_times2.h"
    
    /* Include model header file for global data */
    #include "ex_slfunc_comp.h"
    #include "ex_slfunc_comp_private.h"
    
    void func_times2(my_int x1, my_int *y1)
    {
      *y1 = x1 << 1;
    }
  • Local function code

    The code generator places the definition for the local (scoped) function func_times3 in file build folder in file ex_slfunc_comp.c.

    void ex_slfunc_comp_func_times3(my_int rtu_x1, my_int *rty_y1)
    {
      *rty_y1 = 3 * rtu_x1;
    }
  • Calls to generated functions

    The model execution (step) function, in model file ex_slfunc_comp.c, calls the two Simulink functions: global function func_times2 and local function ex_slfunc_comp_func_times3. The name ex_slfunc_comp_func_times3 reflects the scope of the local function by combining the name of the model and the name of the function.

    void ex_slfunc_comp_step(void)
    {
      my_int rtb_FunctionCaller2;
    
      func_times2(ex_slfunc_comp_U.In1, &rtb_FunctionCaller2);
      
      ex_slfunc_comp_Y.Out1 = rtb_FunctionCaller2;
    
      ex_slfunc_comp_func_times3(ex_slfunc_comp_U.In2, &rtb_FunctionCaller2);
    
      ex_slfunc_comp_Y.Out2 = rtb_functionCaller2;
      .
      .
      .
  • Entry-point declaration for local function

    The model header file ex_slfunc_comp.h includes an extern declaration for function ex_slfunc_comp_func_times3. That statement declares the function entry point.

    extern void ex_slfunc_comp_func_times3(my_int rtu_x1, my_int *rty_y1);
  • Include statements for global function

    The model header file ex_slfunc_comp.h lists include statements for the global functionfunc_times2.

    #include "func_times2_private.h"
    #include "func_times2.h"
    
  • Local macros and data for global function

    The subsystem header file func_times2_private.h defines macros and includes header files that declare data and functions for the global function, func_times2.

    #ifndef RTW_HEADER_func_times2_private_h_
    #define RTW_HEADER_func_times2_private_h_
    #ifndef ex_slfunc_comp_COMMON_INCLUDES_
    #define ex_slfunc_comp_COMMON_INCLUDES_
    #include "rtwtypes.h"
    #endif
    #endif
    
  • Entry-point declaration for global function

    The shared header file func_times2.h, in the shared utilities folder slprj/stf/_sharedutils, lists shared type includes for rtwtypes.h. The file also includes an extern declaration for the global function, func_times2. That statement declares the function entry point.

    #ifndef RTW_HEADER_func_times2_
    #define RTW_HEADER_func_times2_
    
    #include "rtwtypes.h"
    
    extern void func_times2(my_int rtu_x1, my_int *rty_y1);
    
    #endif
    

Generate Code for a Simulink Function and Function Caller

This example shows how to generate C code for Simulink Function and Function Caller blocks and displays the relevant generated code.

Open the example model rtwdemo_export_functions. The model uses Stateflow software, but this example reviews only the code generated from the referenced models.

Generate Code for Function Definition

  1. To view the contents of the subsystem, double-click rtwdemo_functions. The Simulink Function block is the f3 subsystem defined as y = f3(u).

  2. Generate code.

The code generator creates rtwdemo_functions.c. This file contains the function definition and function initialization code.

  • Initialization code for function f3:

    void f3_Init(void)
    {
      rtDWork.Delay_DSTATE = 1;
    }
  • Code for function f3:

    void f3(real_T rtu_u, real_T *rty_y)
    {
      rtY.TicToc10 = rtDWork.Delay_DSTATE;
    
      rtDWork.Delay_DSTATE = (int8_T)(int32_T)-(int32_T)rtY.TicToc10;
    
      adder_h(rtB.Subtract, rtU.U2, rtu_u, rtB.FunctionCaller);
    
      *rty_y = rtB.FunctionCaller;
    }
    
    void adder_h(real_T rtu_u1, 
                 real_T rtu_u2, 
                 real_T rtu_u3, 
                 real_T *rty_y)
    {
      *rty_y = (rtu_u1 + rtu_u2) + rtu_u3;
    }

  • The shared header file f3.h contains the entry point declaration for function f3.

    #include "rtwtypes.h"
    
    extern void f3(real_T rtu_u, real_T *rty_y);
    

Generate Code for Function Caller

  1. In the rtwdemo_export_functions model, double-click rtwdemo_caller to view the contents of the caller subsystem.

  2. Generate code.

The code generator creates the files rtwdemo_caller.h and rtwdemo_caller.c in the folder rtwdemo_caller_ert_rtw.

rtwdemo_caller.h includes the shared header file, f3.h, which contains the function entry-point declaration.

rtwdemo_caller.c calls function f3.

void rtwdemo_caller_t_10tic(const real_T *rtu_u, 
                            real_T *rty_y)
{
  f3(*rtu_u, rty_y);
}

Related Topics