Main Content

Build S-Functions Automatically Using S-Function Builder

The S-Function Builder is a Simulink® block that integrates C/C++ code to build an S-function from specifications and C code that you supply. The S-Function Builder also serves as a wrapper for the generated S-function in models that use the S-function.

Create an S-Function Builder Block and Specify Settings

To use the S-Function Builder, click the Simulink canvas and type S-Function Builder or drag a S-Function Builder block from Simulink Library > User-Defined. To open the S-Function Builder editor, double-click the S-Function Builder block icon or select the block. Then select Open Block from the Edit menu on the model editor or the block's context menu.

S-Function Builder editor is composed of four user interface components:

  • S-Function Builder toolstrip

  • Settings pane

  • Ports and Parameters Table

  • Libraries table

Specify a name for your S-function to start constructing your S-Function Builder block. Note that when you name an S-function, all functions in the S-Function Builder editor changes and the S-function name becomes a prefix to all wrapper functions.

Use the S-Function Builder toolstrip to specify a target language for the S-function that you would like to generate:

  • C — Generate C S-functions.

  • C++ — Generate C++ S-functions.

  • Inherit from model — Inherit the language settings from the Language setting of the model. See Language (Simulink Coder) for more information.

Customize the basic features your S-function, such as initial conditions, and number of states, by using the Settings pane on the right of the S-Function Builder editor. The S-Function Builder uses the information that you enter on this pane to generate the mdlInitializeSizes callback method. The Simulink engine invokes this method during the model initialization phase of the simulation to obtain basic information about the S-function.

S-function builder settings with the number of discrete states set to 2, discrete states set to 1 comma 1, number of continuous states set to 0. Array layout set to column-major. Sample mode set to discrete. Sample time value is grayed out. Number of P works set to 0. Enable access to SimStruct is unchecked. Direct feedthrough is checked.

Set Number of States and Initial Conditions

Using the Settings table, you can specify the number of discrete and continuous states and their initial conditions.

You can enter the values of initial conditions as a comma-seperated list, (for example, 1,1,1) or as a vector (for example, [1 1 1]). The number of initial conditions must be equal to the number of states indicated.

Set Array Layout

Set the array layout of your C/C++ code. You can select one of the options listed in the table.

OptionArray Layout of C/C++ FunctionAction
Column-majorColumn-major

The S-Function Builder block adds the SimStruct function ssSetArrayLayoutForCodeGen in mdlInitializeSizes to mark the S-function for column-major code generation.

Row-majorRow-major

The S-Function Builder block adds the SimStruct function ssSetArrayLayoutForCodeGen in mdlInitializeSizes to mark the S-function for row-major code generation.

During simulation, if your C/C++ code involves matrices or multidimensional inputs, outputs, or parameters, transposes are added to these S-function callback methods:

  • mdlOutputs

  • mdlUpdate

  • mdlDerivatives

Simulink also applies the these transposes when running simulation in Accelerator and Rapid Accelerator modes. The S-function is not inlined by using TLC. Instead, the MEX S-function with transposes is compiled directly.

AnyC/C++ function is not affected by array layout

The S-Function Builder block adds the SimStruct function ssSetArrayLayoutForCodeGen in mdlInitializeSizes to mark the S-function as accepting both row-major and column-major code generation.

Select Sample Mode

Select the sample mode of your S-function. The sample mode determines the length of the interval between the times when the S-function updates its output. You can select one of the following options:

  • Inherited — The S-function inherits its sample time from the block connected to its input port. When inherited sample time is selected, note that the Sample time value field of the Settings table is inactive.

  • Continuous — The block updates its outputs at each simulation step. When continuous sample time is selected, note that the Sample time value field of the Settings table is inactive.

  • Discrete — The S-function updates its outputs at the rate specified in the Sample time value field of the Settings table.

Set the Number of PWorks

Set PWorks, the number of data pointers used by the S-function. PWorks points to the memory over the lifecycle of the block. If you enter any other value than 0 for Number of PWorks, it adds a pointer, void **PW, to all functions in S-Function Builder. For example, you can declare and initialize a pointer to a file or memory at the Start_wrapper, access it in Outputs_wrapper, Update_wrapper, and Derivatives_wrapper functions, and deallocate it at the Terminate_wrapper function. The code written in these functions is called by the mdlStart, mdlOutputs, mdlUpdate, mdlDerivatives, and mdlTerminate methods. See the examples Moving Average with Start and Terminate and Permutation using Cpp Classes in S-Function Demos.

Note

Use of PWorks affects the SimState compliance. If you declare PWorks, the use of SimState save and restore is not allowed. Otherwise, the default SimState compliance setting, USE_DEFAULT_SIM_STATE, is used.

Enable Access to SimStruct

Make the SimStruct (S) accessible to the wrapper functions that the S-Function Builder is using. This selection enables you to use the SimStruct macros and functions with your code in the Outputs_wrapper, Derivatives_wrapper, and the Update_wrapper functions. When you turn this setting on, SimStruct *S is added to the list of parameters.

For example, with this option enabled, you can use macros such as ssGetT in code that computes the S-function outputs:

double t = ssGetT(S);
  if(t < 2) 
  {
    y0[0] = u0[0];
  } else 
  {
    y0[0]= 0.0;
  }

Direct Feedthrough

Select the Direct Feedthrough check box on the Settings table if the values from the current time step of the S-function inputs are used to compute its outputs. The Simulink engine uses this information to detect algebraic loops created by directly or indirectly connecting the S-function output to the S-function input.

Specify Ports and Parameters for the S-Function

Use the Ports and Parameters table on the bottom of the editor to specify input and output ports and parameters for the S-function. To add a port or parameter:

  • Select the Ports and Parameters table, and from the S-Function Builder toolstrip, select your choice under Insert Port.

  • Select the Ports and Parameters table and right-click on one of the headers on the table.

To delete a port or parameter in the table, select the port or parameter you would like to delete, right-click to open the menu and click Delete.

The order of the ports and parameters in the table is the order of ports and parameters on the block. For example, the first input port on the table is the input port with the index 0, and the first parameter is the parameter with index 0.

In the Ports and Parameters, table you can define the following:

  1. Name — Name of the port or parameter. To change the name of a port or parameter, double-click on the name.

  2. Scope — Scope of the port or parameter. The variable could be an input or output port or a parameter. Click on the scope value to change scope of the port or parameter.

  3. Data type — Data type of the port or parameter. You can specify all Simulink built-in data types as well as fixdt data types and buses as ports.

  4. Dimensions — Dimension of the port or parameter. For ports, specify the dimension to -1 to inherit the dimensions from another block. For parameters, the dimension is set to -1 and is inherited from the block parameters on the block interface.

    To specify a 1-D dimension, only enter the number of rows for the dimension, for example, [2]. To enter a 2-D dimension, enter the dimension as number of row and columns, for example, [2,1].

  5. Complexity — You can specify the complexity of the port or parameter as real or complex.

Use the Libraries Table to Specify External Code and Paths

The Libraries table allows you to specify the location of external code files referenced by custom code that you enter in other the wrapper methods of the S-Function Builder editor.

To enter a new path or an entry, select the Libraries table and select one of the options under Insert Paths on the S-Function Builder toolstrip. Alternatively, click on one of the tags or values on the Libraries table. Once you select path or entry, you can change your selection by clicking on the tag on the table. You can enter paths or entries to the external libraries, object codes and source files referenced by custom code on the S-Function Builder editor.

Tag to choosePurpose
LIB_PATHSpecify object and library paths. .
INC_PATHSpecify the include search paths for header files and source files.
SRC_PATHSpecify search paths for object files and source files.
ENV_PATHSpecify environment variables.
ENTRY

Specify the object, source, and library file names. You can also enter preprocessor directives in this field, for example, -DDEBUG, -DHARDWARE_VARIANT=1 or -UDEBUG. Specify each file on a separate line.

Enclose the names of the header files on the top of the code editor. If you are using custom header files that are not on the same path, make sure to include the directory with the INC_PATH tag. Similarly, use the top of the editor to declare of external functions that are not declared in the header files. Include each declaration on a separate line.

You can enter paths or entries for to the external libraries, object codes, and source files referenced by custom code on the S-Function Builder editor.

For example, consider an S-Function Builder project in C:\Program Files\MATLAB\work. The table shows on how to link to external files:

File LocationsEntries to the Libraries Table

c:\customfolder\customfunctions.lib

LIB_PATH c:\customfolder

ENTRY customfunctions.lib

C:\Program Files\MATLAB\work\customobjs\userfunctions.obj

LIB_PATH $MATLABROOT\customobjs

ENTRY userfunctions.obj

D:\externalsource\freesource.c

SRC_PATH D:\externalsource

ENTRY freesource.c

You can use LIB_PATH to specify both object and library file paths. You can include the library name in the LIB_PATH declaration, however, you must place the object file name on a separate line. The tag $MATLABROOT indicates a path relative to the MATLAB® installation. List multiple LIB_PATH entries on separate lines. The paths are searched in the order you specify.

Each directive must be listed on a separate line.

Note

Do not put quotation marks around the library path, even if the path name has spaces in it. If you add quotation marks, the compiler will not find the library.

Develop the S-Function

S-Function Builder uses wrapper methods to specify the S-function code and properties that generate the corresponding S-function. Use the appropriate wrapper methods to construct the S-function body. Click Apply on the toolstrip to generate the S-function code.

This table provides a summary of S-Function Builder methods:

S-Function MethodWrapper MethodPurpose
Start and Terminate

<system_name>_Start_wrapper

<system_name>_Terminate_wrapper

Allocate and deallocate memory at the start and the end of simulation. Allocated memory is referenced using PWorks for use throughout the S-Function.
Outputs

<system_name>_Outputs_wrapper

Enter the code that computes the outputs of the S-function at each simulation time step.
Update

<system_name>_Update_wrapper

Enter the code that computes the value of discrete states at the next time step using the values at the current time step. This method only exists when you specify Number of Discrete States on the Settings table.
Derivatives

<system_name>_Derivatives_wrapper

Enter the code to compute state derivatives. This method only exists when you specify Number of Continuous States on the Settings table.

Now, you can investigate these methods in more detail.

Compute Outputs Using Outputs Method

In the S-Function Builder, use the Outputs_wrapper method to enter the code that computes the outputs of the S-function at each simulation time step. Note that when generating the S-function code, S-Function Builder generates a wrapper method using the name of your model and a wrapper function in the form of <system_name>_<function_name>_wrapper. For example, if your model's name is dsfunc_builder, the output method appears as dsfunc_builder_Output_wrapper in the S-Function Builder editor. If you do not have a name for your S-Function Builder block yet, it appears as system_Output_wrapper.

See the following code for an example of the Outputs method:

void sfun_Outputs_wrapper(const real_T *u,
                          real_T       *y,
				const real_T *xD, /* optional */
				const real_T *xC, /* optional */
				const real_T  *param0, /* optional */
				int_T p_width0 /* optional */
				real_T  *param1 /* optional */
				int_t p_width1 /* optional */
				int_T y_width, /* optional */
				int_T u_width) /* optional */
{

/* Your code inserted here */
}

where sfun is the name of the S-function. The S-Function Builder inserts a call to this wrapper function in the mdlOutputs callback method that it generates for your S-function. The Simulink engine invokes the mdlOutputs method at each simulation or sample time step to compute the S-function output. The mdlOutputs method in turn invokes the wrapper function containing your output code. Your output code then computes and returns the S-function output.

The mdlOutputs method passes some or all of the following arguments to the outputs wrapper function.

ArgumentDescription
u0, u1, ... uNPointers to arrays containing the inputs to the S-function, where N is the number of input ports specified on the Input scope found on the Ports and Parameters table. The names of the arguments that appear in the outputs wrapper function are the same as the names found on the Input scope of the Ports and Parameters table. The width of each array is the same as the input width specified for each input on the Dimensions pane. If the input is a matrix, the width equals the product of the dimensions of the arrays. If you specify -1 as an input width, the width of the array is specified by the wrapper function's u_width argument.
y0, y1, ... yNPointer to arrays containing the outputs of the S-function, where N is the number of output ports specified on the Scope pane on the Ports and Parameters table. The names of the arguments that appear in the outputs wrapper function are the same as the names found on the Output scope of Ports and Parameters table. The width of each array is the same as the output width specified for each output on the Dimensions pane. If an output is a matrix, the width equals the product of the dimensions of the arrays. If you specified -1 as the output width, the width of the array is specified by the wrapper function's y_width argument. Use this array to pass the outputs that your code computes back to the Simulink engine.
xDPointer to an array containing the discrete states of the S-function. This argument appears only if you specify discrete states on the Number of discrete states in the Settings menu. At the first simulation time step, the discrete states have the initial values that you specify on the Discrete states IC. At subsequent sample-time steps, the states are obtained from the values that the S-function computes at the preceding time step.
xCPointer to an array containing the continuous states of the S-function. This argument appears only if you specify number of continuous states on the Number of continuous states row of the Settings menu. At the first simulation time step, the continuous states have the initial values that you specify on the Continuous states IC row. At subsequent time steps, the states are obtained by numerically integrating the derivatives of the states at the preceding time step.
param0, p_width0, param1, p_width1, ... paramN, p_widthNparam0, param1, ...paramN are pointers to arrays containing the S-function parameters, where N is the number of parameters specify on the Ports and Parameters table. p_width0, p_width1, ...p_widthN are the widths of the parameter arrays. If a parameter is a matrix, the width equals the product of the dimensions of the arrays. For example, the width of a 3-by-2 matrix parameter is 6.
y_widthWidth of the array containing the S-function outputs. This argument appears in the generated code only if you specify -1 as the width of the S-function output. If the output is a matrix, y_width is the product of the dimensions of the matrix.
u_widthWidth of the array containing the S-function inputs. This argument appears in the generated code only if you specify -1 as the width of the S-function input. If the input is a matrix, u_width is the product of the dimensions of the matrix.

These arguments permit you to compute the output of the block as a function of its inputs and, optionally, its states and parameters. The code that you enter in this field can invoke external functions declared in the header files or external declarations on the Libraries table, which allows you to use existing code to compute the outputs of the S-function.

Update Discrete States Using Update Method

Use the Update_wrapper function to enter code that computes the values of the discrete states at the next time step according to the values at the current time step. See the following code:

void sfun_Update_wrapper(const real_T *u,
                         const real_T *y,
                         real_T *xD,
                         const real_T  *param0,  /* optional */
                         int_T p_width0, /* optional */
                         real_T  *param1,/* optional */
                         int_T p_width1, /* optional */
                         int_T y_width, /* optional */
                         int_T u_width) /* optional */
{

	/* Your code inserted here. */

}

where sfun is the name of the S-function. The S-Function Builder inserts a call to this wrapper function in the mdlUpdate callback method that it generates for the S-function. The Simulink engine calls the mdlUpdate method at the end of each time step to obtain the values of the discrete states at the next time step (see Simulink Engine Interaction with C S-Functions). At the next time step, the engine passes the updated states back to the mdlOutputs method.

The mdlUpdate callback method generated for the S-function passes the following arguments to the Update_wrapper function:

  • u

  • y

  • xD

  • param0, p_width0, param1, p_width1, ... paramN, p_widthN

  • y_width

  • u_width

See Compute Outputs Using Outputs Method for the meanings and usage of these arguments. Your code should use the discrete states variable, xD, to return the values of the discrete states that it computes. The arguments allow your code to compute the discrete states as functions of the S-function inputs, outputs, and, optionally, parameters. Your code can invoke external functions declared in the code editor.

Compute Continuous States Using Derivatives Method

If the S-function has continuous states, use the Derivatives_wrapper function to enter the code required to compute the state derivatives. Enter code for the mdlDerivatives function to compute the derivatives of the continuous states editor under this field. An example declaration of the function may look like the following:

void system_Derivatives_wrapper(const real_T *u,
				      const real_T *y, 
				      real_T *dx,
				      real_T *xC, 
				      const real_T  *param0,  /* optional */
				      int_T p_width0, /* optional */
				      real_T  *param1,/* optional */
	 			     int_T p_width1, /* optional */
				      int_T y_width, /* optional */
		 		     int_T u_width) /* optional */
{

	/* Your code inserted here. */

}

where system is the name of the S-function.

The S-Function Builder inserts a call to this wrapper function in the mdlDerivatives method that it generates for the S-function. The Simulink engine calls the mdlDerivatives method at the end of each time step to obtain the derivatives of the continuous states (see Simulink Engine Interaction with C S-Functions). The Simulink solver numerically integrates the derivatives to determine the continuous states at the next time step. At the next time step, the engine passes the updated states back to the mdlOutputs method. For more information on how Simulink engine interacts with S-functions, see Simulink Engine Interaction with C S-Functions.

The mdlDerivatives method generated for the S-function passes the following arguments to the derivatives wrapper function:

  • u

  • y

  • dx

  • xC

  • param0, p_width0, param1, p_width1, ... paramN, p_widthN

  • y_width

  • u_width

The dx argument is a pointer to an array whose width is the same as the number of continuous derivatives specified on the Settings pane. Your code should use this array to return the values of the derivatives that it computes. See the explanation under Compute Outputs Using Outputs Method method for the meanings and usage of the other arguments. The arguments allow your code to compute derivatives as a function of the S-function inputs, outputs, and, optionally, parameters. Your code can invoke external functions declared in the code editor.

Allocate and Deallocate Memory Using Start and Terminate Methods

Use the Start_wrapper method to write code to allocate memory at the start of simulation. The allocated is referenced by Pworks for use throughout the S-function. Similarly, use the Terminate_wrapper method to write code to free up the memory allocated at the Start_wrapper method. Memory referenced by PWorks can also be seen by Terminate, and should be deallocated here.

See the Set the Number of PWorks to learn more about PWorks.

Build the S-Function

After entering the code in the S-Function Builder editor, investigate the options under Build menu to build your S-function.

S-function Builder build pane. There are two buttons; Build and Generate Code Only. Then there are options. These options are: Show compile steps, Create a debuggable MEX-file, Generate wrapper TLC, Enable support for coverage, Enable support for design verifier.

The build menu contains the following selections:

  • Show compile steps — Log each build step in the Build Log field.

  • Create a debuggable MEX-file — Include debugging information in the generated MEX file.

  • Generate wrapper TLC — Selecting this option allows you to generate a TLC file. You need to generate a TLC file if you are running your model in Rapid Accelerator mode or generating Simulink Coder™ code from your model. Also, while it is not necessary for Accelerator mode simulations, the TLC file generates code for the S-function and thus makes your model run faster in Accelerator mode.

  • Enable support for coverage — Make an S-Function compatible with model coverage. For more information, see Coverage for Custom C/C++ Code in Simulink Models (Simulink Coverage) in the Simulink Coverage™ documentation.

  • Enable support for design verifier — Generate an S-function for use with the Simulink Design Verifier™. For more information, see Support Limitations for S-Functions and C/C++ Code (Simulink Design Verifier).

When you make your selection, click Build to build your S-function and build a MEX-file. To exclude building of a MEX-file from the generated source code, select Generate Code Only.

See Also

|

Related Topics