0% found this document useful (0 votes)
12 views466 pages

Sfun

Uploaded by

u1903162
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views466 pages

Sfun

Uploaded by

u1903162
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 466

Simulink®

Developing S-Functions

R2021a
How to Contact MathWorks

Latest news: www.mathworks.com

Sales and services: www.mathworks.com/sales_and_services

User community: www.mathworks.com/matlabcentral

Technical support: www.mathworks.com/support/contact_us

Phone: 508-647-7000

The MathWorks, Inc.


1 Apple Hill Drive
Natick, MA 01760-2098
Developing S-Functions
© COPYRIGHT 1998–2021 by The MathWorks, Inc.
The software described in this document is furnished under a license agreement. The software may be used or copied
only under the terms of the license agreement. No part of this manual may be photocopied or reproduced in any form
without prior written consent from The MathWorks, Inc.
FEDERAL ACQUISITION: This provision applies to all acquisitions of the Program and Documentation by, for, or through
the federal government of the United States. By accepting delivery of the Program or Documentation, the government
hereby agrees that this software or documentation qualifies as commercial computer software or commercial computer
software documentation as such terms are used or defined in FAR 12.212, DFARS Part 227.72, and DFARS 252.227-7014.
Accordingly, the terms and conditions of this Agreement and only those rights specified in this Agreement, shall pertain
to and govern the use, modification, reproduction, release, performance, display, and disclosure of the Program and
Documentation by the federal government (or other entity acquiring for or through the federal government) and shall
supersede any conflicting contractual terms or conditions. If this License fails to meet the government's needs or is
inconsistent in any respect with federal procurement law, the government agrees to return the Program and
Documentation, unused, to The MathWorks, Inc.
Trademarks
MATLAB and Simulink are registered trademarks of The MathWorks, Inc. See
www.mathworks.com/trademarks for a list of additional trademarks. Other product or brand names may be
trademarks or registered trademarks of their respective holders.
Patents
MathWorks products are protected by one or more U.S. patents. Please see www.mathworks.com/patents for
more information.
Revision History
October 1998 First printing Revised for Version 3.0 (Release R11)
November 2000 Second printing Revised for Version 4.0 (Release R12)
July 2002 Third printing Revised for Version 5.0 Release R13)
April 2003 Online only Revised for Version 5.1 (Release R13SP1)
April 2004 Online only Revised for Version 5.1.1 (Release R13SP1+)
June 2004 Online only Revised for Version 6.0 (Release R14)
October 2004 Online only Revised for Version 6.1 (Release R14SP1)
March 2005 Online only Revised for Version 6.2 (Release R14SP2)
September 2005 Online Only Revised for Version 6.3 (Release R14SP3)
March 2006 Online only Revised for Version 6.4 (Release 2006a)
September 2006 Online only Revised for Version 6.5 (Release 2006b)
March 2007 Online only Revised for Version 6.6 (Release 2007a)
September 2007 Online only Revised for Version 7.0 (Release 2007b)
March 2008 Online only Revised for Version 7.1 (Release 2008a)
October 2008 Online only Revised for Version 7.2 (Release 2008b)
March 2009 Online only Revised for Version 7.3 (Release 2009a)
September 2009 Online only Revised for Version 7.4 (Release 2009b)
March 2010 Online only Revised for Version 7.5 (Release 2010a)
September 2010 Online only Revised for Version 7.6 (Release 2010b)
April 2011 Online only Revised for Version 7.7 (Release 2011a)
September 2011 Online only Revised for Version 7.8 (Release 2011b)
March 2012 Online only Revised for Version 7.9 (Release 2012a)
September 2012 Online only Revised for Version 8.0 (Release 2012b)
March 2013 Online only Revised for Version 8.1 (Release 2013a)
September 2013 Online only Revised for Version 8.2 (Release 2013b)
March 2014 Online only Revised for Version 8.3 (Release 2014a)
October 2014 Online only Revised for Version 8.4 (Release 2014b)
March 2015 Online only Revised for Version 8.5 (Release 2015a)
September 2015 Online only Revised for Version 8.6 (Release 2015b)
October 2015 Online only Rereleased for Simulink 8.5.1 (Release 2015aSP1)
March 2016 Online only Revised for Version 8.7 (Release 2016a)
September 2016 Online only Revised for Version 8.8 (Release 2016b)
March 2017 Online only Revised for Version 8.9 (Release 2017a)
September 2017 Online only Revised for Version 9.0 (Release 2017b)
March 2018 Online only Revised for Version 9.1 (Release 2018a)
September 2018 Online only Revised for Version 9.2 (Release 2018b)
March 2019 Online only Revised for Version 9.3 (Release 2019a)
September 2019 Online only Revised for Version 10.0 (Release 2019b)
March 2020 Online only Revised for Version 10.1 (Release 2020a)
September 2020 Online only Revised for Version 10.2 (Release 2020b)
March 2021 Online only Revised for Version 10.3 (Release 2021a)
Contents

Introduction to S-Functions
1
What Is an S-Function? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-2
How S-Functions Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-2
Use S-Functions in Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-3

S-Function Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-7


Direct Feedthrough . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-7
Dynamically Sized Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-7
Setting Sample Times and Offsets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-8

Implementing S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-12


MATLAB S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-12
MEX S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-12

S-Function Callback Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-14


Callback Methods Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-14
Callback Methods for C MEX S-Functions . . . . . . . . . . . . . . . . . . . . . . . . 1-14
Callback Methods for Level-2 MATLAB S-Functions . . . . . . . . . . . . . . . . 1-16

S-Function Compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-18

S-Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-19


Overview of Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-19
Level-2 MATLAB S-Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 1-20
Level-1 MATLAB S-Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . 1-21
C S-Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-21
Fortran S-Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-24
C++ S-Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-25
Organizing S-Function in a Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1-25

Selecting an S-Function Implementation


2
Available S-Function Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-2

S-Function Features and Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-3


S-Function Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-3
S-Function Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-5

S-Functions Incorporate Legacy C Code . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-7


Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2-7
Using a Hand-Written S-Function to Incorporate Legacy Code . . . . . . . . . 2-8

v
Using the S-Function Builder to Incorporate Legacy Code . . . . . . . . . . . . 2-9
Using the Legacy Code Tool to Incorporate Legacy Code . . . . . . . . . . . . 2-12

Writing S-Functions in MATLAB


3
Write Level-2 MATLAB S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-2
About Level-2 MATLAB S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-2
About Run-Time Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-2
Level-2 MATLAB S-Function Template . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-3
Level-2 MATLAB S-Function Callback Methods . . . . . . . . . . . . . . . . . . . . . 3-3
Using the setup Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-4
Example of Writing a Level-2 MATLAB S-Function . . . . . . . . . . . . . . . . . . 3-5
Instantiating a Level-2 MATLAB S-Function . . . . . . . . . . . . . . . . . . . . . . . 3-7
Operations for Variable-Size Signals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-7
Generating Code from a Level-2 MATLAB S-Function . . . . . . . . . . . . . . . . 3-8
MATLAB S-Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-8
MATLAB S-Function Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-8

Maintain Level-1 MATLAB S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-9


About the Maintenance of Level-1 MATLAB S-Functions . . . . . . . . . . . . . . 3-9
Level-1 MATLAB S-Function Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . 3-9
Level-1 MATLAB S-Function Outputs . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-10
Define S-Function Block Characteristics . . . . . . . . . . . . . . . . . . . . . . . . . 3-11
Processing S-Function Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-11
Convert Level-1 MATLAB S-Functions to Level-2 . . . . . . . . . . . . . . . . . . 3-12

Writing S-Functions in C
4
Create a Basic C MEX S-Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-2
About C MEX S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-2
Introducing an Example of a Basic C MEX S-Function . . . . . . . . . . . . . . . . 4-3
Defines and Includes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-5
Callback Method Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-5
Simulink/Simulink Coder Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-7
Building the Timestwo Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-7

Use a Bus Signal with S-Function Builder to Create an S-Function . . . . . 4-8


How the S-Function Builder Builds an S-Function . . . . . . . . . . . . . . . . . . 4-11
Deploy the Generated S-Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-12

Build S-Functions Automatically Using S-Function Builder . . . . . . . . . . 4-13


Create an S-Function Builder Block and Specify Settings . . . . . . . . . . . . 4-13
Specify Ports and Parameters for the S-Function . . . . . . . . . . . . . . . . . . 4-17
Use the Libraries Table to Specify External Code and Paths . . . . . . . . . . 4-18
Develop the S-Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-19
Build the S-Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-24

vi Contents
Model a State-Space System Using S-Function Builder . . . . . . . . . . . . . . 4-25
Configure the S-Function Builder Settings . . . . . . . . . . . . . . . . . . . . . . . 4-25
Define Ports and Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-25
Define the Output Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-26
Define the Update Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-27
Build the State-Space System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-27

Templates for C S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-28


About the Templates for C S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . 4-28
S-Function Source File Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-28
The SimStruct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-30
Data Types in S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-30
Compiling C S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-30

Integrate C Functions Using Legacy Code Tool . . . . . . . . . . . . . . . . . . . . 4-31


Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-31
Integrate C Functions into Simulink Models with Legacy Code Tool . . . . 4-33
Integrate C Function Whose Arguments Are Pointers to Structures . . . . . 4-35
Registering Legacy Code Tool Data Structures . . . . . . . . . . . . . . . . . . . . 4-39
Declaring Legacy Code Tool Function Specifications . . . . . . . . . . . . . . . . 4-40
Generating and Compiling the S-Functions . . . . . . . . . . . . . . . . . . . . . . . 4-46
Generating a Masked S-Function Block for Calling a Generated S-Function
..................................................... 4-46
Forcing Simulink Accelerator Mode to Use S-Function TLC Inlining Code
..................................................... 4-47
Calling Legacy C++ Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-47
Handling Multiple Registration Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-47
Deploying Generated S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-48
Legacy Code Tool Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-48
Legacy Code Tool Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-48

Simulink Engine Interaction with C S-Functions . . . . . . . . . . . . . . . . . . . 4-50


Process View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-50
Data View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-55

S-Functions in Normal Mode Referenced Models . . . . . . . . . . . . . . . . . . 4-59


Supporting the Use of Multiple Instances of Referenced Models That Are in
Normal Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-59

Debug C MEX S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-61


About Debugging C MEX S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-61
Debug in Simulink Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-61
Debug Using Third-Party Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-64

Convert Level-1 C MEX S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-67


Guidelines for Converting Level-1 C MEX S-Functions to Level-2 . . . . . . 4-67
Obsolete Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4-69

vii
Creating C++ S-Functions
5
Create C++ Source File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-2
C++ References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-2

Make C++ Objects Persistent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-3

Build C++ S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-4

Creating Fortran S-Functions


6
Create Level-2 Fortran S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-2
About Creating Level-2 Fortran S-Functions . . . . . . . . . . . . . . . . . . . . . . . 6-2
Template File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-2
C/Fortran Interfacing Tips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-2
Constructing the Gateway . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-5
Example C MEX S-Function Calling Fortran Code . . . . . . . . . . . . . . . . . . . 6-7

Port Legacy Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-9


Find the States . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-9
Sample Times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-9
Store Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-9
Use Flints if Needed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-10
Considerations for Real Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-10

Run S-Function Analyzer


7
Check S-Functions Using S-Function Analyzer APIs . . . . . . . . . . . . . . . . . 7-2
Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-2
Setup the Working Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-2
Run the S-Function Checks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-3
Explore and Fix Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-4

Troubleshoot S-Function Checks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-12


Environment Checks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-12
Source Code Checks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-12
S-Function MEX File Checks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-12
Input Parameter Robustness Check . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-13

viii Contents
Using Work Vectors
8
DWork Vector Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-2
What is a DWork Vector? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-2
DWork Vectors and the Simulink Engine . . . . . . . . . . . . . . . . . . . . . . . . . . 8-3
DWork Vectors and the Simulink Coder Product . . . . . . . . . . . . . . . . . . . . 8-3

Types of DWork Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-4

How to Use DWork Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-5


Using DWork Vectors in C MEX S-Functions . . . . . . . . . . . . . . . . . . . . . . . 8-5
DWork Vector C MEX Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-7
Using DWork Vectors With Legacy Code . . . . . . . . . . . . . . . . . . . . . . . . . . 8-8

DWork Vector Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-9


General DWork Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-9
DWork Scratch Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-10
DState Work Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-11
DWork Mode Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-13

Elementary Work Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-15


Description of Elementary Work Vector . . . . . . . . . . . . . . . . . . . . . . . . . . 8-15
Relationship to DWork Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-15
Using Elementary Work Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-16
Additional Work Vector Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-17
Elementary Work Vector Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-17

Memory Allocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8-21

Implementing Block Features for C/C++ S-Functions


9
Pass Dialog Parameters to S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-2
About Dialog Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-2
Tunable Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-3

Create and Update S-Function Run-Time Parameters . . . . . . . . . . . . . . . . 9-5


About Run-Time Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-5
Creating Run-Time Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-6
Updating Run-Time Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-9
Tuning Run-Time Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-10
Accessing Run-Time Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-10

Input and Output Ports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-12


Creating Input Ports for C S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . 9-12
Creating Output Ports for C S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . 9-14
Scalar Expansion of Inputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-15
Masked Multiport S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-16

ix
Configure Custom Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-18
Custom Data Types in C S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-18
Using Simulink Recognizable Data Types in C S-Functions . . . . . . . . . . . 9-18
Using Opaque Data Types in C S-Functions . . . . . . . . . . . . . . . . . . . . . . 9-18

Specify S-Function Sample Times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-20


About Sample Times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-20
Block-Based Sample Times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-21
Specifying Port-Based Sample Times . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-23
Hybrid Block-Based and Port-Based Sample Times . . . . . . . . . . . . . . . . . 9-27
Multirate S-Function Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-27
Multirate S-Functions and Sample Time Hit Calculations . . . . . . . . . . . . 9-28
Synchronizing Multirate S-Function Blocks . . . . . . . . . . . . . . . . . . . . . . . 9-29
Specifying Model Reference Sample Time Inheritance . . . . . . . . . . . . . . 9-29

Zero Crossings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-31

S-Function Compliance with the ModelOperatingPoint . . . . . . . . . . . . . . 9-34


ModelOperatingPoint Compliance Specification for C MEX S-Functions . 9-34

MATLAB Data in C S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-36


mxArray Manipulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-36
mxArrays Using 32-bit APIs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-36
mxArrays Using Interleaved Complex Representation . . . . . . . . . . . . . . . 9-37

Implement Function-Call Subsystems with S-Functions . . . . . . . . . . . . . 9-39

Use C/C++ S-Functions as Sim Viewing Devices in External Mode . . . . 9-43

Handle Errors in S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-44


About Handling Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-44
Exception Free Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-45
ssSetErrorStatus Termination Criteria . . . . . . . . . . . . . . . . . . . . . . . . . . 9-45
Checking Array Bounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-46

Guidelines for Writing Thread-Safe S-Functions . . . . . . . . . . . . . . . . . . . 9-47


Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-47
Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-47

C MEX S-Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-51


About S-Function Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-51
Continuous States . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-51
Discrete States . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-55
Continuous and Discrete States . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-59
Variable Sample Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-63
Array Inputs and Outputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-67
Zero-Crossing Detection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-73
Discontinuities in Continuous States . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9-83

x Contents
Implement Block Features for MATLAB S-Functions
10
Pass Dialog Parameters to S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-2
Using Level-2 MATLAB S-Function Dialog Parameters . . . . . . . . . . . . . . 10-2
Tunable Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-2

Create and Update S-Function Run-Time Parameters . . . . . . . . . . . . . . . 10-4


Create Run-Time Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-4
Update Run-Time Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-4

Create Input and Output Ports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-6


Creating Input Ports for Level-2 MATLAB S-Functions . . . . . . . . . . . . . . 10-6
Creating Output Ports for Level-2 MATLAB S-Functions . . . . . . . . . . . . . 10-6
Scalar Expansion of Inputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-7
Masked Multiport S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-7

Inherit Custom Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-8

Specify S-Function Sample Times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-9


About Sample Times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-9
Block Based Sample Times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-9
Specifying Port-Based Sample Times . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-9
Hybrid Block-Based and Port-Based Sample Times . . . . . . . . . . . . . . . . 10-10
Multirate S-Function Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-10
Synchronizing Multirate S-Function Blocks . . . . . . . . . . . . . . . . . . . . . 10-11

S-Function Compliance with ModelOperatingPoint . . . . . . . . . . . . . . . . 10-12


ModelOperatingPoint Compliance Specification for Level-2 MATLAB S-
Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-12

Use DWork Vectors in S-Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-14


What is a DWork Vector? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10-14
How to use DWork Vectors in Level-2 MATLAB S-Functions . . . . . . . . . 10-15
Level-2 MATLAB S-Function DWork Vector Example . . . . . . . . . . . . . . . 10-16

Use MATLAB S-Functions as Sim Viewing Devices in External Mode . . 10-18

S-Function Callback Methods


11

S-Function SimStruct Functions Reference


12
S-Function SimStruct Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12-2
About SimStruct Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12-2
Language Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12-2
The SimStruct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12-2

xi
SimStruct Macros and Functions Listed by Usage . . . . . . . . . . . . . . . . . . 12-2

S-Function Options
13

xii Contents
1

Introduction to S-Functions

• “What Is an S-Function?” on page 1-2


• “S-Function Concepts” on page 1-7
• “Implementing S-Functions” on page 1-12
• “S-Function Callback Methods” on page 1-14
• “S-Function Compatibility” on page 1-18
• “S-Function Examples” on page 1-19
1 Introduction to S-Functions

What Is an S-Function?
S-functions (system-functions) provide a powerful mechanism for extending the capabilities of the
Simulink environment. An S-function is a computer language description of a Simulink block written
in MATLAB®, C, C++, or Fortran. C, C++, and Fortran S-functions are compiled as MEX files using
the mex utility (see “Build C MEX Function”). As with other MEX files, S-functions are dynamically
linked subroutines that the MATLAB execution engine can automatically load and execute.

S-functions use a special calling syntax called the S-function API that enables you to interact with the
Simulink engine. This interaction is very similar to the interaction that takes place between the
engine and built-in Simulink blocks.

S-functions follow a general form and can accommodate continuous, discrete, and hybrid systems. By
following a set of simple rules, you can implement an algorithm in an S-function and use the S-
Function block to add it to a Simulink model. After you write your S-function and place its name in an
S-Function block (available in the User-Defined Functions block library), you can customize the user
interface using masking (see “Create Block Masks”).

If you have Simulink Coder™, you can use S-functions in a model and generate code. You can also
customize the code generated for S-functions by writing a Target Language Compiler (TLC) file. For
more information, see “S-Functions and Code Generation” (Simulink Coder).

How S-Functions Work


S-functions define how a block works during different parts of simulation, such as initialization,
update, derivatives, outputs and termination. In every step of a simulation, a method is invoked by
the simulation engine to fulfill a specific task. S-function basics require fundamental knowledge of
mathematical relationships between the block inputs, states, and outputs. To understand how S-
functions work, first you need to understand the mathematics of how Simulink simulates a model,
namely the stages of simulation. See “Simulation Phases in Dynamic Systems” for more detailed
information.

Mathematics of Simulink Blocks

A Simulink block consists of a set of inputs, a set of states, a set of parameters, and a set of outputs,
where the outputs are a function of the simulation time, the inputs, parameters, and the states.

The following equations express the mathematical relationships between the inputs, outputs,
parameters, states, and simulation time.

y = f 0(t, x, u) (Outputs)
ẋ = f d(t, x, u) (Derivatives)
xdk + 1 = f u(t, xc, xdk, u), (Update)

where x = [xc; xd] .

Simulation Stages

Execution of a Simulink model proceeds in stages. In the Initialization phase, the Simulink engine
incorporates library blocks into the model, propagates signal widths, data types, and sample times,

1-2
What Is an S-Function?

evaluates block parameters, determines block execution order, and allocates memory. The engine
then enters a simulation loop, where each pass through the loop is referred to as a simulation step.
During each simulation step, the engine executes each block in the model in the order determined
during initialization. For each block, the engine invokes functions that compute the block states,
derivatives, and outputs for the current sample time. The entire simulation loop then continues until
the simulation is complete.

Model Initialization - Model is prepared for simulation. In this stage, block parameters are evaluated,
block execution order is determined, and memory for each operation is allocated. After this stage,
blocks go through a simulation loop.

Update of Continuous States and Time - Takes place only if the model has continuous states. You can
modify minor step methods such as mdlOutputs, mdlDerivatives, and mdlZeroCrossing to
compute the outputs

S-Function Callback Methods

An S-function comprises a set of S-function callback methods that perform tasks required at each
simulation stage. During simulation of a model, at each simulation stage, the Simulink engine calls
the appropriate methods for each S-Function block in the model. Tasks performed by S-function
callback methods include:

• Compilation — In this stage, the Simulink engine initializes the S-function. Tasks include:

• Incorporating library blocks into the model, and propagating signal widths, data types, and
sample times
• Setting the number and dimensions of input and output ports
• Evaluating block parameters, and determining the block execution order
• Allocating memory and storage areas.
• Calculation of outputs — At this state, outputs are calculated until all the block output ports are
valid for the current time step, namely all output values are at a certain error range.
• Update discrete states — In this call, the block performs once-per-time-step activities such as
updating discrete states.
• Initialize and Terminate Methods — These optional methods perform initialization and termination
activities required by S-function only once. The initialization activities may include setting up user
data, or initializing state vectors in an S-function. The termination method performs any actions
such as freeing of memory that is required when the simulation is terminated, or when an S-
function block is deleted from a model.
• Integration — This applies to models with continuous states and/or nonsampled zero crossings. If
your S-function has continuous states, the engine calls the output and derivative portions of your
S-function at minor time steps. This is so the solvers can compute the states for your S-function. If
your S-function has nonsampled zero crossings, the engine also calls the output and zero-
crossings portions of your S-function at minor time steps so that it can locate the zero crossings.

To understand the terminology on simulations especially for S-functions, see “S-Function Concepts”
on page 1-7.

Use S-Functions in Models


• “Passing Parameters to S-Functions” on page 1-4

1-3
1 Introduction to S-Functions

• “When to Use an S-Function” on page 1-5

1 To incorporate a C MEX S-function in a model, drag a S-function block from the Simulink
Library Browser. Similarly, to incorporate a Level-2 MATLAB S-function to the model, drag a
Level-2 MATLAB S-function block to the model.
2 Open up the Block Parameters dialog and specify the S-function name at the S-function name
field to provide functionality for the S-function block. For example, type timestwo and hit Apply
to add a C MEX S-function that multiplies the incoming signal by two.

Note If the MATLAB path includes a C MEX file and a MATLAB file having the same name referenced
by an S-Function block, the S-Function block uses the C MEX file.

Passing Parameters to S-Functions

In both S-function block and Level-2 MATLAB S-Function Block Parameters window allows you to
specify parameter values to pass to the corresponding S-function. To use these fields, you must know
the parameters the S-function requires and the order in which the function requires them. (If you do
not know, consult the S-function's author, documentation, or source code.) Enter the parameters,
separated by a comma, in the order required by the S-function. The parameter values can be
constants, names of variables defined in the MATLAB or model workspace, or MATLAB expressions.

The following example illustrates usage of the Parameters field to enter user-defined parameters for
a Level-2 MATLAB S-function.

The model msfcndemo_limintm in this example incorporates the sample S-function


msfcn_limintm.m.

1-4
What Is an S-Function?

The msfcn_limintm.m S-function accepts three parameters: a lower bound, an upper bound, and an
initial condition. The S-function outputs the time integral of the input signal if the time integral is
between the lower and upper bounds, the lower bound if the time integral is less than the lower
bound, and the upper bound if the time integral is greater than the upper bound. The dialog box in
the example specifies a lower and upper bound and an initial condition of -5.0, 5.0, and 0,
respectively. The scope shows the resulting output when the input is a sine wave of amplitude 5.

See “Processing S-Function Parameters” on page 3-11 and “Handle Errors in S-Functions” on page
9-44 for information on how to access user-specified parameters in an S-function.

You can use the masking facility to create custom dialog boxes and icons for your S-Function blocks.
Masked dialog boxes can make it easier to specify additional parameters for S-functions. For a
discussion on masking, see “Create Block Masks”.

When to Use an S-Function

You can use S-functions for a variety of applications, including:

• Creating new general purpose blocks


• Adding blocks that represent hardware device drivers
• Incorporating existing C code into a simulation (see “Integrate C Functions Using Legacy Code
Tool” on page 4-31)
• Describing a system as a set of mathematical equations
• Using graphical animations (see the inverted pendulum example, “Inverted Pendulum with
Animation”)

The most common use of S-functions is to create custom Simulink blocks (see “Block Creation
Basics”). When you use an S-function to create a general-purpose block, you can use it many times in
a model, varying parameters with each instance of the block.

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

1-5
1 Introduction to S-Functions

More About
• “S-Function Concepts” on page 1-7
• “S-Function Callback Methods” on page 1-14
• “S-Function Features and Limitations” on page 2-3
• “S-Functions and Code Generation” (Simulink Coder)

1-6
S-Function Concepts

S-Function Concepts
In this section...
“Direct Feedthrough” on page 1-7
“Dynamically Sized Arrays” on page 1-7
“Setting Sample Times and Offsets” on page 1-8

Direct Feedthrough
Direct feedthrough means that the output (or the variable sample time for variable sample time
blocks) is controlled directly by the value of an input port signal. Typically, an S-function input port
has direct feedthrough if

• The output function (mdlOutputs) is a function of the input u. That is, there is direct feedthrough
if the input u is accessed by mdlOutputs. Outputs can also include graphical outputs, as in the
case of an XY Graph scope.
• The “time of next hit” function (mdlGetTimeOfNextVarHit) of a variable sample time S-function
accesses the input u.

An example of a system that requires its inputs (that is, has direct feedthrough) is the operation

y = k × u,

where u is the input, k is the gain, and y is the output.

An example of a system that does not require its inputs (that is, does not have direct feedthrough) is
the simple integration algorithm

y = x,

ẋ = u,

where x is the state, ẋ is the state derivative with respect to time, u is the input, and y is the output.
Simulink integrates the variable ẋ .

It is very important to set the direct feedthrough flag correctly because it affects the execution order
of the blocks in your model and is used to detect algebraic loops (see “Algebraic Loop Concepts” in
Using Simulink). If the simulation results for a model containing your S-function do not converge, or
the simulation fails, you may have the direct feedthrough flag set incorrectly. Try turning on the
direct feedthrough flag and setting the Algebraic loop solver diagnostic to warning (see the
“Algebraic loop” option on the “Model Configuration Parameters: Diagnostics” reference page in
Simulink Graphical User Interface). Subsequently running the simulation displays any algebraic loops
in the model and shows if the engine has placed your S-function within an algebraic loop.

Dynamically Sized Arrays


You can write your S-function to support arbitrary input dimensions. In this case, the Simulink engine
determines the actual input dimensions when the simulation is started by evaluating the dimensions
of the input vectors driving the S-function. Your S-function can also use the input dimensions to
determine the number of continuous states, the number of discrete states, and the number of outputs.

1-7
1 Introduction to S-Functions

Note A dynamically sized input can have a different size for each instance of the S-function in a
particular model or during different simulations, however the input size of each instance of the S-
function is static over the course of a particular simulation.

A C MEX S-function and Level-2 MATLAB S-function can have multiple input and output ports and
each port can have different dimensions. The number of dimensions and the size of each dimension
can be determined dynamically.

For example, the following illustration shows two instances of the same S-Function block in a model.

The upper S-Function block is driven by a block with a three-element output vector. The lower S-
Function block is driven by a block with a scalar output. By specifying that the S-Function block has
dynamically sized inputs, the same S-function can accommodate both situations. The Simulink engine
automatically calls the block with the appropriately sized input vector. Similarly, if other block
characteristics, such as the number of outputs or the number of discrete or continuous states, are
specified as dynamically sized, the engine defines these vectors to be the same length as the input
vector.

See “Input and Output Ports” on page 9-12 for more information on configuring S-function input and
output ports.

Setting Sample Times and Offsets


Both Level-2 MATLAB and C MEX S-functions provide the following sample time options, which allow
for a high degree of flexibility in specifying when an S-function executes:

• Continuous sample time — For S-functions that have continuous states and/or nonsampled zero
crossings (see “Simulation Phases in Dynamic Systems” for an explanation of zero crossings). For
this type of S-function, the output changes in minor time steps.
• Continuous, but fixed in minor time step sample time — For S-functions that need to execute at
every major simulation step, but do not change value during minor time steps.
• Discrete sample time — If the behavior of your S-function is a function of discrete time intervals,
you can define a sample time to control when the Simulink engine calls the S-function. You can
also define an offset that delays each sample time hit. The value of the offset cannot exceed the
corresponding sample time.

A sample time hit occurs at time values determined by the formula

TimeHit = (n * period) + offset

where the integer n is the current simulation step. The first value of n is always zero.

If you define a discrete sample time, the engine calls the S-function mdlOutputs and mdlUpdate
routines at each sample time hit (as defined in the previous equation).

1-8
S-Function Concepts

• Variable sample time — A discrete sample time where the intervals between sample hits can vary.
At the start of each simulation step, S-functions with variable sample times are queried for the
time of the next hit.
• Inherited sample time — Sometimes an S-function has no inherent sample time characteristics
(that is, it is either continuous or discrete, depending on the sample time of some other block in
the system). In this case, you can specify that the sample time is inherited. A simple example of
this is a Gain block that inherits its sample time from the block driving it.

An S-function can inherit its sample time from

• The driving block


• The destination block
• The fastest sample time in the system

To specify an S-function sample time is inherited, use -1 in Level-2 MATLAB S-functions and
INHERITED_SAMPLE_TIME in C MEX S-functions as the sample time. For more information on the
propagation of sample times, see “How Propagation Affects Inherited Sample Times” in the
Simulink User's Guide.

S-functions can be either single or multirate; a multirate S-function has multiple sample times.

Sample times are specified in pairs in this format: [sample_time, offset_time].

Valid C MEX S-Function Sample Times

The valid sample time pairs for a C MEX S-function are

[CONTINUOUS_SAMPLE_TIME, 0.0]
[CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]
[discrete_sample_time_period, offset]
[VARIABLE_SAMPLE_TIME, 0.0]

where

CONTINUOUS_SAMPLE_TIME = 0.0
FIXED_IN_MINOR_STEP_OFFSET = 1.0
VARIABLE_SAMPLE_TIME = -2.0

and variable names in italics indicate that a real value is required.

Alternatively, you can specify that the sample time is inherited from the driving block. In this case,
the C MEX S-function has only one sample time pair, either

[INHERITED_SAMPLE_TIME, 0.0]

or

[INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]

where

INHERITED_SAMPLE_TIME = -1.0

Valid Level-2 MATLAB S-Function Sample Times

The valid sample time pairs for a Level-2 MATLAB S-function are

1-9
1 Introduction to S-Functions

[0 offset] % Continuous sample time


[discrete_sample_time_period, offset] % Discrete sample time
[-1, 0] % Inherited sample time
[-2, 0] % Variable sample time

where variable names in italics indicate that a real value is required. When using a continuous
sample time, an offset of 1 indicates the output is fixed in minor integration time steps. An offset
of 0 indicates the output changes at every minor integration time step.

Guidelines for Choosing a Sample Time

Use the following guidelines for help with specifying sample times:

• A continuous S-function that changes during minor integration steps should register the
[CONTINUOUS_SAMPLE_TIME, 0.0] sample time.
• A continuous S-function that does not change during minor integration steps should register the
[CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET] sample time.
• A discrete S-function that changes at a specified rate should register the discrete sample time
pair, [discrete_sample_time_period, offset], where

discrete_sample_period > 0.0

and

0.0 ≤ offset < discrete_sample_period


• A discrete S-function that changes at a variable rate should register the variable-step discrete
sample time.

[VARIABLE_SAMPLE_TIME, 0.0]

In a C MEX S-function, the mdlGetTimeOfNextVarHit routine is called to get the time of the
next sample hit for the variable-step discrete task. In a Level-2 MATLAB S-function, the
NextTimeHit property is set in the Outputs method to set the next sample hit.

If your S-function has no intrinsic sample time, you must indicate that your sample time is inherited.
There are two cases:

• An S-function that changes as its input changes, even during minor integration steps, should
register the [INHERITED_SAMPLE_TIME, 0.0] sample time.
• An S-function that changes as its input changes, but does not change during minor integration
steps (that is, remains fixed during minor time steps), should register the
[INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET] sample time.

The Scope block is a good example of this type of block. This block runs at the rate of its driving
block, either continuous or discrete, but never runs in minor steps. If it did, the scope display
would show the intermediate computations of the solver rather than the final result at each time
point.

See “Specify S-Function Sample Times” on page 9-20 for information on implementing different
types of sample times in S-functions.

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

1-10
S-Function Concepts

More About
• “Use S-Functions in Models” on page 1-3
• “S-Function Callback Methods” on page 1-14
• “S-Function Features and Limitations” on page 2-3

1-11
1 Introduction to S-Functions

Implementing S-Functions
In this section...
“MATLAB S-Functions” on page 1-12
“MEX S-Functions” on page 1-12

MATLAB S-Functions
Level-2 MATLAB S-functions allow you to create blocks with many of the features and capabilities of
Simulink built-in blocks, including:

• Multiple input and output ports


• The ability to accept vector or matrix signals
• Support for various signal attributes including data type, complexity, and signal frames
• Ability to operate at multiple sample rates

A Level-2 MATLAB S-function consists of a setup routine to configure the basic properties of the S-
function, and a number of callback methods that the Simulink engine invokes at appropriate times
during the simulation.

A basic annotated version of the template resides at msfuntmpl_basic.m.

The template consists of a top-level setup function and a set of skeleton local functions, each of
which corresponds to a particular callback method. Each callback method performs a specific S-
function task at a particular point in the simulation. The engine invokes the local functions using
function handles defined in the setup routine. See “Level-2 MATLAB S-Function Callback Methods”
on page 3-3 for a table of the supported Level-2 MATLAB S-function callback methods.

A more detailed Level-2 MATLAB S-function template resides at msfuntmpl.m.

We recommend that you follow the structure and naming conventions of the templates when creating
Level-2 MATLAB S-functions. This makes it easier for others to understand and maintain the MATLAB
S-functions that you create. See “Write Level-2 MATLAB S-Functions” on page 3-2 for information
on creating Level-2 MATLAB S-functions.

MEX S-Functions
Like a Level-2 MATLAB S-function, a MEX S-function consists of a set of callback methods that the
Simulink engine invokes to perform various block-related tasks during a simulation. MEX S-functions
can be implemented in C, C++, or Fortran. The engine directly invokes MEX S-function routines
instead of using function handles as with MATLAB S-functions. Because the engine invokes the
functions directly, MEX S-functions must follow standard naming conventions specified by the S-
function API.

An annotated C MEX S-function template resides at sfuntmpl_doc.c.

The template contains skeleton implementations of all the required and optional callback methods
that a C MEX S-function can implement.

For a more basic version of the template see sfuntmpl_basic.c.

1-12
Implementing S-Functions

MEX Versus MATLAB S-Functions

Level-2 MATLAB and MEX S-functions each have advantages. The advantage of Level-2 MATLAB S-
functions is speed of development. Developing Level-2 MATLAB S-functions avoids the time
consuming compile-link-execute cycle required when developing in a compiled language. Level-2
MATLAB S-functions also have easier access to MATLAB toolbox functions and can utilize the
MATLAB Editor/Debugger.

MEX S-functions are more appropriate for integrating legacy code into a Simulink model. For more
complicated systems, MEX S-functions may simulate faster than MATLAB S-functions because the
Level-2 MATLAB S-function calls the MATLAB execution engine for every callback method.

See “Available S-Function Implementations” on page 2-2 for information on choosing the type of S-
function best suited for your application.

See Also
“Comparison of Custom Block Functionality” | Level-2 MATLAB S-Function | MATLAB Function | S-
Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “S-Function Callback Methods” on page 1-14
• “S-Function Features and Limitations” on page 2-3

1-13
1 Introduction to S-Functions

S-Function Callback Methods


In this section...
“Callback Methods Overview” on page 1-14
“Callback Methods for C MEX S-Functions” on page 1-14
“Callback Methods for Level-2 MATLAB S-Functions” on page 1-16

Callback Methods Overview


Every S-function must implement a set of methods, called callback methods, that the Simulink engine
invokes when simulating a model that contains the S-function.

The S-function callback methods perform tasks required at each simulation stage. During simulation
of a model, at each simulation stage the Simulink engine calls the appropriate methods for each S-
Function block in the model.

Tasks performed by S-function callback methods include:

• Initialization — Prior to the first simulation loop, the engine initializes the S-function, including:

• Initializing the SimStruct, a simulation structure that contains information about the S-
function
• Setting the number and dimensions of input and output ports
• Setting the block sample times
• Allocating storage areas
• Calculation of next sample hit — If you created a variable sample time block, this stage
calculates the time of the next sample hit; that is, it calculates the next step size.
• Calculation of outputs in the major time step — After this call is complete, all the block
output ports are valid for the current time step.
• Update of discrete states in the major time step — In this call, the block performs once-per-
time-step activities such as updating discrete states.
• Integration — This applies to models with continuous states and/or nonsampled zero crossings. If
your S-function has continuous states, the engine calls the output and derivative portions of your
S-function at minor time steps. This is so the solvers can compute the states for your S-function. If
your S-function has nonsampled zero crossings, the engine also calls the output and zero-
crossings portions of your S-function at minor time steps so that it can locate the zero crossings.

Note See “Simulation Phases in Dynamic Systems” for an explanation of major and minor time
steps.

Some callback methods are optional. The engine invokes an optional callback only if the S-function
defines the callback.

Callback Methods for C MEX S-Functions


Required Callback Methods

C MEX S-functions must implement the following callback methods:

1-14
S-Function Callback Methods

• mdlInitializeSizes – Specifies the sizes of various parameters in the SimStruct, such as the
number of output ports for the block.
• mdlInitializeSampleTimes – Specifies the sample time(s) of the block.
• mdlOutputs – Calculates the output of the block.
• mdlTerminate – Performs any actions required at the termination of the simulation. If no actions
are required, this function can be implemented as a stub.

Optional Callback Methods

The following callback methods are optional. The engine invokes an optional callback only if the S-
function defines the callback.

• mdlCheckParameters
• mdlDerivatives
• mdlDisable
• mdlEnable
• mdlGetSimState
• mdlGetTimeOfNextVarHit
• mdlInitializeConditions
• mdlProcessParameters
• mdlProjection
• mdlRTW
• mdlSetDefaultPortComplexSignals
• mdlSetDefaultPortDataTypes
• mdlSetDefaultPortDimensionInfo
• mdlSetInputPortComplexSignal
• mdlSetInputPortDataType
• mdlSetInputPortDimensionInfo
• mdlSetInputPortDimensionsModeFcn
• mdlSetInputPortSampleTime
• mdlSetInputPortWidth
• mdlSetOutputPortComplexSignal
• mdlSetOutputPortDataType
• mdlSetOutputPortDimensionInfo
• mdlSetOutputPortSampleTime
• mdlSetOutputPortWidth
• mdlSetSimState
• mdlSetWorkWidths
• mdlSimStatusChange
• mdlSetupRuntimeResources
• mdlCleanupRuntimeResources
• mdlStart

1-15
1 Introduction to S-Functions

• mdlUpdate
• mdlZeroCrossings

Callback Methods for Level-2 MATLAB S-Functions


Required Callback Methods

Level-2 MATLAB S-functions must implement the following callback methods:

• setup – Specifies the sizes of various parameters in the SimStruct, such as the number of
output ports for the block.
• Outputs – Calculates the output of the block.
• Terminate – Performs any actions required at the termination of the simulation. If no actions are
required, this function can be implemented as a stub.

For information on writing callback methods, see “Write Level-2 MATLAB S-Functions” on page 3-
2.

Optional Callback Methods

The following callback methods are optional. The engine invokes an optional callback only if the S-
function defines the callback.

• CheckParameters
• Derivatives
• Disable
• Enable
• GetSimState
• InitializeConditions
• PostPropagationSetup
• ProcessParameters
• Projection
• SetInputPortComplexSignal
• SetInputPortDataType
• SetInputPortDimensions
• SetInputPortDimensionsMode
• SetInputPortSampleTime
• SetOutputPortComplexSignal
• SetOutputPortDataType
• SetOutputPortDimensions
• SetOutputPortSampleTime
• SetSimState
• SimStatusChange
• Start

1-16
S-Function Callback Methods

• Update
• WriteRTW

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “Use S-Functions in Models” on page 1-3
• “S-Function Features and Limitations” on page 2-3

1-17
1 Introduction to S-Functions

S-Function Compatibility
User-written level-2 S-functions are backward compatible in terms of their source code (C/C++). An
S-function written in an older release that is recompiled in a newer release retains the functionality
and behavior from the older release.

In addition, user-written level-2 S-function MEX files are backward compatible. For each release, all
example S-function MEX files included in the previous 10 releases of MATLAB on the Windows®
platform are tested for backward compatibility. In general, S-function MEX files created more than 10
releases before a new release can work in a new release if the platform and associated libraries
either remain unchanged or maintain backward compatibility.

Note If a user-written S-function contains code that depends on additional libraries (e.g., by using
the ‘-l’ option with the MEX command), S-function compatibility might not be supported with a library
update, a newer release of MATLAB, or a platform upgrade.

For best results, recompile the S-function source code in your current version of MATLAB. For more
information on MEX compatibility, see “MEX Version Compatibility”.

1-18
S-Function Examples

S-Function Examples

In this section...
“Overview of Examples” on page 1-19
“Level-2 MATLAB S-Function Examples” on page 1-20
“Level-1 MATLAB S-Function Examples” on page 1-21
“C S-Function Examples” on page 1-21
“Fortran S-Function Examples” on page 1-24
“C++ S-Function Examples” on page 1-25
“Organizing S-Function in a Project” on page 1-25

Overview of Examples
To run an example:

1 In the MATLAB Command Window, enter sfundemos.

The S-function example library opens.

Each block represents a category of S-function examples.


2 Double-click a category to display the examples that it includes. For example, click C-files.

1-19
1 Introduction to S-Functions

3 Double-click a block to open and run the example that it represents.

It might be helpful to examine some sample S-functions as you read the next chapters. Code for the
examples is stored in the following folder under the MATLAB root folder.

MATLAB code toolbox/simulink/simdemos/simfeatures


C, C++, and Fortran code toolbox/simulink/simdemos/simfeatures/src

Level-2 MATLAB S-Function Examples


The matlabroot/toolbox/simulink/simdemos/simfeatures folder (open) contains many
Level-2 MATLAB S-functions. Consider starting off by looking at these files.

Filename Model Name Description


msfcn_dsc.m msfcndemo_sfundsc1 Implement an S-function with an inherited
sample time.
msfcn_limintm.m msfcndemo_limintm Implement a continuous limited integrator
where the output is bounded by lower and
upper bounds and includes initial
conditions.
msfcn_multirate.m msfcndemo_multirate Implement a multirate system.
msfcn_times_two.m msfcndemo_timestwo Implement an S-function that doubles its
input.

1-20
S-Function Examples

Filename Model Name Description


msfcn_unit_delay.m msfcndemo_sfundsc2 Implement a unit delay.
msfcn_varpulse.m msfcndemo_varpulse Implement a variable pulse width
generator by calling set_param from
within a Level-2 MATLAB S-function. Also
demonstrates how to use custom set and
get methods for the block SimState.
msfcn_vs.m msfcndemo_vsfunc Implement a variable sample time block in
which the first input is delayed by an
amount of time determined by the second
input.

Level-1 MATLAB S-Function Examples


The matlabroot/toolbox/simulink/simdemos/simfeatures folder (open) also contains many
Level-1 MATLAB S-functions, provided as reference for legacy models. Most of these Level-1 MATLAB
S-functions do not have associated example models.

Filename Description
csfunc.m Define a continuous system in state-space format.
dsfunc.m Define a discrete system in state-space format.
limintm.m Implement a continuous limited integrator where the output is
bounded by lower and upper bounds and includes initial
conditions.
mixedm.m Implement a hybrid system consisting of a continuous
integrator in series with a unit delay.
sfun_varargm.m Implement an S-function that shows how to use the MATLAB
command varargin.
vsfunc.m Illustrate how to create a variable sample time block. This S-
function implements a variable step delay in which the first
input is delayed by an amount of time determined by the second
input.

C S-Function Examples
The matlabroot/toolbox/simulink/simdemos/simfeatures/src folder (open) contains
examples of C MEX S-functions, many of which have a MATLAB S-function counterpart. The C MEX
S-functions are listed in the following table.

Filename Model Name Description


csfunc.c sfcndemo_csfunc Implement a continuous system.
dlimintc.c No model available Implement a discrete-time limited integrator.
dsfunc.c sfcndemo_dsfunc Implement a discrete system.
limintc.c No model available Implement a limited integrator.

1-21
1 Introduction to S-Functions

Filename Model Name Description


mixedm.c sfcndemo_mixedm Implement a hybrid dynamic system consisting
of a continuous integrator (1/s) in series with a
unit delay (1/z).
mixedmex.c sfcndemo_mixedmex Implement a hybrid dynamic system with a
single output and two inputs.
slexQuantizeSFcn.c sfcndemo_sfun_quantize Implement a vectorized quantizer. Quantizes
the input into steps as specified by the
quantization interval parameter, q.
sdotproduct.c sfcndemo_sdotproduct Compute dot product (multiply-accumulate) of
two real or complex vectors.
sfbuilder_bususage.c sfbuilder_bususage Access S-Function Builder with a bus input and
output.
sfbuilder_movingAver sfbuilder_movingAverage Implement simple time window moving
age.c average using Start and Terminate.
sftable2.c sfcndemo_sftable2 Implement a two-dimensional table lookup.
sfun_atol.c sfcndemo_sfun_atol Set different absolute tolerances for each
continuous state.
sfun_cplx.c sfcndemo_cplx Add complex data for an S-function with one
input port and one parameter.
sfun_directlook.c No model available Implement a direct 1-D lookup.
sfun_dtype_io.c sfcndemo_dtype_io Implement an S-function that uses Simulink
data types for inputs and outputs.
sfun_dtype_param.c sfcndemo_dtype_param Implement an S-function that uses Simulink
data types for parameters.
sfun_dynsize.c sfcndemo_sfun_dynsize Implements dynamically-sized outputs .
sfun_errhdl.c sfcndemo_sfun_errhdl Check parameters using the
mdlCheckParameters S-function routine.
sfun_fcncall.c sfcndemo_sfun_fcncall Execute function-call subsystems on the first
and second output elements.
sfun_frmad.c sfcndemo_frame Implement a frame-based A/D converter.
sfun_frmda.c sfcndemo_frame Implement a frame-based D/A converter.
sfun_frmdft.c sfcndemo_frame Implement a multichannel frame-based
Discrete-Fourier transformation (and its
inverse).
sfun_frmunbuff.c sfcndemo_frame Implement a frame-based unbuffer block.
sfun_multiport.c sfcndemo_sfun_multiport Configure multiple input and output ports.
sfun_manswitch.c No model available Implement a manual switch.
sfun_matadd.c sfcndemo_matadd Add matrices in an S-function with one input
port, one output port, and one parameter.
sfun_multirate.c sfcndemo_sfun_multirate Demonstrate how to specify port-based sample
times.

1-22
S-Function Examples

Filename Model Name Description


sfun_port_constant.c sfcndemo_port_constant Demonstrate how to specify constant port-
based sample times.
sfun_port_triggered. sfcndemo_port_triggered Demonstrate how to use port-based sample
c times in a triggered subsystem.
sfun_runtime1.c sfcndemo_runtime Implement run-time parameters for all tunable
parameters.
sfun_runtime2.c sfcndemo_runtime Register individual run-time parameters.
sfun_runtime3.c sfcndemo_runtime Register dialog parameters as run-time
parameters.
sfun_runtime4.c sfcndemo_runtime Implement run-time parameters as a function
of multiple dialog parameters.
sfun_simstate.c No model available Demonstrate the S-function API for saving and
restoring the SimState.
sfun_zc.c sfcndemo_sfun_zc Demonstrate use of nonsampled zero crossings
to implement abs(u). This S-function is
designed to be used with a variable-step solver.
sfun_zc_sat.c sfcndemo_sfun_zc_sat Demonstrate zero crossings with saturation.
sfun_zc_cstate_sat.c sfcndemo_sfun_zc_cstate_sa Implement a continuous integrator with
t saturation limits and zero-crossing detection.
sfun_integrator_loca sfcndemo_sfun_localsolver Demonstrate a continuous integrator where
lsolver.c the continuous states are solved using a
separate local solver instead of that used by
the model.
sfun_angle_events.c sfcndemo_angle_events Implement a method for robust and efficient
detection of a rotating body crossing specified
angles.
sfun_angle_events.c No model available Demonstrate angle detection and incorporate
Stateflow® to schedule function calls.
sfunmem.c sfcndemo_sfunmem Implement a one-integration-step delay and
hold memory function.
simomex.c sfcndemo_simomex Implement a single-input, two-output state-
space dynamic system described by the state-
space equations:

dx/dt = Ax + Bu
y = Cx + Du

where x is the state vector, u is vector of


inputs, and y is the vector of outputs.

1-23
1 Introduction to S-Functions

Filename Model Name Description


stspace.c sfcndemo_stspace Implement a set of state-space equations. You
can turn this into a new block by using the S-
Function block and mask facility. This example
MEX file performs the same function as the
built-in State-Space block. This is an example
of a MEX file where the number of inputs,
outputs, and states is dependent on the
parameters passed in from the workspace.
stvctf.c sfcndemo_stvctf Implement a continuous-time transfer function
whose transfer function polynomials are
passed in via the input vector. This is useful for
continuous time adaptive control applications.
stvdtf.c sfcndemo_stvdtf Implement a discrete-time transfer function
whose transfer function polynomials are
passed in via the input vector. This is useful for
discrete-time adaptive control applications.
stvmgain.c sfcndemo_stvmgain Implement a time-varying matrix gain.
table3.c No model available Implement a 3-D lookup table.
timestwo.c sfcndemo_timestwo Implement a C MEX S-function that doubles its
input.
vdlmintc.c No model available Implement a discrete-time vectorized limited
integrator.
vdpmex.c sfcndemo_vdpmex Implement the Van der Pol equation.
vlimintc.c No model available Implement a vectorized limited integrator.
vsfunc.c sfcndemo_vsfunc Illustrate how to create a variable sample time
block. This block implements a variable-step
delay in which the first input is delayed by an
amount of time determined by the second
input.
sfun_pwm.c sfcndemo_pwm Illustrate how to create a controllable sample
time block.
sfun_d2c sfcndemo_d2c Illustrate how to convert a discrete input
signal into a smooth continuous output signal

Fortran S-Function Examples


The following table lists sample Fortran S-functions available in the matlabroot/toolbox/
simulink/simdemos/simfeatures/src folder (open).

Filename Model Name Description


sfun_atmos.c sfcndemo_atmos Calculate the 1976 standard atmosphere
sfun_atmos_sub.F to 86 km using a Fortran subroutine.

1-24
S-Function Examples

C++ S-Function Examples


The following table lists sample C++ S-functions available in the matlabroot/toolbox/
simulink/simdemos/simfeatures/src folder (open).

Filename Model Name Description


sfun_counter_cpp.cpp sfcndemo_counter_cpp Store a C++ object in the pointers vector
PWork.
sfbuilder_permute.cpp sfbuilder_permutation Implement permutation by calling external
C++ classes using Start and Terminate.
sfbuilder_linfilt.cpp sfbuilder_upsampling Implement linear filtering with C++ STL
algorithms for the upsampling of a sign
wave.

Organizing S-Function in a Project


The following example shows how to arrange S-function artifacts into a project. This project can be
shared with others as a zip archive or as a MATLAB toolbox.

Filename Description
slexBlocksetDesignerExample Organize S-function artifacts into a single project
using Blockset Designer.

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “Use S-Functions in Models” on page 1-3
• “S-Function Callback Methods” on page 1-14

1-25
2

Selecting an S-Function Implementation

• “Available S-Function Implementations” on page 2-2


• “S-Function Features and Limitations” on page 2-3
• “S-Functions Incorporate Legacy C Code” on page 2-7
2 Selecting an S-Function Implementation

Available S-Function Implementations


You can implement your S-function in one of five ways:

• A Level-2 MATLAB S-function provides access to a more extensive set of the S-function API and
supports code generation. In most cases, use a Level-2 MATLAB S-function when you want to
implement your S-function in MATLAB.

• A handwritten C MEX S-function provides the most programming flexibility. You can implement
your algorithm as a C MEX S-function or write a wrapper S-function to call existing C, C++, or
Fortran code. Writing a new S-function requires knowledge of the S-function API and, if you want
to generate inlined code for the S-function, the Target Language Compiler (TLC).
• The S-Function Builder is a graphical user interface for programming a subset of S-function
functionality. If you are new to writing C MEX S-functions, you can use the S-Function Builder to
generate new S-functions or incorporate existing C or C++ code without interacting with the S-
function API. The S-Function Builder can also generate TLC files for inlining your S-function
during code generation with the Simulink Coder product.
• The Legacy Code Tool is a set of MATLAB commands that helps you create an S-function to
incorporate legacy C or C++ code. Like the S-Function Builder, the Legacy Code Tool can
generate a TLC file to inline your S-function during code generation. The Legacy Code Tool
provides access to fewer of the methods in the S-function API than the S-Function Builder or a
handwritten C MEX S-function.

The following sections describe the uses, features, and differences of these S-function
implementations. The last section compares using a handwritten C MEX S-function, the S-Function
Builder, and the Legacy Code Tool to incorporate an existing C function into your Simulink model.

2-2
S-Function Features and Limitations

S-Function Features and Limitations

S-Function Features
The following tables give overviews of the features supported by different types of S-functions. The
first table focuses on handwritten S-functions. The second table compares the features of S-functions
automatically generated by the S-Function Builder or Legacy Code Tool.

Features of Hand-Written S-Functions

Feature Level-2 MATLAB S-Function Handwritten C MEX S-Function


Data types Supports any data type supported by Supports any data type supported by
Simulink software, including fixed-point Simulink software, including fixed-point
types. types.
Numeric types Supports real and complex signals. Supports real and complex signals.
Frame support Supports framed and unframed signals. Supports framed and unframed signals.
Port dimensions Supports scalar, 1-D, and Supports scalar, 1-D, and multidimensional
multidimensional input and output input and output signals.
signals.
S-function API Supports a larger set of the S-function Supports the entire S-function API.
API. See “Level-2 MATLAB S-Function
Callback Methods” on page 3-3 for a
list of supported methods.
Code generation Requires a handwritten TLC file to Natively supports code generation. Requires
support generate code. a handwritten TLC file to inline the S-
function during code generation.
Simulink Accelerator Provides the option to use a TLC file in Provides the option to use a TLC or MEX file
mode Accelerator mode, instead of running in Accelerator mode.
interpretively.
Model reference Supports Normal and Accelerator mode Provides options for sample time inheritance
simulations when used in a referenced and Normal mode support when used in a
model. Requires a TLC file for referenced model. See “Model Reference
Accelerator mode. Requirements and Limitations”
Simulink. Supports Simulink.NumericType and Supports all of these classes (see “Configure
AliasType and Simulink.AliasType classes (see Custom Data Types” on page 9-18).
Simulink. “Configure Custom Data Types” on page
NumericType 9-18).
support
Bus input and output Does not support bus input or output Supports nonvirtual bus input or output
signals signals. signals.
Tunable and run-time Supports tunable and run-time Supports tunable and run-time parameters.
parameters parameters.
Work vectors Supports DWork vectors (see “Using Supports all work vector types (see “Types
DWork Vectors in Level-2 MATLAB S- of DWork Vectors” on page 8-4).
Functions” on page 10-15).

2-3
2 Selecting an S-Function Implementation

Features of Automatically Generated S-Functions

Feature S-Function Builder Legacy Code Tool


Data types Supports any data type supported by Supports all built-in data types. To use
Simulink software, including fixed- a fixed-point data type, you must
point types. specify the data type as a
Simulink.NumericType. You cannot
use a fixed-point type with unspecified
scaling.
Numeric types Supports real and complex signals. Supports complex signals only for
built-in data types.
Frame support Supports framed and unframed Does not support frame-based signals.
signals.
Port dimensions Supports scalar, 1-D, and Supports scalar, 1-D, and
multidimensional input and output multidimensional input and output
signals. signals.
S-function API Supports creation of custom Supports mdlInitializeSizes,
mdlInitializeSizes, mdlInitializeSampleTimes,
mdlInitializeSampleTimes, mdlStart,
mdlDerivatives, mdlUpdate, and mdlInitializeConditions,
mdlOutputs. Also allows for mdlOutputs, and mdlTerminate.
automatic generation of mdlStart and
mdlTerminate.
Code generation support Natively supports code generation. Natively supports code generation
Also, automatically generates a TLC optimized for embedded systems. Also,
file for inlining the S-function during automatically generates a TLC file that
code generation. supports expression folding for inlining
the S-function during code generation.
Simulink Accelerator™ mode Uses a TLC file in Accelerator mode, if Provides the option to use a TLC or
the file was generated. Otherwise, uses MEX file in Accelerator mode.
the MEX file.
Model reference Uses default behaviors when used in a Uses default behaviors when used in a
referenced model. referenced model.
Simulink.AliasType and Does not support these classes. Supports Simulink.AliasType and
Simulink.NumericType Simulink.NumericType.
Bus input and output signals Supports bus input and output signals. Supports bus input and output signals.
See sfbuilder_bususage for an You must define a Simulink.Bus
example. object in the MATLAB workspace that
is equivalent to the structure of the
input or output used in the legacy
code. Does not support bus
parameters.
Tunable and run-time Supports tunable parameters only Supports tunable and run-time
parameters during simulation. Supports run-time parameters.
parameters.

2-4
S-Function Features and Limitations

Feature S-Function Builder Legacy Code Tool


Work vectors Does not provide access to work Supports DWork vectors with the
vectors. usage type
SS_DWORK_USED_AS_DWORK. See
“Types of DWork Vectors” on page 8-
4 for a discussion on the different
DWork vector usage types.

S-Function Limitations
The following table summarizes the major limitations of the different types of S-functions.

Implementation Limitations
Level-2 MATLAB S- • Does not support bus input and output signals.
functions • Cannot incorporate legacy code during simulation, only during code
generation through a TLC file.
Handwritten C MEX S- Supports model reference with some limitations. See “S-Functions in Referenced
function Models”.
S-Function Builder • Generates S-function code using a wrapper function which incurs additional
overhead.
• Does not support the following S-function features:

• Work vectors
• Port-based sample times
• Multiple sample times or a nonzero offset time
• Dynamically-sized input and output signals for an S-function with multiple
input and output ports

Note S-functions with one input and one output port can have dynamically-sized
signals

2-5
2 Selecting an S-Function Implementation

Implementation Limitations
Legacy Code Tool • Generates C MEX S-functions for existing functions written in C or C++ only.
The tool does not support transformation of MATLAB or Fortran functions.
• Can interface with C++ functions, but not C++ objects.
• Does not support simulating continuous or discrete states.
• Does not support use of function pointers as the output of the legacy function
being called.
• Always sets the S-function's flag for direct feedthrough on page 1-7
(sizes.DirFeedthrough) to true.
• Supports only the continuous, but fixed in minor time step, sample time and
offset on page 1-8 option.
• Supports complex numbers, but only with Simulink built-in data types.
• Does not support the following S-function features:

• Work vectors, other than general DWork vectors


• Frame-based input and output signals
• Port-based sample times
• Multiple block-based sample times

2-6
S-Functions Incorporate Legacy C Code

S-Functions Incorporate Legacy C Code


In this section...
“Overview” on page 2-7
“Using a Hand-Written S-Function to Incorporate Legacy Code” on page 2-8
“Using the S-Function Builder to Incorporate Legacy Code” on page 2-9
“Using the Legacy Code Tool to Incorporate Legacy Code” on page 2-12

Overview
C MEX S-functions allow you to call existing C code within your Simulink models. For example,
consider the simple C function doubleIt.c that outputs a value two times the value of the function
input.

double doubleIt(double u)
{
return(u * 2.0);
}

You can create an S-function that calls doubleIt.c by either:

• Writing a wrapper S-function on page 2-8. Using this method, you hand write a new C S-
function and associated TLC file. This method requires the most knowledge about the structure of
a C S-function.
• Using an S-Function Builder block on page 2-9. Using this method, you enter the characteristics
of the S-function into a block dialog. This method does not require any knowledge about writing S-
functions. However, a basic understanding of the structure of an S-function can make the S-
Function Builder dialog box easier to use.
• Using the Legacy Code Tool on page 2-12. Using this command line method, you define the
characteristics of your S-function in a data structure in the MATLAB workspace. This method
requires the least amount of knowledge about S-functions.

You can also call external C code from a Simulink model using a MATLAB Function block. For more
information see “Integrate C Code Using the MATLAB Function Block”.

The following sections describe how to create S-functions for use in a Simulink simulation and with
Simulink Coder code generation, using the previous three methods. The example model contains
blocks that use these S-functions. If you plan to create the model, copy the files doubleIt.c and
doubleIt.h from the folder docroot/toolbox/simulink/examples into your working folder.

2-7
2 Selecting an S-Function Implementation

Using a Hand-Written S-Function to Incorporate Legacy Code


The S-function wrapsfcn.c calls the legacy function doubleIt.c in its mdlOutputs method. Save
the wrapsfcn.c file into your working folder, if you are planning to create the example model.

To incorporate the legacy code into the S-function, wrapsfcn.c begins by declaring doubleIt.c
with the following line:
extern real_T doubleIt(real_T u);

Once declared, the S-function can use doubleIt.c in its mdlOutputs method. For example:
/* Function: mdlOutputs =======================================
* Abstract:
* Calls the doubleIt.c function to multiple the input by 2.
*/
static void mdlOutputs(SimStruct *S, int tid){
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);

*y = doubleIt(*uPtrs[0]);
}

To compile the wrapsfcn.c S-function, run the following mex command. Make sure that the
doubleIt.c file is in your working folder.
mex wrapsfcn.c doubleIt.c

To generate code for the S-function using the Simulink Coder code generator, you need to write a
Target Language Compiler (TLC) file. The following TLC file wrapsfcn.tlc uses the

2-8
S-Functions Incorporate Legacy C Code

BlockTypeSetup function to declare a function prototype for doubleIt.c. The TLC file's Outputs
function then tells the Simulink Coder code generator how to inline the call to doubleIt.c. For
example:
%implements "wrapsfcn" "C"
%% File : wrapsfcn.tlc
%% Abstract:
%% Example tlc file for S-function wrapsfcn.c
%%

%% Function: BlockTypeSetup ================================


%% Abstract:
%% Create function prototype in model.h as:
%% "extern double doubleIt(double u);"
%%

%function BlockTypeSetup(block, system) void


%openfile buffer

%% PROVIDE ONE LINE OF CODE AS A FUNCTION PROTOTYPE


extern double doubleIt(double u);

%closefile buffer
%<LibCacheFunctionPrototype(buffer)>
%%endfunction %% BlockTypeSetup

%% Function: Outputs =======================================


%% Abstract:
%% CALL LEGACY FUNCTION: y = doubleIt( u );
%%

%function Outputs(block, system) Output

/* %<Type> Block: %<Name> */

%assign u = LibBlockInputSignal(0, "", "", 0)


%assign y = LibBlockOutputSignal(0, "", "", 0)

%% PROVIDE THE CALLING STATEMENT FOR "doubleIt"


%<y> = doubleIt( %<u> );

%endfunction %% Outputs

For more information on the TLC, see “Target Language Compiler Basics” (Simulink Coder).

Using the S-Function Builder to Incorporate Legacy Code


The S-Function Builder automates the creation of S-functions and TLC files that incorporate legacy
code. For this example, in addition to doubleIt.c, you need the header file doubleIt.h that
declares the doubleIt.c function format, as follows:

extern real_T doubleIt(real_T in1);

Use the S-Function Builder block to configure the block dialog to call the legacy function
doubleIt.c. In the S-Function Builder block dialog:

• The S-function name field in the Parameters pane defines the name builder_wrapsfcn for
the generated S-function.
• The Data Properties pane names the input and output ports as in1 and out1, respectively.
• The Libraries pane provides the interface to the legacy code.

• The Library/Object/Source files field contains the source file name doubleIt.c.

2-9
2 Selecting an S-Function Implementation

• The Includes field contains the following line to include the header file that declares the
legacy function:

#include <doubleIt.h>
• The Outputs pane calls the legacy function with the lines:

/* Call function that multiplies the input by 2 */

*out1 = doubleIt(*in1);
• The Build Info pane selects the Generate wrapper TLC option.

When you click Build, the S-Function Builder generates three files.

File Name Description


builder_wrapsfcn.c The main S-function.
builder_wrapsfcn_wrapper.c A wrapper file containing separate functions for the
code entered in the Outputs, Continuous
Derivatives, and Discrete Updates panes of the S-
Function Builder.
builder_wrapsfcn.tlc The S-function's TLC file.

The builder_wrapsfcn.c file follows a standard format:

• The file begins with a set of #define statements that incorporate the information from the S-
Function Builder. For example, the following lines define the first input port:
#define NUM_INPUTS 1
/* Input Port 0 */
#define IN_PORT_0_NAME in1
#define INPUT_0_WIDTH 1
#define INPUT_DIMS_0_COL 1
#define INPUT_0_DTYPE real_T
#define INPUT_0_COMPLEX COMPLEX_NO
#define IN_0_FRAME_BASED FRAME_NO
#define IN_0_DIMS 1-D
#define INPUT_0_FEEDTHROUGH 1

• Next, the file declares all the wrapper functions found in the builder_wrapsfcn_wrapper.c
file. This example requires only a wrapper function for the Outputs code.
extern void builder_wrapsfcn_Outputs_wrapper(const real_T *in1,
real_T *out1);

• Following these definitions and declarations, the file contains the S-function methods, such as
mdlInitializeSizes, that initialize the S-function's input ports, output ports, and parameters.
See “Process View” on page 4-50 for a list of methods that are called during the S-function
initialization phase.
• The mdlOutputs method of the file calls the builder_wrapsfcn_wrapper.c function. The
method uses the input and output names in1 and out1, as defined in the Data Properties pane,
when calling the wrapper function. For example:
/* Function: mdlOutputs =============================================
*
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
const real_T *in1 = (const real_T*) ssGetInputPortSignal(S,0);
real_T *out1 = (real_T *)ssGetOutputPortRealSignal(S,0);

2-10
S-Functions Incorporate Legacy C Code

builder_wrapsfcn_Outputs_wrapper(in1, out1);
}

• The file builder_wrapsfcn.c concludes with the required mdlTerminate method.

The wrapper function builder_wrapsfcn_wrapper.c has three parts:

• The Include Files section includes the doubleIt.h file, along with the standard S-function
header files:
/*
* Include Files
*
*/
#if defined(MATLAB_MEX_FILE)
#include "tmwtypes.h"
#include "simstruc_types.h"
#else
#include "rtwtypes.h"
#endif
/* %%%-SFUNWIZ_wrapper_includes_Changes_BEGIN --- EDIT HERE TO _END */
#include <math.h>
#include <doubleIt.h>
/* %%%-SFUNWIZ_wrapper_includes_Changes_END --- EDIT HERE TO _BEGIN */

• The External References section contains information from the External reference
declarations field on the Libraries pane. This example does not use this section.
• The Output functions section declares the function builder_wrapfcn_Outputs_wrapper,
which contains the code entered in the S-Function Builder block dialog's Outputs pane:
/*
* Output functions
*
*/
void builder_wrapfcn_Outputs_wrapper(const real_T *in1,
real_T *out1)
{
/* %%%-SFUNWIZ_wrapper_Outputs_Changes_BEGIN --- EDIT HERE TO _END */
/* Call function that multiplies the input by 2 */

*out1 = doubleIt(*in1);
/* %%%-SFUNWIZ_wrapper_Outputs_Changes_END --- EDIT HERE TO _BEGIN */
}

Note Compared to a handwritten S-function, the S-Function Builder places the call to the legacy C
function down an additional level through the wrapper file builder_wrapsfcn_wrapper.c.

The TLC file builder_wrapsfcn.tlc generated by the S-Function Builder is similar to the previous
handwritten version. The file declares the legacy function in BlockTypeSetup and calls it in the
Outputs method.
%implements builder_wrapsfcn "C"
%% Function: BlockTypeSetup ====================================
%%
%% Purpose:
%% Set up external references for wrapper functions in the
%% generated code.
%%
%function BlockTypeSetup(block, system) Output
%openfile externs

extern void builder_wrapsfcn_Outputs_wrapper(const real_T *in1,


real_T *out1);
%closefile externs
%<LibCacheExtern(externs)>
%%

2-11
2 Selecting an S-Function Implementation

%endfunction

%% Function: Outputs ===========================================


%%
%% Purpose:
%% Code generation rules for mdlOutputs function.
%%
%function Outputs(block, system) Output
/* S-Function "builder_wrapsfcn_wrapper" Block: %<Name> */

%assign pu0 = LibBlockInputSignalAddr(0, "", "", 0)


%assign py0 = LibBlockOutputSignalAddr(0, "", "", 0)
%assign py_width = LibBlockOutputSignalWidth(0)
%assign pu_width = LibBlockInputSignalWidth(0)
builder_wrapsfcn_Outputs_wrapper(%<pu0>, %<py0> );

%%
%endfunction

Using the Legacy Code Tool to Incorporate Legacy Code


The section “Integrate C Functions into Simulink Models with Legacy Code Tool” on page 4-33 in
“Writing S-Functions in C” shows how to use the Legacy Code Tool to create an S-function that
incorporates doubleIt.c. For a script that performs the steps in that example, copy the file
lct_wrapsfcn.m to your working folder. Make sure that the doubleIt.c and doubleIt.h files are
in your working folder then run the script by typing lct_wrapsfcn at the MATLAB command
prompt. The script creates and compiles the S-function legacy_wrapsfcn.c and creates the TLC
file legacy_wrapsfcn.tlc via the following commands.
% Create the data structure
def = legacy_code('initialize');

% Populate the data struture


def.SourceFiles = {'doubleIt.c'};
def.HeaderFiles = {'doubleIt.h'};
def.SFunctionName = 'legacy_wrapsfcn';
def.OutputFcnSpec = 'double y1 = doubleIt(double u1)';
def.SampleTime = [-1,0];

% Generate the S-function


legacy_code('sfcn_cmex_generate', def);

% Compile the MEX-file


legacy_code('compile', def);

% Generate a TLC-file
legacy_code('sfcn_tlc_generate', def);

The S-function legacy_wrapsfcn.c generated by the Legacy Code Tool begins by including the
doubleIt.h header file. The mdlOutputs method then directly calls the doubleIt.c function, as
follows:
static void mdlOutputs(SimStruct *S, int_T tid)
{
/*
* Get access to Parameter/Input/Output/DWork/size information
*/
real_T *u1 = (real_T *) ssGetInputPortSignal(S, 0);
real_T *y1 = (real_T *) ssGetOutputPortSignal(S, 0);

/*
* Call the legacy code function
*/
*y1 = doubleIt( *u1);
}

2-12
S-Functions Incorporate Legacy C Code

The S-function generated by the Legacy Code Tool differs from the S-function generated by the S-
Function Builder as follows:

• The S-function generated by the S-Function Builder calls the legacy function doubleIt.c
through the wrapper function builder_wrapsfcn_wrapper.c. The S-function generated by the
Legacy Code Tool directly calls doubleIt.c from its mdlOutputs method.
• The S-Function Builder uses the input and output names entered into the Data Properties pane,
allowing you to customize these names in the S-function. The Legacy Code Tool uses the default
names y and u for the outputs and inputs, respectively. You cannot specify customized names to
use in the generated S-function when using the Legacy Code Tool.
• The S-Function Builder and Legacy Code Tool both specify an inherited sample time, by default.
However, the S-Function Builder uses an offset time of 0.0 while the Legacy Code Tool specifies
that the offset time is fixed in minor time steps.

The TLC file legacy_wrapsfcn.tlc supports expression folding by defining BlockInstanceSetup


and BlockOutputSignal functions. The TLC file also contains a BlockTypeSetup function to
declare a function prototype for doubleIt.c and an Outputs function to tell the Simulink Coder
code generator how to inline the call to doubleIt.c.:
%% Function: BlockTypeSetup ===============================================
%%
%function BlockTypeSetup(block, system) void
%%
%% The Target Language must be C
%if ::GenCPP==1
%<LibReportFatalError("This S-Function generated by the Legacy Code Tool
must be only used with the C Target Language")>
%endif
%<LibAddToCommonIncludes("doubleIt.h")>
%<LibAddToModelSources("doubleIt")>
%%
%endfunction

%% Function: BlockInstanceSetup ===========================================


%%
%function BlockInstanceSetup(block, system) void
%%
%<LibBlockSetIsExpressionCompliant(block)>
%%
%endfunction

%% Function: Outputs ======================================================


%%
%function Outputs(block, system) Output
%%
%if !LibBlockOutputSignalIsExpr(0)
%assign u1_val = LibBlockInputSignal(0, "", "", 0)
%assign y1_val = LibBlockOutputSignal(0, "", "", 0)
%%
%<y1_val = doubleIt( %<u1_val>);
%endif
%%
%endfunction

%% Function: BlockOutputSignal ============================================


%%
%function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void
%%
%assign u1_val = LibBlockInputSignal(0, "", "", 0)
%assign y1_val = LibBlockOutputSignal(0, "", "", 0)
%%
%switch retType
%case "Signal"
%if portIdx == 0
%return "doubleIt( %<u1_val>)"

2-13
2 Selecting an S-Function Implementation

%else
%assign errTxt = "Block output port index not supported: %<portIdx>"
%endif
%default
%assign errTxt = "Unsupported return type: %<retType>"
%<LibBlockReportError(block,errTxt)>
%endswitch
%%
%endfunction

2-14
3

Writing S-Functions in MATLAB

• “Write Level-2 MATLAB S-Functions” on page 3-2


• “Maintain Level-1 MATLAB S-Functions” on page 3-9
3 Writing S-Functions in MATLAB

Write Level-2 MATLAB S-Functions

In this section...
“About Level-2 MATLAB S-Functions” on page 3-2
“About Run-Time Objects” on page 3-2
“Level-2 MATLAB S-Function Template” on page 3-3
“Level-2 MATLAB S-Function Callback Methods” on page 3-3
“Using the setup Method” on page 3-4
“Example of Writing a Level-2 MATLAB S-Function” on page 3-5
“Instantiating a Level-2 MATLAB S-Function” on page 3-7
“Operations for Variable-Size Signals” on page 3-7
“Generating Code from a Level-2 MATLAB S-Function” on page 3-8
“MATLAB S-Function Examples” on page 3-8
“MATLAB S-Function Limitations” on page 3-8

About Level-2 MATLAB S-Functions


The Level-2 MATLAB S-function API allows you to use the MATLAB language to create custom blocks
with multiple input and output ports and capable of handling any type of signal produced by a
Simulink model, including matrix and frame signals of any data type. The Level-2 MATLAB S-function
API corresponds closely to the API for creating C MEX S-functions. Much of the documentation for
creating C MEX S-functions applies also to Level-2 MATLAB S-functions. To avoid duplication, this
section focuses on providing information that is specific to writing Level-2 MATLAB S-functions.

A Level-2 MATLAB S-function is MATLAB function that defines the properties and behavior of an
instance of a Level-2 MATLAB S-Function block that references the MATLAB function in a Simulink
model. The MATLAB function itself comprises a set of callback methods (see “Level-2 MATLAB S-
Function Callback Methods” on page 3-3) that the Simulink engine invokes when updating or
simulating the model. The callback methods perform the actual work of initializing and computing the
outputs of the block defined by the S-function.

To facilitate these tasks, the engine passes a run-time object to the callback methods as an argument.
The run-time object effectively serves as a MATLAB proxy for the S-Function block, allowing the
callback methods to set and access the block properties during simulation or model updating.

About Run-Time Objects


When the Simulink engine invokes a Level-2 MATLAB S-function callback method, it passes an
instance of the Simulink.MSFcnRunTimeBlock class to the method as an argument. This instance,
known as the run-time object for the S-Function block, serves the same purpose for Level-2 MATLAB
S-function callback methods as the SimStruct structure serves for C MEX S-function callback
methods. The object enables the method to provide and obtain information about various elements of
the block ports, parameters, states, and work vectors. The method does this by getting or setting
properties or invoking methods of the block run-time object. See the documentation for the
Simulink.MSFcnRunTimeBlock class for information on getting and setting run-time object
properties and invoking run-time object methods.

3-2
Write Level-2 MATLAB S-Functions

Run-time objects do not support MATLAB sparse matrices. For example, if the variable block is a
run-time object, the following line in a Level-2 MATLAB S-function produces an error:

block.Outport(1).Data = speye(10);

where the speye command forms a sparse identity matrix.

Note Other MATLAB programs besides MATLAB S-functions can use run-time objects to obtain
information about a MATLAB S-function in a model that is simulating. See “Access Block Data During
Simulation” in Using Simulink for more information.

Level-2 MATLAB S-Function Template


Use the basic Level-2 MATLAB S-function template msfuntmpl_basic.m to get a head start on
creating a new Level-2 MATLAB S-function. The template contains skeleton implementations of the
required callback methods defined by the Level-2 MATLAB S-function API. To write a more
complicated S-function, use the annotated template msfuntmpl.m.

To create a MATLAB S-function, make a copy of the template and edit the copy as necessary to reflect
the desired behavior of the S-function you are creating. The following two sections describe the
contents of the MATLAB code template. The section “Example of Writing a Level-2 MATLAB S-
Function” on page 3-5 describes how to write a Level-2 MATLAB S-function that models a unit
delay.

Level-2 MATLAB S-Function Callback Methods


The Level-2 MATLAB S-function API defines the signatures and general purposes of the callback
methods that constitute a Level-2 MATLAB S-function. The S-function itself provides the
implementations of these callback methods. The implementations in turn determine the block
attributes (e.g., ports, parameters, and states) and behavior (e.g., the block outputs as a function of
time and the block inputs, states, and parameters). By creating an S-function with an appropriate set
of callback methods, you can define a block type that meets the specific requirements of your
application.

A Level-2 MATLAB S-function must include the following callback methods:

• A setup function to initialize the basic S-function characteristics


• An Outputs function to calculate the S-function outputs

Your S-function can contain other methods, depending on the requirements of the block that the S-
function defines. The methods defined by the Level-2 MATLAB S-function API generally correspond to
similarly named methods defined by the C MEX S-function API. For information on when these
methods are called during simulation, see “Process View” on page 4-50 in “Simulink Engine
Interaction with C S-Functions” on page 4-50.

The following table lists all the Level-2 MATLAB S-function callback methods and their C MEX
counterparts.

3-3
3 Writing S-Functions in MATLAB

Level-2 MATLAB Method Equivalent C MEX Method


setup (see “Using the setup Method” on page mdlInitializeSizes
3-4)
CheckParameters mdlCheckParameters
Derivatives mdlDerivatives
Disable mdlDisable
Enable mdlEnable
InitializeConditions mdlInitializeConditions
Outputs mdlOutputs
PostPropagationSetup mdlSetWorkWidths
ProcessParameters mdlProcessParameters
Projection mdlProjection
SetInputPortComplexSignal mdlSetInputPortComplexSignal
SetInputPortDataType mdlSetInputPortDataType
SetInputPortDimensions mdlSetInputPortDimensionInfo
SetInputPortDimensionsModeFcn mdlSetInputPortDimensionsModeFcn
SetInputPortSampleTime mdlSetInputPortSampleTime
SetOutputPortComplexSignal mdlSetOutputPortComplexSignal
SetOutputPortDataType mdlSetOutputPortDataType
SetOutputPortDimensions mdlSetOutputPortDimensionInfo
SetOutputPortSampleTime mdlSetOutputPortSampleTime
SimStatusChange mdlSimStatusChange
Start mdlStart
Terminate mdlTerminate
Update mdlUpdate
WriteRTW mdlRTW

Using the setup Method


The body of the setup method in a Level-2 MATLAB S-function initializes the instance of the
corresponding Level-2 MATLAB S-Function block. In this respect, the setup method is similar to the
mdlInitializeSizes and mdlInitializeSampleTimes callback methods implemented by C
MEX S-functions. The setup method performs the following tasks:

• Initializing the number of input and output ports of the block.


• Setting attributes such as dimensions, data types, complexity, and sample times for these ports.
• Specifying the block sample time. See “Specify Sample Time” in Using Simulink for more
information on how to specify valid sample times.
• Setting the number of S-function dialog parameters.
• Registering S-function callback methods by passing the handles of local functions in the MATLAB
S-function to the RegBlockMethod method of the S-Function block's run-time object. See the

3-4
Write Level-2 MATLAB S-Functions

documentation for Simulink.MSFcnRunTimeBlock for information on using the


RegBlockMethod method.

Example of Writing a Level-2 MATLAB S-Function


The following steps illustrate how to write a simple Level-2 MATLAB S-function. When applicable, the
steps include examples from the S-function example msfcn_unit_delay.m used in the model
msfcndemo_sfundsc2. All lines of code use the variable name block for the S-function run-time
object.
1 Copy the Level-2 MATLAB S-function template msfuntmpl_basic.m to your working folder. If
you change the file name when you copy the file, change the function name in the function line
to the same name.
2 Modify the setup method to initialize the S-function's attributes. For this example:

• Set the run-time object's NumInputPorts and NumOutputPorts properties to 1 in order to


initialize one input port and one output port.
• Invoke the run-time object's “SetPreCompInpPortInfoToDynamic” and
“SetPreCompOutPortInfoToDynamic” methods to indicate that the input and output ports
inherit their compiled properties (dimensions, data type, complexity, and sampling mode)
from the model.
• Set the DirectFeedthrough property of the run-time object's InputPort to false in order
to indicate the input port does not have direct feedthrough. Retain the default values for all
other input and output port properties that are set in your copy of the template file. The
values set for the Dimensions, DatatypeID, and Complexity properties override the
values inherited using the SetPreCompInpPortInfoToDynamic and
SetPreCompOutPortInfoToDynamic methods.
• Set the run-time object's NumDialogPrms property to 1 in order to initialize one S-function
dialog parameter.
• Specify that the S-function has an inherited sample time by setting the value of the runtime
object's SampleTimes property to [-1 0].
• Call the run-time object's RegBlockMethod method to register the following four callback
methods used in this S-function.

• PostPropagationSetup
• InitializeConditions
• Outputs
• Update

Remove any other registered callback methods from your copy of the template file. In the
calls to RegBlockMethod, the first input argument is the name of the S-function API method
and the second input argument is the function handle to the associated local function in the
MATLAB S-function.

The following setup method from msfcn_unit_delay.m performs the previous list of steps:
function setup(block)

%% Register a single dialog parameter


block.NumDialogPrms = 1;

3-5
3 Writing S-Functions in MATLAB

%% Register number of input and output ports


block.NumInputPorts = 1;
block.NumOutputPorts = 1;

%% Setup functional port properties to dynamically


%% inherited.
block.SetPreCompInpPortInfoToDynamic;
block.SetPreCompOutPortInfoToDynamic;

%% Hard-code certain port properties


block.InputPort(1).Dimensions = 1;
block.InputPort(1).DirectFeedthrough = false;

block.OutputPort(1).Dimensions = 1;

%% Set block sample time to [0.1 0]


block.SampleTimes = [0.1 0];

%% Register methods
block.RegBlockMethod('PostPropagationSetup',@DoPostPropSetup);
block.RegBlockMethod('InitializeConditions',@InitConditions);
block.RegBlockMethod('Outputs', @Output);
block.RegBlockMethod('Update', @Update);

If your S-function needs continuous states, initialize the number of continuous states in the
setup method using the run-time object's NumContStates property. Do not initialize discrete
states in the setup method.
3 Initialize the discrete states in the PostPropagationSetup method. A Level-2 MATLAB S-
function stores discrete state information in a DWork vector. The default
PostPropagationSetup method in the template file suffices for this example.

The following PostPropagationSetup method from msfcn_unit_delay.m, named


DoPostPropSetup, initializes one DWork vector with the name x0.

function DoPostPropSetup(block)

%% Setup Dwork
block.NumDworks = 1;
block.Dwork(1).Name = 'x0';
block.Dwork(1).Dimensions = 1;
block.Dwork(1).DatatypeID = 0;
block.Dwork(1).Complexity = 'Real';
block.Dwork(1).UsedAsDiscState = true;

If your S-function uses additional DWork vectors, initialize them in the PostPropagationSetup
method, as well (see “Using DWork Vectors in Level-2 MATLAB S-Functions” on page 10-15).
4 Initialize the values of discrete and continuous states or other DWork vectors in the
InitializeConditions or Start callback methods. Use the Start callback method for values
that are initialized once at the beginning of the simulation. Use the InitializeConditions
method for values that need to be reinitialized whenever an enabled subsystem containing the S-
function is reenabled.

For this example, use the InitializeConditions method to set the discrete state's initial
condition to the value of the S-function's dialog parameter. For example, the
InitializeConditions method in msfcn_unit_delay.m is:

3-6
Write Level-2 MATLAB S-Functions

function InitConditions(block)

%% Initialize Dwork
block.Dwork(1).Data = block.DialogPrm(1).Data;

For S-functions with continuous states, use the ContStates run-time object method to initialize
the continuous state data. For example:

block.ContStates.Data(1) = 1.0;
5 Calculate the S-function's outputs in the Outputs callback method. For this example, set the
output to the current value of the discrete state stored in the DWork vector.

The Outputs method in msfcn_unit_delay.m is:

function Output(block)

block.OutputPort(1).Data = block.Dwork(1).Data;
6 For an S-function with continuous states, calculate the state derivatives in the Derivatives
callback method. Run-time objects store derivative data in their Derivatives property. For
example, the following line sets the first state derivative equal to the value of the first input
signal.

block.Derivatives.Data(1) = block.InputPort(1).Data;

This example does not use continuous states and, therefore, does not implement the
Derivatives callback method.
7 Update any discrete states in the Update callback method. For this example, set the value of the
discrete state to the current value of the first input signal.

The Update method in msfcn_unit_delay.m is:

function Update(block)

block.Dwork(1).Data = block.InputPort(1).Data;
8 Perform any cleanup, such as clearing variables or memory, in the Terminate method. Unlike C
MEX S-functions, Level-2 MATLAB S-function are not required to have a Terminate method.

For information on additional callback methods, see “Level-2 MATLAB S-Function Callback Methods”
on page 3-3. For a list of run-time object properties, see the reference page for
Simulink.MSFcnRunTimeBlock and the parent class Simulink.RunTimeBlock.

Instantiating a Level-2 MATLAB S-Function


To use a Level-2 MATLAB S-function in a model, copy an instance of the Level-2 MATLAB S-
Functionblock into the model. Open the Block Parameters dialog box for the block and enter the
name of the MATLAB file that implements your S-function into the S-function name field. If your S-
function uses any additional parameters, enter the parameter values as a comma-separated list in the
Block Parameters dialog box Parameters field.

Operations for Variable-Size Signals


Following are modifications to the Level-2 MATLAB S-functions template (msfuntmpl_basic.m) and
additional operations that allow you to use variable-size signals.

3-7
3 Writing S-Functions in MATLAB

function setup(block)
% Register the properties of the output port
block.OutputPort(1).DimensionsMode = 'Variable';
block.RegBlockMethod('SetInputPortDimensionsMode', @SetInputDimsMode);

function DoPostPropSetup(block)
%Register dependency rules to update current output size of output port a depending on
%input ports b and c
block.AddOutputDimsDependencyRules(a, [b c], @setOutputVarDims);

%Configure output port b to have the same dimensions as input port a


block.InputPortSameDimsAsOutputPort(a,b);

%Configure DWork a to have its size reset when input size changes.
block.DWorkRequireResetForSignalSize(a,true);

function SetInputDimsMode(block, port, dm)


% Set dimension mode
block.InputPort(port).DimensionsMode = dm;
block.OutputPort(port).DimensionsMode = dm;

function setOutputVarDims(block, opIdx, inputIdx)


% Set current (run-time) dimensions of the output
outDimsAfterReset = block.InputPort(inputIdx(1)).CurrentDimensions;
block.OutputPort(opIdx).CurrentDimensions = outDimsAfterReset;

Generating Code from a Level-2 MATLAB S-Function


Generating code for a model containing a Level-2 MATLAB S-function requires that you provide a
corresponding Target Language Compiler (TLC) file. You do not need a TLC file to accelerate a model
containing a Level-2 MATLAB S-function. The Simulink Accelerator software runs Level-2 MATLAB S-
functions in interpreted mode. However, M-file S-functions do not work with accelerated mode if the
M-file S-function is in a model reference. For more information on writing TLC files for MATLAB S-
functions, see “Inlining S-Functions” (Simulink Coder) and “Inline MATLAB File S-Functions”
(Simulink Coder).

MATLAB S-Function Examples


The Level-2 MATLAB S-function examples provide a set of self-documenting models that illustrate the
use of Level-2 MATLAB S-functions. Enter sfundemos at the MATLAB command prompt to view the
examples.

MATLAB S-Function Limitations


• Level-2 MATLAB S-functions do not support zero-crossing detection.
• You cannot trigger a function-call subsystem from a Level-2 MATLAB S-function.

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “S-Function Examples” on page 1-19
• “Configure Block Features for MATLAB S-Functions”

3-8
Maintain Level-1 MATLAB S-Functions

Maintain Level-1 MATLAB S-Functions

In this section...
“About the Maintenance of Level-1 MATLAB S-Functions” on page 3-9
“Level-1 MATLAB S-Function Arguments” on page 3-9
“Level-1 MATLAB S-Function Outputs” on page 3-10
“Define S-Function Block Characteristics” on page 3-11
“Processing S-Function Parameters” on page 3-11
“Convert Level-1 MATLAB S-Functions to Level-2” on page 3-12

About the Maintenance of Level-1 MATLAB S-Functions

Note The information provided in this section is intended only for use in maintaining existing Level-1
MATLAB S-functions. Use the more capable Level-2 API to develop new MATLAB S-functions (see
“Write Level-2 MATLAB S-Functions” on page 3-2). Level-1 MATLAB S-functions support a much
smaller subset of the S-function API then Level-2 MATLAB S-functions, and their features are limited
compared to built-in blocks.

A Level-1 MATLAB S-function is a MATLAB function of the following form

[sys,x0,str,ts]=f(t,x,u,flag,p1,p2,...)

where f is the name of the S-function. During simulation of a model, the Simulink engine repeatedly
invokes f, using the flag argument to indicate the task (or tasks) to be performed for a particular
invocation. The S-function performs the task and returns the results in an output vector.

A template implementation of a Level-1 MATLAB S-function, sfuntmpl.m, resides in the folder


matlabroot/toolbox/simulink/blocks. The template consists of a top-level function and a set
of skeleton local functions, called S-function callback methods, each of which corresponds to a
particular value of flag. The top-level function invokes the local function indicated by flag. The
local functions perform the actual tasks required of the S-function during simulation.

Level-1 MATLAB S-Function Arguments


The Simulink engine passes the following arguments to a Level-1 MATLAB S-function:

t Current time
x State vector
u Input vector
flag Integer value that indicates the task to be performed by the S-function

The following table describes the values that flag can assume and lists the corresponding Level-2
MATLAB S-function method for each value.

3-9
3 Writing S-Functions in MATLAB

Flag Argument

Level-1 Flag Level-2 Callback Method Description


0 setup Defines basic S-Function block
characteristics, including sample
times, initial conditions of continuous
and discrete states, and the sizes
array (see “Define S-Function Block
Characteristics” on page 3-11 for a
description of the sizes array).
1 mdlDerivatives Calculates the derivatives of the
continuous state variables.
2 mdlUpdate Updates discrete states, sample times,
and major time step requirements.
3 mdlOutputs Calculates the outputs of the S-
function.
4 mdlOutputs method updates the run-time Calculates the time of the next hit in
object NextTimeHit property absolute time. This routine is used
only when you specify a variable
discrete-time sample time in the
setup method.
9 mdlTerminate Performs any necessary end-of-
simulation tasks.

Level-1 MATLAB S-Function Outputs


A Level-1 MATLAB S-function returns an output vector containing the following elements:

• sys, a generic return argument. The values returned depend on the flag value. For example, for
flag = 3, sys contains the S-function outputs.
• x0, the initial state values (an empty vector if there are no states in the system). x0 is ignored,
except when flag = 0.
• str, originally intended for future use. Level-1 MATLAB S-functions must set this to the empty
matrix, [].
• ts, a two-column matrix containing the sample times and offsets of the block (see “Specify Sample
Time” in Using Simulink for information on how to specify a sample times and offsets).

For example, if you want your S-function to run at every time step (continuous sample time), set
ts to [0 0]. If you want your S-function to run at the same rate as the block to which it is
connected (inherited sample time), set ts to [-1 0]. If you want it to run every 0.25 seconds
(discrete sample time) starting at 0.1 seconds after the simulation start time, set ts to [0.25
0.1].

You can create S-functions that do multiple tasks, each at a different sample rate (i.e., a multirate
S-function). In this case, ts should specify all the sample rates used by your S-function in
ascending order by sample time. For example, suppose your S-function performs one task every
0.25 second starting from the simulation start time and another task every 1 second starting 0.1
second after the simulation start time. In this case, your S-function should set ts equal to [.25
0; 1.0 .1]. This will cause the Simulink engine to execute the S-function at the following times:

3-10
Maintain Level-1 MATLAB S-Functions

[0 0.1 0.25 0.5 0.75 1 1.1 ...]. Your S-function must decide at every sample time which
task to perform at that sample time.

You can also create an S-function that performs some tasks continuously (i.e., at every time step)
and others at discrete intervals.

Define S-Function Block Characteristics


For the Simulink engine to recognize a Level-1 MATLAB S-function, you must provide it with specific
information about the S-function. This information includes the number of inputs, outputs, states, and
other block characteristics.

To provide this information, call the simsizes function at the beginning of the S-function.

sizes = simsizes;

This function returns an uninitialized sizes structure. You must load the sizes structure with
information about the S-function. The table below lists the fields of the sizes structure and
describes the information contained in each field.

Fields in the sizes Structure

Field Name Description


sizes.NumContStates Number of continuous states
sizes.NumDiscStates Number of discrete states
sizes.NumOutputs Number of outputs
sizes.NumInputs Number of inputs
sizes.DirFeedthrough Flag for direct feedthrough
sizes.NumSampleTimes Number of sample times

After you initialize the sizes structure, call simsizes again:

sys = simsizes(sizes);

This passes the information in the sizes structure to sys, a vector that holds the information for use
by the Simulink engine.

Processing S-Function Parameters


When invoking a Level-1 MATLAB S-function, the Simulink engine always passes the standard block
parameters, t, x, u, and flag, to the S-function as function arguments. The engine can pass
additional block-specific parameters specified by the user to the S-function. The user specifies the
parameters in the S-function parameters field of the S-Function Block Parameters dialog box (see
“Passing Parameters to S-Functions” on page 1-4). If the block dialog specifies additional parameters,
the engine passes the parameters to the S-function as additional function arguments. The additional
arguments follow the standard arguments in the S-function argument list in the order in which the
corresponding parameters appear in the block dialog. You can use this block-specific S-function
parameter capability to allow the same S-function to implement various processing options. See the
limintm.m example in the folder matlabroot/toolbox/simulink/simdemos/simfeatures for
an example of an S-function that uses block-specific parameters.

3-11
3 Writing S-Functions in MATLAB

Convert Level-1 MATLAB S-Functions to Level-2


You can convert Level-1 MATLAB S-functions to Level-2 MATLAB S-functions by mapping the code
associated with each Level-1 S-function flag to the appropriate Level-2 S-function callback method.
See the Flag Arguments on page 3-9 table for a mapping of Level-1 flags to Level-2 callback methods.
In addition:

• Store discrete state information for Level-2 MATLAB S-functions in DWork vectors, initialized in
the PostPropagationSetup method.
• Access Level-2 MATLAB S-function dialog parameters using the DialogPrm run-time object
property, instead of passing them into the S-function as function arguments.
• For S-functions with variable sample times, update the NextTimeHit run-time object property in
the Outputs method to set the next sample time hit for the Level-2 MATLAB S-function.

For example, the following table shows how to convert the Level-1 MATLAB S-function sfundsc2.m
to a Level-2 MATLAB S-function. The example uses the Level-2 MATLAB S-function template
msfuntmpl_basic.m as a starting point when converting the Level-1 MATLAB S-function. The line
numbers in the table corresponds to the lines of code in sfundsc2.m.

sfundsc2_level2.m

function sfundsc2_level2(block)
%SFUNDSC2_LEVEL2 Level-2 version of sfundsc2.m
%
% Copyright 2003-2007 The MathWorks, Inc.

%%
%% The setup method is used to setup the basic attributes of the
%% S-function such as ports, parameters, etc. Do not add any other
%% calls to the main body of the function.
%%
setup(block);

%endfunction

function setup(block)

% Register number of ports


block.NumInputPorts = 1;
block.NumOutputPorts = 1;

% Setup port properties to be inherited or dynamic


block.SetPreCompInpPortInfoToDynamic;
block.SetPreCompOutPortInfoToDynamic;

% Override input port properties


block.InputPort(1).DatatypeID = 0; % double
block.InputPort(1).Complexity = 'Real';
block.InputPort(1).Dimensions = 1;
block.InputPort(1).DirectFeedthrough = false;

% Override output port properties


block.OutputPort(1).DatatypeID = 0; % double
block.OutputPort(1).Complexity = 'Real';
block.OutputPort(1).Dimensions = 1;

3-12
Maintain Level-1 MATLAB S-Functions

% Register parameters
block.NumDialogPrms = 0;

% Register sample times


block.SampleTimes = [.10 0];

%% -----------------------------------------------------------------
%% Options
%% -----------------------------------------------------------------
% Specify if Accelerator should use TLC or call back into
% MATLAB file
block.SetAccelRunOnTLC(false);

%% -----------------------------------------------------------------
%% Register methods
%% -----------------------------------------------------------------
block.RegBlockMethod('PostPropagationSetup', @DoPostPropSetup);
block.RegBlockMethod('InitializeConditions', @InitializeConditions);
block.RegBlockMethod('Outputs', @Outputs);
block.RegBlockMethod('Update', @Update);
%endfunction

%% -------------------------------------------------------------------
%% The local functions
%% -------------------------------------------------------------------
function DoPostPropSetup(block)

block.NumDworks = 1;
block.Dwork(1).Name = 'x0';
block.Dwork(1).Dimensions = 1;
block.Dwork(1).DatatypeID = 0; % double
block.Dwork(1).Complexity = 'Real'; % real
block.Dwork(1).UsedAsDiscState = true;

%endfunction

function InitializeConditions(block)
%% Initialize Dwork
block.Dwork(1).Data = 0;

%endfunction

function Outputs(block)

block.OutputPort(1).Data = block.Dwork(1).Data;

%endfunction

function Update(block)

block.Dwork(1).Data = block.InputPort(1).Data;

%endfunction

3-13
3 Writing S-Functions in MATLAB

Line Code in sfundsc2.m Code in Level-2 MATLAB file (sfundsc2_level2.m)


Number
1 function [sys,x0,str,ts]= ... function sfundsc2(block)
sfundsc2(t,x,u,flag) setup(block);

The syntax for the function line changes to accept one


input argument block, which is the Level-2 MATLAB S-
Function block's run-time object. The main body of the
Level-2 MATLAB S-function contains a single line that
calls the local setup function.
13 - 19 switch flag, function setup(block)

case 0, The flag value of zero corresponds to calling the setup


[sys,x0,str,ts] = ...
mdlInitializeSizes;
method. A Level-2 MATLAB S-function does not use a
switch statement to invoke the callback methods.
Instead, the local setup function registers callback
methods that are directly called during simulation.
24 - 31 case 2, The setup function registers two local functions
sys = mdlUpdate(t,x,u);
associated with flag values of 2 and 3.
case 3,
sys = mdlOutputs(t,x,u); block.RegBlockMethod('Outputs' ,@Output);
block.RegBlockMethod('Update' ,@Update);

53 - 66 sizes = simsizes; The setup function also initializes the attributes of the
sizes.NumContStates = 0; Level-2 MATLAB S-function:
sizes.NumDiscStates = 1;
sizes.NumOutputs = 1; block.NumInputPorts = 1;
sizes.NumInputs = 1; block.NumOutputPorts = 1;
sizes.DirFeedthrough = 0; block.InputPort(1).Dimensions = 1;
sizes.NumSampleTimes = 1; block.InputPort(1).DirectFeedthrough = false;
block.OutputPort(1).Dimensions = 1;
sys = simsizes(sizes); block.NumDialogPrms = 0;
block.SampleTimes = [0.1 0];
x0 = 0;
str = []; Because this S-function has discrete states, the setup
ts = [.1 0]; method registers the PostPropagationSetup callback
method to initialize a DWork vector and the
InitializeConditions callback method to set the
initial state value.
block.RegBlockMethod('PostPropagationSetup',...
@DoPostPropSetup);
block.RegBlockMethod('InitializeConditions', ...
@InitConditions);

56 sizes.NumDiscStates = 1; The PostPropagationSetup method initializes the


DWork vector that stores the single discrete state.
function DoPostPropSetup(block)

%% Setup Dwork
block.NumDworks = 1;
block.Dwork(1).Name = 'x0';
block.Dwork(1).Dimensions = 1;
block.Dwork(1).DatatypeID = 0;
block.Dwork(1).Complexity = 'Real';
block.Dwork(1).UsedAsDiscState = true;

3-14
Maintain Level-1 MATLAB S-Functions

Line Code in sfundsc2.m Code in Level-2 MATLAB file (sfundsc2_level2.m)


Number
64 x0 = 0; The InitializeConditions method initializes the
discrete state value.
function InitConditions(block)

%% Initialize Dwork
block.Dwork(1).Data = 0

77 - 78 function sys = ... The Update method calculates the next value of the
mdlUpdate(t,x,u)
discrete state.
sys = u;
function Update(block)
block.Dwork(1).Data = block.InputPort(1).Data;

88 - 89 function sys = ... The Outputs method calculates the S-function's output.
mdlOutputs(t,x,u)
sys = x; function Outputs(block)
block.OutputPort(1).Data = block.Dwork(1).Data;

3-15
4

Writing S-Functions in C

• “Create a Basic C MEX S-Function” on page 4-2


• “Use a Bus Signal with S-Function Builder to Create an S-Function” on page 4-8
• “Build S-Functions Automatically Using S-Function Builder” on page 4-13
• “Model a State-Space System Using S-Function Builder” on page 4-25
• “Templates for C S-Functions” on page 4-28
• “Integrate C Functions Using Legacy Code Tool” on page 4-31
• “Simulink Engine Interaction with C S-Functions” on page 4-50
• “S-Functions in Normal Mode Referenced Models” on page 4-59
• “Debug C MEX S-Functions” on page 4-61
• “Convert Level-1 C MEX S-Functions” on page 4-67
4 Writing S-Functions in C

Create a Basic C MEX S-Function


In this section...
“About C MEX S-Functions” on page 4-2
“Introducing an Example of a Basic C MEX S-Function” on page 4-3
“Defines and Includes” on page 4-5
“Callback Method Implementations” on page 4-5
“Simulink/Simulink Coder Interfaces” on page 4-7
“Building the Timestwo Example” on page 4-7

About C MEX S-Functions


You can create C MEX S-functions using any of the following approaches:

• Handwritten S-function — You can write a C MEX S-function from scratch. (“Create a Basic C
MEX S-Function” on page 4-2 provides a step-by-step example.) See “Templates for C S-
Functions” on page 4-28 for a complete skeleton implementation of a C MEX S-function that you
can use as a starting point for creating your own S-functions.
• S-Function Builder — This block integrates a C/C++ code and builds a C MEX S-function from
specifications and code fragments that you supply using a graphical user interface. This eliminates
the need for you to write S-functions from scratch. See “Use a Bus Signal with S-Function Builder
to Create an S-Function” on page 4-8 for more information about the S-Function Builder.
• Legacy Code Tool — This utility builds a C MEX S-function from existing C code and specifications
that you supply using MATLAB code. See “Integrate C Functions Using Legacy Code Tool” on page
4-31 for more information about integrating legacy C code into Simulink models.

Each of these approaches involves a tradeoff between the ease of writing an S-function and the
features supported by the S-function. Although handwritten S-functions support the widest range of
features, they can be difficult to write. The S-Function Builder block simplifies the task of writing C
MEX S-functions but supports fewer features. The Legacy Code Tool provides the easiest approach to
creating C MEX S-functions from existing C code but supports the fewest features. See “Available S-
Function Implementations” on page 2-2 for more information on the features and limitations of each
of these approaches to writing a C MEX S-function.

If you have Simulink Coder, in addition to the previous three approaches, the Simulink Coder product
provides a method for generating a C MEX S-function from a graphical subsystem. If you are new to
writing C MEX S-functions, you can build portions of your application in a Simulink subsystem and
use the S-function target to convert it to an S-function. The generated files provides insight on how
particular blocks can be implemented within an S-function. For details and limitations on using the S-
function target, see “Accelerate Simulation, Reuse Code, or Protect Intellectual Property by Using S-
Function Target” (Simulink Coder). You can develop an S-function to represent external code using an
API that interacts with the Simulink engine. Use this S-function with the code generator to produce
code. For details regarding different types of S-functions in code generation, see “S-Functions and
Code Generation” (Simulink Coder).

A C MEX S-function must provide information about the function to the Simulink engine during the
simulation. As the simulation proceeds, the engine, the ODE solver, and the C MEX S-function
interact to perform specific tasks. These tasks include defining initial conditions and block
characteristics, and computing derivatives, discrete states, and outputs.

4-2
Create a Basic C MEX S-Function

As with MATLAB S-functions, the Simulink engine interacts with a C MEX S-function by invoking
callback methods that the S-function implements. Each method performs a predefined task, such as
computing block outputs, required to simulate the block whose functionality the S-function defines.
However, the S-function is free to perform the task in each method according to the functionality the
S-function implements. For example, the mdlOutputs method must compute the block outputs at the
current simulation time. However, the S-function can calculate these outputs in any way that is
appropriate for the function. This callback-based API allows you to create S-functions, and hence
custom blocks, of any desired functionality.

The set of callback methods that C MEX S-functions can implement is larger than that available for
MATLAB S-functions. C MEX S-functions are required to implement only a small subset of the
callback methods in the S-function API. If your block does not implement a particular feature, such as
matrix signals, you are free to omit the callback methods needed to implement a feature. This allows
you to create simple blocks very quickly.

The general format of a C MEX S-function is shown below:

#define S_FUNCTION_NAME your_sfunction_name_here


#define S_FUNCTION_LEVEL 2
#include "simstruc.h"

static void mdlInitializeSizes(SimStruct *S)


{
}

<additional S-function routines/code>

static void mdlTerminate(SimStruct *S)


{
}
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a
MEX-file? */
#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration
function */
#endif

mdlInitializeSizes is the first routine the Simulink engine calls when interacting with the S-
function. The engine subsequently invokes other S-function methods (all starting with mdl). At the
end of a simulation, the engine calls mdlTerminate.

Introducing an Example of a Basic C MEX S-Function


This section presents an example of a C MEX S-function that you can use as a model for creating
simple C S-functions. The example S-function timestwo.c outputs twice its input.

The following model uses the timestwo S-function to double the amplitude of a sine wave and plot it
in a scope.

4-3
4 Writing S-Functions in C

The block dialog for the S-function specifies timestwo as the S-function name; the parameters field
is empty.

The timestwo S-function contains the S-function callback methods shown in this figure. At the end of
S-function, include the code snippet as described in “Simulink/Simulink Coder Interfaces” on page 4-
7.

The contents of timestwo.c are shown below. A description of the code is provided after the
example.
#define S_FUNCTION_NAME timestwo /* Defines and Includes on page 4-5 */
#define S_FUNCTION_LEVEL 2

#include "simstruc.h"

static void mdlInitializeSizes on page 4-5(SimStruct *S)


{
ssSetNumSFcnParams(S, 0);
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch reported by the Simulink engine*/
}

if (!ssSetNumInputPorts(S, 1)) return;


ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
ssSetInputPortDirectFeedThrough(S, 0, 1);

if (!ssSetNumOutputPorts(S,1)) return;
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);

ssSetNumSampleTimes(S, 1);

/* Take care when specifying exception free code - see sfuntmpl.doc */


ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}

static void mdlInitializeSampleTimes on page 4-6(SimStruct *S)


{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
}

static void mdlOutputs on page 4-6(SimStruct *S, int_T tid)


{
int_T i;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
int_T width = ssGetOutputPortWidth(S,0);

4-4
Create a Basic C MEX S-Function

for (i=0; i<width; i++) {


*y++ = 2.0 *(*uPtrs[i]);
}
}

static void mdlTerminate on page 4-7(SimStruct *S){}

#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */


#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif

This example has three parts:

• Defines and includes


• Callback method implementations
• Simulink (or Simulink Coder) product interfaces

Defines and Includes


The example starts with the following define statements.

#define S_FUNCTION_NAME timestwo


#define S_FUNCTION_LEVEL 2

The first define statement specifies the name of the S-function (timestwo). The second define
statement specifies that the S-function is in the Level 2 format (for more information about Level 1
and Level 2 S-functions, see “Convert Level-1 C MEX S-Functions” on page 4-67).

After defining these two items, the example includes simstruc.h, which is a header file that gives
access to the SimStruct data structure and the MATLAB Application Program Interface (API)
functions.

#define S_FUNCTION_NAME timestwo


#define S_FUNCTION_LEVEL 2
#include "simstruc.h"

The simstruc.h file defines a data structure, called the SimStruct, that the Simulink engine uses
to maintain information about the S-function. The simstruc.h file also defines macros that enable
your MEX file to set values in and get values (such as the input and output signal to the block) from
the SimStruct (see “About SimStruct Functions” on page 12-2).

Callback Method Implementations


The next part of the timestwo S-function contains implementations of required callback methods.

mdlInitializeSizes

The Simulink engine calls mdlInitializeSizes to inquire about the number of input and output
ports, sizes of the ports, and any other information (such as the number of states) needed by the S-
function.

The timestwo implementation of mdlInitializeSizes specifies the following size information:

4-5
4 Writing S-Functions in C

• Zero parameters

Therefore, the S-function parameters field of the S-Function Block Parameters dialog box must
be empty. If it contains any parameters, the engine reports a parameter mismatch.
• One input port and one output port

The widths of the input and output ports are dynamically sized. This tells the engine that the S-
function can accept an input signal of any width. By default, the widths of dynamically sized input
and output port are equal when the S-function has only one input and output port.
• One sample time

The mdlInitializeSampleTimes callback method specifies the actual value of the sample time.
• Exception free code

Specifying exception-free code speeds up execution of your S-function. You must take care when
specifying this option. In general, if your S-function is not interacting with the MATLAB
environment, you can safely specify this option. For more details, see “Simulink Engine Interaction
with C S-Functions” on page 4-50.

mdlInitializeSampleTimes

The Simulink engine calls mdlInitializeSampleTimes to set the sample times of the S-function. A
timestwo block executes whenever the driving block executes. Therefore, it has a single inherited
sample time, INHERITED_SAMPLE_TIME.

mdlOutputs

The engine calls mdlOutputs at each time step to calculate the block outputs. The timestwo
implementation of mdlOutputs multiplies the input signal by 2 and writes the answer to the output.

The line:

InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

accesses the input signal. The ssGetInputPortRealSignalPtrs macro returns a vector of


pointers, which you must access using

*uPtrs[i]

For more details on accessing input signals, see “Accessing Signals Using Pointers” on page 4-56.

The line:

real_T *y = ssGetOutputPortRealSignal(S,0);

accesses the output signal. The ssGetOutputPortRealSignal macro returns a pointer to an array
containing the block outputs.

The line:

int_T width = ssGetOutputPortWidth(S,0);

obtains the width of the signal passing through the block. The S-function loops over the inputs to
compute the outputs.

4-6
Create a Basic C MEX S-Function

mdlTerminate

The engine calls mdlTerminate to provide the S-function with an opportunity to perform tasks at the
end of the simulation. This is a mandatory S-function routine. The timestwo S-function does not
perform any termination actions, and this routine is empty.

Simulink/Simulink Coder Interfaces


At the end of the S-function, include the following code to attach your S-function to either the
Simulink or Simulink Coder products.

#ifdef MATLAB_MEX_FILE
#include "simulink.c"
#else
#include "cg_sfun.h"
#endif

This trailer is required at the end of every S-function. If it is omitted, any attempt to compile your S-
function will abort with a failure during build of exports file error message.

Building the Timestwo Example


To compile this S-function, enter

mex timestwo.c

at the command line. The mex command compiles and links the timestwo.c file using the default
compiler. The mex command creates a dynamically loadable executable for the Simulink software to
use. If you have multiple MATLAB-supported compilers, you can change the default using the mex -
setup command. See “Change Default Compiler” and the list of .

The resulting executable is referred to as a MEX S-function, where MEX stands for “ MATLAB
Executable. ” The MEX file extension varies from platform to platform. For example, on a 32–bit
Microsoft® Windows system, the MEX file extension is .mexw32.

4-7
4 Writing S-Functions in C

Use a Bus Signal with S-Function Builder to Create an S-


Function
In this section...
“How the S-Function Builder Builds an S-Function” on page 4-11
“Deploy the Generated S-Function” on page 4-12

This topic will show you how to create a bus signal and connect it to an S-Function Builder. At the
end, you will use the S-Function Builder to build a simple C MEX S-function.

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. To learn more on S-Function Builder, see
“Build S-Functions Automatically Using S-Function Builder” on page 4-13. To see more examples on
how to use S-Function Builder, type sfundemos in the MATLAB Command Window, then navigate to
C-file S-functions > S-function Builder.

1 Start building your S-function by setting the current folder to the folder in which you want to
create an S-function. Then, add this folder to the MATLAB path.

mkdir newSfun
addpath(fullfile(pwd,'newSfun'))
cd('newSfun')
2 If you wish to connect a bus signal to the Input or Output port of the S-Function Builder, you
must first create a bus object. You can create a bus object interactively using the Simulink Bus
Editor (see “Create and Specify Simulink.Bus Objects”). Alternatively, you can use
Simulink.Bus:

a In the MATLAB Command Window, enter:

a = Simulink.Bus

As a result, the HeaderFile for the bus defaults to the empty character vector:

a =

Simulink.Bus
Description: ''
HeaderFile: ''
Elements: [0x1 double]
b If you want to specify the header file for the bus, then at the MATLAB Command Window,
enter the following:

a.Headerfile = 'Busdef.h'

If you do not specify a header file, Simulink automatically generates


Sfunctionname_bus.h

4-8
Use a Bus Signal with S-Function Builder to Create an S-Function

For a demonstration on how to use the S-Function Builder with a bus, enter the following
command at the MATLAB Command Window:

open_system(fullfile(matlabroot,'/toolbox/simulink/simdemos/simfeatures/sfbuilder_bususage'))
3 Create a new Simulink model. Click on the canvas and type S-Function Builder to create an
instance of the S-Function Builder block. Alternatively, drag an S-Function Builder block from the
User-Defined Functions library in the Library Browser into the new model.
4 Click on the canvas and type S-Function Builder or copy an instance of the S-Function Builder
block from the User-Defined Functions library into the new model.

4-9
4 Writing S-Functions in C

5 Double-click to open the S-Function Builder editor.


6 Use the specification and code entry panes on the S-Function Builder dialog box to enter
information and custom source code required to tailor the generated S-function to your
application.
7 Check whether the bus signal is in between the specified upper and lower saturation limits. To do
so this, create two input and two output ports, and assign the ports according to the table below:

8 Fill the Outputs_wrapper function with the logic of your code. See the example code:

4-10
Use a Bus Signal with S-Function Builder to Create an S-Function

void sfbuilder_bus_Outputs_wrapper(const SFB_COUNTERBUS *u0,


const int32_T *u1,
const SFB_COUNTERBUS *y0,
int32_T *y1)
{
/* Output_BEGIN */
int32_T limit;
boolean_T inputGElower;

/* limit is sum of SFB_SIGNALBUS.input and the second input(u1) */


limit = u0->inputsignal.input + *u1;

/* check if SFB_SIGNALBUS.limit is >= LIMITBUS.lower_saturation_limit */


inputGElower = (limit >= u0->limits.lower_saturation_limit);

if((u0->limits.upper_saturation_limit >= limit) && inputGElower)


{
*y1 = limit;
}
else
{
if(inputGElower)
{
limit = u0->limits.upper_saturation_limit;
} else
{
limit = u0->limits.lower_saturation_limit;
}
*y1 = limit;
}

y0->inputsignal.input = *y1;
y0->limits = u0->limits;
/* Output_END */
}
9 Click the arrow under Build and Select Generate TLC wrapper to create a wrapper for your S-
function for code generation.
10 Click Build on the S-Function Builder to start the build process.

The S-Function Builder builds a MEX file that implements the specified S-function and saves the
file in the current folder (see “How the S-Function Builder Builds an S-Function” on page 4-11).
11 Save the model containing the S-Function Builder block.

How the S-Function Builder Builds an S-Function


The S-Function Builder builds an S-function by generating the following source files in the current
folder:

• sfun.c — This file contains the C source code representation of the standard portions of the
generated S-function.
• sfun_wrapper.c — This file contains the custom code that you entered in the S-Function Builder
dialog box.

4-11
4 Writing S-Functions in C

• sfun.tlc — This file permits the generated S-function to run in Simulink Rapid Accelerator
mode and allows for inlining the S-function during code generation. In addition, this file generates
code for the S-function in Accelerator mode which allows the model to run faster.
• sfun_bus.h — If you specify any Input port or Output port as a bus in the Ports and
Parameters table, but do not specify a header file, then the S-Function Builder automatically
generates this header file.

After generating the S-function source code, the S-Function Builder uses the mex command to build
the MEX file representation of the S-function from the generated source code and any external
custom source code and libraries that you specified.

Deploy the Generated S-Function


To use the generated S-function in another model, first check to ensure that the folder containing the
generated S-function is on the MATLAB path. Then, copy the S-Function Builder block from the model
used to create the S-function into the target model. If necessary, set the target model's parameters to
the values required by the target model.

Alternatively, you can deploy the generated S-function without using the S-Function Builder block or
exposing the underlying C source file.

1 Open the Simulink model that will include the S-function.


2 Click the canvas to create an instance of the S-function block, or copy an S-Function block from
the User-Defined Functions library in the Library Browser into the model.
3 Double-click on the S-Function block.
4 In the Block Parameters dialog box that opens, in the S-function name edit field, enter the
name of the executable file generated by the S-Function Builder.
5 Enter any parameters for the S-function into the S-function parameters edit field. Enter the
parameters in the order in which they appear in the S-Function Builder dialog box.
6 Click OK on the S-Function Block Parameters dialog box.

By creating a block from a generated S-function, you can also create a mask for your S-Function
block. To learn more about how to add a mask for your S-function, see “Create Block Masks”. To see
an example of how block masks are added to an S-function, enter the following command on the
MATLAB Command Window.

open_system(fullfile(matlabroot,'/toolbox/simulink/simdemos/simfeatures/sfcndemo_matadd'));

You can use the generated executable file in any S-Function block in any model as long as the
executable file is on the MATLAB path.

See Also
S-Function | S-Function Builder

More About
• “Build S-Functions Automatically Using S-Function Builder” on page 4-13
• “Model a State-Space System Using S-Function Builder” on page 4-25

4-12
Build S-Functions Automatically Using S-Function Builder

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.

4-13
4 Writing S-Functions in C

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.

4-14
Build S-Functions Automatically Using S-Function Builder

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.

Option Array Layout of C/C++ Action


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

4-15
4 Writing S-Functions in C

Option Array Layout of C/C++ Action


Function
Row-major Row-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.
Any C/C++ function is not The S-Function Builder block adds the
affected by array layout 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,

4-16
Build S-Functions Automatically Using S-Function Builder

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.

4-17
4 Writing S-Functions in C

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 choose Purpose


LIB_PATH Specify object and library paths.
INC_PATH Specify the include search paths for header files
and source files.
SRC_PATH Specify search paths for object files and source
files.
ENV_PATH Specify 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:

4-18
Build S-Functions Automatically Using S-Function Builder

File Locations Entries to the Libraries Table


c:\customfolder\customfunctions.lib LIB_PATH c:\customfolder

ENTRY customfunctions.lib
C:\Program Files\MATLAB\work LIB_PATH $MATLABROOT\customobjs
\customobjs\userfunctions.obj
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 Method Wrapper Method Purpose


Start and Terminate <system_name>_Start_wrap Allocate and deallocate memory
per at the start and the end of
simulation. Allocated memory is
<system_name>_Terminate_ referenced using PWorks for
wrapper use throughout the S-Function.
Outputs <system_name>_Outputs_wr Enter the code that computes
apper the outputs of the S-function at
each simulation time step.
Update <system_name>_Update_wra Enter the code that computes
pper 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.

4-19
4 Writing S-Functions in C

S-Function Method Wrapper Method Purpose


Derivatives <system_name>_Derivative Enter the code to compute state
s_wrapper 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.

4-20
Build S-Functions Automatically Using S-Function Builder

Argument Description
u0, u1, ... uN Pointers 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, ... yN Pointer 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.
xD Pointer 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.
xC Pointer 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, param0, param1, ...paramN are pointers to arrays containing the S-
param1, p_width1, ... function parameters, where N is the number of parameters specify on
paramN, p_widthN 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_width Width 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.

4-21
4 Writing S-Functions in C

Argument Description
u_width Width 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” on page 4-50). 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” on page 4-20 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.

4-22
Build S-Functions Automatically Using S-Function Builder

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” on page 4-50). 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” on page 4-50.

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” on
page 4-20 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.

4-23
4 Writing S-Functions in C

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” on page 4-16 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.

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
S-Function | S-Function Builder

More About
• “Model a State-Space System Using S-Function Builder” on page 4-25
• “Use a Bus Signal with S-Function Builder to Create an S-Function” on page 4-8

4-24
Model a State-Space System Using S-Function Builder

Model a State-Space System Using S-Function Builder


In this example, you will use the basic methods in the S-Function Builder to model a two-input, two-
output discrete state-space system with two states. The state-space matrices are parameters to the S-
function, and the S-function input and output are vectors.

If you would like to study a manually written version of the created S-function, see dsfunc.c. Note
that to create a S-function from an example S-Function Builder model, you need to build the model
first.

Configure the S-Function Builder Settings


Specify the number of discrete states and their initial conditions, the sample mode, and the sample
time of the S-function. This example contains two discrete states, each initialized to 1, and a discrete
sample mode with a sample time of 1. Ensure that the Direct feedthrough is selected, because the
current values of the S-function inputs are used to compute its outputs.

Define Ports and Parameters


Use the Ports and Parameters table on the bottom of the editor to specify the ports and parameters
of the S-function. For this example, we have one input, one output port, and four parameters.

To set or change the values of the block parameters, you can:

• Double-click the S-Function Builder block on the model.


• Use the Block Parameters from the context menu.

4-25
4 Writing S-Functions in C

Alternatively, you can store the state-space matrices in variables in the MATLAB workspace and enter
the variable names into the Value field for each parameter. Enter the values in the image for state-
space parameters on the Value field of the Block Parameters table.

Define the Output Method


In this example, The Outputs_wrapper method calculates the S-function output as a function of the
input and state vectors and the state-space matrices. In the outputs code, reference S-function
parameters using the parameter names defined on the Ports and Parameters table. Index into 2-D
matrices using a scalar index, again keeping in mind that S-functions use zero-based indexing. For
example, to access the element C(2,1) in the S-function parameter C, use C[1]in the S-function
code.

void dsfunc_builder_Outputs_wrapper(const real_T *u,


real_T *y,
const real_T *xD,
const real_T *xC,
const real_T *A, const int_T p_width0,
const real_T *B, const int_T p_width1,
const real_T *C, const int_T p_width2,
const real_T *D, const int_T p_width3)
{
/* Output_BEGIN */
y[0]=C[0]*xD[0]+C[2]*xD[1]+D[0]*u[0]+D[2]*u[1];
y[1]=C[1]*xD[0]+C[3]*xD[1]+D[1]*u[0]+D[3]*u[1];
/* Output_END */
}

4-26
Model a State-Space System Using S-Function Builder

Define the Update Method


The Update_wrapper method updates the discrete states. As with the outputs code, use the S-
function parameter names and index into 2-D matrices using a scalar index, keeping in mind that S-
functions use zero-based indexing. For example, to access the element A(2,1) in the S-function
parameter A, use A[1]in the S-function code. The variable xD stores the final values of the discrete
states. Enter the following code in the Update_wrapper function.

void dsfunc_builder_Update_wrapper(const real_T *u,


real_T *y,
real_T *xD,
const real_T *A, const int_T p_width0,
const real_T *B, const int_T p_width1,
const real_T *C, const int_T p_width2,
const real_T *D, const int_T p_width3)
{
/* Update_BEGIN */
real_T tempX[2] = {0.0, 0.0};
tempX[0]=A[0]*xD[0]+A[2]*xD[1]+B[0]*u[0]+B[2]*u[1];
tempX[1]=A[1]*xD[0]+A[3]*xD[1]+B[1]*u[0]+B[3]*u[1];

xD[0] = tempX[0];
xD[1] = tempX[1];
/* Update_END */
}

Build the State-Space System


Click the arrow under Build and select the following options:

• Show compile steps


• Create a debuggable MEX-file
• Generate wrapper TLC

To learn more about what each option does, see “Build S-Functions Automatically Using S-Function
Builder” on page 4-13.

To build your S-function, click Build on the toolstrip to create an executable file for this S-function.
You can now run the model and compare the output to the original discrete state-space S-function
contained in sfcndemo_dsfunc.

See Also
S-Function | S-Function Builder

More About
• “Build S-Functions Automatically Using S-Function Builder” on page 4-13
• “Use a Bus Signal with S-Function Builder to Create an S-Function” on page 4-8

4-27
4 Writing S-Functions in C

Templates for C S-Functions

In this section...
“About the Templates for C S-Functions” on page 4-28
“S-Function Source File Requirements” on page 4-28
“The SimStruct” on page 4-30
“Data Types in S-Functions” on page 4-30
“Compiling C S-Functions” on page 4-30

About the Templates for C S-Functions


Use one of the provided C MEX S-function templates as a starting point for creating your own S-
function. The templates contain skeleton implementations of callback methods with comments that
explain their use. The template file, sfuntmpl_basic.c, contains commonly used S-function
routines. A template containing all available routines (as well as more comments) can be found in
sfuntmpl_doc.c in the same folder.

Note We recommend that you use the C MEX file template when developing MEX S-functions.

S-Function Source File Requirements


This section describes requirements that every S-function source file must meet to compile correctly.
The S-function templates meet these requirements.

Statements Required at the Top of S-Functions

For S-functions to operate properly, each source module of your S-function that accesses the
SimStruct must contain the following sequence of defines and include
#define S_FUNCTION_NAME your_sfunction_name_here
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"

where your_sfunction_name_here is the name of your S-function (i.e., what you enter in the S-
Function Block Parameters dialog box). These statements give you access to the SimStruct data
structure that contains pointers to the data used by the simulation. The included code also defines the
macros used to store and retrieve data in the SimStruct, described in detail in “Convert Level-1 C
MEX S-Functions” on page 4-67. In addition, the code specifies that you are using the Level-2 S-
function format.

Note All S-functions from Simulink version 1.3 through version 2.1 are considered to be Level-1 S-
functions. They are compatible with newer versions of the software, but we recommend that you
write new S-functions in the Level-2 format.

The following headers are included by simstruc.h when compiling as a MEX file.

4-28
Templates for C S-Functions

Header Files Included by simstruc.h When Compiling as a MEX File

Header File Description


matlabroot/extern/include/tmwtypes.h General data types, e.g., real_T
matlabroot/simulink/include/simstruc_types.h SimStruct data types, e.g., DTypeId
matlabroot/extern/include/mex.h MATLAB MEX file API routines to interface
MEX files with the MATLAB environment
matlabroot/extern/include/matrix.h MATLAB External Interface API routines to
query and manipulate MATLAB matrices

When compiling your S-function for use with the Simulink Coder product, simstruc.h includes the
following.

Header Files Included by simstruc.h When Used by the Simulink Coder Product

Header File Description


matlabroot/extern/include/tmwtypes.h General types, e.g., real_T
matlabroot/simulink/include/simstruc_types.h SimStruct data types, e.g., DTypeId
matlabroot/rtw/c/src/rt_matrx.h Macros for MATLAB API routines

Callback Methods That an S-Function Must Implement

Your S-function must implement the following functions (see “Configure C/C++ S-Function
Features” ):

• mdlInitializeSizes specifies the sizes of various parameters in the SimStruct, such as the
number of output ports for the block.
• mdlInitializeSampleTimes specifies the sample time(s) of the block.
• mdlOutputs calculates the output of the block.
• mdlTerminate performs any actions required at the termination of the simulation. If no actions
are required, this function can be implemented as a stub.

Statements Required at the Bottom of S-Functions

Your S-function must include the following trailer code at the end of the main module only.
#ifdef MATLAB_MEX_FILE /* Is this being compiled as MEX-file? */
#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration func */
#endif

These statements select the appropriate code for your particular application:

• simulink.c is included if the file is being compiled into a MEX- file.


• cg_sfun.h is included if the file is being used with the Simulink Coder product to produce a
standalone or real-time executable.

Note This trailer code must not be in the body of any S-function routine.

4-29
4 Writing S-Functions in C

The SimStruct
The file simstruc.h is a C language header file that defines the SimStruct data structure and its
access macros. It encapsulates all the data relating to the model or S-function, including block
parameters and outputs.

There is one SimStruct data structure allocated for the Simulink model. Each S-function in the
model has its own SimStruct associated with it. The organization of these SimStructs is much like
a folder tree. The SimStruct associated with the model is the root SimStruct. Any SimStruct
associated with an S-function is a child SimStruct.

The Simulink product provides a set of macros that S-functions can use to access the fields of the
SimStruct. See “About SimStruct Functions” on page 12-2 for more information.

Data Types in S-Functions


The file tmwtypes.h is a C language header file that defines a set of data types used in the S-
function template and in the SimStruct. These data types, such as real_T, uint32_T, etc., provide
a way to switch between different data types for 16, 32, and 64 bit systems, allowing greater platform
independence and flexibility.

S-functions are not required to use these data types. For example, you can edit the example
csfunc.c and change real_T to double and int_T to int. If you compile and simulate the S-
function, the results will be identical to the results using the previous data types.

Compiling C S-Functions
Your S-function can be compiled in one of three modes, defined either by the mex command or by the
Simulink Coder product when the S-function is built:

• MATLAB_MEX_FILE — Indicates that the S-function is being built as a MEX file for use with the
Simulink product.
• RT — Indicates that the S-function is being built with the Simulink Coder product for a real-time
application using a fixed-step solver.
• NRT — Indicates that the S-function is being built with the Simulink Coder product for a non-real-
time application using a variable-step solver.

The build process you use automatically defines the mode for your S-function.

4-30
Integrate C Functions Using Legacy Code Tool

Integrate C Functions Using Legacy Code Tool


In this section...
“Overview” on page 4-31
“Integrate C Functions into Simulink Models with Legacy Code Tool” on page 4-33
“Integrate C Function Whose Arguments Are Pointers to Structures” on page 4-35
“Registering Legacy Code Tool Data Structures” on page 4-39
“Declaring Legacy Code Tool Function Specifications” on page 4-40
“Generating and Compiling the S-Functions” on page 4-46
“Generating a Masked S-Function Block for Calling a Generated S-Function” on page 4-46
“Forcing Simulink Accelerator Mode to Use S-Function TLC Inlining Code” on page 4-47
“Calling Legacy C++ Functions” on page 4-47
“Handling Multiple Registration Files” on page 4-47
“Deploying Generated S-Functions” on page 4-48
“Legacy Code Tool Examples” on page 4-48
“Legacy Code Tool Limitations” on page 4-48

Overview
You can integrate existing C (or C++) functions, such as device drivers, lookup tables, and general
functions and interfaces, into Simulink models by using the Legacy Code Tool. Using specifications
that you supply as MATLAB code, the tool transforms existing functions into C MEX S-functions that
you can include in Simulink models. If you use Simulink Coder to generate code, Legacy Code Tool
can insert an appropriate call to your C function into the generated code. For details, see “Import
Calls to External Code into Generated Code with Legacy Code Tool” (Simulink Coder).

In comparison to using the S-Function Builder or writing an S-function, Legacy Code Tool is easier to
use and generates optimized code (does not generate wrapper code) often required by embedded
systems. However, consider alternative approaches for a hybrid system, such as a system that
includes a plant and controller, or a system component written in a language other than C or C++.
Alternative approaches are more flexible in that they support more features and programming
languages.

To interact with the Legacy Code Tool, you

• Use a Legacy Code Tool data structure to specify

• A name for the S-function


• Specifications for the existing C functions
• Files and paths required for compilation
• Options for the generated S-function
• Use the legacy_code function to

• Initialize the Legacy Code Tool data structure for a given C function
• Generate an S-function for use during simulation

4-31
4 Writing S-Functions in C

• Compile and link the generated S-function into a dynamically loadable executable
• Generate a masked S-function block for calling the generated S-function
• Generate a TLC block file and, if necessary, an sFunction_makecfg.m or rtwmakecfg.m file
for code generation (Simulink Coder product license required)

Note Before you can use legacy_code, ensure that a C compiler is set up for your MATLAB
installation.

The following diagram illustrates a general procedure for using the Legacy Code Tool. “Integrate C
Functions into Simulink Models with Legacy Code Tool” on page 4-33 provides an example that uses
the Legacy Code Tool to transform an existing C function into a C MEX S-function.

If you have a Simulink Coder product license, see “Import Calls to External Code into Generated
Code with Legacy Code Tool” (Simulink Coder) for information on using the Legacy Code Tool for
code generation.

4-32
Integrate C Functions Using Legacy Code Tool

Integrate C Functions into Simulink Models with Legacy Code Tool


This example demonstrates how to integrate an existing C function into a Simulink model using
Legacy Code Tool.

Suppose that you have a C function that outputs the value of its floating-point input multiplied by two.
The function is defined in a source file named doubleIt.c, and its declaration exists in a header file
named doubleIt.h.

1 Initialize a MATLAB struct def with fields that represent Legacy Code Tool properties using the
legacy_code function.
def = legacy_code('initialize')

The Legacy Code Tool data structure named def displays its fields in the MATLAB command
window as shown here:
def =

SFunctionName: ''
InitializeConditionsFcnSpec: ''
OutputFcnSpec: ''
StartFcnSpec: ''
TerminateFcnSpec: ''
HeaderFiles: {}
SourceFiles: {}
HostLibFiles: {}
TargetLibFiles: {}
IncPaths: {}
SrcPaths: {}
LibPaths: {}
SampleTime: 'inherited'
Options: [1x1 struct]
2 Specify appropriate values for fields in the Legacy Code Tool data structure to identify properties
of the existing C function. For example, specify the C function source and header filenames by
entering the following commands at the MATLAB command prompt:
def.SourceFiles = {'doubleIt.c'};
def.HeaderFiles = {'doubleIt.h'};

You must also specify information about the S-function that the Legacy Code Tool produces from
the C code. For example, specify a name for the S-function and its output function declaration by
entering:
def.SFunctionName = 'ex_sfun_doubleit';
def.OutputFcnSpec = 'double y1 = doubleIt(double u1)';

4-33
4 Writing S-Functions in C

For information about the various data structure fields, see the legacy_code reference page.
3 Generate an S-function source file from the existing C function by using the legacy_code
function. At the MATLAB command prompt, type:

legacy_code('sfcn_cmex_generate', def);

The Legacy Code Tool uses the information specified in def to create the S-function source file
named ex_sfun_doubleit.c in the current MATLAB folder.
4 Compile and link the S-function source file into a dynamically loadable executable for Simulink
using thelegacy_code function. At the MATLAB command prompt, type:

legacy_code('compile', def);

The following messages appear in the MATLAB command window:

### Start Compiling ex_sfun_doubleit


mex('ex_sfun_doubleit.c', 'd:\work\lct_demos\doubleIt.c',
'-Id:\work\lct\lct_demos')
### Finish Compiling ex_sfun_doubleit
### Exit

On a 32-bit Microsoft Windows system, the resulting S-function executable is named


ex_sfun_doubleit.mexw32.
5 Insert a masked S-Function block into a Simulink model.

legacy_code('slblock_generate', def);

The Legacy Code Tool configures the block to use the C MEX S-function created in the previous
step. Also, the tool masks the block such that it displays the value of its OutputFcnSpec
property (see the description of the legacy_code function).
6 Add a Sine Wave block of amplitude 1 to the input of the C-MEX S-function block and a Scope
block to the output.

Run the simulation. The C-MEX S-Function block returns the value of its floating-point input
multiplied by two. It behaves like the C function doubleIt.

4-34
Integrate C Functions Using Legacy Code Tool

Integrate C Function Whose Arguments Are Pointers to Structures


This example shows how to use the Legacy Code Tool to integrate a C function whose arguments are
pointers to structures.

In Simulink®, create a Simulink.Bus object to represent a structure type. Use bus signals in a
model to represent structured signals and states. Create MATLAB structures in a workspace or in a
block parameter dialog box to represent parameter structures.

For basic information about bus signals, see “Virtual Bus”. For basic information about parameter
structures, see “Organize Related Block Parameter Definitions in Structures”. To create bus objects,
see “Create and Specify Simulink.Bus Objects”.

Explore External Code

Copy this custom source code into a file named ex_mySrc_LCT.c in your current folder.

#include "ex_myTypes_LCT.h"

void myFcn(sigStructType *in, paramStructType *params, sigStructType *out)


{
out->sig1 = in->sig1 * params->param1;
out->sig2 = in->sig2 * params->param2 + params->param3;
}

The arguments of the function myFcn are pointers to structures. The function accepts an input signal
argument, a parameter argument, and an output signal argument.

4-35
4 Writing S-Functions in C

Copy this custom header code into a file named ex_myTypes_LCT.h in your current folder.

#ifndef _MY_TYPES_H_
#define _MY_TYPES_H_

typedef struct {
double sig1;
double sig2;
} sigStructType;

typedef struct {
double param1;
double param2;
double param3;
} paramStructType;

void myFcn(sigStructType *in, paramStructType *params, sigStructType *out);

#endif

The file defines the signal and parameter structure types that myFcn uses.

Create Bus Objects to Represent Structure Types in Simulink

At the command prompt, use the function Simulink.importExternalCTypes to generate bus


objects in the base workspace.
Simulink.importExternalCTypes('ex_myTypes_LCT.h');

The bus objects correspond to the struct types that ex_myTypes_LCT.h defines.

Create Block to Execute External Code

Create a structure variable, def, to store the specifications for an S-function that calls the external
code. Use the function legacy_code to create the structure and set default values.
def = legacy_code('initialize');

Set the name of the S-function to sfun_ex_mySrc_LCT.


def.SFunctionName = 'sfun_ex_mySrc_LCT';

Identify the external source and header files by their file names.
def.SourceFiles = {'ex_mySrc_LCT.c'};
def.HeaderFiles = {'ex_myTypes_LCT.h'};

Specify the prototype of the output function, which the model calls every simulation step, by copying
the prototype of the external function myFcn. Set the names of the arguments to u1, p1, and y1 to
represent the input argument, the parameter argument, and the output argument. Use the syntax
[1] to specify that each argument is a pointer.
def.OutputFcnSpec = ['void myFcn(sigStructType u1[1], ',...
'paramStructType p1[1], sigStructType y1[1])'];

Use the function legacy_code to create the S-function and the corresponding C MEX executable
from the specification, def. Specify the option 'generate_for_sim' to prepare the S-function for
normal and accelerated simulations.

4-36
Integrate C Functions Using Legacy Code Tool

legacy_code('generate_for_sim',def);

### Start Compiling sfun_ex_mySrc_LCT


mex('-IC:\TEMP\Bdoc21a_1606923_3504\ib7FC80E\9\tp4a3531fa\ex12763634', '-c', '-outdir', 'C:\T
Building with 'Microsoft Visual C++ 2019 (C)'.
MEX completed successfully.
mex('sfun_ex_mySrc_LCT.c', '-IC:\TEMP\Bdoc21a_1606923_3504\ib7FC80E\9\tp4a3531fa\ex12763634',
Building with 'Microsoft Visual C++ 2019 (C)'.
MEX completed successfully.
### Finish Compiling sfun_ex_mySrc_LCT
### Exit

Create a masked S-Function block that calls the S-function during simulation.
legacy_code('slblock_generate', def);

The block appears in a new model.

To use the S-Function block in your model, create a bus signal of type sigStructType to use as the
block input. The block output is also a bus signal. The block mask accepts a parameter, P1. To set the
value of the parameter, use a MATLAB structure whose fields match those of the structure type
paramStructType.

Verify Execution of External Code

Create a harness model that verifies the execution of the external code during simulation.

For an example, view the model ex_lct_struct.


open_system('ex_lct_struct')

In the Constant block dialog box, the Constant value parameter is set to a structure whose fields
match those of the structure type sigStructType. On the Signal Attributes tab, Output data
type is set to the bus object sigStructType.

The S-Function block calls the S-function sfun_ex_mySrc_LCT that you created. The output of the
block enters a Bus Selector block, which extracts the signal elements sig1 and sig2.

4-37
4 Writing S-Functions in C

The S-Function block accepts a parameter through the mask dialog box. Create a MATLAB structure
structParam to use as the value of the parameter.

structParam = struct;
structParam.param1 = 15;
structParam.param2 = 20;
structParam.param3 = 5;

Optionally, use a Simulink.Parameter object to contain the structure. If you use a parameter
object, you can set the data type of the structure by using the bus object paramStructType.

structParam = Simulink.Parameter(structParam);
structParam.DataType = 'Bus: paramStructType';

In the mask dialog box, set P1 to structParam.

set_param('ex_lct_struct/sfun_ex_mySrc_LCT','SParameter1','structParam')

Simulate the model. The Scope blocks show that the S-Function block calls the external function
myFcn.

open_system('ex_lct_struct/Scope')
open_system('ex_lct_struct/Scope1')
sim('ex_lct_struct')
%

4-38
Integrate C Functions Using Legacy Code Tool

Registering Legacy Code Tool Data Structures


The first step to using the Legacy Code Tool is to register one or more MATLAB structures with fields
that represent properties of the existing C code and the S-function being generated. The registration
process is flexible. You can choose to set up resources and initiate registration in a variety of ways,
including

• Placing all required header and source files in the current working folder or in a hierarchical
folder structure
• Generating and placing one or more S-functions in the current working folder
• Having one or more registration files in the same folder

To register a Legacy Code Tool data structure:

1 Use the legacy_code function, specifying 'initialize' as the first argument.


lct_spec = legacy_code('initialize')

The Legacy Code Tool data structure named lct_spec displays its fields in the MATLAB
command window as shown below:
lct_spec =

SFunctionName: ''
InitializeConditionsFcnSpec: ''
OutputFcnSpec: ''
StartFcnSpec: ''
TerminateFcnSpec: ''
HeaderFiles: {}
SourceFiles: {}

4-39
4 Writing S-Functions in C

HostLibFiles: {}
TargetLibFiles: {}
IncPaths: {}
SrcPaths: {}
LibPaths: {}
SampleTime: 'inherited'
Options: [1x1 struct]
2 Define values for the data structure fields (properties) that apply to your existing C function and
the S-function you intend to generate. Minimally, you must specify

• Source and header files for the existing C function (SourceFiles and HeaderFiles)
• A name for the S-function (SFunctionName)
• At least one function specification for the S-function (InitializeConditionsFcnSpec,
OutputFcnSpec, StartFcnSpec, TerminateFcnSpec)

For a complete list and descriptions of the fields in the structure, see the legacy_code function
reference page.

If you define fields that specify compilation resources and you specify relative paths, the Legacy Code
Tool searches for the resources relative to the following directories, in the following order:

1 Current working folder


2 C-MEX S-function folder, if different than the current working folder
3 Directories you specify

• IncPaths for header files


• SrcPaths for source files
• LibPaths for target and host libraries
4 Directories on the MATLAB search path, excluding toolbox directories

Declaring Legacy Code Tool Function Specifications


The InitializeConditionsFcnSpec, OutputFcnSpec, StartFcnSpec, and TerminateFcnSpec
fields defined in the Legacy Code Tool data structure (see the description of the legacy_code
function) require character vector values that adhere to a specific syntax format. The required syntax
format enables the Legacy Code Tool to map the return value and arguments of an existing C function
to the return value, inputs, outputs, parameters, and work vectors of the S-function that the tool
generates.

General syntax

return-spec = function-name(argument-spec)

For example, the following character vector specifies a function named doubleIt with return
specification double y1 and input argument specification double u1.

def.OutputFcnSpec = 'double y1 = doubleIt(double u1)';

For more detail on declaring function specifications, see

• “Return Specification” on page 4-41


• “Function Name” on page 4-41

4-40
Integrate C Functions Using Legacy Code Tool

• “Argument Specification” on page 4-41


• “Supported Data Types” on page 4-44
• “Legacy Code Tool Function Specification Rules” on page 4-45
• “Legacy C Function Rules” on page 4-45

Return Specification

The return specification defines the data type and variable name for the return value of the existing C
function.

return-type return-variable

return-type A data type listed in “Supported Data Types” on page 4-44.


return-variable Token of the form y1, y2, ..., yn, where n is the total number of output
arguments.

If the function does not return a value, you can omit the return specification or specify it as void.

The following table shows valid function specification syntax for an integer return value. Use the
table to identify the syntax you should use for your C function prototype.

Return Type C Function Prototype Legacy Code Tool Function


Specification
No return value void myfunction(...) void myfunction(...)
Scalar value int = myfunction(...) int16 y1 = myfunction(...)

Function Name

The function name that you specify must be the same as your existing C function name.

For example, consider the following C function prototype:

float doubleIt(float inVal);

In this case, the function name in the Legacy Code Tool function specification must be doubleIt.

You should not specify the name of a C macro. If you must, set the field Options.isMacro to true
in case expression folding is enabled.

Argument Specification

The argument specification defines one or more data type and token pairs that represent the input,
output, parameter, and work vector arguments of the existing C function. The function input and
output arguments map to block input and output ports and parameters map to workspace
parameters.

argument-type argument-token

argument-type A data type listed in “Supported Data Types” on page 4-44.

4-41
4 Writing S-Functions in C

argument-token Token of one of the following forms:

• Input — u1, u2, ..., un, where n is the total number of input
arguments
• Output — y1, y2, ..., yn, where n is the total number of output
arguments
• Parameter — p1, p2, ..., pn, where n is the total number of
parameter arguments
• Work vectors (persistent memory) — work1, work2, ..., workn,
where n is the total number of work vector arguments

If the function has no arguments, you can omit the argument specification or specify it as void.

Consider the following C function prototype:

float powerIt(float inVal, int exponent);

To generate an S-function that calls the preceding function at each time step, set the Legacy Code
Tool data structure field OutputFcnSpec to the following:

'single y1 = powerIt(single u1, int16 p1)'

Using this function specification, the Legacy Code Tool maps the following information.

Return Value or of C Type To Token of Data Type


Argument
Return value float y1 single
inVal float u1 single
exponent int p1 int16

If your function requires a Simulink S-function block with multiple input and output ports, map
function arguments to input ports using a uniquely numbered u token. For output ports, use a
uniquely numbered y token. These tokens are described in the preceding argument specification
table. For example, consider the following C function prototype:

void myfunc(double *y2, double u2, double u3, double u1, double *y1);

An OutputFcnSpec character vector mapping the arguments to input and output ports looks similar
to the following:

'void myfunc(double y2[1], double u2, double u3, double u1, double y1[1])'

The resulting S-function block includes three input ports and two output ports. The first input maps
to function argument u1, the second input to u2, and the third input to u3. For the output ports, the
function argument y1[1] maps to the first output, and argument y2[1] maps to the second output.
For another example of mapping a function prototype to multiple input and output ports, see “Using
Buses with Legacy Functions Having Structure Arguments”.

The following table shows valid function specification syntax for arguments of type integer. Use the
table to identify and then adapt the syntax you should use for your C function prototype.

4-42
Integrate C Functions Using Legacy Code Tool

Argument Type C Function Prototype Legacy Code Tool Function


Specification
Input Arguments
No arguments function(void) function(void)
Scalar pass by value function(int in1) function(int16 u1)
Scalar pass by pointer function(int *in1) function(int16 u1[1])
Fixed vector function(int in1[10]) or function(int16 u1[10])
function(int *in1)
Variable vector function(int in1[]) or function(int16 u1[])
function(int *in1)
Fixed matrix function(int in1[15]) or function(int16 u1[3][5])
function(int in1[]) or
function(int *in1)
Variable matrix function(int in1[]) or function(int16 u1[][])
function(int *in1)
Output Arguments
Scalar pointer function(int *y1) function(int16 y1[1])
Fixed vector function(int y1[10]) or function(int16 y1[10])
function(int *y1)
Fixed matrix function(int y1[15]) or function(int16 y1[3][5])
function(int y1[]) or
function(int *y1)
Parameter Arguments
Scalar pass by value function(int p1) function(int16 p1)
Scalar pass by pointer function(int *p1) function(int16 p1[1])
Fixed vector function(int p1[10]) or function(int16 p1[10])
function(int *p1)
Variable vector function(int p1[]) or function(int16 p1[])
function(int *p1)
Fixed matrix function(int p1[15]) or function(int16 p1[3][5])
function(int p1[]) or
function(int *p1)
Variable matrix function(int p1[]) or function(int16 p1[][])
function(int *p1)
Work Vector Arguments
Scalar passed by value function(int work1) function(int16 work1)
Scalar pointer function(int *work1) function(int16 work1[1])
function(void *work1) void function(void *work1)
function(void **work1) void function(void **work1)
Fixed vector function(int work1[10]) or function(int16 work1[10])
function(int *work1)

4-43
4 Writing S-Functions in C

Argument Type C Function Prototype Legacy Code Tool Function


Specification
Fixed matrix function(int work1[15]) or function(int16 work1[3][5])
function(int work1[]) or
function(int *work1)

Supported Data Types

Data Type Supported for Input Supported for Supported for


and Output? Parameters? Work Vectors?
“Data Types Supported by Simulink” Yes Yes Yes
(with the exception of string)
Simulink.Bus1 Yes Yes Yes
2
Array of Simulink.Bus Yes No Yes
3
Simulink.NumericType Yes Yes Yes
1
Simulink.AliasType Yes Yes Yes
1
enum Yes Yes Yes
4
Fixed-point Yes Yes Yes
Fi objects N/A Yes N/A
5
Complex numbers Yes Yes Yes
1-D array Yes Yes Yes
6
2-D array Yes Yes Yes
7
n-D array Yes Yes Yes
void * No No Yes
void ** No No Yes

1 You must supply the header file that defines the structure of the bus, defines the enum type, or
defines the data type with the same name as an alias. The structure of the bus declared in the
header file must match the structure of the bus object (for example, the number and order of
elements, data types and widths of elements, and so on). For an example, see “Using Buses with
Legacy Functions Having Structure Arguments”.

To generate data type objects and enumeration classes that correspond to custom data types that
your C code defines, use the Simulink.importExternalCTypes function.
2 A bus element can be complex, but only with Simulink built-in data types. Nesting of arrays to
any level is also supported.
3 You must supply the header file that defines the data type only if the numeric data type is also an
alias.
4 You must declare the data as a Simulink.NumericType object (unspecified scaling is not
supported). For examples, see “Fixed Point Signals in Legacy Functions” and “Fixed Point
Parameters in Legacy Functions”.
5 Limited to use with Simulink built-in data types. To specify a complex data type, enclose the built-
in data type within angle brackets (<>) and prepend the word complex (for example,
complex<double>). For an example, see “Complex Signals in Legacy Function”.

4-44
Integrate C Functions Using Legacy Code Tool

6 The MATLAB, Simulink, and Simulink Coder products store multidimensional array data in
column-major format as a vector. If your external function code is written for row-major data, use
convertNDArrayToRowMajor S-function option in legacy_code.
7 For a multidimensional signal, you can use the size function to determine the number of
elements in the signal. For examples, see “Lookup Tables Implemented in Legacy Functions” and
“Multi-Dimensional Signals in Legacy Functions”.

For more information, see “Data Types Supported by Simulink”.

Legacy Code Tool Function Specification Rules

Specifications for the legacy_code must adhere to the following rules:

• If an argument is not scalar, you must pass the argument by reference.


• The numbering of input, output, parameter, and work vector argument tokens must start at 1 and
increase monotonically.
• For a given Legacy Code Tool data structure, the data type and size of input, output, parameter,
and work vector arguments must be the same across function specifications for StartFcnSpec,
InitializeConditionsFcnSpec, OutputFcnSpec, and TerminateFcnSpec.
• You can specify argument dimensions with expressions that use the following:

• Functions: numel , size


• Parameter values
• Operators: +, -, *, and /
• Integer and floating point literals
• Parentheses for grouping sub-expressions

For example:

def.OutputFcnSpec=
foo4(int8 p1[], int8 u1[], double y1[numel(u1)+2][numel(u1)+3], ...
int32 (numel(p1)+numel(u1))*2+size(y1,2))';

Legacy C Function Rules

To integrate a C function using the Legacy Code Tool, the function must adhere to the following rules:

• The function must not change the value of input arguments. If an input signal is a passed-by-
reference argument to the function, the function must not modify the data pointed to by the
argument.
• The function's return value cannot be a pointer.
• Function specifications you define for the StartFcnSpec, InitializeConditionsFcnSpec, or
TerminateFcnSpec cannot access input or output arguments. For StartFcnSpec and
InitializeConditionsFcnSpec, you can access output ports if the S-Function option
outputsConditionallyWritten is set to true. With this option setting, the generated S-
Function specifies that the memory associated with each output port cannot be overwritten and is
global (SS_NOT_REUSABLE_AND_GLOBAL).

4-45
4 Writing S-Functions in C

Generating and Compiling the S-Functions


After you register a Legacy Code Tool data structure for an existing C function, use the legacy_code
function as explained below to generate, compile, and link the S-function.
1 Generate a C MEX S-function based on the information defined in the structure. Call
legacy_code with 'sfcn_cmex_generate' as the first argument and the name of the data
structure as the second argument.
legacy_code('sfcn_cmex_generate', lct_spec);
2 Compile and link the S-function. This step assumes that a C compiler is set up for your MATLAB
installation. Call legacy_code with 'compile' as the first argument and the name of the data
structure as the second argument.
legacy_code('compile', lct_spec);

Informational messages similar to the following appear in the MATLAB command window and a
dynamically loadable executable results. On a 32-bit Windows system, the Simulink software
names the file ex_sfun_doubleit.mexw32.
### Start Compiling ex_sfun_doubleit
mex ex_sfun_doubleit.c -Id:\work\lct\lct_demos
### Finish Compiling ex_sfun_doubleit
### Exit

As a convenience, you can generate, compile, and link the S-function in a single step by calling
legacy_code with the character vector 'generate_for_sim'. The function also generates a TLC
file for accelerated simulations, if the Options.useTlcWithAccel field of the Legacy Code Tool
data structure is set to 1.

Once you have generated a dynamically loadable executable, you or others can use it in a model by
adding an S-Function block that specifies the compiled S-function.

Generating a Masked S-Function Block for Calling a Generated S-


Function
You have the option of using the Legacy Code Tool to generate a masked S-function block (graphical
representation) that is configured to call a generated C MEX S-function. To generate such a block,
call legacy_code with 'slblock_generate' as the first argument and the name of the Legacy
Code Tool data structure as the second argument.
legacy_code('slblock_generate', lct_spec);

The tool masks the block such that it displays the value of the OutputFcnSpec field. You can then
add the block to a model manually.

If you prefer that the Legacy Code Tool add the block to a model automatically, specify the name of
the model as a third argument. For example:
legacy_code('slblock_generate', lct_spec, 'myModel');

If the specified model (for example, myModel) exists, legacy_code opens the model and adds the
masked S-function block described by the Legacy Code Tool data structure. If the model does not
exist, the function creates a new model with the specified name and adds the masked S-function
block.

4-46
Integrate C Functions Using Legacy Code Tool

Forcing Simulink Accelerator Mode to Use S-Function TLC Inlining


Code
If you are using Simulink Accelerator mode, you can generate and force the use of TLC inlining code
for the S-function generated by the Legacy Code Tool. To do this:

1 Generate a TLC block file for the S-function by calling the legacy_code function with
'sfcn_tlc_generate' as the first argument and the name of the Legacy Code Tool data
structure as the second argument.

legacy_code('sfcn_tlc_generate', lct_spec);

Consider the example in “Integrate C Functions into Simulink Models with Legacy Code Tool” on
page 4-33. To generate a TLC file for the model shown at the end of that example, enter the
following command:

legacy_code('sfcn_tlc_generate', def);
2 Force Accelerator mode to use the TLC file by using the ssSetOptions SimStruct function to
set the S-function option SS_OPTION_USE_TLC_WITH_ACCELERATOR.

Calling Legacy C++ Functions


To call a legacy C++ function after initializing the Legacy Code Tool data structure, assign the value
'C++' to the Options.language field. For example,

def = legacy_code('initialize');
def.Options.language = 'C++';

To verify the new setting, enter

def.Options.language

Note The Legacy Code Tool can interface with C++ functions, but not C++ objects. For a work
around, see “Legacy Code Tool Limitations” on page 4-48 in the Simulink documentation.

Handling Multiple Registration Files


You can have multiple registration files in the same folder and generate an S-function for each file
with a single call to legacy_code. Likewise, you can use a single call to legacy_code in order to
compile and link the S-functions and another to generate corresponding TLC block files, if
appropriate.

Consider the following example, where lct_register_1, lct_register_2, and lct_register_3


each create and initialize fields of a Legacy Code Tool structure.

defs1 = lct_register_1;
defs2 = lct_register_2;
defs3 = lct_register_3;
defs = [defs1(:);defs2(:);defs3(:)];

You can then use the following sequence of calls to legacy_code in order to generate files based on
the three registration files:

4-47
4 Writing S-Functions in C

legacy_code('sfcn_cmex_generate', defs);
legacy_code('compile', defs);
legacy_code('sfcn_tlc_generate', defs);

Alternatively, you can process each registration file separately. For example:

defs1 = lct_register1;
legacy_code('sfcn_cmex_generate', defs1);
legacy_code('compile', defs1);
legacy_code('sfcn_tlc_generate', defs1);
.
.
.
defs2 = lct_register2;
legacy_code('sfcn_cmex_generate', defs2);
legacy_code('compile', defs2);
legacy_code('sfcn_tlc_generate', defs2);
.
.
.
defs3 = lct_register3;
legacy_code('sfcn_cmex_generate', defs3);
legacy_code('compile', defs3);
legacy_code('sfcn_tlc_generate', defs3);

Deploying Generated S-Functions


You can deploy the S-functions that you generate with the Legacy Code Tool for use by others. To
deploy an S-function for simulation use only, you need to share only the compiled dynamically
loadable executable.

Legacy Code Tool Examples


For examples of the Legacy Code Tool, see “Implement Algorithms Using Legacy Code Tool”.

Legacy Code Tool Limitations


Legacy Code Tool

• Generates C MEX S-functions for existing functions written in C or C++. The tool does not
support transformation of MATLAB or Fortran functions.
• Can interface with C++ functions, but not C++ objects. One way of working around this limitation
is to use the S-Function Builder to generate the shell of an S-function and then call the legacy C+
+ code from the S-function's mdlOutputs callback function.
• Does not support simulating continuous or discrete states. This prevents you from using the
mdlUpdate and mdlDerivatives callback functions. If your application requires this support,
see “Using the S-Function Builder to Incorporate Legacy Code” on page 2-9.
• Always sets the S-functions flag for direct feedthrough on page 1-7 (sizes.DirFeedthrough) to
true. Due to this setting and the preceding limitation, the generated S-function cannot break
algebraic loops.
• Supports only the continuous, but fixed in minor time step, sample time and offset on page 1-8
option.

4-48
Integrate C Functions Using Legacy Code Tool

• Supports complex numbers, but only with Simulink built-in data types.
• Does not support use of function pointers as the output of the legacy function being called.
• Does not support the following S-function features:

• Work vectors, other than general DWork vectors


• Frame-based input and output signals
• Port-based sample times
• Multiple block-based sample times
• Does not support use of the scope (::) operator for access of C++ class data and methods. For
static methods, you can write simple preprocessor macros, similar to the following, to work
around this:

#define CCommon_computeVectorDotProduct CCommon::computeVectorDotProduct


• Can generate a terminate function when you have not specified one if the function specification
includes a Simulink data type that has the property HeaderFile. For an export function model,
this terminate function can make the generated S-function incompatible with code generation.

4-49
4 Writing S-Functions in C

Simulink Engine Interaction with C S-Functions


In this section...
“Process View” on page 4-50
“Data View” on page 4-55

You can examine how the Simulink engine interacts with S-functions from two perspectives:

• Process perspective, i.e., at which points in a simulation the engine invokes the S-function.
• Data perspective, i.e., how the engine and the S-function exchange information during a
simulation.

Process View
The following figures show the order in which the Simulink engine invokes the callback methods in an
S-function. Solid rectangles indicate callbacks that always occur during model initialization or at
every time step. Dotted rectangles indicate callbacks that may occur during initialization and/or at
some or all time steps during the simulation loop. See the documentation for each callback method to
determine the exact circumstances under which the engine invokes the callback.

Note The process view diagram represents the execution of S-functions that contain continuous and
discrete states, enable zero-crossing detection, and reside in a model that uses a variable-step solver.
Different solvers omit certain steps in the diagram. For a better understanding of how the Simulink
engine executes your particular S-function, run the model containing the S-function using the
Simulink debugger. For more information, see “Introduction to the Debugger”.

In the following model initialization loop, the Simulink engine configures the S-function for an
upcoming simulation. The engine always makes the required calls to mdlInitializeSizes and
mdlInitializeSampleTime to set up the fundamental attributes of the S-function, including input
and output ports, S-function dialog parameters, work vectors, sample times, etc.

The engine calls additional methods, as needed, to complete the S-function initialization. For
example, if the S-function uses work vectors, the engine calls mdlSetWorkWidths. Also, if the
mdlInitializeSizes method deferred setting up input and output port attributes, the engine calls
any methods necessary to complete the port initialization, such as mdlSetInputPortWidth, during
signal propagation. The mdlStart method calls the mdlCheckParameters and
mdlProcessParameters methods if the S-function uses dialog parameters.

4-50
Simulink Engine Interaction with C S-Functions

Note The mdlInitializeSizes callback method also runs when you enter the name of a compiled
S-function into the S-Function Block Parameters dialog box.

After initialization, the Simulink engine executes the following simulation loop. If the simulation loop
is interrupted, either manually or when an error occurs, the engine jumps directly to the
mdlTerminate method. If the simulation was manually halted, the engine first completes the current
time step before invoking mdlTerminate.

4-51
4 Writing S-Functions in C

If your model contains multiple S-Function blocks at a given level of model hierarchy, the engine
invokes a particular method for every S-function before proceeding to the next method. For example,
the engine calls all the mdlInitializeSizes methods before calling any
mdlInitializeSampleTimes methods. The engine uses the block sorted order to determine the
order to execute the S-functions. To learn more about how the engine determines the block execution
order, see “Control and Display Execution Order”.

Calling Structure for Code Generation

If you use the Simulink Coder product to generate code for a model containing S-functions, the
Simulink engine does not execute the entire calling sequence outlined above. Initialization proceeds
as outlined above until the engine reaches the mdlStart method. The engine then calls the S-
function methods shown in the following figure, where the mdlRTW method is unique to the Simulink
Coder product.

4-52
Simulink Engine Interaction with C S-Functions

If the S-function resides in a conditionally executed subsystem, it is possible for the generated code
to interleave calls to mdlInitializeConditions and mdlStart. Consider the following Simulink
model.

The model contains two nonvirtual subsystems, the conditionally executed enabled subsystem named
Reset and the atomic subsystem named Atomic. Each subsystem contains an S-Function block that
calls the S-function dsfunc.c, which models a discrete state-space system with two states. The
enabled subsystem Reset resets the state values when the subsystem is enabled, and the output
values when the subsystem is disabled.

Using the generic real-time (GRT) target, the generated code for the model-wide Start function calls
the Start functions of the two subsystems before calling the model-wide MdlInitialize function,
as shown in the following code:

4-53
4 Writing S-Functions in C

void MdlStart(void)
{
/* snip */

/* Start for enabled SubSystem: '<Root>/Reset' */


sfcndemo_enablesub_Reset_Start();

/* end of Start for SubSystem: '<Root>/Reset' */

/* Start for atomic SubSystem: '<Root>/Atomic' */


sfcndemo_enablesub_Atomic_Start();

/* end of Start for SubSystem: '<Root>/Atomic' */

MdlInitialize();

The Start function for the enabled subsystem calls the subsystem's InitializeConditions
function:

void sfcndemo_enablesub_Reset_Start(void)
{
sfcndemo_enablesub_Reset_Init();
/* snip */
}

The MdlInitialize function, called in MdlStart, contains a call to the InitializeConditions


function for the atomic subsystem:

void MdlInitialize(void)
{
/* InitializeConditions for atomic SubSystem:
'<Root>/Atomic' */

sfcndemo_enablesub_Atomic_Init();
}

Therefore, the model-wide Start function interleaves calls to the Start and
InitializeConditions functions for the two subsystems and the S-functions they contain.

For more information about the Simulink Coder product and how it interacts with S-functions, see “S-
Functions and Code Generation” (Simulink Coder).

Alternate Calling Structure for External Mode

When you are running a Simulink model in external mode, the calling sequence for S-function
routines changes as shown in the following figure.

4-54
Simulink Engine Interaction with C S-Functions

The engine calls mdlRTW once when it enters external mode and again each time a parameter
changes or when you click Update Model on the Modeling tab.

Note Running a Simulink model in external mode requires the Simulink Coder product.

Data View
S-function blocks have input and output signals, parameters, and internal states, plus other general
work areas. In general, block inputs and outputs are written to, and read from, a block I/O vector.
Inputs can also come from

• External inputs via the root Inport blocks


• Ground if the input signal is unconnected or grounded

Block outputs can also go to the external outputs via the root Outport blocks. In addition to input and
output signals, S-functions can have

• Continuous states
• Discrete states
• Other working areas such as real, integer, or pointer work vectors

You can parameterize S-function blocks by passing parameters to them using the S-Function Block
Parameters dialog box.

The following figure shows the general mapping between these various types of data.

4-55
4 Writing S-Functions in C

An S-function's mdlInitializeSizes routine sets the sizes of the various signals and vectors. S-
function methods called during the simulation loop can determine the sizes and values of the signals.

An S-function method can access input signals in two ways:

• Via pointers
• Using contiguous inputs

Accessing Signals Using Pointers

During the simulation loop, access the input signals using

InputRealPtrsType uPtrs =
ssGetInputPortRealSignalPtrs(S,portIndex)

This returns an array of pointers for the input port with index portIndex, where portIndex starts
at 0. There is one array of pointers for each input port. To access an element of this array you must
use

*uPtrs[element]

The following figure describes how to access the input signals of an S-function with two inputs.

4-56
Simulink Engine Interaction with C S-Functions

As shown in the previous figure, the input array pointers can point at noncontiguous places in
memory.

You can retrieve the output signal by using this code.

real_T *y = ssGetOutputPortSignal(S,outputPortIndex);

Accessing Contiguous Input Signals

An S-function's mdlInitializeSizes method can specify that the elements of its input signals must
occupy contiguous areas of memory, using ssSetInputPortRequiredContiguous. If the inputs
are contiguous, other methods can use ssGetInputPortSignal to access the inputs.

Accessing Input Signals of Individual Ports

This section describes how to access all input signals of a particular port and write them to the output
port. The preceding figure shows that the input array of pointers can point to noncontiguous entries
in the block I/O vector. The output signals of a particular port form a contiguous vector. Therefore,
the correct way to access input elements and write them to the output elements (assuming the input
and output ports have equal widths) is to use this code.
int_T element;
int_T portWidth = ssGetInputPortWidth(S,inputPortIndex);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,inputPortIndex);
real_T *y = ssGetOutputPortSignal(S,outputPortIdx);

for (element=0; element<portWidth; element++) {


y[element] = *uPtrs[element];
}

A common mistake is to try to access the input signals via pointer arithmetic. For example, if you
were to place

real_T *u = *uPtrs; /* Incorrect */

just below the initialization of uPtrs and replace the inner part of the above loop with

4-57
4 Writing S-Functions in C

*y++ = *u++; /* Incorrect */

the code compiles, but the MEX file might crash the Simulink software. This is because it is possible
to access invalid memory (which depends on how you build your model). When accessing the input
signals incorrectly, a crash occurs when the signals entering your S-function block are not
contiguous. Noncontiguous signal data occurs when signals pass through virtual connection blocks
such as the Mux or Selector blocks.

To verify that your S-function correctly accesses wide input signals, pass a replicated signal to each
input port of your S-function. To do this, create a Mux block with the number of input ports equal to
the width of the desired signal entering your S-function. Then, connect the driving source to each S-
function input port, as shown in the following figure. Finally, run your S-function using this input
signal to verify that it does not crash and produces expected results.

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “Use S-Functions in Models” on page 1-3
• “S-Function Callback Methods” on page 1-14

4-58
S-Functions in Normal Mode Referenced Models

S-Functions in Normal Mode Referenced Models

Note For additional information, see “Model Reference Requirements and Limitations”.

When a C S-function appears in a referenced model that executes in Normal mode, successful
execution is impossible if all of the following are true:

• The S-function has both an mdlProcessParameters function and an mdlStart function.


• The mdlProcessParameters function depends on the mdlStart function.
• The referenced model calls mdlProcessParameters before calling mdlStart.

Execution fails because mdlProcessParameters has dependency requirements that mdlStart has
not satisfied. Automated analysis cannot guard against all possible causes of such failure: you must
check your code manually and verify that mdlProcessParameters is not in any way dependent on
mdlStart being called first. Examples of such dependency include:

• Allocating memory in mdlStart and using that memory in mdlProcessParameters. This is


often done using ssSetUserData and ssGetUserData.
• Initializing any DWork or any global memory in mdlStart and reading the values in
mdlProcessParameters.

To remind you to check for any such dependency problems, an error message appears by default for
any S-function that is used in a Normal mode referenced model and contains both an
mdlProcessParameters function and an mdlStart function. The error message does not mean
that any dependency problems exist, but only that they might exist.

If you get such an error message, check for any problematic dependencies in the S-function, and
recode as needed to eliminate them. When no such dependencies exist, you can safely suppress the
error message and use the S-function in a Normal mode referenced model. To certify that the S-
function is compliant, and the message is therefore unnecessary, include the following statement in
mdlInitializeSizes:
ssSetModelReferenceNormalModeSupport (S, MDL_START_AND_MDL_PROCESS_PARAMS_OK);

For information about referenced models, see “Model Reference Basics”.

Supporting the Use of Multiple Instances of Referenced Models That


Are in Normal Mode
You may need to modify S-functions that are used by a model so that the S-functions work with
multiple instances of referenced models in Normal mode. The S-functions must indicate explicitly that
they support multiple exec instances.

• For C S-functions, use ssSupportsMultipleExecInstances(s, true).


• For MATLAB file S-functions, use blockSupportMultipleExecInstances = true.

The limitations for using S-functions with multiple instances of referenced models in Normal mode
are the same as the limitations that apply to using S-functions with For Each Subsystem block.

4-59
4 Writing S-Functions in C

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “S-Function Features and Limitations” on page 2-3
• “S-Function SimStruct Functions” on page 12-2

4-60
Debug C MEX S-Functions

Debug C MEX S-Functions


In this section...
“About Debugging C MEX S-Functions” on page 4-61
“Debug in Simulink Environment” on page 4-61
“Debug Using Third-Party Software” on page 4-64

About Debugging C MEX S-Functions


This section provides high-level tips on how to debug C MEX S-functions within the Simulink
environment and using third-party software. The following lists highlight some of the more common
errors made when writing an S-function. For a more detailed analysis, use the debugger provided
with your C compiler.

The examples at the end of this section show how to debug a C MEX S-function during simulation,
using third-party software.

• The first example uses the Microsoft Visual C++® .NET (version 7.0) environment.
• The second example debugs an S-function on The Open Group UNIX® platform.

Refer to your compiler documentation for further information on debugging files.

Debug in Simulink Environment


Before you begin, make sure you have a good understanding of how to write C S-functions and the
required callback methods. For assistance:

• Read the section “Available S-Function Implementations” on page 2-2 to determine if you
implemented your S-function using the most appropriate method.

• Use the S-Function Builder block to generate simple S-functions and study the contents of the
source files.
• Inspect the S-function example models available in sfundemos. The folder matlabroot/
simulink/src (open) contains the S-function source files for these models.

If your S-function is not compiling, first ensure that the mex command is properly configured and
your S-function includes all necessary files:

• Run mex -setup to ensure that your compiler is correctly installed.


• Confirm that you are passing all the source files needed by your S-function to the mex command.
• Check that these additional source files are on the MATLAB path.
• Make sure that your S-function includes the simstruc.h header file. If you are accessing legacy
code, make sure that any header files needed by that code are also included in your S-function.
• Make sure that your S-function does not include the simstruc_types.h or rtwtypes.h header
files. These Simulink and Simulink Coder header files are automatically included for you. If you
are compiling your S-function as a MEX file for simulation, including the rtwtypes.h file results
in errors.

If the mex command compiles your S-function, but your S-function does not simulate or the
simulation produces incorrect results, inspect your S-function source code to ensure that:

4-61
4 Writing S-Functions in C

• You are not overwriting important memory


• You are not using any uninitialized variables

The following table describes additional common S-function constructs that can lead to compilation
and simulation errors.

Does your S-function... Look for...


Use for loops to assign memory? Instances where your S-function might inadvertently assign
values outside of the array bounds.
Use global variables? Locations in the code where the global variables can be
corrupted. If you have multiple instances of your S-function in a
model, they can write over the same memory location.
Allocate memory? Memory your S-function does not deallocate. Always free
memory that your S-function allocates, using the malloc and
free commands to allocate and deallocate memory,
respectively.
Have direct feedthrough? An incorrect direct feedthrough flag setting in your S-function.
An S-function can access its inputs in the mdlOutputs method
only if it specifies that the input ports have direct feedthrough.
Accessing input signals in mdlOutputs when the input port
direct feedthrough flag is set to false leads to indeterminate
behavior. To check if you have a direct feedthrough flag
incorrectly set, you can turn on the model property
TryForcingSFcnDF using the command
set_param(model_name,'TryForcingSFcnDF','on')

This command specifies that all S-functions in the model


model_name have a direct feedthrough flag of true for all their
input ports. After you turn on this property, if your simulation
produces correct answers without causing an algebraic loop,
one of your S-functions in the model potentially set an incorrect
direct feedthrough flag. Consult the on direct feedthrough in
sfuntmpl_doc.c for more information on diagnosing direct
feedthrough errors.

4-62
Debug C MEX S-Functions

Does your S-function... Look for...


Access input signals correctly? Instances in the code where your S-function uses incorrect
macros to access input signals, for example when accessing a
discontiguous signal. Discontiguous signals result when an S-
function input port is fed by a Selector block that selects every
other element of a vector signal. For discontiguous input
signals, use the following commands:
// In mdlInitializeSizes
ssSetInputPortRequiredContiguous(S, 0, 0);

// In mdlOutputs, access the inputs using


InputRealPtrsType uPtrs1 =
ssGetInputPortRealSignalPtrs(S,0);

For contiguous input signals, use the following commands:


// In mdlInitializeSizes
ssSetInputPortRequiredContiguous(S, 0, 1);

// In mdlOutputs, access the inputs using


const real_T *u0 =
(const real_T*) ssGetInputPortSignal(S,0);

/* If ssSetInputPortRequiredContiguous is 0,
ssGetInputPortSignal returns an invalid pointer.*/

Debugging Techniques

You can use the following techniques for additional assistance with debugging your S-function.

• Compile the S-function in debug mode using the -g option for the mex command. This enables
additional diagnostics features that are called only when you compile your S-function in debug
mode.

• Place ssPrintf statements inside your callback methods to ensure that they are running and that
they are executing in the order you expect. Also, use ssPrintf statements to print return values
to the MATLAB command prompt to check if your code is producing the expected results.
• Type feature memstats at the MATLAB command prompt to query the memory usage.
• Use the MATLAB File & folder Comparisons tool, or other text differencing application, to look for
textual changes in different versions of your S-function. This can help you locate changes that
disabled an S-function that previously compiled and ran. See “Compare Files and Folders and
Merge Files” for instructions on how to use the File & folder Comparisons tool.
• Use settings on the Configuration Parameters dialog box to check for memory problems.

• Set the Solver data inconsistency diagnostic to warning.


• Set the Array bounds exceeded diagnostic to warning or error (See “Checking Array
Bounds” on page 9-46 for more information on how to use this diagnostic).
• Turn the “Signal storage reuse” (Simulink Coder) optimization off.
• Separate the S-function's algorithm from its Simulink interface then use the S-Function Builder to
generate a new Simulink interface for the algorithm. The S-Function Builder ensures that the
interface is implemented in the most consistent method.

4-63
4 Writing S-Functions in C

Debug Using Third-Party Software


You can debug and profile the algorithm portion of your S-function using third-party software if you
separate the algorithm from the S-function's Simulink interface. You cannot debug and profile the S-
function's interface with the Simulink engine because the Simulink interface code does not ship with
the product.

You can additionally use third-party software to debug an S-function during simulation, as shown in
the following two examples. These examples use the Simulink model sfcndemo_timestwo and the C
MEX S-function timestwo.c.

Debugging C MEX S-Functions Using the Microsoft Visual C++ .NET Environment

Before beginning the example, save the files sfcndemo_timestwo and timestwo.c into your
working folder.

1 Open the Simulink model sfcndemo_timestwo.


2 Create a version of the MEX file that you can debug by compiling the C file using the mex
command with the -g option.

mex -g timestwo.c

The -g option creates the executable timestwo.mexw64 with debugging symbols included. At
this point, you may want to simulate the sfcndemo_timestwo model to ensure it runs properly.
3 Without exiting the MATLAB environment, start the Microsoft Development Environment.
4 From the Microsoft Development Environment menu bar, select Tools > Debug Processes.
5 In the Processes dialog box that opens, select the MATLAB.exe process in the Available
Processes list and click Attach.
6 In the Attach to Process dialog box that opens, select Native in the list of program types and
click OK. You should now be attached to the MATLAB process.
7 Click Close on the Processes dialog box.
8 Clear the MEX functions in MATLAB using the clear command.

clear mex
9 From the Microsoft Development Environment File menu, select Open > File. Select the
timestwo.c source files from the file browser that opens.
10 Set a breakpoint on the desired line of code by right-clicking on the line and selecting Insert
Breakpoint from the context menu. If you have not previously run the model, the breakpoint
may show up with a question mark, indicating that the executable is not loaded. Subsequently
running the model loads the .mexw32 file and removes the question mark from the breakpoint.
11 Start the simulation from the sfcndemo_timestwo Simulink model. You should be running the
S-function in the Microsoft Development Environment and can debug the file within that
environment.

Debugging C MEX S-Functions on The Open Group UNIX Platforms

Before beginning the example, save the files sfcndemo_timestwo and timestwo.c into your
working folder.

Create a version of the MEX file for debugging:

4-64
Debug C MEX S-Functions

1 Open the Simulink model sfcndemo_timestwo.


2 Create a version of the MEX file that you can debug by compiling the C file using the mex
command with the -g option:

mex -g timestwo.c

The -g option creates the executable timestwo.mexa64 with debugging symbols included.
3 Simulate the sfcndemo_timestwo model to ensure it runs properly.
4 Exit the MATLAB environment.

Debug the MEX file:

1 Start the MATLAB environment in debugging mode using this command:

matlab -D<nameOfDebugger>

The -D flag starts the MATLAB environment within the specified debugger. For example, to use
the gdb debugging tool on the Linux® platform, enter this command.

matlab -Dgdb
2 Once the debugger has loaded, continue loading the MATLAB environment by typing run at the
debugger prompt (gdb).

run -nodesktop

Starting program: matlab


...

Note The debugger might stop on spurious segmentation violation signals that result from
interactions with the underlying Java® Virtual Machine (JVM™). You can ignore these messages
and continue, using the cont command. If you are not debugging segmentation violation signals
and want to suppress these messages, enter the command handle SIGSEGV nostop noprint
pass.
3 Open the sfcndemo_timestwo Simulink model.
4 Press Ctrl+C to open the debugger.
5 At the (gdb) prompt, set breakpoints in the source code, for example:

break timestwo.c:37

Breakpoint 1 (timestwo.c:37) pending


(gdb)
6 At the (gdb) prompt, enter the cont command to continue.

cont
7 Use your debugger routines to debug the S-function. For more information, see the gdb
documentation that is part of your operating system documentation.

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

4-65
4 Writing S-Functions in C

More About
• “Check S-Functions Using S-Function Analyzer APIs” on page 7-2

4-66
Convert Level-1 C MEX S-Functions

Convert Level-1 C MEX S-Functions


In this section...
“Guidelines for Converting Level-1 C MEX S-Functions to Level-2” on page 4-67
“Obsolete Macros” on page 4-69

Guidelines for Converting Level-1 C MEX S-Functions to Level-2


Level-2 S-functions were introduced with Simulink version 2.2. Level-1 S-functions refer to S-
functions that were written to work with Simulink version 2.1 and previous releases. Level-1 S-
functions are compatible with Simulink version 2.2 and subsequent releases; you can use them in new
models without making any code changes. However, to take advantage of new features in S-functions,
Level-1 S-functions must be updated to Level-2 S-functions. Here are some guidelines:

• Start by looking at simulink/src/sfunctmpl_doc.c. This template S-function file concisely


summarizes Level-2 S-functions.
• At the top of your S-function file, add this define:

#define S_FUNCTION_LEVEL 2
• Update the contents of mdlInitializeSizes. In particular, add the following error handling for
the number of S-function parameters:
ssSetNumSFcnParams(S, NPARAMS); /*Number of expected parameters*/
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
/* Return if number of expected != number of actual parameters */
return;
}
Set up the inputs using:
if (!ssSetNumInputPorts(S, 1)) return; /*Number of input ports */
ssSetInputPortWidth(S, 0, width); /* Width of input
port one (index 0)*/
ssSetInputPortDirectFeedThrough(S, 0, 1); /* Direct feedthrough
or port one */
ssSetInputPortRequiredContiguous(S, 0);
Set up the outputs using:
if (!ssSetNumOutputPorts(S, 1)) return;
ssSetOutputPortWidth(S, 0, width); /* Width of output port
one (index 0) */

• If your S-function has a nonempty mdlInitializeConditions, update it to the following form:

#define MDL_INITIALIZE_CONDITIONS
static void mdlInitializeConditions(SimStruct *S)
{
}

Otherwise, delete the function.

• Access the continuous states using ssGetContStates. The ssGetX macro has been removed.
• Access the discrete states using ssGetRealDiscStates(S). The ssGetX macro has been
removed.
• For mixed continuous and discrete state S-functions, the state vector no longer consists of the
continuous states followed by the discrete states. The states are saved in separate vectors and
hence might not be contiguous in memory.
• The mdlOutputs prototype has changed from

4-67
4 Writing S-Functions in C

static void mdlOutputs( real_T *y, const real_T *x,


const real_T *u, SimStruct *S, int_T tid)

to
static void mdlOutputs(SimStruct *S, int_T tid)

Since y, x, and u are not explicitly passed in to Level-2 S-functions, you must use

• ssGetInputPortSignal to access inputs


• ssGetOutputPortSignal to access the outputs
• ssGetContStates or ssGetRealDiscStates to access the states
• The mdlUpdate function prototype has changed from
void mdlUpdate(real_T *x, real_T *u, Simstruct *S, int_T tid)

to
void mdlUpdate(SimStruct *S, int_T tid)
• If your S-function has a nonempty mdlUpdate, update it to this form:
#define MDL_UPDATE
static void mdlUpdate(SimStruct *S, int_T tid)
{
}

Otherwise, delete the function.


• If your S-function has a nonempty mdlDerivatives, update it to this form:
#define MDL_DERIVATIVES
static void mdlDerivatives(SimStruct *S)
{
}

Otherwise, delete the function.


• Replace all obsolete SimStruct macros. See “Obsolete Macros” on page 4-69 for a complete list
of obsolete macros.
• When converting Level-1 S-functions to Level-2 S-functions, you should build your S-functions
with full (i.e., highest) warning levels. For example, if you have gcc on a UNIX1 system, use these
options with the mex utility.
mex CC=gcc CFLAGS=-Wall sfcn.c

If your system has Lint, use this code.


lint -DMATLAB_MEX_FILE -I<matlabroot>/simulink/include
-Imatlabroot/extern/include sfcn.c

On a PC, to use the highest warning levels, you must create a project file inside the integrated
development environment (IDE) for the compiler you are using. Within the project file, define
MATLAB_MEX_FILE and add
matlabroot/simulink/include
matlabroot/extern/include

1. UNIX is a registered trademark of The Open Group in the United States and other countries.

4-68
Convert Level-1 C MEX S-Functions

to the path (be sure to build with alignment set to 8).

Obsolete Macros
The following macros are obsolete. Replace each obsolete macro with the macro specified in the
following table.

Obsolete Macro Replace with


ssGetU(S), ssGetUPtrs(S) ssGetInputPortSignalPtrs(S,port),
ssGetInputPortSignal(S,port)
ssGetY(S) ssGetOutputPortRealSignal(S,port)
ssGetX(S) ssGetContStates(S), ssGetRealDiscStates(S)
ssGetStatus(S) Normally not used, but ssGetErrorStatus(S) is
available.
ssSetStatus(S,msg) ssSetErrorStatus(S,msg)
ssGetSizes(S) Specific call for the wanted item (i.e.,
ssGetNumContStates(S))
ssGetMinStepSize(S) No longer supported
ssGetPresentTimeEvent(S,sti) ssGetTaskTime(S,sti)
ssGetSampleTimeEvent(S,sti) ssGetSampleTime(S,sti)
ssSetSampleTimeEvent(S,t) ssSetSampleTime(S,sti,t)
ssGetOffsetTimeEvent(S,sti) ssGetOffsetTime(S,sti)
ssSetOffsetTimeEvent(S,sti,t) ssSetOffsetTime(S,sti,t)
ssIsSampleHitEvent(S,sti,tid) ssIsSampleHit(S,sti,tid)
ssGetNumInputArgs(S) ssGetNumSFcnParams(S)
ssSetNumInputArgs(S, numInputArgs) ssSetNumSFcnParams(S,numInputArgs)
ssGetNumArgs(S) ssGetSFcnParamsCount(S)
ssGetArg(S,argNum) ssGetSFcnParam(S,argNum)
ssGetNumInputs ssGetNumInputPorts(S) and
ssGetInputPortWidth(S,port)
ssSetNumInputs ssSetNumInputPorts(S,nInputPorts) and
ssSetInputPortWidth(S,port,val)
ssGetNumOutputs ssGetNumOutputPorts(S) and
ssGetOutputPortWidth(S,port)
ssSetNumOutputs ssSetNumOutputPorts(S,nOutputPorts) and
ssSetOutputPortWidth(S,port,val)

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “Create a Basic C MEX S-Function” on page 4-2

4-69
5

Creating C++ S-Functions

The procedure for creating C++ S-functions is nearly the same as that for creating C S-functions. The
following sections explain the differences.

• “Create C++ Source File” on page 5-2


• “Make C++ Objects Persistent” on page 5-3
• “Build C++ S-Functions” on page 5-4
5 Creating C++ S-Functions

Create C++ Source File


To create a C++ S-function from a C S-function, see “C++ References” on page 5-2.

In addition, set up the MEX function to use a C++ compiler (see “Build C MEX Function”). To build
the C++ S-function, see “Build C++ S-Functions” on page 5-4.

C++ References

[1] Meyers, S., More Effective C++, Boston, Addison-Wesley, 1996, Item 34

[2] Oualline, S., Practical C++ Programming, Sebastopol, California, O'Reilly, 1995, Chapter 27

[3] Stroustrup, B., The C++ Programming Language, 3rd Ed., Boston, Addison-Wesley, 1997,
Appendix B

5-2
Make C++ Objects Persistent

Make C++ Objects Persistent


Your C++ callback methods might need to create persistent C++ objects, that is, objects that
continue to exist after the method exits. For example, a callback method might need to access an
object created during a previous invocation. Or one callback method might need to access an object
created by another callback method. To create persistent C++ objects in your S-function:

1 Create a pointer work vector to hold pointers to the persistent object between method
invocations:
static void mdlInitializeSizes(SimStruct *S)
{
...
ssSetNumPWork(S, 1); // reserve element in the pointers vector
// to store a C++ object
...
}
2 Store a pointer to each object that you want to be persistent in the pointer work vector:
static void mdlStart(SimStruct *S)
{
ssGetPWork(S)[0] = (void *) new counter; // store new C++ object in the
} // pointers vector
3 Retrieve the pointer in any subsequent method invocation to access the object:
static void mdlOutputs(SimStruct *S, int_T tid)
{
counter *c = (counter *) ssGetPWork(S)[0]; // retrieve C++ object from
real_T *y = ssGetOutputPortRealSignal(S,0); // the pointers vector and
y[0] = c->output(); // use member functions of
} // the object
4 Destroy the objects when the simulation terminates:
static void mdlTerminate(SimStruct *S)
{
counter *c = (counter *) ssGetPWork(S)[0]; // retrieve and destroy C++
delete c; // object in the termination
} // function

5-3
5 Creating C++ S-Functions

Build C++ S-Functions


Use the mex command to build C++ S-functions exactly the way you use it to build C S-functions. For
example, to build the C++ version of the sfun_counter_cpp.cpp file, enter

mex sfun_counter_cpp.cpp

at the MATLAB command prompt.

Note The extension of the source file for a C++ S-function must be .cpp to ensure that the compiler
treats the contents of the file as C++ code.

5-4
6

Creating Fortran S-Functions

• “Create Level-2 Fortran S-Functions” on page 6-2


• “Port Legacy Code” on page 6-9
6 Creating Fortran S-Functions

Create Level-2 Fortran S-Functions


In this section...
“About Creating Level-2 Fortran S-Functions” on page 6-2
“Template File” on page 6-2
“C/Fortran Interfacing Tips” on page 6-2
“Constructing the Gateway” on page 6-5
“Example C MEX S-Function Calling Fortran Code” on page 6-7

About Creating Level-2 Fortran S-Functions


To use the features of a Level-2 S-function with Fortran code, you must write a skeleton S-function in
C that has code for interfacing to the Simulink software and also calls your Fortran code.

Using the C MEX S-function as a gateway is quite simple if you are writing the Fortran code from
scratch. If instead you have legacy Fortran code that exists as a standalone simulation, there is some
work to be done to identify parts of the code that need to be registered with the Simulink software,
such as identifying continuous states if you are using variable-step solvers or getting rid of static
variables if you want to have multiple copies of the S-function in a Simulink model (see “Port Legacy
Code” on page 6-9).

Template File
The file sfuntmpl_gate_fortran.c contains a template for creating a C MEX-file S-function that
invokes a Fortran subroutine in its mdlOutputs method. It works with a simple Fortran subroutine if
you modify the Fortran subroutine name in the code. The template allocates DWork vectors to store
the data that communicates with the Fortran subroutine. See “How to Use DWork Vectors” on page 8-
5 for information on setting up DWork vectors.

C/Fortran Interfacing Tips


The following are some tips for creating the C-to-Fortran gateway S-function.

MEX Environment

mex -setup needs to find the MATLAB, C, and the Fortran compilers, but it can work with only one
of these compilers at a time. If you change compilers, you must run mex -setup between other mex
commands.

Test the installation and setup using sample MEX-files from the MATLAB, C, and Fortran MEX
examples in the folder matlabroot/extern/examples/mex (open), as well as S-function examples.

If using a C compiler on a Microsoft Windows platform, test the mex setup using the following
commands and the example C source code file, yprime.c, in matlabroot/extern/examples/mex.

cd(fullfile(matlabroot,'\extern\examples\mex'))
mex yprime.c

If using a Fortran compiler, test the mex setup using the following commands and the example
Fortran source code files, yprime.F and yprimefg.F, in matlabroot/extern/examples/mex.

6-2
Create Level-2 Fortran S-Functions

cd(fullfile(matlabroot,'\extern\examples\mex'))
mex yprimef.f yprimefg.f

For more information, see “Build C MEX Function”.

Compiler Compatibility

Your C and Fortran compilers need to use the same object format. If you use the compilers explicitly
supported by the mex command this is not a problem. When you use the C gateway to Fortran, it is
possible to use Fortran compilers not supported by the mex command, but only if the object file
format is compatible with the C compiler format. Common object formats include ELF and COFF.

The compiler must also be configurable so that the caller cleans up the stack instead of the callee.
Intel® Visual Fortran (the replacement for Compaq® Visual Fortran) has the default stack cleanup as
the caller.

Symbol Decorations

Symbol decorations can cause run-time errors. For example, g77 decorates subroutine names with a
trailing underscore when in its default configuration. You can either recognize this and adjust the C
function prototype or alter the Fortran compiler's name decoration policy via command-line switches,
if the compiler supports this. See the Fortran compiler manual about altering symbol decoration
policies.

If all else fails, use utilities such as od (octal dump) to display the symbol names. For example, the
command
od -s 2 <file>

lists character vectors and symbols in binary (.obj) files.

These binary utilities can be obtained for the Windows platform as well. The MKS, Inc. company
provides commercial versions of powerful utilities for The Open Group UNIX platforms. Additional
utilities can also be obtained free on the Web. hexdump is another common program for viewing
binary files. As an example, here is the output of
od -s 2 sfun_atmos_for.o

on a Linux platform.
0000115 E¨
0000136 E¨
0000271 E¨"
0000467 ˙E¨@
0000530 ˙E¨
0000575 E¨ E 5@
0001267 CfƒVC- :C
0001323 :|.-:8˘#8 Kw6
0001353 ?333@
0001364 333
0001414 01.01
0001425 GCC: (GNU) egcs-2.91.66 19990314/
0001522 .symtab
0001532 .strtab
0001542 .shstrtab
0001554 .text
0001562 .rel.text

6-3
6 Creating Fortran S-Functions

0001574 .data
0001602 .bss
0001607 .note
0001615 .comment
0003071 sfun_atmos_for.for
0003101 gcc2_compiled.
0003120 rearth.0
0003131 gmr.1
0003137 htab.2
0003146 ttab.3
0003155 ptab.4
0003164 gtab.5
0003173 atmos_
0003207 exp
0003213 pow_d

Note that Atmos has been changed to atmos_, which the C program must call to be successful.

With Visual Fortran on 32-bit Windows machines, the symbol is suppressed, so that Atmos becomes
ATMOS (no underscore).

Fortran Math Library

Fortran math library symbols might not match C math library symbols. For example, A^B in Fortran
calls library function pow_dd, which is not in the C math library. In these cases, you must tell mex to
link in the Fortran math library. For gcc environments, these routines are usually found in /usr/
local/lib/libf2c.a, /usr/lib/libf2c.a, or equivalent.

The mex command becomes


mex -L/usr/local/lib -lf2c cmex_c_file fortran_object_file

Note On a UNIX system, the -lf2c option follows the conventional UNIX library linking syntax,
where -l is the library option itself and f2c is the unique part of the library file's name, libf2c.a.
Be sure to use the -L option for the library search path, because -I is only followed while searching
for include files.

The f2c package can be obtained for the Windows and UNIX environments from the Internet. The file
libf2c.a is usually part of g77 distributions, or else the file is not needed as the symbols match. In
obscure cases, it must be installed separately, but even this is not difficult once the need for it is
identified.

On 32-bit Windows machines, using Microsoft Visual C++ and Intel Visual Fortran 10.1, this example
can be compiled using the following two mex commands. Enter each command on one line. The mex
-setup C command must be run to return to the C compiler before executing the second command.
In the second command, replace the variable IFORT_COMPILER10 with the name of the system's
environment variable pointing to the Visual Fortran 10.1 root folder on your system.
mex -v -c fullfile(matlabroot,'toolbox','simulink','simdemos','simfeatures',
'srcFortran','sfun_atmos_sub.F'), -f fullfile(matlabroot,'bin','win32',
'mexopts','intelf10msvs2005opts.bat'))

!mex -v -L"%IFORT_COMPILER10%\IA32\LIB" -llibifcoremd -lifconsol


-lifportmd -llibmmd -llibirc sfun_atmos.c sfun_atmos_sub.obj

On 64-bit Windows machines, using Visual C++ and Visual Fortran 10.1, this example can be
compiled using the following two mex commands (each command is on one line). The mex -setup C

6-4
Create Level-2 Fortran S-Functions

command must be run to return to the C compiler before executing the second command. The
variable IFORT_COMPILER10 is the name of the system's environment variable pointing to the Visual
Fortran 10.1 root folder and may vary on your system. Replace matlabroot with the path name to
your MATLAB root folder.
mex -v -c fullfile(matlabroot,'toolbox','simulink','simdemos','simfeatures',
'srcFortran','sfun_atmos_sub.F'), -f fullfile(matlabroot,'bin','win64','mexopts',
'intelf10msvs2005opts.bat'))

!mex -v -L"%IFORT_COMPILER10%\EM64T\LIB" -llibifcoremd -lifconsol


-lifportmd -llibmmd -llibirc sfun_atmos.c sfun_atmos_sub.obj

CFortran

Or you can try using CFortran to create an interface. CFortran is a tool for automated interface
generation between C and Fortran modules, in either direction. Search the Web for cfortran or visit

http://www-zeus.desy.de/~burow/cfortran/

for downloading.

Choosing a Fortran Compiler

On a Windows machine, using Visual C++ with Fortran is best done with Visual Fortran 10.1.

For an up-to-date list of all the supported compilers, see the MathWorks supported and compatible
compiler list at:

https://www.mathworks.com/support/compilers/current_release/

Constructing the Gateway


The mdlInitializeSizes and mdlInitializeSampleTimes methods are coded in C. It is
unlikely that you will need to call Fortran routines from these S-function methods. In the simplest
case, the Fortran is called only from mdlOutputs.

Simple Case

The Fortran code must at least be callable in one-step-at-a-time fashion. If the code doesn't have any
states, it can be called from mdlOutputs and no mdlDerivatives or mdlUpdate method is
required.

Code with States

If the code has states, you must decide whether the Fortran code can support a variable-step solver
or not. For fixed-step solver only support, the C gateway consists of a call to the Fortran code from
mdlUpdate, and outputs are cached in an S-function DWork vector so that subsequent calls by the
Simulink engine into mdlOutputs will work properly and the Fortran code won't be called until the
next invocation of mdlUpdate. In this case, the states in the code can be stored however you like,
typically in the work vector or as discrete states.

If instead the code needs to have continuous time states with support for variable-step solvers, the
states must be registered and stored with the engine as doubles. You do this in
mdlInitializeSizes (registering states), then the states are retrieved and sent to the Fortran
code whenever you need to execute it. In addition, the main body of code has to be separable into a
call form that can be used by mdlDerivatives to get derivatives for the state integration and also
by the mdlOutputs and mdlUpdate methods as appropriate.

6-5
6 Creating Fortran S-Functions

Setup Code

If there is a lengthy setup calculation, it is best to make this part of the code separable from the one-
step-at-a-time code and call it from mdlStart. This can either be a separate SUBROUTINE called from
mdlStart that communicates with the rest of the code through COMMON blocks or argument I/O, or it
can be part of the same piece of Fortran code that is isolated by an IF-THEN-ELSE construct. This
construct can be triggered by one of the input arguments that tells the code if it is to perform either
the setup calculations or the one-step calculations.

SUBROUTINE Versus PROGRAM

To be able to call Fortran from the Simulink software directly without having to launch processes,
etc., you must convert a Fortran PROGRAM into a SUBROUTINE. This consists of three steps. The first
is trivial; the second and third can take a bit of examination.

1 Change the line PROGRAM to SUBROUTINE subName.

Now you can call it from C using C function syntax.


2 Identify variables that need to be inputs and outputs and put them in the SUBROUTINE argument
list or in a COMMON block.

It is customary to strip out all hard-coded cases and output dumps. In the Simulink environment,
you want to convert inputs and outputs into block I/O.
3 If you are converting a standalone simulation to work inside the Simulink environment, identify
the main loop of time integration and remove the loop and, if you want the Simulink engine to
integrate continuous states, remove any time integration code. Leave time integrations in the
code if you intend to make a discrete time (sampled) S-function.

Arguments to a SUBROUTINE

Most Fortran compilers generate SUBROUTINE code that passes arguments by reference. This means
that the C code calling the Fortran code must use only pointers in the argument list.

PROGRAM ...

becomes

SUBROUTINE somename( U, X, Y )

A SUBROUTINE never has a return value. You manage I/O by using some of the arguments for input,
the rest for output.

Arguments to a FUNCTION

A FUNCTION has a scalar return value passed by value, so a calling C program should expect this. The
argument list is passed by reference (i.e., pointers) as in the SUBROUTINE.

If the result of a calculation is an array, then you should use a subroutine, as a FUNCTION cannot
return an array.

Interfacing to COMMON Blocks

While there are several ways for Fortran COMMON blocks to be visible to C code, it is often
recommended to use an input/output argument list to a SUBROUTINE or FUNCTION. If the Fortran
code has already been written and uses COMMON blocks, it is a simple matter to write a small

6-6
Create Level-2 Fortran S-Functions

SUBROUTINE that has an input/output argument list and copies data into and out of the COMMON
block.

The procedure for copying in and out of the COMMON block begins with a write of the inputs to the
COMMON block before calling the existing SUBROUTINE. The SUBROUTINE is called, then the output
values are read out of the COMMON block and copied into the output variables just before returning.

Example C MEX S-Function Calling Fortran Code


The S-function example sfcndemo_atmos contains an example of a C MEX S-function calling a
Fortran subroutine. The Fortran subroutine Atmos is in the file sfun_atmos_sub.F. This subroutine
calculates the standard atmosphere up to 86 kilometers. The subroutine has four arguments.

SUBROUTINE Atmos(alt, sigma, delta, theta)

The gateway C MEX S-function, sfun_atmos.c, declares the Fortran subroutine.

/*
* Windows uses upper case for Fortran external symbols
*/
#ifdef _WIN32
#define atmos_ ATMOS
#endif

extern void atmos_(float *alt,


float *sigma,
float *delta,
float *theta);

The mdlOutputs method calls the Fortran subroutine using pass-by-reference for the arguments.

/* call the Fortran routine using pass-by-reference */


atmos_(&falt, &fsigma, &fdelta, &ftheta);

To see this example working in the sample model sfcndemo_atmos, enter the following command at
the MATLAB command prompt.

sfcndemo_atmos

Building Gateway C MEX S-Functions on a Windows System

On 64-bit Windows systems using Intel C++ 12.0 and Intel Visual Fortran 12, you need to use
separate commands to compile the Fortran file and then link it to the C gateway file. Each command
is on one line.

1 Run cd(matlabroot) to go to your MATLAB root.


2 Run mex -setup Fortran to select a Fortran compiler.
3 Compile the Fortran file using the following command. Enter the command on one line.
mex -v -c toolbox/simulink/simdemos/simfeatures/srcFortran/sfun_atmos_sub.F ...
-f bin/win64/mexopts/intelf12msvs2008opts.bat

4 Run mex -setup C to select a C compiler.


5 Link the compiled Fortran subroutine to the gateway C MEX S-function using the following
command. The variable IFORT_COMPILER12 is the name of the system's environment variable
pointing to the Visual Fortran 12 root folder and may vary on your system.

6-7
6 Creating Fortran S-Functions

!mex -v -L"%IFORT_COMPILER12%\IA64\LIB" -llibifcoremd -lifconsol -lifportmd ...


-llibmmd -llibirc
toolbox\simulink\simdemos\simfeatures\srcFortran\sfun_atmos.c sfun_atmos_sub.obj
mex -v -c toolbox/simulink/simdemos/simfeatures/srcFortran/sfun_atmos_sub.F
-f bin/win64/mexopts/intelf12msvs2008opts.bat

Building Gateway C MEX S-Functions on a UNIX System

Build the gateway on a UNIX system using the command

mex sfun_atmos.c sfun_atmos_sub.o

On some UNIX systems where the C and Fortran compilers were installed separately (or are not
aware of each other), you might need to reference the library libf2c.a. To do this, use the -lf2c
flag.

If the libf2c.a library is not on the library path, you need to add the path to the mex process
explicitly with the -L command. For example:

mex -L/usr/local/lib/ -lf2c sfun_atmos.c sfun_atmos_sub.o

6-8
Port Legacy Code

Port Legacy Code


In this section...
“Find the States” on page 6-9
“Sample Times” on page 6-9
“Store Data” on page 6-9
“Use Flints if Needed” on page 6-10
“Considerations for Real Time” on page 6-10

Find the States


If a variable-step solver is being used, it is critical that all continuous states are identified in the code
and put into the C S-function state vector for integration instead of being integrated by the Fortran
code. Likewise, all derivative calculations must be made available separately to be called from the
mdlDerivatives method in the C S-function. Without these steps, any Fortran code with continuous
states will not be compatible with variable-step solvers if the S-function is registered as a continuous
block with continuous states.

Telltale signs of implicit advancement are incremented variables such as M=M+1 or X=X+0.05. If the
code has many of these constructs and you determine that it is impractical to recode the source so as
not to “ratchet forward,” you might need to try another approach using fixed-step solvers.

If it is impractical to find all the implicit states and to separate out the derivative calculations for the
Simulink engine, another approach can be used, but you are limited to using fixed-step solvers. The
technique here is to call the Fortran code from the mdlUpdate method so the Fortran code is only
executed once per major simulation integration step. Any block outputs must be cached in a work
vector so that mdlOutputs can be called as often as needed and output the values from the work
vector instead of calling the Fortran routine again (causing it to inadvertently advance time). See
sfuntmpl_gate_fortran.c for an example that uses DWork vectors. See “How to Use DWork
Vectors” on page 8-5 for details on allocating data-typed work vectors.

Sample Times
If the Fortran code has an implicit step size in its algorithm, coefficients, etc., ensure that you
register the proper discrete sample time in the C S-function mdlInitializeSampleTimes method
and only change the block's output values from the mdlUpdate method.

Store Data
If you plan to have multiple copies of this S-function used in one Simulink model, you need to allocate
storage for each copy of the S-function in the model. The recommended approach is to use DWork
vectors (see “DWork Vector Basics” on page 8-2).

If you plan to have only one copy of the S-function in the model, DWork vectors still provide the most
advanced method for storing data. However, another alternative is to allocate a block of memory
using the malloc command and store the pointer to that memory in a PWork vector (see “Elementary
Work Vectors” on page 8-15). In this case, you must remember to deallocate the memory using the
free command in the S-function mdlTerminate method.

6-9
6 Creating Fortran S-Functions

Use Flints if Needed


Use flints (floating-point ints) to keep track of time. Flints (for IEEE-754 floating-point numerics)
have the useful property of not accumulating roundoff error when adding and subtracting flints.
Using flint variables in DOUBLE PRECISION storage (with integer values) avoids roundoff error
accumulation that would accumulate when floating-point numbers are added together thousands of
times.

DOUBLE PRECISION F
:
:
F = F + 1.0
TIME = 0.003 * F

This technique avoids a common pitfall in simulations.

Considerations for Real Time


Since very few Fortran applications are used in a real-time environment, it is common to come across
simulation code that is incompatible with a real-time environment. Common failures include
unbounded (or large) iterations and sporadic but time-intensive side calculations. You must deal with
these directly if you expect to run in real time.

Conversely, it is still perfectly good practice to have iterative or sporadic calculations if the generated
code is not being used for a real-time application.

6-10
7

Run S-Function Analyzer

• “Check S-Functions Using S-Function Analyzer APIs” on page 7-2


• “Troubleshoot S-Function Checks” on page 7-12
7 Run S-Function Analyzer

Check S-Functions Using S-Function Analyzer APIs


Use the S-function analyzer APIs to run quality checks on C-MEX S-functions in a model or library.
These checks identify potential problems and improvements and suggest solutions. In this tutorial,
you see how to:

• Find the S-functions in a model


• Specify the build information for S-functions
• Specify the options and run the S-function analyzer
• See and interpret the results

Prerequisites
To complete the tutorial, you need the following products:

• MATLAB
• Simulink
• Polyspace® (optional)
• C compiler – For most platforms, a default C compiler is supplied with the MATLAB installation.
For a list of supported compilers, see “Change Default Compiler”. You can also change the default
compiler using mex -setup command.

Setup the Working Environment


1 Create a local working folder, for example C:\sfa.
2 Change to the docroot\toolbox\simulink\examples folder. At the MATLAB command line,
enter:

cd(fullfile(docroot, 'toolbox', 'simulink', 'examples'))


3 Copy the listed files to your local working folder:

• ex_slexSfunctionCheckExample.slx
• external.c
• external.h
• sfcnModifyMinorStepDiscState.c
• sfcnModifyMinorStepDiscState_wrapper.c
• sfcnUpdateModifyContinuous.c
• sfcnUpdateModifyContinuous_wrapper.c
• sfcnUseExternalSrc.c
• slexBadSFcn.c
• slexBadSFcn_wrapper.c

7-2
Check S-Functions Using S-Function Analyzer APIs

Run the S-Function Checks


Open a new script from the MATLAB Editor, and save it as ex_slexSfunctionCheckScript.m. To
see the example model, double-click ex_slexSfunctionCheckExample.slx in Current Folder or
type ex_slexSfunctionCheckExample at the MATLAB command line.

Specify the Model

model = 'ex_slexSfunctionCheckExample'

Find S-Functions in the Model (Optional)

Use Simulink.sfunction.analyzer.findSfunctions method to see all the S-functions to be


analyzed in the model. This method does not find S-functions in a referenced model.

sfunctions = Simulink.sfunction.analyzer.findSfunctions(model)

Specify Build Information (Optional)

To specify the build information, such as S-function source code, external libraries, and header files,
you can use Simulink.sfunction.analyzer.BuildInfo. In this tutorial, because we have four
S-functions in the model, there are four different BuildInfo objects. For more information, see
Simulink.sfunction.Analyzer and Simulink.sfunction.analyzer.BuildInfo.

bdInfo1= Simulink.sfunction.analyzer.BuildInfo('sfcnUseExternalSrc.c',...
'ExtraSrcFileList',{'external.c'},...
'SrcPaths',{pwd},'IncPaths',{pwd});
bdInfo2= Simulink.sfunction.analyzer.BuildInfo('sfcnModifyMinorStepDiscState.c',...
'ExtraSrcFileList',{'sfcnModifyMinorStepDiscState_w
'SrcPaths',{pwd});
bdInfo3= Simulink.sfunction.analyzer.BuildInfo('sfcnUpdateModifyContinuous.c',...
'ExtraSrcFileList',{'sfcnUpdateModifyContinuous_wra
'SrcPaths',{pwd});
bdInfo4= Simulink.sfunction.analyzer.BuildInfo('slexBadSFcn.c',...
'ExtraSrcFileList',{'slexBadSFcn_wrapper.c'},...
'SrcPaths',{pwd});

Specify Options (Optional)

You can configure the options for executing the S-function analyzer using
Simulink.sfunction.analyzer.Options class. You can enable Polyspace Code Prover™ check
and parameter robustness check, set the maximum model simulation time, and set the report path. If
you do not use the class to specify any of the options, the default options are applied to the analysis.
See Simulink.sfunction.analyzer.Options for more details.

Note Running Polyspace Code Prover and parameter robustness checks take some time.

Performing Polyspace Code Prover checks requires a Polyspace license. For more information on
using Polyspace checks in the S-function analyzer, see “Enable Polyspace Checks” on page 7-9. For
this tutorial, parameter robustness checks are turned on.

opts = Simulink.sfunction.analyzer.Options();
opts.EnableRobustness = 1;

7-3
7 Run S-Function Analyzer

Run Checks and See Results

Run the S-function analyzer checks using Simulink.sfunction.Analyzer.run. Then use the
Simulink.sfunction.Analyzer.generateReport to see the issues in your model or code.

sfunAnalyzer = Simulink.sfunction.Analyzer(model,'BuildInfo',{bdInfo1,bdInfo2,bdInfo3,bdInfo4},'O
analysisResult=sfunAnalyzer.run();
sfunAnalyzer.generateReport()

When applied, the generateReport method produces a struct and an HTML report of the result of
S-function analyzer checks.

analysisResult =

struct with fields:

TimeGenerated: '13-Jul-2017 13:22:37'


Platform: 'win64'
Release: '(R2017b)'
SimulinkVersion: '9.0'
ExemptedBlocks: {}
MexConfiguration: [1×1 mex.CompilerConfiguration]
Data: [4×4 struct]

Explore and Fix Problems


The example model has two warnings in Source Code Checks and four issues on S-function
MEX-file checks. In general, warnings are not as significant as fails, but they are good sources to
obtain further information about your S-functions.

7-4
Check S-Functions Using S-Function Analyzer APIs

This issue has the description code MinorStepModifyDiscreteStates. This error description
indicates that block's discrete states is modified at a minor step in mdlOutputs. To fix this issue,
discrete states should only be modified at a major step guarded by ssIsMajorTimeStep.

Original Code Modified Code


MinorStepDiscState_Outputs_wrapper(u0, y0, xC);
if (ssGetT(S)>0.5) {
if (ssGetT(S)>0.5) { if (ssIsMajorTimeStep(S)) {
xD[0] = xD[0]+1; xD[0] = xD[0]+1;
xD[1] = xD[0]+xD[1]*2.0; xD[1] = xD[0]+xD[1]*2.0;
} }
}
2

The MEX Compile Check description code indicates that there is an unused variable in the line
indicated on the report. This warning is eliminated by deleting the line 208 and rerunning the
code.

Original Code Modified Code


static void mdlOutputs(SimStruct *S, int_T
static
tid)void mdlOutputs(SimStruct *S, int_T tid)
{ {
const real_T *u0 = (const real_T*) ssGetInputPortSignal(S,0);
// const real_T *u0 = (const real_T*) ssGetInp
real_T *y0 = (real_T *)ssGetOutputPortRealSignal(S,0);
real_T *y0 = (real_T *)ssGetOutputPort
real_T *xC = ssGetContStates(S); real_T *xC = ssGetContStates(S);

*y0 = *xC; *y0 = *xC;


} }

7-5
7 Run S-Function Analyzer

The description code MdlUpdateModifyContinuousStates indicates that in the S-function


source code (in this case, in the source code of block S-Function1), the continuous states are
modified in its mdlUpdate method. You can only change the states of a block at a major time step
using ssSetSolverNeedsReset macro.

Original Code Modified Code


#define MDL_UPDATE #define MDL_UPDATE
static void mdlUpdate(SimStruct *S, int_T
static
tid) void mdlUpdate(SimStruct *S, int_T tid)
{ {
real_T *xC = ssGetContStates(S); real_T *xC = ssGetContStates(S);
*xC = *xC + 1.0; // Modify continuous states
} if (ssIsMajorTimeStep(S)) {
*xC = *xC + 1.0;
ssSetSolverNeedsReset(S);
}
}

The description code MEX Compile Check indicates that variables outputDimsInfo and
inputDimsInfo are not used in the source code. You can fix this by commenting or deleting the
lines that contain these variables.

Original Code Modified Code


static void mdlInitializeSizes(SimStructstatic
*S) void mdlInitializeSizes(SimStruct *S)
{ {
DECL_AND_INIT_DIMSINFO(inputDimsInfo); // DECL_AND_INIT_DIMSINFO(inputDimsInfo);
DECL_AND_INIT_DIMSINFO(outputDimsInfo); // DECL_AND_INIT_DIMSINFO(outputDimsInfo);
ssSetNumSFcnParams(S, NPARAMS); ssSetNumSFcnParams(S, NPARAMS);

7-6
Check S-Functions Using S-Function Analyzer APIs

The description code CombinedMdlOutputsMdlUpdateWithDiscreteState indicates that the


S-function has discrete states, and you need to use MdlUpdate and MdlOutputs methods in
your code separately. As a solution for this description code, define a separate mdlUpdate to
change states in your S-function.

#define MDL_UPDATE
static void mdlUpdate(SimStruct *S, int_T tid)
{
/* update the discrete states here! */
real_T *xD = ssGetDiscStates(S);
}
6

The description code DeclareCanBeConditionalExecWithState indicates that you have


state-like data or if you are using multiple sample times in your model, you cannot use
SS_OPTION_CAN_BE_CALLED_CONDITIONALLY option in your S-function source code.

To fix the issue in this particular example, delete the ssSetOptions function.

7-7
7 Run S-Function Analyzer

Original Code Modified Code


static void mdlInitializeSizes(SimStructstatic
*S) void mdlInitializeSizes(SimStruct *S)
{ {
DECL_AND_INIT_DIMSINFO(inputDimsInfo); DECL_AND_INIT_DIMSINFO(inputDimsInfo);
DECL_AND_INIT_DIMSINFO(outputDimsInfo); DECL_AND_INIT_DIMSINFO(outputDimsInfo);
ssSetNumSFcnParams(S, NPARAMS); ssSetNumSFcnParams(S, NPARAMS);
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S))
if (ssGetNumSFcnParams(S)
{ != ssGetSFcnParamsC
return; /* Parameter mismatch will be reported
return; by
/* Simulink
Parameter*/mismatch will be reporte
} }

ssSetNumContStates(S, NUM_CONT_STATES); ssSetNumContStates(S, NUM_CONT_STATES);


ssSetNumDiscStates(S, NUM_DISC_STATES); ssSetNumDiscStates(S, NUM_DISC_STATES);

if (!ssSetNumInputPorts(S, NUM_INPUTS)) if
return;
(!ssSetNumInputPorts(S, NUM_INPUTS)) return
ssSetInputPortWidth(S, 0, INPUT_0_WIDTH);
ssSetInputPortWidth(S, 0, INPUT_0_WIDTH);
ssSetInputPortDataType(S, 0, SS_DOUBLE);ssSetInputPortDataType(S, 0, SS_DOUBLE);
ssSetInputPortComplexSignal(S, 0, INPUT_0_COMPLEX);
ssSetInputPortComplexSignal(S, 0, INPUT_0_COMP
ssSetInputPortDirectFeedThrough(S, 0, INPUT_0_FEEDTHROUGH);
ssSetInputPortDirectFeedThrough(S, 0, INPUT_0_
ssSetInputPortRequiredContiguous(S, 0, 1);
ssSetInputPortRequiredContiguous(S,
/*direct input signal access*/ 0, 1); /*d

if (!ssSetNumOutputPorts(S, NUM_OUTPUTS))
if return;
(!ssSetNumOutputPorts(S, NUM_OUTPUTS)) retu
ssSetOutputPortWidth(S, 0, OUTPUT_0_WIDTH);
ssSetOutputPortWidth(S, 0, OUTPUT_0_WIDTH);
ssSetOutputPortDataType(S, 0, SS_DOUBLE);
ssSetOutputPortDataType(S, 0, SS_DOUBLE);
ssSetOutputPortComplexSignal(S, 0, OUTPUT_0_COMPLEX);
ssSetOutputPortComplexSignal(S, 0, OUTPUT_0_CO
ssSetNumSampleTimes(S, 1); ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0); ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0); ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0); ssSetNumPWork(S, 0);
ssSetNumModes(S, 0); ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0); ssSetNumNonsampledZCs(S, 0);

}
ssSetOptions(S, SS_OPTION_CAN_BE_CALLED_CONDITIONALLY);
}

When you fix all issues in your model, the report shows green check marks for each group of checks.

7-8
Check S-Functions Using S-Function Analyzer APIs

Enable Undocumented API Checks

You can check for the use of undocumented APIs in your S-function source code to avoid any
incompatibilities in the future releases. To enable this check, in the
Simulink.sfunction.analyzer.Options object, set EnableUsePublishedOnly to 1. Alternatively, when
building your MEX files, use -DUSE_PUBLISHED_ONLY option. For example, try building
sfcnModifyMinorStepDiscState.c with this option using:

mex sfcnModifyMinorStepDiscState.c -DUSE_PUBLISHED_ONLY

Enable Polyspace Checks

S-function analyzer gives you the option to run Polyspace Code Prover checks on your code. To enable
the check, in the Simulink.sfunction.analyzer.Options object, set EnablePolyspace to 1. Polyspace
Code Prover divides checks into red, green, orange, and gray checks. For more information on types
of checks, see “Code Prover Result and Source Code Colors” (Polyspace Code Prover).

In the S-function analyzer, the most important error code is red. If the S-function source code fails
execution in all paths, Polyspace Code Prover gives a red check error. For more information, see
“Interpret Code Prover Results in Polyspace Desktop User Interface” (Polyspace Code Prover). Here
is an example on how to troubleshoot the red check error.

The red Polyspace Code Prover check indicates that there is a problem in your S-function source
code. To investigate the issue using Polyspace, click the hyperlink in the report. This link
automatically opens a Polyspace Project window. In the Results List pane, expand Red Check and
select the error. The source file opens in the Source window. From this window, you can fix and save
your code the same way as you do in the MATLAB Editor.

7-9
7 Run S-Function Analyzer

The Polyspace Code Prover error code indicates a problem with a pointer. Pointer *p is out of bounds
because it is used in an earlier loop. Correct the error by replacing this pointer with a variable of
your choice, or deleting *p from this line of code.

Original Code Modified Code


if (return_val < 3) if (return_val < 3)
{ {
tmp = *p+5; /* Out of bounds */ tmp = 5;
tmp++; tmp++;
return_val = 10; return_val = 10;
} }

See Also
Simulink.sfunction.Analyzer | Simulink.sfunction.analyzer.BuildInfo |
Simulink.sfunction.analyzer.Options | findSfunctions

See Also

More About
• “Troubleshoot S-Function Checks” on page 7-12

7-10
Check S-Functions Using S-Function Analyzer APIs

See Also

Related Examples
• “Run Quality Checks on S-Functions”

7-11
7 Run S-Function Analyzer

Troubleshoot S-Function Checks


To help you identify and troubleshoot issues with your S-function model and source code, S-function
checks use S-function analyzer APIs to help you detect potential S-function issues.

Environment Checks
Environment checks inspect your environment for S-function analyzer APIs. This check includes the
MEX compiler setup check.

MEX Setup Check

This check indicated the presence of the MEX compiler in the current machine. A default C compiler is
included in your MATLAB installation. To see the supported list of compilers, see “Change Default
Compiler”. You can change the default compiler by typing mex -setup at the command line.

Source Code Checks


These checks inspect the source code for S-functions. These checks include the MEX compile check
and Polyspace Code Prover check.

MEX Compile Check

The MEX compile check uses MEX compilers to identify compiler warnings and errors in the S-function
source code.

Polyspace Code Prover Check

If you have a Polyspace license, this check uses Polyspace Code Prover to conduct code analysis. See
“Polyspace Verification” (Polyspace Code Prover) for more information.

S-Function MEX File Checks


This set of checks inspects the semantics of S-function MEX-file.

MdlOutputsModifyContinuousStates

This check indicates that the continuous states of the S-function are modified in the mdlOutputs
method. Continuous states can only be modified at a major time step and requires the
ssSetSolverNeedsReset macro to reset the S-function solver. Changing the S-function states
without resetting the solver can lead to unexpected results.

MdlUpdateModifyContinuousStates

This check indicates whether the continuous states of the S-function are modified in the mdlUpdate
method. Continuous states can only be modified at a major time step and requires the
ssSetSolverNeedsReset macro to reset the S-function solver. Changing the S-function states
without resetting the solver can lead to unexpected results.

7-12
Troubleshoot S-Function Checks

MinorStepModifyDiscreteStates

This check indicates whether the discrete states of the S-function are modified in its mdlOutputs
method at a minor step. Discrete states of an S-function can only be modified at a major step,
guarded by sslsMajorTimeStep.

MinorStepModifyModes

This check investigates whether the mode vector of the S-function is modified in the mdlOutputs at
a minor step. Mode step of the S-function can only be modified at a major time step, guarded by
ssIsMajorTimeStep function.

GlobalStaticAsInvisibleState

This check indicates whether the S-function is using static or global variables to represent internal
states. To avoid unexpected behavior that results from having the S-function in multiple S-function
blocks, declare the S-function states explicitly using ssSetNumDiscreteStates or using data store
memory function APIs.

ContinuousStateWithoutContinuousSampleTime

This check inspects whether the S-function continuous sample time is explicitly declared when it has
continuous states. You can use ssSetSampleTime to specify continuous sample time when S-
function has continuous states.

CombinedMdlOutputsMdlUpdateWithDiscreteState

This check inspects whether the S-function mdlUpdate and mdlOutputs methods are combined
when the S-function has discrete states. When the S-function has discrete states, define mdlUpdate
and mdlOutputs methods separately and modify discrete states only in the mdlUpdate method.

DeclareCanBeConditionalExecWithState

This check inspects whether the S-function sets the SS_OPTION_CAN_BE_CALLED_CONDITIONALLY


option when it has state-like data or multiple sample times. This option could cause the Simulink
engine to move the S-functions into a conditionally executed mode, such as a conditionally executed
subsystem. Remove this option when you have state-like or multiple sample times.

TestHarnessCreationError

This error code indicates that the S-function analyzer encounters an error when creating test harness
for the input S-functions in a Simulink library. Possible reasons could be a missing .mex file or mask
parameter definitions.

ModelCompileError

The S-function analyzer encounters error when compiling the input model. You can try to recompile
the model and check the diagnostic viewer for more information.

Input Parameter Robustness Check


This check inspects the robustness of S-functions by specifying incompatible number and type of
parameters with certain random values. This check could cause MATLAB to crash. To fix this issue,
follow the reproduction steps when you relaunch S-function checks and check the S-function
parameter data types and values.

7-13
7 Run S-Function Analyzer

See Also
Simulink.sfunction.Analyzer | Simulink.sfunction.analyzer.BuildInfo |
Simulink.sfunction.analyzer.Options | findSfunctions

More About
• “Check S-Functions Using S-Function Analyzer APIs” on page 7-2

See Also

Related Examples
• “Run Quality Checks on S-Functions”

7-14
8

Using Work Vectors

• “DWork Vector Basics” on page 8-2


• “Types of DWork Vectors” on page 8-4
• “How to Use DWork Vectors” on page 8-5
• “DWork Vector Examples” on page 8-9
• “Elementary Work Vectors” on page 8-15
• “Memory Allocation” on page 8-21
8 Using Work Vectors

DWork Vector Basics


In this section...
“What is a DWork Vector?” on page 8-2
“DWork Vectors and the Simulink Engine” on page 8-3
“DWork Vectors and the Simulink Coder Product” on page 8-3

What is a DWork Vector?


DWork vectors are blocks of memory that an S-function asks the Simulink engine to allocate to each
instance of the S-function in a model. If multiple instances of your S-function can occur in a model,
your S-function must use DWork vectors instead of global or static memory to store instance-specific
values of S-function variables. Otherwise, your S-function runs the risk of one instance overwriting
data needed by another instance, causing a simulation to fail or produce incorrect results. The ability
to keep track of multiple instances of an S-function is called reentrancy.

You can create an S-function that is reentrant by using DWork vectors that the engine manages for
each particular instance of the S-function.

DWork vectors have several advantages:

• Provide instance-specific storage for block variables


• Support floating-point, integer, pointer, and general data types
• Eliminate static and global variables
• Interact directly with the Simulink engine to perform memory allocation, initialization, and
deallocation
• Facilitate inlining the S-function during code generation
• Provide more control over how data appears in the generated code

Note DWork vectors are the most generalized and versatile type of work vector and the following
sections focus on their use. The Simulink product provides additional elementary types of work
vectors that support floating-point, integer, pointer, and mode data. You can find a discussion of these
work vectors in “Elementary Work Vectors” on page 8-15.

DWork vectors provide the most flexibility for setting data types, names, etc., of the data in the
simulation and during code generation. The following list describes all the properties that you can set
on a DWork vector:

• Data type
• Size
• Numeric type, either real or complex
• Name
• Usage type (see “Types of DWork Vectors” on page 8-4)
• Simulink Coder identifier
• Simulink Coder storage class

8-2
DWork Vector Basics

• Simulink Coder C type qualifier

See “How to Use DWork Vectors” on page 8-5 for instructions on how to set these properties. The
three Simulink Coder properties pertain only to code generation and have no effect during
simulation.

DWork Vectors and the Simulink Engine


A key advantage of DWork vectors is their connection to the Simulink engine. Over the course of the
simulation, the engine relieves the S-function of all memory management tasks related to DWork
vectors.

To see how this connection is useful, consider an S-function that uses a global variable to store data.
If more than one copy of the S-function exists in a model, each instance of the S-function must
carefully allocate, manipulate, and deallocate each piece of memory it uses.

In an S-function that uses DWork vectors, the engine, not the S-function, manages the memory for the
DWork vector. At the start of a simulation, the engine allocates the memory required for each
instance of the S-function based on the size and the data type of the DWork vector contents. At the
end of the simulation, the engine automatically deallocates the memory.

Note You have no control over how the engine allocates memory for DWork vectors during
simulation. When using the Simulink Coder software, you can use storage classes to customize the
memory allocation during code generation. See the ssSetDWorkRTWStorageClass reference page
for more information on using storage classes.

The engine also performs special tasks based on the type of DWork vector used in the S-function. For
example, it includes DWork vectors that store discrete state information in the model-wide state
vector and makes them available during state logging.

DWork Vectors and the Simulink Coder Product


DWork vectors allow you to customize how data appears in the generated code. When code is
generated, the Simulink Coder code generator includes the DWork vector in the data structure for the
model. The DWork vector controls the field name used in the structure. DWork vectors also control
the storage class and C type qualifier used in the generated code. See sfun_rtwdwork.c for an
example.

8-3
8 Using Work Vectors

Types of DWork Vectors


All DWork vectors are S-function memory that the Simulink engine manages. The Simulink software
supports four types of DWork vectors:

• General DWork vectors contain information of any data type.


• DState vectors contain discrete state information. Information stored in a DState vector appears
as a state in the linearized model and is available during state logging.
• Scratch vectors contain values that do not need to persist from one time step to the next.
• Mode vectors contain mode information, usually stored as Boolean or integer data.

S-functions register the DWork vector type using the ssSetDWorkUsageType macro. This macro
accepts one of the four usage types described in the following table.

DWork Usage Type Functionality


General SS_DWORK_USED_AS_DWORK Store instance specific persistent data. General DWork
vectors can also be used to store discrete state and mode
data, however the Simulink engine will not treat this
information specially. You might choose to use a general
DWork vector to store state information if you want to
avoid data logging.
DState SS_DWORK_USED_AS_DSTATE Store discrete state information. Using the DState vector
instead of ssSetNumDiscStates to store discrete states
provides more flexibility for naming and data typing the
states. The engine marks blocks with discrete states as
special during sample time propagation. In addition, the
engine makes the data stored in the DState vector available
during data logging.
Mode SS_DWORK_USED_AS_MODE Indicate to the Simulink engine that the S-function contains
modes. The engine handles blocks with modes specially
when solving algebraic loops. In addition, the engine
updates an S-function with modes only at major time steps.
DWork mode vectors are more efficient than standard
mode work vectors (see “Elementary Work Vectors” on
page 8-15) because they can store mode information as
Boolean data. In addition, while an S-function has only one
mode work vectors, it can have multiple DWork vectors
configured to store modes.
Scratch SS_DWORK_USED_AS_SCRATCH Store memory that is not persistent, for example, a large
variable that you do not want to mark on the stack. Scratch
vectors are scoped to a particular S-function method (for
example, mdlOutputs) and exist across a single time step.
Scratch memory can be shared across S-function blocks.
The Simulink engine attempts to minimize the amount of
memory used by scratch variables and reuses scratch
memory whenever possible.

8-4
How to Use DWork Vectors

How to Use DWork Vectors


In this section...
“Using DWork Vectors in C MEX S-Functions” on page 8-5
“DWork Vector C MEX Macros” on page 8-7
“Using DWork Vectors With Legacy Code” on page 8-8

Using DWork Vectors in C MEX S-Functions


The following steps show how to initialize and use DWork vectors in a C MEX S-function. For a full list
of SimStruct macros pertaining to DWork vectors, see “DWork Vector C MEX Macros” on page 8-
7.

1 In mdlInitializeSizes, specify the number of DWork vectors using the ssSetNumDWork


macro. For example, to specify that the S-function contains two DWork vectors, use the command

ssSetNumDWork(S, 2);

Although the mdlInitializeSizes method tells the Simulink engine how many DWork vectors
the S-function will use, the engine does not allocate memory for the DWork vectors, at this time.

An S-function can defer specifying the number of DWork vectors until all information about the S-
function inputs is available by passing the value DYNAMICALLY_SIZED to the ssSetNumDWork
macro. If an S-function defers specifying the number of DWork vectors in
mdlInitializeSizes, it must provide a mdlSetWorkWidths method to set up the DWork
vectors.
2 If the S-function does not provide an mdlSetWorkWidths method, the mdlInitializeSizes
method sets any applicable attributes for each DWork vector. For example, the following lines
initialize the widths and data types of the DWork vectors initialized in the previous step.

ssSetDWorkWidth(S, 0, 2);
ssSetDWorkWidth(S, 1, 1);

ssSetDWorkDataType(S, 0, SS_DOUBLE);
ssSetDWorkDataType(S, 1, SS_BOOLEAN);

The following table lists attributes you can set for a DWork vector and shows an example of the
macro that sets it. See ssSetDWorkRTWStorageClass for a list of supported storage classes.

Attribute Macro
Data type ssSetDWorkDataType(S, 0, SS_DOUBLE);
Size ssSetDWorkWidth(S, 0, 2);
Name ssSetDWorkName(S, 0, "sfcnState");
Usage type ssSetDWorkUsageType(S, 0, SS_DWORK_USED_AS_DSTATE);
Numeric type, either real or ssSetDWorkComplexSignal(S, 0, COMPLEX_NO);
complex
Simulink Coder identifier ssSetDWorkRTWIdentifier(S, 0, "Gain");
Simulink Coder storage class ssSetDWorkRTWStorageClass(S, 0, 2);

8-5
8 Using Work Vectors

Attribute Macro
Simulink Coder C type qualifier ssSetDWorkRTWTypeQualifier(S, 0, "volatile");
3 In mdlStart, initialize the values of any DWork vectors that should be set only at the beginning
of the simulation. Use the ssGetDWork macro to retrieve a pointer to each DWork vector and
initialize the values. For example, the following mdlStart method initializes the first DWork
vector.
static void mdlStart(SimStruct *S)
{
real_T *x = (real_T*) ssGetDWork(S,0);

/* Initialize the first DWork vector */


x[0] = 0;
x[1] = 2;
}

The Simulink engine allocates memory for the DWork vector before calling the mdlStart
method. Because the mdlStart method is called only once at the beginning of the simulation, do
not use it for data or states that need to be reinitialized, for example, when reenabling a disabled
subsystem containing the S-function.
4 In mdlInitializeConditions, initialize the values of any DWork vectors that need to be
reinitialized at certain points in the simulation. The engine executes
mdlInitializeConditions at the beginning of the simulation and any time an enabled
subsystem containing the S-function is reenabled. See the mdlStart example in the previous
step for the commands used to initialize DWork vector values.
5 In mdlOutputs, mdlUpdate, etc., use the ssGetDWork macro to retrieve a pointer to the DWork
vector and use or update the DWork vector values. For example, for a DWork vector storing two
discrete states, the following mdlOutputs and mdlUpdate methods calculate the output and
update the discrete state values.

The S-function previously defined U(element) as (*uPtrs[element]), A, B, C, and D as the


state-space matrices for a discrete state-space system.
/* Function: mdlOutputs ==============================================
* Abstract:
* y = Cx + Du
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
if( ssGetDWorkUsageType(S, 0) == SS_DWORK_USED_AS_DSTATE) {
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *x = (real_T*) ssGetDWork(S, 0);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

UNUSED_ARG(tid); /* not used in single tasking mode */

/* y=Cx+Du */
y[0]=C[0][0]*x[0]+C[0][1]*x[1]+D[0][0]*U(0)+D[0][1]*U(1);
y[1]=C[1][0]*x[0]+C[1][1]*x[1]+D[1][0]*U(0)+D[1][1]*U(1);
}
}

#define MDL_UPDATE
/* Function: mdlUpdate ===============================================
* Abstract:
* xdot = Ax + Bu
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
real_T tempX[2] = {0.0, 0.0};

8-6
How to Use DWork Vectors

real_T *x = (real_T*) ssGetDWork(S, 0);


InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

UNUSED_ARG(tid); /* not used in single tasking mode */

/* xdot=Ax+Bu */
tempX[0]=A[0][0]*x[0]+A[0][1]*x[1]+B[0][0]*U(0)+B[0][1]*U(1);
tempX[1]=A[1][0]*x[0]+A[1][1]*x[1]+B[1][0]*U(0)+B[1][1]*U(1);

x[0]=tempX[0];
x[1]=tempX[1];
}

You do not have to include any code in the mdlTerminate method to deallocate the memory used to
store the DWork vector. Similarly, if you are generating inlined code for the S-function, you do not
have to write an mdlRTW method to access the DWork vector in the TLC file. The Simulink software
handles these aspects of the DWork vector for you.

DWork Vector C MEX Macros


The following table lists the C MEX macros pertaining to DWork vectors.

Macro Description
ssSetNumDWork Specify the number of DWork vectors.
ssGetNumDWork Query the number of DWork vectors.
ssGetDWork Get a pointer to a specific DWork vector.
ssGetDWorkComplexSignal Determine if a specific DWork vector is
real or complex.
ssGetDWorkDataType Get the data type of a DWork vector.
ssGetDWorkName Get the name of a DWork vector.
ssGetDWorkRTWIdentifier Get the identifier used to declare a
DWork vector in the generated code.
ssGetDWorkRTWIdentifierMustResolveToSignalObject Indicate if a DWork vector must resolve
to a Simulink.Signal object in the
MATLAB workspace.
ssGetDWorkRTWStorageClass Get the storage class of a DWork vector.
ssGetDWorkRTWTypeQualifier Get the C type qualifier used to declare a
DWork vector in the generated code.
ssGetDWorkUsageType Determine how a DWork vector is used in
the S-function.
ssGetDWorkUsedAsDState Determine if a DWork vector stores
discrete states.
ssGetDWorkWidth Get the size of a DWork vector.
ssSetDWorkComplexSignal Specify if the elements of a DWork vector
are real or complex.
ssSetDWorkDataType Specify the data type of a DWork vector.
ssSetDWorkName Specify the name of a DWork vector.

8-7
8 Using Work Vectors

Macro Description
ssSetDWorkRTWIdentifier Specify the identifier used to declare a
DWork vector in the generated code.
ssSetDWorkRTWIdentifierMustResolveToSignalObject Specify if a DWork vector must resolve to
a Simulink.Signal object.
ssSetDWorkRTWStorageClass Specify the storage class for a DWork
vector.
ssSetDWorkRTWTypeQualifier Specify the C type qualifier used to
declare a DWork vector in the generated
code.
ssSetDWorkUsageType Specify how a DWork vector is used in
the S-function.
ssSetDWorkUsedAsDState Specify that a DWork vector stores
discrete state values.
ssSetDWorkWidth Specify the width of a DWork vector.

Using DWork Vectors With Legacy Code


You can use DWork vectors to communicate with legacy code. If you have existing code that allocates
data structures in memory, store a pointer to those data structures in a DWork vector. Your S-function
can then communicate with the legacy code via the pointer. Alternatively, for simplicity in setting up
your S-function, you can use a pointer work vector to store the pointer. See “Elementary Work
Vectors” on page 8-15 for a description of pointer work vectors.

You can also use DWork vectors to store the state of legacy code. The template file
sfuntmpl_gate_fortran.c shows how to use DWork vectors to interact with legacy Fortran code.
The Legacy Code Tool uses DWork vectors to maintain the states of legacy C or C++ code
incorporated through the tool. See “Integrate C Functions Using Legacy Code Tool” on page 4-31 for
more information on the Legacy Code Tool.

8-8
DWork Vector Examples

DWork Vector Examples


In this section...
“General DWork Vector” on page 8-9
“DWork Scratch Vector” on page 8-10
“DState Work Vector” on page 8-11
“DWork Mode Vector” on page 8-13

General DWork Vector


The S-function sfun_rtwdwork.c shows how to configure a DWork vector for use with the Simulink
Coder product. The Simulink model sfcndemo_sfun_rtwdwork uses this S-function to implement a
simple accumulator.

The following portion of the mdlInitializeSizes method initializes the DWork vector and all code
generation properties associated with it.
ssSetNumDWork(S, 1);
ssSetDWorkWidth(S, 0, 1);
ssSetDWorkDataType(S, 0, SS_DOUBLE);

/* Identifier; free any old setting and update */


id = ssGetDWorkRTWIdentifier(S, 0);
if (id != NULL) {
free(id);
}
id = malloc(80);
mxGetString(ID_PARAM(S), id, 80);
ssSetDWorkRTWIdentifier(S, 0, id);

/* Type Qualifier; free any old setting and update */


tq = ssGetDWorkRTWTypeQualifier(S, 0);
if (tq != NULL) {
free(tq);
}
tq = malloc(80);
mxGetString(TQ_PARAM(S), tq, 80);
ssSetDWorkRTWTypeQualifier(S, 0, tq);

/* Storage class */
sc = ((int_T) *((real_T*) mxGetPr(SC_PARAM(S)))) - 1;
ssSetDWorkRTWStorageClass(S, 0, sc);

The S-function initializes the DWork vector in mdlInitializeConditions.


#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ============================
* Abstract:
* Initialize both continuous states to zero
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x = (real_T*) ssGetDWork(S,0);

/* Initialize the dwork to 0 */


x[0] = 0.0;
}

The mdlOutputs method assigns the DWork vector value to the S-function output.
/* Function: mdlOutputs ========================================
* Abstract:

8-9
8 Using Work Vectors

* y = x
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *x = (real_T*) ssGetDWork(S,0);

/* Return the current state as the output */


y[0] = x[0];
}

The mdlUpdate method increments the DWork value by the input.


#define MDL_UPDATE
/* Function: mdlUpdate ============================================
* Abstract:
* This function is called once for every major integration
* time step. Discrete states are typically updated here, but
* this function is useful for performing any tasks that should
* only take place once per integration step.
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
real_T *x = (real_T*) ssGetDWork(S,0);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

/*
* Increment the state by the input
* U is defined as U(element) (*uPtrs[element])
*/
x[0] += U(0);
}

DWork Scratch Vector


The following example uses a scratch DWork vector to store a static variable value. The
mdlInitializeSizes method configures the width and data type of the DWork vector. The
ssSetDWorkUsageType macro then specifies the DWork vector is a scratch vector.
ssSetNumDWork(S, 1);

ssSetDWorkWidth(S, 0, 1);
ssSetDWorkDataType(S, 0, SS_DOUBLE);
ssSetDWorkUsageType(S,0, SS_DWORK_USED_AS_SCRATCH);

The remainder of the S-function uses the scratch DWork vector exactly as it would any other type of
DWork vector. The InitializeConditions method sets the initial value and the mdlOutputs
method uses the value stored in the DWork vector.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ================================ */
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x = (real_T*) ssGetDWork(S,0);
/* Initialize the dwork to 0 */
x[0] = 0.0;
}
/* Function: mdlOutputs ============================================= */
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *x1 = (real_T*) ssGetDWork(S,1);

x[0] = 2000;
y[0] = x[0] * 2;
}

8-10
DWork Vector Examples

If you have Simulink Coder, the Simulink Coder software handles scratch DWork differently from
other DWork vectors when generating code for inlined S-function. To inline the S-function, create the
following Target Language Compiler (TLC) file to describe the mdlOutputs method.
%implements sfun_dscratch "C"

%% Function: Outputs ==========================================================


%%
/* dscratch Block: %<Name> */
%<LibBlockDWork(DWork[0], "", "", 0)> = 2000.0;
%<LibBlockOutputSignal(0,"","",0)> = %<LibBlockDWork(DWork[0],"","", 0)> * 2;

When the Simulink Coder software generates code for the model, it inlines the S-function and
declares the second DWork vector as a local scratch vector. For example, the model outputs function
contains the following lines:
/* local scratch DWork variables */
real_T SFunction_DWORK1;
SFunction_DWORK1 = 2000.0;

If the S-function used a general DWork vector instead of a scratch DWork vector, generating code
with the same TLC file would have resulted in the DWork vector being included in the data structure,
as follows:
sfcndemo_dscratch_DWork.SFunction_DWORK1 = 2000.0;

DState Work Vector


This example rewrites the S-function example dsfunc.c to use a DState vector instead of an explicit
discrete state vector. The mdlInitializeSizes macro initializes the number of discrete states as
zero and, instead, initializes one DWork vector.

The mdlInitializeSizes method then configures the DWork vector as a DState vector using a call
to ssSetDWorkUsedAsDState. This is equivalent to calling the ssSetDWorkUsageType macro with
the value SS_DWORK_USED_AS_DSTATE. The mdlInitializeSizes method sets the width and data
type of the DState vector and gives the state a name using ssSetDWorkName.

Note DWork vectors configured as DState vectors must be assigned a name for the Simulink engine
to register the vector as discrete states. The function
Simulink.BlockDiagram.getInitialStates(mdl) returns the assigned name in the label
field for the initial states.

static void mdlInitializeSizes(SimStruct *S)


{
ssSetNumSFcnParams(S, 0); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch reported by the Simulink engine */
}

ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 0);

if (!ssSetNumInputPorts(S, 1)) return;


ssSetInputPortWidth(S, 0, 2);
ssSetInputPortDirectFeedThrough(S, 0, 1);

if (!ssSetNumOutputPorts(S, 1)) return;


ssSetOutputPortWidth(S, 0, 2);

ssSetNumSampleTimes(S, 1);

8-11
8 Using Work Vectors

ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);

ssSetNumDWork(S, 1);
ssSetDWorkUsedAsDState(S, 0, SS_DWORK_USED_AS_DSTATE);
ssSetDWorkWidth(S, 0, 2);
ssSetDWorkDataType(S, 0, SS_DOUBLE);
ssSetDWorkName(S, 0, "SfunStates");

ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}

The mdlInitializeConditions method initializes the DState vector values using the pointer
returned by ssGetDWork.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ===============================
* Abstract:
* Initialize both discrete states to one.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x0 = (real_T*) ssGetDWork(S, 0);
int_T lp;

for (lp=0;lp<2;lp++) {
*x0++=1.0;
}
}

The mdlOutputs method then uses the values stored in the DState vector to compute the output of
the discrete state-space equation.
/* Function: mdlOutputs ========================================
* Abstract:
* y = Cx + Du
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *x = (real_T*) ssGetDWork(S, 0);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

UNUSED_ARG(tid); /* not used in single tasking mode */

/* y=Cx+Du */
y[0]=C[0][0]*x[0]+C[0][1]*x[1]+D[0][0]*U(0)+D[0][1]*U(1);
y[1]=C[1][0]*x[0]+C[1][1]*x[1]+D[1][0]*U(0)+D[1][1]*U(1);
}

Finally, the mdlUpdate method updates the DState vector with new values for the discrete states.
#define MDL_UPDATE
/* Function: mdlUpdate ============================================
* Abstract:
* xdot = Ax + Bu
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
real_T tempX[2] = {0.0, 0.0};
real_T *x = (real_T*) ssGetDWork(S, 0);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

UNUSED_ARG(tid); /* not used in single tasking mode */

/* xdot=Ax+Bu */
tempX[0]=A[0][0]*x[0]+A[0][1]*x[1]+B[0][0]*U(0)+B[0][1]*U(1);
tempX[1]=A[1][0]*x[0]+A[1][1]*x[1]+B[1][0]*U(0)+B[1][1]*U(1);

8-12
DWork Vector Examples

x[0]=tempX[0];
x[1]=tempX[1];
}

DWork Mode Vector


This example rewrites the S-function sfun_zc.c to use a DWork mode vector instead of an explicit
mode work vector (see “Elementary Work Vectors” on page 8-15 for more information on mode work
vectors). This S-function implements an absolute value block.

The mdlInitializeSizes method sets the number of DWork vectors and zero-crossing vectors (see
“Zero Crossings” on page 9-31) to DYNAMICALLY_SIZED. The DYNAMICALLY_SIZED setting allows
the Simulink engine to defer specifying the work vector sizes until it knows the dimensions of the
input, allowing the S-function to support an input with an arbitrary width.
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0);
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch reported by the Simulink engine */
}

ssSetNumContStates( S, 0);
ssSetNumDiscStates( S, 0);

if (!ssSetNumInputPorts(S, 1)) return;


ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
ssSetInputPortDirectFeedThrough(S, 0, 1);

if (!ssSetNumOutputPorts(S,1)) return;
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);

ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumDWork(S, 1);
ssSetNumModes(S, 0);

/* Initializes the zero-crossing and DWork vectors */


ssSetDWorkWidth(S,0,DYNAMICALLY_SIZED);
ssSetNumNonsampledZCs(S, DYNAMICALLY_SIZED);

/* Take care when specifying exception free code - see sfuntmpl_doc.c */


ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}

The Simulink engine initializes the number of zero-crossing vectors and DWork vectors to the number
of elements in the signal coming into the first S-function input port. The engine then calls the
mdlSetWorkWidths method, which uses ssGetNumDWork to determine how many DWork vectors
were initialized and then sets the properties for each DWork vector.
#define MDL_SET_WORK_WIDTHS
static void mdlSetWorkWidths(SimStruct *S) {
int_T numdw = ssGetNumDWork(S);
int_T i;

for (i = 0; i < numdw; i++) {


ssSetDWorkUsageType(S, i, SS_DWORK_USED_AS_MODE);
ssSetDWorkDataType(S, i, SS_BOOLEAN);
ssSetDWorkComplexSignal(S, i, COMPLEX_NO);
}
}

8-13
8 Using Work Vectors

The mdlOutputs method uses the value stored in the DWork mode vector to determine if the output
signal should be equal to the input signal or the absolute value of the input signal.
static void mdlOutputs(SimStruct *S, int_T tid)
{
int_T i;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
int_T width = ssGetOutputPortWidth(S,0);
boolean_T *mode = ssGetDWork(S,0);

UNUSED_ARG(tid); /* not used in single tasking mode */

if (ssIsMajorTimeStep(S)) {
for (i = 0; i < width; i++) {
mode[i] = (boolean_T)(*uPtrs[i] >= 0.0);
}
}

for (i = 0; i < width; i++) {


y[i] = mode[i]? (*uPtrs[i]): -(*uPtrs[i]);
}
}

8-14
Elementary Work Vectors

Elementary Work Vectors

In this section...
“Description of Elementary Work Vector” on page 8-15
“Relationship to DWork Vectors” on page 8-15
“Using Elementary Work Vectors” on page 8-16
“Additional Work Vector Macros” on page 8-17
“Elementary Work Vector Examples” on page 8-17

Description of Elementary Work Vector


In addition to DWork vectors, the Simulink software provides a simplified set of work vectors. In some
S-functions, these elementary work vectors can provide an easier solution than using DWork vectors:

• IWork vectors store integer data.


• Mode vectors model zero crossings or other features that require a single mode vector.
• PWork vectors store pointers to data structures, such as those that interface the S-function to
legacy code, another software application, or a hardware application.
• RWork vectors store floating-point (real) data.

Relationship to DWork Vectors


The following table compares each type of work vector to a DWork vector.

Work Vector Comparison to DWork Vector How to create equivalent DWork vector
Type
IWork IWork vectors cannot be ssSetNumDWork(S,1);
customized in the generated code. ssSetDWorkDataType(S, 0, SS_INT8);
Also, you are allowed only one
IWork vector.
Mode Mode vectors require more memory ssSetNumDWork(S,1);
than DWork vectors since the mode ssSetDWorkUsageType(S, 0,
vector is always stored with an SS_DWORK_USED_AS_MODE);
integer data type. Also, you are ssSetDWorkDataType(S, 0, SS_INT8);
allowed only one Mode vector.
PWork Unlike DWork vectors, PWork ssSetNumDWork(S,1);
vectors cannot be named in the ssSetDWorkDataType(S, 0, SS_POINTER);
generated code. Also, you are
The DWork vector then stores a pointer.
allowed only one PWork vector.
RWork RWork vectors cannot be ssSetNumDWork(S,1);
customized in the generated code. ssSetDWorkDataType(S, 0, SS_DOUBLE);
Also, you are allowed only one
RWork vector.

8-15
8 Using Work Vectors

Using Elementary Work Vectors


The process for using elementary work vectors is similar to that for DWork vectors (see “Using
DWork Vectors in C MEX S-Functions” on page 8-5.) The elementary work vectors have fewer
properties, so the initialization process is simpler. However, if you need to generate code for the S-
function, the S-function becomes more involved than when using DWork vectors.

The following steps show how to set up and use elementary work vectors. See “Additional Work
Vector Macros” on page 8-17 for a list of macros related to each step in the following process.

1 In mdlInitializeSizes, specify the size of the work vectors using the ssSetNumXWork
macro, for example:

ssSetNumPWork(S, 2);

This macro indicates how many elements the work vector contains, however, the Simulink engine
does not allocate memory, at this time.

An S-function can defer specifying the length of the work vectors until all information about the
S-function inputs is available by passing the value DYNAMICALLY_SIZED to the ssSetNumXWork
macro. If an S-function defers specifying the length of the work vectors in
mdlInitializeSizes, it must provide a mdlSetWorkWidths method to set up the work
vectors.

Note If an S-function uses mdlSetWorkWidths, all work vectors used in the S-function must be
set to DYNAMICALLY_SIZED in mdlInitializeSizes, even if the exact value is known before
mdlInitializeSizes is called. The sizes to be used by the S-function are than specified in
mdlSetWorkWidths.

For an example, see sfun_dynsize.c.


2 In mdlStart, assign values to the work vectors that are initialized only at the start of the
simulation. Use the ssGetXWork macro to retrieve a pointer to each work vector and use the
pointer to initialize the work vector values. Alternatively, use the ssGetXWorkValues to assign
values to particular elements of the work vector.

The Simulink engine calls the mdlStart method once at the beginning of the simulation. Before
calling this method, the engine allocates memory for the work vectors. Do not use the mdlStart
method for data that needs to be reinitialized over the course of the simulation, for example, data
that needs to be reinitialized when an enabled subsystem containing the S-function is enabled.
3 In mdlInitializeConditions, initialize the values of any work vectors that might need to be
reinitialized at certain points in the simulation. The engine executes
mdlInitializeConditions at the beginning of the simulation and any time an enabled
subsystem containing the S-function is reenabled.
4 In mdlOutputs, mdlUpdate, etc., use the ssGetXWork macro to retrieve a pointer to the work
vector and use the pointer to access or update the work vector values.
5 Write an mdlRTW method to allow the Target Language Compiler (TLC) to access the work vector.
This step is not necessary if the S-function uses DWork vectors. For information on writing
parameter data in an mdlRTW method, see ssWriteRTWParamSettings. For more information
on generating code using an mdlRTW method, see “Write Fully Inlined S-Functions with mdlRTW
Routine” (Simulink Coder).

8-16
Elementary Work Vectors

Additional Work Vector Macros


Macro Description
ssSetNumRWork Specify the width of the real work vector.
ssGetNumRWork Query the width of the real work vector.
ssSetNumIWork Specify the width of the integer work vector.
ssGetNumIWork Query the width of the integer work vector.
ssSetNumPWork Specify the width of the pointer work vector.
ssGetNumPWork Query the width of the pointer work vector.
ssSetNumModes Specify the width of the mode work vector.
ssGetNumModes Query the width of the mode work vector.
ssGetIWork Get a pointer to the integer work vector.
ssGetIWorkValue Get an element of the integer work vector.
ssGetModeVector Get a pointer to the mode work vector.
ssGetModeVectorValue Get an element of the mode work vector.
ssGetPWork Get a pointer to the pointer work vector.
ssGetPworkValue Get one element from the pointer work vector.
ssGetRWork Get a pointer to the floating-point work vector.
ssGetRWorkValue Get an element of the floating-point work vector.
ssSetIWorkValue Set the value of one element of the integer work vector.
ssSetModeVectorValue Set the value of one element of the mode work vector.
ssSetPWorkValue Set the value of one element of the pointer work vector.
ssSetRWorkValue Set the value of one element of the floating-point work vector.

Elementary Work Vector Examples


The following sections provide examples of the four types of elementary work vectors.

Pointer Work Vector

This example opens a file and stores the FILE pointer in the pointer work vector.

The following statement, included in the mdlInitializeSizes function, indicates that the pointer
work vector is to contain one element.

ssSetNumPWork(S, 1) /* pointer-work vector */

The following code uses the pointer work vector to store a FILE pointer, returned from the standard
I/O function fopen.
#define MDL_START /* Change to #undef to remove function. */
#if defined(MDL_START)
static void mdlStart(real_T *x0, SimStruct *S)
{
FILE *fPtr;
void **PWork = ssGetPWork(S);
fPtr = fopen("file.data", "r");

8-17
8 Using Work Vectors

PWork[0] = fPtr;
}
#endif /* MDL_START */

The following code retrieves the FILE pointer from the pointer work vector and passes it to fclose
in order to close the file.
static void mdlTerminate(SimStruct *S)
{
if (ssGetPWork(S) != NULL) {
FILE *fPtr;
fPtr = (FILE *) ssGetPWorkValue(S,0);
if (fPtr != NULL) {
fclose(fPtr);
}
ssSetPWorkValue(S,0,NULL);
}
}

Note Although the Simulink engine handles deallocating the PWork vector, the mdlTerminate
method must always free the memory stored in the PWork vector.

Real and Integer Work Vectors

The S-function stvctf.c uses RWork and IWork vectors to model a time-varying continuous transfer
function. For a description of this S-function, see the example “Discontinuities in Continuous States”
on page 9-83.

Mode Vector

The following example implements a switch block using a mode work vector. The
mdlInitializeSizes method configures two input ports with direct feedthrough and one output
port. The mode vector element indicates if the signal from the first or second input port is propagated
to the output. The S-function uses one S-function parameter and a corresponding run-time parameter
to store the mode value and allow the switch to be toggled during simulation.
static void mdlInitializeSizes(SimStruct *S)
{
/* Initialize one S-function parameter to toggle the mode value */
ssSetNumSFcnParams(S, 1);
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
/* Return if number of expected != number of actual parameters */
return;
}

{
int iParam = 0;
int nParam = ssGetNumSFcnParams(S);

for ( iParam = 0; iParam < nParam; iParam++ )


{
ssSetSFcnParamTunable( S, iParam, SS_PRM_TUNABLE );
}
}
/* Initialize two input ports with direct feedthrough */
if (!ssSetNumInputPorts(S, 2)) return;
ssSetInputPortWidth(S, 0, 1);
ssSetInputPortWidth(S, 1, 1);
ssSetInputPortDataType( S, 0, SS_DOUBLE);
ssSetInputPortDataType( S, 1, SS_DOUBLE);
ssSetInputPortDirectFeedThrough( S, 0, 1);
ssSetInputPortDirectFeedThrough( S, 1, 1);

/* Initialize one output port */

8-18
Elementary Work Vectors

if (!ssSetNumOutputPorts(S, 1)) return;


ssSetOutputPortWidth(S, 0, 1);
ssSetOutputPortDataType( S, 0, SS_DOUBLE);

/* Initialize one element in the mode vector */


ssSetNumSampleTimes(S, 1);
ssSetNumModes(S,1);

ssSetOptions(S,
SS_OPTION_WORKS_WITH_CODE_REUSE |
SS_OPTION_USE_TLC_WITH_ACCELERATOR |
SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME |
SS_OPTION_NONVOLATILE);
}

The mdlInitializeConditions method initializes the mode vector value using the current value
of the S-function dialog parameter.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions =============================
* Abstract:
* Initialize the mode vector value.
*/
static void mdlInitializeConditions(SimStruct *S)
{
int_T *mv = ssGetModeVector(S);
real_T param = mxGetScalar(ssGetSFcnParam(S,0));
mv[0] = (int_T)param;
}

The mdlProcessParameters and mdlSetWorkWidths methods initialize and update the run-time
parameter. As the simulation runs, changes to the S-function dialog parameter are mapped to the
run-time parameter.
/* Function: mdlSetWorkWidths =============================================
* Abstract:
* Sets the number of runtime parameters.
*/
#define MDL_SET_WORK_WIDTHS
static void mdlSetWorkWidths(SimStruct *S) {
ssSetNumRunTimeParams(S,1);
ssRegDlgParamAsRunTimeParam(S,0,0,"P1",SS_INT16);
}

/* Function: mdlProcessParameters ===========================================


* Abstract:
* Update run-time parameters.
*/
#define MDL_PROCESS_PARAMETERS
static void mdlProcessParameters(SimStruct *S)
{
ssUpdateDlgParamAsRunTimeParam(S,0);
}

The mdlOutputs method updates the mode vector value with the new run-time parameter value at
every major time step. It then uses the mode vector value to determine which input signal to pass
through to the output.
static void mdlOutputs(SimStruct *S, int_T tid)
{
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
InputRealPtrsType u2Ptrs = ssGetInputPortRealSignalPtrs(S,1);
real_T *y = ssGetOutputPortSignal(S,0);
int_T *mode = ssGetModeVector(S);
real_T param = mxGetScalar(ssGetSFcnParam(S,0));

if (ssIsMajorTimeStep(S)) {
mode[0] = (int_T)param;
}

8-19
8 Using Work Vectors

if (!mode[0]) {
/* first input */
y[0] = (*uPtrs[0]);
}
if (mode[0]) {
/* second input */
y[0] = (*u2Ptrs[0]);
}
}

8-20
Memory Allocation

Memory Allocation
When you create an S-function, you might need to allocate memory for each instance of your S-
function. The standard MATLAB API memory allocation routines mxCalloc and mxFree should not
be used with C MEX S-functions, because these routines are designed to be used with MEX files that
are called from the MATLAB environment and not the Simulink environment. The correct approach
for allocating memory is to use the stdlib.h library routines calloc and free. In mdlStart,
allocate and initialize the memory

UD *ptr = (UD *)calloc(1,sizeof(UD));

where UD, in this example, is a data structure defined at the beginning of the S-function. Then, place
the pointer to it either in the pointer work vector

ssSetPWorkValue(S, 0, ptr);

or attach it as user data.

ssSetUserData(S,ptr);

In mdlTerminate, free the allocated memory. For example, if the pointer was stored in the user data

UD *ptr = ssGetUserData(S);
free(ptr);

ssGetUserData | ssSetPWorkValue | ssSetUserData

8-21
9

Implementing Block Features for C/C++


S-Functions

• “Pass Dialog Parameters to S-Functions” on page 9-2


• “Create and Update S-Function Run-Time Parameters” on page 9-5
• “Input and Output Ports” on page 9-12
• “Configure Custom Data Types” on page 9-18
• “Specify S-Function Sample Times” on page 9-20
• “Zero Crossings” on page 9-31
• “S-Function Compliance with the ModelOperatingPoint” on page 9-34
• “MATLAB Data in C S-Functions” on page 9-36
• “Implement Function-Call Subsystems with S-Functions” on page 9-39
• “Use C/C++ S-Functions as Sim Viewing Devices in External Mode” on page 9-43
• “Handle Errors in S-Functions” on page 9-44
• “Guidelines for Writing Thread-Safe S-Functions” on page 9-47
• “C MEX S-Function Examples” on page 9-51
9 Implementing Block Features for C/C++ S-Functions

Pass Dialog Parameters to S-Functions


In this section...
“About Dialog Parameters” on page 9-2
“Tunable Parameters” on page 9-3

About Dialog Parameters


You can pass parameters to an S-function at the start of and during the simulation, using the S-
function parameters field of the Block Parameters dialog box. Such parameters are called dialog
box parameters to distinguish them from run-time parameters created by the S-function to facilitate
code generation (see “Create and Update S-Function Run-Time Parameters” on page 9-5).

Note You cannot use the Model Explorer, the S-function Block Parameters dialog box, or a mask to
tune the parameters of a source S-function, i.e., an S-function that has outputs but no inputs, while a
simulation is running. For more information, see “Tune and Experiment with Block Parameter
Values”.

Using C S-Function Dialog Parameters

The Simulink engine stores the values of the dialog box parameters in the S-function SimStruct
structure. Use the S-function callback methods and SimStruct macros to access and check the
parameters and use them to compute the S-function output. To use dialog parameters in your C S-
function, perform the following steps when you create the S-function:

1 Determine the order in which the parameters are to be specified in the block's dialog box.
2 In the mdlInitializeSizes function, use the ssSetNumSFcnParams macro to tell the
Simulink engine how many parameters the S-function accepts. Specify S as the first argument
and the number of dialog box parameters as the second argument. If your S-function implements
the mdlCheckParameters method, the mdlInitializeSizes routine should call
mdlCheckParameters to check the validity of the initial values of the parameters. For example,
the mdlInitializeSizes function in sfun_runtime1.c begins with the following code.
ssSetNumSFcnParams(S, NPARAMS); /* Number of expected parameters */
#if defined(MATLAB_MEX_FILE)
if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) {
mdlCheckParameters(S);
if (ssGetErrorStatus(S) != NULL) {
return;
}
} else {
return; /* Parameter mismatch reported by the Simulink engine*/
}
#endif
3 Access the dialog box parameters in the S-function using the ssGetSFcnParam macro.

Specify S as the first argument and the relative position of the parameter in the list entered on
the dialog box (0 is the first position) as the second argument. The ssGetSFcnParam macro
returns a pointer to the mxArray containing the parameter. You can use
ssGetDTypeIdFromMxArray to get the data type of the parameter. Alternatively, you can use
ssGetSFcnParamDataType to get the data type of the parameter by specifying the index of the
parameter.

9-2
Pass Dialog Parameters to S-Functions

For example, in sfun_runtime1.c, the following #define statements at the beginning of the S-
function specify the order of three dialog box parameters and access their values on the block's
dialog.
#define SIGNS_IDX 0
#define SIGNS_PARAM(S) ssGetSFcnParam(S,SIGNS_IDX) /* First parameter */

#define GAIN_IDX 1
#define GAIN_PARAM(S) ssGetSFcnParam(S,GAIN_IDX) /* Second parameter */

#define OUT_IDX 2
#define OUT_PARAM(S) ssGetSFcnParam(S,OUT_IDX) /* Third parameter */

When running a simulation, you must specify the parameters in the S-Function parameters field of
the S-Function Block Parameters dialog box in the same order that you defined them in step 1. You
can enter any valid MATLAB expression as the value of a parameter, including literal values, names of
workspace variables, function invocations, or arithmetic expressions. The Simulink engine evaluates
the expression and passes its value to the S-function.

As another example, the following code is part of a device driver S-function. Four input parameters
are used: BASE_ADDRESS_PRM, GAIN_RANGE_PRM, PROG_GAIN_PRM, and NUM_OF_CHANNELS_PRM.
The code uses #define statements at the top of the S-function to associate particular input
arguments with the parameter names.
/* Input Parameters */
#define BASE_ADDRESS_PRM(S) ssGetSFcnParam(S, 0)
#define GAIN_RANGE_PRM(S) ssGetSFcnParam(S, 1)
#define PROG_GAIN_PRM(S) ssGetSFcnParam(S, 2)
#define NUM_OF_CHANNELS_PRM(S) ssGetSFcnParam(S, 3)

When running the simulation, enter four variable names or values in the S-function parameters
field of the S-Function Block Parameters dialog box. The first corresponds to the first expected
parameter, BASE_ADDRESS_PRM(S). The second corresponds to the next expected parameter, and so
on.

The mdlInitializeSizes function contains this statement.


ssSetNumSFcnParams(S, 4);

Tunable Parameters
Dialog parameters can be either tunable or nontunable. A tunable parameter is a parameter that a
user can change while the simulation is running.

Note Dialog box parameters are tunable by default. Nevertheless, it is good programming practice to
set the tunability of every parameter, even those that are tunable. If you enable the simulation
diagnostic S-function upgrades needed, the Simulink engine issues the diagnostic whenever it
encounters an S-function that fails to specify the tunability of all its parameters.

The mdlCheckParameters method enables you to validate changes to tunable parameters during a
simulation. The engine invokes the mdlCheckParameters method whenever you change the values
of parameters during the simulation loop. This method should check the S-function dialog box
parameters to ensure that the changes are valid.

The optional mdlProcessParameters callback method allows an S-function to process changes to


tunable parameters. The engine invokes this method only if valid parameter changes have occurred in

9-3
9 Implementing Block Features for C/C++ S-Functions

the previous time step. A typical use of this method is to perform computations that depend only on
the values of parameters and hence need to be computed only when parameter values change. The
method can cache the results of the parameter computations in work vectors or, preferably, as run-
time parameters (see “Create and Update S-Function Run-Time Parameters” on page 9-5).

Using Tunable Parameters in a C S-Function

In a C S-function, use the macro ssSetSFcnParamTunable in mdlInitializeSizes to specify the


tunability of each S-function dialog box parameter. The code below is taken from the
mdlInitializeSizes function in the example sfun_runtime1.c. The code first sets the number
of S-function dialog box parameters to three before invoking mdlCheckParameters. If the
parameter check passes, the tunability of the three S-function dialog box parameters is specified.
ssSetNumSFcnParams(S, 3); /* Three dialog box parameters*/

#if defined(MATLAB_MEX_FILE)
if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) {
mdlCheckParameters(S);
if (ssGetErrorStatus(S) != NULL) {
return;
}
} else {
return; /* Parameter mismatch reported by the Simulink engine*/
}
#endif

ssSetSFcnParamTunable(S,GAIN_IDX,true); /* Tunable */
ssSetSFcnParamTunable(S,SIGNS_IDX,false); /* Not tunable */
ssSetSFcnParamTunable(S,OUT_IDX,false); /* Not tunable */

Note The S-function mdlInitializeSizes routine invokes the mdlCheckParameters method to


ensure that the initial values of the parameters are valid.

Tuning Parameters in External Mode

When you tune parameters during simulation, the Simulink engine invokes the S-function
mdlCheckParameters method to validate the changes and then the S-functions'
mdlProcessParameters method to give the S-function a chance to process the parameters in some
way. The engine also invokes these methods when running in external mode, but it passes the
unprocessed changes to the S-function target. Thus, if it is essential that your S-function process
parameter changes, you need to create a Target Language Compiler (TLC) file that inlines the S-
function, including its parameter processing code, during the code generation process. For
information on inlining S-functions, see “Inlining S-Functions” in the Simulink Coder Target
Language Compiler documentation.

See Also
ssGetSFcnParamDataType

More About
• “Create and Update S-Function Run-Time Parameters” on page 10-4
• “SimStruct Macros and Functions Listed by Usage” on page 12-2

9-4
Create and Update S-Function Run-Time Parameters

Create and Update S-Function Run-Time Parameters

In this section...
“About Run-Time Parameters” on page 9-5
“Creating Run-Time Parameters” on page 9-6
“Updating Run-Time Parameters” on page 9-9
“Tuning Run-Time Parameters” on page 9-10
“Accessing Run-Time Parameters” on page 9-10

About Run-Time Parameters


You can create internal representations of external S-function dialog box parameters called run-time
parameters. Every run-time parameter corresponds to one or more dialog box parameters and can
have the same value and data type as its corresponding external parameters or a different value or
data type. If a run-time parameter differs in value or data type from its external counterpart, the
dialog parameter is said to have been transformed to create the run-time parameter. The value of a
run-time parameter that corresponds to multiple dialog parameters is typically a function of the
values of the dialog parameters. The Simulink engine allocates and frees storage for run-time
parameters and provides functions for updating and accessing them, thus eliminating the need for S-
functions to perform these tasks. Run-time parameters facilitate the following kinds of S-function
operations:

• Computed parameters

Often the output of a block is a function of the values of several dialog parameters. For example,
suppose a block has two parameters, the volume and density of some object, and the output of the
block is a function of the input signal and the mass of the object. In this case, the mass can be
viewed as a third internal parameter computed from the two external parameters, volume and
density. An S-function can create a run-time parameter corresponding to the computed weight,
thereby eliminating the need to provide special case handling for weight in the output
computation. See “Creating Run-Time Parameters from Multiple S-Function Parameters” on page
9-7 for more information.
• Data type conversions

Often a block needs to change the data type of a dialog parameter to facilitate internal processing.
For example, suppose that the output of the block is a function of the input and a dialog parameter
and the input and dialog parameter are of different data types. In this case, the S-function can
create a run-time parameter that has the same value as the dialog parameter but has the data
type of the input signal, and use the run-time parameter in the computation of the output.
• Code generation

During code generation, the Simulink Coder product writes all run-time parameters automatically
to the model.rtw file, eliminating the need for the S-function to perform this task via an mdlRTW
method.

The sfcndemo_runtime Simulink model contains four example S-functions that create run-time
parameters.

9-5
9 Implementing Block Features for C/C++ S-Functions

Creating Run-Time Parameters


In a C S-function, you can create run-time parameters in a number of ways. The following sections
describe different methods for creating run-time parameters in a C S-function.

Creating Run-Time Parameters All at Once

Use the SimStruct function ssRegAllTunableParamsAsRunTimeParams in mdlSetWorkWidths


to create run-time parameters corresponding to all tunable parameters. This function requires that
you pass it an array of names, one for each run-time parameter. The Simulink Coder product uses
these names as the names of the parameters during code generation. The S-function
sfun_runtime1.c shows how to create run-time parameters all at once.

This approach to creating run-time parameters assumes that there is a one-to-one correspondence
between an S-function run-time parameters and its tunable dialog parameters. This might not be the
case. For example, an S-function might want to use a computed parameter whose value is a function
of several dialog parameters. In such cases, the S-function might need to create the run-time
parameters individually.

Creating Run-Time Parameters Individually

To create run-time parameters individually, the S-function mdlSetWorkWidths method should


1 Specify the number of run-time parameters it intends to use, using ssSetNumRunTimeParams.
2 Use ssRegDlgParamAsRunTimeParam to register a run-time parameter that corresponds to a
single dialog parameter, even if there is a data type transformation, or
ssSetRunTimeParamInfo to set the attributes of a run-time parameter that corresponds to
more than one dialog parameter.

The following example uses ssRegDlgParamAsRunTimeParam and is taken from the S-function
sfun_runtime3.c. This example creates a run-time parameter directly from the dialog parameter
and with the same data type as the first input port's signal.
static void mdlSetWorkWidths(SimStruct *S)
{
/* Get data type of input to use for run-time parameter */
DTypeId dtId = ssGetInputPortDataType(S, 0);

/* Define name of run-time parameter */


const char_T *rtParamName = "Gain";

ssSetNumRunTimeParams(S, 1); /* One run-time parameter */


if (ssGetErrorStatus(S) != NULL) return;
ssRegDlgParamAsRunTimeParam(S, GAIN_IDX, 0, rtParamName, dtId);
}
#endif /* MDL_SET_WORK_WIDTHS */

The next example uses ssSetRunTimeParamInfo and is taken from the S-function
sfun_runtime2.c.
static void mdlSetWorkWidths(SimStruct *S)
{
ssParamRec p; /* Initialize an ssParamRec structure */
int dlgP = GAIN_IDX; /* Index of S-function parameter */

/* Configure run-time parameter information */


p.name = "Gain";
p.nDimensions = 2;
p.dimensions = (int_T *) mxGetDimensions(GAIN_PARAM(S));
p.dataTypeId = SS_DOUBLE;
p.complexSignal = COMPLEX_NO;

9-6
Create and Update S-Function Run-Time Parameters

p.data = (void *)mxGetPr(GAIN_PARAM(S));


p.dataAttributes = NULL;
p.nDlgParamIndices = 1;
p.dlgParamIndices = &dlgP;
p.transformed = false;
p.outputAsMatrix = false;

/* Set number of run-time parameters */


if (!ssSetNumRunTimeParams(S, 1)) return;

/* Set run-time parameter information */


if (!ssSetRunTimeParamInfo(S, 0, &p)) return;
}

The S-function sfun_runtime2.c defines the parameters GAIN_IDX and GAIN_PARAM as follows,
prior to using these parameters in mdlSetWorkWidths.
#define GAIN_IDX 1
#define GAIN_PARAM(S) ssGetSFcnParam(S,GAIN_IDX)

Creating Run-Time Parameters from Multiple S-Function Parameters

Use the ssSetRunTimeParamInfo function in mdlSetWorkWidths to create run-time parameters


as a function of multiple S-function parameters. For example, consider an S-function with two S-
function parameters, density and volume. The S-function inputs a force (F) and outputs an
acceleration (a). The mdlOutputs method calculates the force using the equation F=m*a, where the
mass (m) is the product of the density and volume.

The S-function sfun_runtime4.c implements this example using a single run-time parameter to
store the mass. The S-function begins by defining the run-time parameter data type, as well as
variables associated with volume and density.
#define RUN_TIME_DATA_TYPE SS_DOUBLE
#if RUN_TIME_DATA_TYPE == SS_DOUBLE
typedef real_T RunTimeDataType;
#endif

#define VOL_IDX 0
#define VOL_PARAM(S) ssGetSFcnParam(S,VOL_IDX)

#define DEN_IDX 1
#define DEN_PARAM(S) ssGetSFcnParam(S,DEN_IDX)

The mdlSetWorkWidths method then initializes the run-time parameter, as follows.


static void mdlSetWorkWidths(SimStruct *S)
{
ssParamRec p; /* Initialize an ssParamRec structure */
int dlg[2]; /* Stores dialog indices */
real_T vol = *mxGetPr(VOL_PARAM(S));
real_T den = *mxGetPr(DEN_PARAM(S));
RunTimeDataType *mass;

/* Initialize dimensions for the run-time parameter as a


* local variable. The Simulink engine makes a copy of this
* information to store in the run-time parameter. */
int_T massDims[2] = {1,1};

/* Allocate memory for the run-time parameter data. The S-function


* owns this memory location. The Simulink engine does not copy the data.*/
if ((mass=(RunTimeDataType*)malloc(1)) == NULL) {
ssSetErrorStatus(S,"Memory allocation error");
return;
}

/* Store the pointer to the memory location in the S-function


* userdata. Since the S-function owns this data, it needs to

9-7
9 Implementing Block Features for C/C++ S-Functions

* free the memory during mdlTerminate */


ssSetUserData(S, (void*)mass);

/* Call a local function to initialize the run-time


* parameter data. The Simulink engine checks that the data is not
* empty so an initial value must be stored. */
calcMass(mass, vol, den);

/* Specify mass as a function of two S-function dialog parameters */


dlg[0] = VOL_IDX;
dlg[1] = DEN_IDX;

/* Configure run-time parameter information. */


p.name = "Mass";
p.nDimensions = 2;
p.dimensions = massDims;
p.dataTypeId = RUN_TIME_DATA_TYPE;
p.complexSignal = COMPLEX_NO;
p.data = mass;
p.dataAttributes = NULL;
p.nDlgParamIndices = 2;
p.dlgParamIndices = &dlg
p.transformed = RTPARAM_TRANSFORMED;
p.outputAsMatrix = false;

/* Set number of run-time parameters */


if (!ssSetNumRunTimeParams(S, 1)) return;

/* Set run-time parameter information */


if (!ssSetRunTimeParamInfo(S,0,&p)) return;

The local function calcMass updates the run-time parameter value in mdlSetWorkWidths and in
mdlProcessParameters, when the values of density or volume are tuned.
/* Function: calcMass ==============================================
* Abstract:
* Local function to calculate the mass as a function of volume
* and density.
*/
static void calcMass(RunTimeDataType *mass, real_T vol, real_T den)
{
*mass = vol * den;
}

The mdlOutputs method uses the stored mass to calculate the force.
/* Function: mdlOutputs ==========================================
* Abstract:
*
* Output acceleration calculated as input force divided by mass.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y1 = ssGetOutputPortRealSignal(S,0);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
RunTimeDataType *mass =
(RunTimeDataType *)((ssGetRunTimeParamInfo(S,0))->data);

/*
* Output acceleration = force / mass
*/
y1[0] = (*uPtrs[0]) / *mass;
}

Lastly, the mdlTerminate method frees the memory allocated for the run-time parameter in
mdlSetWorkWidths.
/* Function: mdlTerminate ==========================================
* Abstract:

9-8
Create and Update S-Function Run-Time Parameters

* Free the user data.


*/
static void mdlTerminate(SimStruct *S)
{
/* Free memory used to store the run-time parameter data*/
RunTimeDataType *mass = ssGetUserData(S);
if (mass != NULL) {
free(mass);
}
}

To run the example, open the Simulink model:


sfcndemo_runtime

Updating Run-Time Parameters


Whenever you change the values of S-function dialog parameters during simulation, the Simulink
engine invokes the S-function mdlCheckParameters method to validate the changes. If the changes
are valid, the engine invokes the S-function mdlProcessParameters method at the beginning of the
next time step. This method should update the S-function run-time parameters to reflect the changes
in the dialog parameters.

In a C S-function, update the run-time parameters using the method appropriate for how the run-time
parameters were created, as described in the following sections.

Updating All Parameters at Once

In a C MEX S-function, if there is a one-to-one correspondence between the S-function tunable dialog
parameters and the run-time parameters, i.e., the run-time parameters were registered using
ssRegAllTunableParamsAsRunTimeParams, the S-function can use the SimStruct function
ssUpdateAllTunableParamsAsRunTimeParams to accomplish this task. This function updates
each run-time parameter to have the same value as the corresponding dialog parameter. See
sfun_runtime1.c for an example.

Updating Parameters Individually

If there is not a one-to-one correspondence between the S-function dialog and run-time parameters or
the run-time parameters are transformed versions of the dialog parameters, the
mdlProcessParameters method must update each parameter individually. Choose the method used
to update the run-time parameter based on how it was registered.

If you register a run-time parameter using ssSetRunTimeParamInfo, the mdlProcessParameters


method uses ssUpdateRunTimeParamData to update the run-time parameter, as shown in
sfun_runtime2.c. This function updates the data field in the parameter's attributes record,
ssParamRec, with a new value. You cannot directly modify the ssParamRec, even though you can
obtain a pointer to the ssParamRec using ssGetRunTimeParamInfo.

If you register a run-time parameter using ssRegDlgParamAsRunTimeParam, the


mdlProcessParameters method uses ssUpdateDlgParamAsRunTimeParam to update the run-
time parameter, as is shown in sfun_runtime3.c.

Updating Parameters as Functions of Multiple S-Function Parameters

If you register a run-time parameter as a function of multiple S-function parameters, the


mdlProcessParameters method uses ssUpdateRunTimeParamData to update the run-time
parameter.

9-9
9 Implementing Block Features for C/C++ S-Functions

The S-function sfun_runtime4.c provides an example. In this example, the


mdlProcessParameters method calculates a new value for the run-time parameter and passes the
value to the pointer of the run-time parameter's memory location, which was allocated during the call
to mdlSetWorkWidths. The mdlProcessParameters method then passes the updated run-time
parameter's pointer to ssUpdateRunTimeParamData.

Tuning Run-Time Parameters


Tuning a dialog parameter tunes the corresponding run-time parameter during simulation and in
code generated only if the dialog parameter meets the following conditions:

• The S-function marks the dialog parameter as tunable, using ssSetSFcnParamTunable.


• The dialog parameter is a MATLAB array of values with a data type supported by the Simulink
product.

Note that you cannot tune a run-time parameter whose value is a cell array or structure.

Accessing Run-Time Parameters


You can easily access run-time parameters from the S-function code. In order to access run-time
parameter data, choose one of the following methods based on the data type.

• If the data is of type double:


real_T *dataPtr = (real_T *) ssGetRunTimeParamInfo(S, #)->data;
• If the parameter is complex, the real and imaginary parts of the data are interleaved. For example,
if a user enters the following:
K = [1+2i, 3+4i; 5+6i, 7+8i]

the matrix that is generated is


K =
1+2i 3+4i
5+6i 7+8i

The memory for this matrix is laid out as


[1, 2, 5, 6, 3, 4, 7, 8]

To access a complex run-time parameter from the S-function code:


for (i = 0; i<width; i++)
{
real_T realData = dataPtr[(2*i)];
real_T imagData = dataPtr[(2*i)+1];
}

Note Matrix elements are written out in column-major format. Real and imaginary values are
interleaved.

See Also
ssGetSFcnParamDataType

9-10
Create and Update S-Function Run-Time Parameters

More About
• “Input and Output Ports” on page 9-12
• “SimStruct Macros and Functions Listed by Usage” on page 12-2

9-11
9 Implementing Block Features for C/C++ S-Functions

Input and Output Ports

In this section...
“Creating Input Ports for C S-Functions” on page 9-12
“Creating Output Ports for C S-Functions” on page 9-14
“Scalar Expansion of Inputs” on page 9-15
“Masked Multiport S-Functions” on page 9-16

Creating Input Ports for C S-Functions


To create and configure input ports, the mdlInitializeSizes method should first specify the
number of S-function input ports, using ssSetNumInputPorts. Then, for each input port, the
method should specify

• The dimensions of the input port (see “Initializing Input Port Dimensions” on page 9-13)

If you want your S-function to inherit its dimensionality from the port to which it is connected, you
should specify that the port is dynamically sized in mdlInitializeSizes (see “Sizing an Input
Port Dynamically” on page 9-13).
• Whether the input port allows scalar expansion of inputs (see “Scalar Expansion of Inputs” on
page 9-15)
• Whether the input port has direct feedthrough, using ssSetInputPortDirectFeedThrough

A port has direct feedthrough if the input is used in either the mdlOutputs or
mdlGetTimeOfNextVarHit functions. The direct feedthrough flag for each input port can be set
to either 1=yes or 0=no. It should be set to 1 if the input, u, is used in the mdlOutputs or
mdlGetTimeOfNextVarHit routine. Setting the direct feedthrough flag to 0 tells the Simulink
engine that u is not used in either of these S-function routines. Violating this leads to
unpredictable results.
• The data type of the input port, if not the default double

Use ssSetInputPortDataType to set the input port's data type. If you want the data type of the
port to depend on the data type of the port to which it is connected, specify the data type as
DYNAMICALLY_TYPED. In this case, you must provide implementations of the
mdlSetInputPortDataType and mdlSetDefaultPortDataTypes methods to enable the data
type to be set correctly during signal propagation.
• The numeric type of the input port, if the port accepts complex-valued signals

Use ssSetInputPortComplexSignal to set the input port's numeric type. If you want the
numeric type of the port to depend on the numeric type of the port to which it is connected,
specify the numeric type as COMPLEX_INHERITED. In this case, you must provide implementations
of the mdlSetInputPortComplexSignal and mdlSetDefaultPortComplexSignals methods
to enable the numeric type to be set correctly during signal propagation.

You can configure additional input port properties using other S-function macros. See “Input and
Output Ports” on page 12-6 in the “SimStruct Macros and Functions Listed by Usage” section for
more information.

9-12
Input and Output Ports

Note The mdlInitializeSizes method must specify the number of ports before setting any
properties. If it attempts to set a property of a port that doesn't exist, it is accessing invalid memory
and a segmentation violation occurs.

Initializing Input Port Dimensions

You can set input port dimensions using one of the following macros:

• If the input signal must be one-dimensional and the input port width is w, use
ssSetInputPortWidth(S, inputPortIdx, w)
• If the input signal must be a matrix of dimension m-by-n, use
ssSetInputPortMatrixDimensions(S, inputPortIdx, m, n)
• Otherwise, if the input signal can have either one or two dimensions, use
ssSetInputPortDimensionInfo(S, inputPortIdx, dimsInfo)

You can use this function to fully or partially initialize the port dimensions (see next section).

Sizing an Input Port Dynamically

If your S-function does not require that its input signals have specific dimensions, you can set the
dimensionality of the input ports to match the dimensionality of the signals connected to them.

To dynamically dimension an input port:

• Specify some or all of the dimensions of the input port as dynamically sized in
mdlInitializeSizes.

If the input port can accept a signal of any dimensionality, use


ssSetInputPortDimensionInfo(S, inputPortIdx, DYNAMIC_DIMENSION)

to set the dimensionality of the input port.

If the input port can accept only vector (1-D) signals but the signals can be of any size, use
ssSetInputPortWidth(S, inputPortIdx, DYNAMICALLY_SIZED)

to specify the dimensionality of the input port.

If the input port can accept only matrix signals but can accept any row or column size, use
ssSetInputPortMatrixDimensions(S, inputPortIdx,
DYNAMICALLY_SIZED, DYNAMICALLY_SIZED)
• Provide an mdlSetInputPortDimensionInfo method that sets the dimensions of the input port
to the size of the signal connected to it.

The Simulink engine invokes this method during signal propagation when it has determined the
dimensionality of the signal connected to the input port.
• Provide an mdlSetDefaultPortDimensionInfo method that sets the dimensions of the block's
ports to a default value. See sfun_dynsize.c for an example that implements this macro.

The engine invokes this method during signal propagation when it cannot determine the
dimensionality of the signal connected to some or all of the block's input ports. This can happen,

9-13
9 Implementing Block Features for C/C++ S-Functions

for example, if an input port is unconnected. If the S-function does not provide this method, the
signal propagation routine sets the dimension of the block's ports to 1-D scalar.

Example: Defining Multiple S-Function Input Ports

The following code in mdlInitializeSizes configures an S-function with two input ports. See
“Input and Output Ports” on page 12-6 in the “SimStruct Macros and Functions Listed by Usage”
section for more information on the macros used in this example.

if (!ssSetNumInputPorts(S, 2)) return;

for (i = 0; i < 2; i++) {


/* Input has direct feedthrough */
ssSetInputPortDirectFeedThrough(S, i, 1);

/* Input is a real signal */


ssSetInputPortComplexSignal(S, i, COMPLEX_NO);

/* Input is a dynamically sized 2-D matrix */


ssSetInputPortMatrixDimensions(S ,i,
DYNAMICALLY_SIZED, DYNAMICALLY_SIZED);

/* Input inherits its sample time */


ssSetInputPortSampleTime(S, i,INHERITED_SAMPLE_TIME);

/* Input signal must be contiguous */


ssSetInputPortRequiredContiguous(S, i, 1);

/* The input port cannot share memory */


ssSetInputPortOverWritable(S, i, 0);
}

During signal propagation, the Simulink engine calls this S-function's


mdlSetInputPortDimensionInfo macro to initialize the input port dimensions. In this example,
mdlSetInputPortDimensionInfo sets the input dimensions to the candidate dimensions passed to
the macro by the engine.

#if defined(MATLAB_MEX_FILE)
#define MDL_SET_INPUT_PORT_DIMENSION_INFO
static void mdlSetInputPortDimensionInfo(SimStruct *S,
int_T port,
const DimsInfo_T *dimsInfo)
{
if(!ssSetInputPortDimensionInfo(S, port, dimsInfo)) return;
}
#endif

For an example that configures an S-function with multiple input and output ports, open the Simulink
model sfcndemo_sfun_multiport and inspect the S-function sfun_multiport.c.

Creating Output Ports for C S-Functions


To create and configure output ports, the mdlInitializeSizes method should first specify the
number of S-function output ports, using ssSetNumOutputPorts. Then, for each output port, the
method should specify

9-14
Input and Output Ports

• Dimensions of the output port

You can set output port dimensions using one of the following macros:

• ssSetOutputPortDimensionInfo
• ssSetOutputPortMatrixDimensions
• ssSetOutputPortVectorDimension
• ssSetOutputPortWidth

If you want the port's dimensions to depend on block connectivity, set the dimensions to
DYNAMIC_DIMENSIONS when using ssSetOutputPortDimensionInfo or to
DYNAMICALLY_SIZED for all other macros. The S-function must then provide
mdlSetOutputPortDimensionInfo and mdlSetDefaultPortDimensionInfo methods to
ensure that output port dimensions are set to the correct values in code generation.
• Data type of the output port

Use ssSetOutputPortDataType to set the output port's data type. If you want the data type of
the port to depend on block connectivity, specify the data type as DYNAMICALLY_TYPED. In this
case, you must provide implementations of the mdlSetOutputPortDataType and
mdlSetDefaultPortDataTypes methods to enable the data type to be set correctly during
signal propagation.
• The numeric type of the output port, if the port outputs complex-valued signals

Use ssSetOutputPortComplexSignal to set the output port's numeric type. If you want the
numeric type of the port to depend on the numeric type of the port to which it is connected,
specify the numeric type as COMPLEX_INHERITED. In this case, you must provide implementations
of the mdlSetOutputPortComplexSignal and mdlSetDefaultPortComplexSignals
methods to enable the numeric type to be set correctly during signal propagation.

See “Creating Input Ports for C S-Functions” on page 9-12 for an example showing how to initialize
an S-function input port. You use the same procedure to initialize the S-function output ports, but
with the corresponding output port macro.

Scalar Expansion of Inputs


Scalar expansion of inputs refers conceptually to the process of expanding scalar input signals to the
same dimensions as wide input signals connected to other S-function input ports. This is done by
setting each element of the expanded signal to the value of the scalar input.

A Level-2 MATLAB S-function uses the default scalar expansion rules if the input and output ports are
specified as dynamically sized (see “Scalar Expansion of Inputs and Parameters” in Using Simulink).

With scalar expansion on, the S-function mdlInitializeSizes method should specify that the input
and output ports are dynamically sized. The Simulink engine uses a default method to set the
dimensions of the input and output ports. If the block has more than two inputs, the input signals can
be scalar or wide signals, where the wide signals all have the same number of elements. In this case,
the engine sets the dimensions of the output ports to the width of the wide input signals and expands
any scalar inputs to this width. If the wide inputs are driven by 1-D and 2-D vectors, the output is a 2-
D vector signal, and the scalar inputs are expanded to a 2-D vector signal.

9-15
9 Implementing Block Features for C/C++ S-Functions

If scalar expansion is not on, the engine assumes that all ports (input and output ports) must have the
same dimensions, and it sets all port dimensions to the same dimensions specified by one of the
driving blocks.

Note The engine ignores the scalar expansion option if the S-function specifies or controls the
dimensions of its input and output ports either by initializing the dimensions in
mdlInitializeSizes, using mdlSetInputPortWidth and mdlSetOutputPortWidth, or using
mdlSetInputPortDimensionInfo, mdlSetOutputPortDimensionInfo, and
mdlSetDefaultPortDimensionInfo.

The best way to understand how to use scalar expansion is to consider the example
sfcndemo_sfun_multiport. This model contains three S-function blocks, each with multiple input
ports. The S-function sfun_multiport.c used in these blocks sets the
SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION option in its mdlInitializeSizes method,
allowing scalar expansion of the inputs. The S-function specifies that its inputs and outputs are
dynamically sized. Therefore, during signal propagation, the engine sets the width of the input ports
to the width of the signal connected to the port, and the width of the output ports to the width of any
wide input signal. The mdlOutputs method performs an element-by-element sum on the input
signals, expanding any scalar inputs, as needed.

/* Calculate an element-by-element sum of the input signals.


yWidth is the width of the output signal. */

for (el = 0; el < yWidth; el++) {

int_T port;
real_T sum = 0.0;
for (port = 0; port < nInputPorts; port++) {
/* Get the input signal value */
InputRealPtrsType uPtrs =
ssGetInputPortRealSignalPtrs(S,port);

if (el < ssGetInputPortWidth(S,port)) {


/* Input is a wide signal. Use specific element */
sum = sum + ((real_T)signs[port] * (*uPtrs[el]));

} else {
/* Use the scalar value to expand the signal */
sum = sum + ((real_T)signs[port] * (*uPtrs[0]));
}
}
}

Masked Multiport S-Functions


If you are developing masked multiport S-function blocks whose number of ports varies based on
some parameter, and want to place them in a Simulink library, you must specify that the mask
modifies the appearance of the block. To do this, execute the command

set_param(blockname,'MaskSelfModifiable','on')

at the MATLAB command prompt before saving the library, where blockname is the full path to the
block. Failure to specify that the mask modifies the appearance of the block means that an instance of

9-16
Input and Output Ports

the block in a model reverts to the number of ports in the library whenever you load the model or
update the library link.

9-17
9 Implementing Block Features for C/C++ S-Functions

Configure Custom Data Types


In this section...
“Custom Data Types in C S-Functions” on page 9-18
“Using Simulink Recognizable Data Types in C S-Functions” on page 9-18
“Using Opaque Data Types in C S-Functions” on page 9-18

Custom Data Types in C S-Functions


C S-Functions can communicate using user-defined data types. There are two broad categories for
these data types:

• Simulink recognizable custom data types — These are custom data types from a
Simulink.AliasType, Simulink.Bus, Simulink.NumericType, or an Enumerated data type
that can also interact with other Simulink blocks.
• Opaque data types — These are data types for use only with S-Function blocks programmed to
understand them. You might define opaque data types in cases in which other Simulink blocks do
not need to use the data types.

Using Simulink Recognizable Data Types in C S-Functions


To register a custom data type recognizable by Simulink, the S-function mdlInitializeSizes
routine must register the data type, using ssRegisterTypeFromNamedObject.

For example, the following code placed at the beginning of mdlInitializeSizes defines a custom
data type from a Simulink.AliasType object named u8 in the MATLAB workspace. The example
then assigns the custom data type to the first output port.

int id1;
ssRegisterTypeFromNamedObject(S, "u8", &id1);
ssSetOutputPortDataType(S, 0, id1);

In addition, you can use the identifier id1 to assign this data type to S-function parameters, DWork
vectors, and input ports.

Using Opaque Data Types in C S-Functions


For cases in which S-Functions need to communicate using a data type that cannot be understood by
Simulink, the S-function mdlInitializeSizes routine must:

1 Register the data type, using ssRegisterDataType.


2 Specify the amount of memory in bytes required to store an instance of the data type, using
ssSetDataTypeSize.
3 Specify the value that represents zero for the data type, using ssSetDataTypeZero.

Define the user-defined data type in an external header file to include in the level 2 C S-Function.

/* Define the structure of the user-defined data type */


typedef struct{
int8_T a;

9-18
Configure Custom Data Types

uint16_T b;
}myStruct;

Place the following code at the beginning of mdlInitializeSizes to set the size and zero
representation of the custom data type myStruct.

/* Define variables */
int_T status;
DTypeId id;

myStruct tmp;

/* Register the user-defined data types */


id = ssRegisterDataType(S, "myStruct");
if(id == INVALID_DTYPE_ID) return;

/* Set the size of the user-defined data type */


status = ssSetDataTypeSize(S, id, sizeof(tmp));
if(status == 0) return;

/* Set the zero representation */


tmp.a = 0;
tmp.b = 1;
status = ssSetDataTypeZero(S, id, &tmp);

Note If you have Simulink Coder, you cannot use the software to generate code for S-functions that
contain macros to define custom data types. You must use an inline S-function that accesses Target
Language Compiler functions to generate code with custom data types. For more information, see
“Inlining S-Functions” (Simulink Coder).

See Also
ssGetNumInputPorts | ssGetNumOutputPorts | ssSetNumInputPorts |
ssSetNumOutputPorts

More About
• “SimStruct Macros and Functions Listed by Usage” on page 12-2

9-19
9 Implementing Block Features for C/C++ S-Functions

Specify S-Function Sample Times

In this section...
“About Sample Times” on page 9-20
“Block-Based Sample Times” on page 9-21
“Specifying Port-Based Sample Times” on page 9-23
“Hybrid Block-Based and Port-Based Sample Times” on page 9-27
“Multirate S-Function Blocks” on page 9-27
“Multirate S-Functions and Sample Time Hit Calculations” on page 9-28
“Synchronizing Multirate S-Function Blocks” on page 9-29
“Specifying Model Reference Sample Time Inheritance” on page 9-29

About Sample Times


You can specify the sample-time behavior of your S-functions in mdlInitializeSampleTimes. Your
S-function can inherit its rates from the blocks that drive it or define its own rates.

You can specify your S-function rates (i.e., sample times) as

• Block-based sample times


• Port-based sample times
• Hybrid block-based and port-based sample times

With block-based sample times, the S-function specifies a set of operating rates for the block as a
whole during the initialization phase of the simulation. With port-based sample times, the S-function
specifies a sample time for each input and output port individually during initialization. During the
simulation phase, with block-based sample times, the S-function processes all inputs and outputs
each time a sample hit occurs for the block. By contrast, with port-based sample times, the block
processes a particular port only when a sample hit occurs for that port.

For example, consider two sample rates, 0.5 and 0.25 seconds, respectively:

• In the block-based method, selecting 0.5 and 0.25 directs the block to execute inputs and outputs
at 0.25 second increments.
• In the port-based method, setting the input port to 0.5 and the output port to 0.25 causes the
block to process inputs at 2 Hz and outputs at 4 Hz.

You should use port-based sample times if your application requires unequal sample rates for input
and output execution or if you do not want the overhead associated with running input and output
ports at the highest sample rate of your block.

In some applications, an S-Function block might need to operate internally at one or more sample
rates while inputting or outputting signals at other rates. The hybrid block- and port-based method of
specifying sample rates allows you to create such blocks.

In typical applications, you specify only one block-based sample time. Advanced S-functions might
require the specification of port-based or multiple block sample times.

9-20
Specify S-Function Sample Times

Block-Based Sample Times


C MEX S-functions specify block-based sample time information in

• mdlInitializeSizes
• mdlInitializeSampleTimes

The next two sections discuss how to specify block-based sample times for C MEX S-functions. A third
section presents a simple example that shows how to specify sample times in
mdlInitializeSampleTimes. For a detailed example, see mixedm.c.

Specifying the Number of Sample Times in mdlInitializeSizes

To configure your S-function for block-based sample times, use

ssSetNumSampleTimes(S,numSampleTimes);

where numSampleTimes > 0. This tells the Simulink engine that your S-function has block-based
sample times. the engine calls mdlInitializeSampleTimes, which in turn sets the sample times.

Setting Sample Times and Specifying Function Calls in mdlInitializeSampleTimes

mdlInitializeSampleTimes specifies two pieces of execution information:

• Sample and offset times — In mdlInitializeSampleTimes, you must specify the sampling
period and offset for each sample time using ssSetSampleTime and ssSetOffsetTime. If
applicable, you can calculate the appropriate sampling period and offset prior to setting them, for
example, by computing the best sample time for the block based on the S-function dialog
parameters obtained using ssGetSFcnParam.
• Function calls — In mdlInitializeSampleTimes, use ssSetCallSystemOutput to specify the
output elements that are performing function calls. Seesfun_fcncall.c for an example and
“Implement Function-Call Subsystems with S-Functions” on page 9-39 for an explanation of this
S-function.

You specify the sample times as pairs [sample_time, offset_time], using these macros

ssSetSampleTime(S, sampleTimePairIndex, sample_time)


ssSetOffsetTime(S, offsetTimePairIndex, offset_time)

where sampleTimePairIndex and offsetTimePairIndex starts at 0.

The valid sample time pairs are (uppercase values are macros defined in simstruc.h):

[CONTINUOUS_SAMPLE_TIME, 0.0 ]
[CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]
[discrete_sample_period, offset ]
[VARIABLE_SAMPLE_TIME , 0.0 ]

Alternatively, you can specify that the sample time is inherited from the driving block, in which case
the S-function can have only one sample time pair,

[INHERITED_SAMPLE_TIME, 0.0 ]

or

[INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]

9-21
9 Implementing Block Features for C/C++ S-Functions

Note If your S-function inherits its sample time, you should specify whether it is safe to use the S-
function in a referenced model, i.e., a model referenced by another model. See “Specifying Model
Reference Sample Time Inheritance” on page 9-29 for more information.

The following guidelines might help in specifying sample times:

• A continuous function that changes during minor integration steps should register the
[CONTINUOUS_SAMPLE_TIME, 0.0] sample time.
• A continuous function that does not change during minor integration steps should register the
[CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET] sample time.
• A discrete function that changes at a specified rate should register the discrete sample time pair

[discrete_sample_period, offset]

where

discrete_sample_period > 0.0

and

0.0 <= offset < discrete_sample_period


• A discrete function that changes at a variable rate should register the variable-step discrete
[VARIABLE_SAMPLE_TIME, 0.0] sample time. In C MEX S-functions, the
mdlGetTimeOfNextVarHit function is called to get the time of the next sample hit for the
variable-step discrete task. The VARIABLE_SAMPLE_TIME can be used with variable-step solvers
only.

If your function has no intrinsic sample time, you must indicate that it is inherited according to the
following guidelines:

• A function that changes as its input changes, even during minor integration steps, should register
the [INHERITED_SAMPLE_TIME, 0.0] sample time.
• A function that changes as its input changes, but doesn't change during minor integration steps
(meaning, is held during minor steps), should register the [INHERITED_SAMPLE_TIME,
FIXED_IN_MINOR_STEP_OFFSET] sample time.

To check for a sample hit during execution (in mdlOutputs or mdlUpdate), use the ssIsSampleHit
or ssIsContinuousTask macro. For example, use the following code fragment to check for a
continuous sample hit:

if (ssIsContinuousTask(S,tid)) {
}

To determine whether the third (discrete) task has a hit, use the following code fragment:

if (ssIsSampleHit(S,2,tid) {
}

The Simulink engine always assigns an index of 0 to the continuous sample rate, if it exists, however
you get incorrect results if you use ssIsSampleHit(S,0,tid).

Example: mdlInitializeSampleTimes

This example specifies that there are two discrete sample times with periods of 0.01 and 0.5 seconds.

9-22
Specify S-Function Sample Times

static void mdlInitializeSampleTimes(SimStruct *S)


{
ssSetSampleTime(S, 0, 0.01);
ssSetOffsetTime(S, 0, 0.0);
ssSetSampleTime(S, 1, 0.5);
ssSetOffsetTime(S, 1, 0.0);
} /* End of mdlInitializeSampleTimes. */

Specifying Port-Based Sample Times


You cannot use port-based sample times with S-functions that have neither input ports nor output
ports. If an S-function uses port-based sample times and has no ports, the S-function produces errors
when the Simulink model is updated or run. If the number of input or output ports on an S-function is
variable, extra protection should be added into the S-function to ensure the total number of ports
does not go to zero.

To use port-based sample times in your C MEX S-function, you must specify the number of sample
times as port-based in the S-function mdlInitializeSizes method:

ssSetNumSampleTimes(S, PORT_BASED_SAMPLE_TIMES)

You must also specify the sample time of each input and output port in the S-function
mdlInitializeSizes method, using the following macros

ssSetInputPortSampleTime(S, idx, period)


ssSetInputPortOffsetTime(S, idx, offset)
ssSetOutputPortSampleTime(S, idx, period)
ssSetOutputPortOffsetTime(S, idx, offset)

The call to ssSetNumSampleTimes can be placed before or after the port-based sample times are
actually specified in mdlInitializeSizes. However, if ssSetNumSampleTimes does not configure
the S-function to use port-based sample times, any sample times set on the ports will be ignored.

For any given port, you can specify

• A specific sample time and period

For example, the following code sets the sample time of the S-function first input port to 0.1 and
the offset time to 0.

ssSetInputPortSampleTime(S, 0, 0.1);
ssSetInputPortOffsetTime(S, 0, 0);
• Inherited sample time (-1), i.e., the port inherits its sample time from the port to which it is
connected (see “Specifying Inherited Sample Time for a Port” on page 9-24)
• Constant sample time (Inf), i.e., the signal coming from the port is constant (see “Specifying
Constant Sample Time (Inf) for a Port” on page 9-24)

Note To be usable in a triggered subsystem, all your S-function ports must have either inherited
(–1) or constant sample time (Inf). For more information, see “Configuring Port-Based Sample
Times for Use in Triggered Subsystems” on page 9-25.

9-23
9 Implementing Block Features for C/C++ S-Functions

Specifying Inherited Sample Time for a Port

To specify that a port's sample time is inherited in a C MEX S-function, the mdlInitializeSizes
method should set its period to -1 and its offset to 0. For example, the following code specifies
inherited sample time for a C MEX S-function first input port:

ssSetInputPortSampleTime(S, 0, -1);
ssSetInputPortOffsetTime(S, 0, 0);

When you specify port-based sample times, the Simulink engine calls
mdlSetInputPortSampleTime and mdlSetOutputPortSampleTime to determine the rates of
inherited signals.

Once all rates have been determined, the engine calls mdlInitializeSampleTimes. Even though
there is no need to initialize port-based sample times at this point, the engine invokes this method to
give your S-function an opportunity to configure function-call connections. Your S-function must thus
provide an implementation for this method regardless of whether it uses port-based sample times or
function-call connections. Although you can provide an empty implementation, you might want to use
it to check the appropriateness of the sample times that the block inherited during sample time
propagation. Use ssGetInputPortSampleTime and ssGetOutputPortSampleTime in
mdlInitializeSampleTimes to obtain the values of the inherited sample times. For example, the
following code in mdlInitializeSampleTimes checks if the S-function first input inherited a
continuous sample time.
if (!ssGetInputPortSampleTime(S,0)) {
ssSetErrorStatus(S,"Cannot inherit a continuous sample time.")
};

Note If you specify that your S-function ports inherit their sample time, you should also specify
whether it is safe to use the S-function in a referenced model, i.e., a model referenced by another
model. See “Specifying Model Reference Sample Time Inheritance” on page 9-29 for more
information.

If you write TLC code to generate inlined code from an S-function, and if the TLC code contains an
Outputs function, you must modify the TLC code if these conditions are true:

• The output port has a constant value. It uses or inherits a sample time of Inf.
• The S-function is a multirate S-function or uses port-based sample times.

In this case, the TLC code must generate code for the constant-valued output port by using the
function OutputsForTID instead of the function Outputs. For more information, see “Specifying
Constant Sample Time (Inf) for a Port” on page 9-24.

To prevent ports from inheriting a sample time of Inf, set the option
SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME in the S-function code. In this case, you can use
the TLC function Outputs to generate code for constant-valued output ports.

Specifying Constant Sample Time (Inf) for a Port

If your S-function uses port-based sample times, it can set a sample time of Inf on any of its ports. A
port-based sample time of Inf means that the signal entering or leaving the port stays constant.

To make a port output a constant value, the S-function must:

9-24
Specify S-Function Sample Times

• Use ssSetOptions in its mdlInitializeSizes method to add support for a sample time of
Inf:

ssSetOptions(S,SS_OPTION_ALLOW_CONSTANT_PORT_SAMPLE_TIME);

Note This option causes the S-function’s ports to support a sample time of Inf, including ports
that inherit their sample times from other blocks. If any S-function ports that inherit sample time
cannot have a sample time of Inf, an error occurs. Set sample times for these ports using the
mdlSetInputPortSampleTime and mdlSetOutputPortSampleTime methods.
• Set the port's sample time to Inf and its offset to 0, e.g.,

ssSetInputPortSampleTime(S,0,mxGetInf());
ssSetInputPortOffsetTime(S,0,0);
• Check in mdlOutputs whether the method's tid argument equals CONSTANT_TID and if so, set
the value of the port's output if it is an output port.

To see an example of how to create ports which output a constant value, see
sfun_port_constant.c, the source file for the sfcndemo_port_constant example.

If you write TLC code to generate inlined code from an S-function, and if the TLC code contains an
Outputs function, modify the TLC code if all these conditions are true:

• The output port has a constant value. It uses or inherits a sample time of Inf.
• The S-function is a multirate S-function or uses port-based sample times.

In this case, the TLC code must generate code for the constant-valued output port by using the
function OutputsForTID instead of the function Outputs. The function OutputsForTID generates
output code for the constant-valued component of the S-function. If you configure a model to
generate multitasking code, OutputsForTID also generates output code for the periodic components
of the S-function.

For example, view the TLC file sfun_port_constant.tlc for the C S-function
sfun_port_constant.c in the model sfcndemo_port_constant. In the model, the input of the
block S-Function2 has a constant value throughout the simulation. In the S-function code, the first
output port inherits the sample time of the input port, so the output port also has a constant value.
The S-function code directly specifies a constant value for the second output port.

In the TLC code, if the port has a constant value, the function Outputs does not generate code for
the first output port. The function does not generate code for the second output port under any
circumstances because the port always has a constant value.

For this S-function, OutputsForTID generates code for output ports that have a constant value. The
code generator invokes the function OutputsForTID, and sets the argument tid to the task
identifier that corresponds to constant values. Only if the task identifier of an output port corresponds
to constant values does ,OutputsForTID then generate code for the port.

Configuring Port-Based Sample Times for Use in Triggered Subsystems

To use a C MEX S-function in a triggered subsystem, your port-based sample time S-function must
perform the following tasks.

• Use ssSetOptions in the mdlInitializeSizes method to indicate the S-function can run in a
triggered subsystem:

9-25
9 Implementing Block Features for C/C++ S-Functions

ssSetOptions(S,
SS_OPTION_ALLOW_PORT_SAMPLE_TIME_IN_TRIGSS);
• Set all of its ports to have either inherited (-1) or constant sample time (Inf) in its
mdlInitializeSizes method.
• Handle inheritance of a triggered sample time in mdlSetInputPortSampleTime and
mdlSetOutputPortSampleTime methods as follows.

Since the S-function ports inherit their sample times, the Simulink engine invokes either
mdlSetInputPortSampleTime or mdlSetOutputPortSampleTime during sample time
propagation. The macro ssSampleAndOffsetAreTriggered can be used in these methods to
determine if the S-function resides in a triggered subsystem. If the S-function does reside in a
triggered subsystem, whichever method is called must set the sample time and offset of the port
for which it is called to INHERITED_SAMPLE_TIME (-1).

Setting a port's sample time and offset both to INHERITED_SAMPLE_TIME indicates that the
sample time of the port is triggered, i.e., it produces an output or accepts an input only when the
subsystem in which it resides is triggered. The method must then also set the sample times and
offsets of all of the other S-function input and output ports to have either triggered or constant
sample time (Inf), whichever is appropriate, e.g.,
static void mdlSetInputPortSampleTime(SimStruct *S,
int_T portIdx,
real_T sampleTime
real_T offsetTime)
{
/* If the S-function resides in a triggered subsystem,
the sample time and offset passed to this method
are both equal to INHERITED_SAMPLE_TIME. Therefore,
if triggered, the following lines set the sample time
and offset of the input port to INHERITED_SAMPLE_TIME.*/

ssSetInputPortSampleTime(S, portIdx, sampleTime);


ssSetInputPortOffsetTime(S, portIdx, offsetTime);

/* If triggered, set the output port to inherited, as well */

if (ssSampleAndOffsetAreTriggered(sampleTime,offsetTime)) {
ssSetOutputPortSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOutputPortOffsetTime(S, 0, INHERITED_SAMPLE_TIME);

/* Note, if there are additional input and output ports


on this S-function, they should be set to either
inherited or constant at this point, as well. */
}
}

There is no way for an S-function residing in a triggered subsystem to predict whether the
Simulink engine will call mdlSetInputPortSampleTime or mdlSetOutputPortSampleTime to
set its port sample times. For this reason, both methods must be able to set the sample times of all
ports correctly so the engine has to call only one of the methods a single time.
• In mdlUpdate and mdlOutputs, use ssGetPortBasedSampleTimeBlockIsTriggered to
check whether the S-function resides in a triggered subsystem and if so, use appropriate
algorithms for computing its states and outputs.

See sfun_port_triggered.c, the source file for the sfcndemo_port_triggered example model,
for an example of how to create an S-function that can be used in a triggered subsystem.

9-26
Specify S-Function Sample Times

Hybrid Block-Based and Port-Based Sample Times


The hybrid method of assigning sample times combines the block-based and port-based methods. You
first specify, in mdlInitializeSizes, the total number of rates at which your block operates,
including both block and input and output rates, using ssSetNumSampleTimes.

You then set the SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED option, using ssSetOptions, to tell


the simulation engine that you are going to use the port-based method to specify the rates of the
input and output ports individually. Next, as in the block-based method, you specify the periods and
offsets of all of the block's rates, both internal and external, using

ssSetSampleTime
ssSetOffsetTime

Finally, as in the port-based method, you specify the rates for each port, using

ssSetInputPortSampleTime(S, idx, period)


ssSetInputPortOffsetTime(S, idx, offset)
ssSetOutputPortSampleTime(S, idx, period)
ssSetOutputPortOffsetTime(S, idx, offset)

Note that each of the assigned port rates must be the same as one of the previously declared block
rates. For an example S-function, see mixedm.c.

Note If you use the SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED option, your S-function cannot


inherit sample times. Instead, you must specify the rate at which each input and output port runs.

Multirate S-Function Blocks


In a multirate S-Function block, you can encapsulate the code that defines each behavior in the
mdlOutputs and mdlUpdate functions with a statement that determines whether a sample hit has
occurred. In a C MEX S-function, the ssIsSampleHit macro determines whether the current time is
a sample hit for a specified sample time. The macro has this syntax:

ssIsSampleHit(S, st_index, tid)

where S is the SimStruct, st_index identifies a specific sample time index, and tid is the task ID
(tid is an argument to the mdlOutputs and mdlUpdate functions).

For example, these statements in a C MEX S-function specify three sample times: one for continuous
behavior and two for discrete behavior.

ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetSampleTime(S, 1, 0.75);
ssSetSampleTime(S, 2, 1.0);

In the mdlUpdate function, the following statement encapsulates the code that defines the behavior
for the sample time of 0.75 second.

if (ssIsSampleHit(S, 1, tid)) {
}

The second argument, 1, corresponds to the second sample time, 0.75 second.

9-27
9 Implementing Block Features for C/C++ S-Functions

Use the following lines to encapsulate the code that defines the behavior for the continuous sample
hit:

if (ssIsContinuousTask(S,tid)) {
}

Example of Defining a Sample Time for a Continuous Block

This example defines a sample time for a block that is continuous.

/* Initialize the sample time and offset. */


static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
}

You must add this statement to the mdlInitializeSizes function.

ssSetNumSampleTimes(S, 1);

Example of Defining a Sample Time for a Hybrid Block

This example defines sample times for a hybrid S-Function block.

/* Initialize the sample time and offset. */


static void mdlInitializeSampleTimes(SimStruct *S)
{
/* Continuous state sample time and offset. */
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);

/* Discrete state sample time and offset. */


ssSetSampleTime(S, 1, 0.1);
ssSetOffsetTime(S, 1, 0.025);
}

In the second sample time, the offset causes the Simulink engine to call the mdlUpdate function at
these times: 0.025 second, 0.125 second, 0.225 second, and so on, in increments of 0.1 second.

The following statement, which indicates how many sample times are defined, also appears in the
mdlInitializeSizes function.

ssSetNumSampleTimes(S, 2);

Multirate S-Functions and Sample Time Hit Calculations


For fixed-step solvers, Simulink uses integer arithmetic, rather than floating-point arithmetic, to
calculate the sample time hits. Consequently, task times are integer multiples of their corresponding
sample time periods.

This calculation method becomes important if you consider performing Boolean logic based upon task
times in multirate S-functions. For example, consider an S-function that has two sample times. The
fact that (ssIsSampleHit(S, idx1) == true && ssIsSampleHit(S,idx2) == true, does not guarantee that
ssGetTaskTime(S, idx1) == ssGetTaskTime(S, idx2).

9-28
Specify S-Function Sample Times

Synchronizing Multirate S-Function Blocks


If tasks running at different rates need to share data, you must ensure that data generated by one
task is valid when accessed by another task running at a different rate. You can use the
ssIsSpecialSampleHit macro in the mdlUpdate or mdlOutputs routine of a multirate S-function
to ensure that the shared data is valid. This macro returns true if a sample hit has occurred at one
rate and a sample hit has also occurred at another rate in the same time step. It thus permits a higher
rate task to provide data needed by a slower rate task at a rate the slower task can accommodate.
When using the ssIsSpecialSampleHit macro, the slower sample time must be an integer multiple
of the faster sample time.

Suppose, for example, that your model has an input port operating at one rate (with a sample time
index of 0) and an output port operating at a slower rate (with a sample time index of 1). Further,
suppose that you want the output port to output the value currently on the input. The following
example illustrates usage of this macro.

if (ssIsSampleHit(S, 0, tid) {
if (ssIsSpecialSampleHit(S, 0, 1, tid) {
/* Transfer input to output memory. */
...
}
}

if (ssIsSampleHit(S, 1, tid) {
/* Emit output. */
...
}

In this example, the first block runs when a sample hit occurs at the input rate. If the hit also occurs
at the output rate, the block transfers the input to the output memory. The second block runs when a
sample hit occurs at the output rate. It transfers the output in its memory area to the block's output.

Note that higher-rate tasks always run before slower-rate tasks. Thus, the input task in the preceding
example always runs before the output task, ensuring that valid data is always present at the output
port.

Specifying Model Reference Sample Time Inheritance


If your C MEX S-function inherits its sample times from the blocks that drive it, your S-function
should specify whether referenced models containing your S-function can inherit sample times from
their parent model. If the S-function output does not depend on its inherited sample time, use the
ssSetModelReferenceSampleTimeInheritanceRule macro to set the S-function sample time
inheritance rule to USE_DEFAULT_FOR_DISCRETE_INHERITANCE. Otherwise, set the rule to
DISALLOW_SAMPLE_TIME_INHERITANCE to disallow sample-time inheritance for referenced models
that include S-functions whose outputs depend on their inherited sample time and thereby avoid
inadvertent simulation errors.

Note If your S-function does not set this flag, the Simulink engine assumes that it does not preclude
a referenced model containing it from inheriting a sample time. However, the engine optionally warns
you that the referenced model contains S-functions that do not specify a sample-time inheritance rule
(see “Blocks Whose Outputs Depend on Inherited Sample Time”).

9-29
9 Implementing Block Features for C/C++ S-Functions

If you are uncertain whether an existing S-function output depends on its inherited sample time,
check whether it invokes any of the following C macros:

• ssGetSampleTime
• ssGetInputPortSampleTime
• ssGetOutputPortSampleTime
• ssGetInputPortOffsetTime
• ssGetOutputPortOffsetTime
• ssGetInputPortSampleTimeIndex
• ssGetOutputPortSampleTimeIndex

or TLC functions:

• LibBlockSampleTime
• CompiledModel.SampleTime
• LibBlockInputSignalSampleTime
• LibBlockInputSignalOffsetTime
• LibBlockOutputSignalSampleTime
• LibBlockOutputSignalOffsetTime

If your S-function does not invoke any of these macros or functions, its output does not depend on its
inherited sample time and hence it is safe to use in referenced models that inherit their sample time.

Sample-Time Inheritance Rule Example

As an example of an S-function that precludes a referenced model from inheriting its sample time,
consider an S-function that has the following mdlOutputs method:

static void mdlOutputs(SimStruct *S, int_T tid) {


const real_T *u = (const real_T*)
ssGetInputPortSignal(S,0);
real_T *y = ssGetOutputPortSignal(S,0);
y[0] = ssGetSampleTime(S,tid) * u[0];
}

The output of this S-function is its inherited sample time, hence its output depends on its inherited
sample time, and hence it is unsafe to use in a referenced model. For this reason, this S-function
should specify its model reference inheritance rule as follows:

ssSetModelReferenceSampleTimeInheritanceRule
(S, DISALLOW_SAMPLE_TIME_INHERITANCE);

See Also
ssGetSampleTime | ssSetInputPortSampleTime | ssSetSampleTime

More About
• “SimStruct Macros and Functions Listed by Usage” on page 12-2

9-30
Zero Crossings

Zero Crossings
S-functions model zero crossings using the mode work vector (or a DWork vector configured as a
mode vector) and the continuous zero-crossing vector. Whether the S-function uses mode or DWork
vectors, the concept and implementation are the same. For an example using DWork vectors to model
zero crossings, see “DWork Mode Vector” on page 8-13 in the “Using Work Vectors” section. The
remainder of this section uses mode vectors to model zero crossings.

Elements of the mode vector are integer values. You specify the number of mode vector elements in
mdlInitializeSizes, using ssSetNumModes(S,num). You can then access the mode vector using
ssGetModeVector. The mode vector values determine how the mdlOutputs routine operates when
the solvers are homing in on zero crossings. The Simulink solvers track the zero crossings or state
events (i.e., discontinuities in the first derivatives) of some signal, usually a function of an input to
your S-function, by looking at the continuous zero crossings. Register the number of continuous zero
crossings in mdlInitializeSizes, using ssSetNumNonsampledZCs(S, num), then include an
mdlZeroCrossings routine to calculate the continuous zero crossings. The S-function
sfun_zc_sat.c contains a zero-crossing example. The remainder of this section describes the
portions of this S-function that pertain to zero-crossing detection. For a full description of this
example, see “Zero-Crossing Detection” on page 9-73.

First, mdlInitializeSizes specifies the sizes for the mode and continuous zero-crossing vectors
using the following lines of code.
ssSetNumModes(S, DYNAMICALLY_SIZED);
ssSetNumNonsampledZCs(S, DYNAMICALLY_SIZED);

Since the number of modes and continuous zero crossings is dynamically sized, mdlSetWorkWidths
must initialize the actual size of these vectors. In this example, shown below, there is one mode vector
for each output element and two continuous zero crossings for each mode. In general, the number of
continuous zero crossings needed for each mode depends on the number of events that need to be
detected. In this case, each output (mode) needs to detect when it hits the upper or the lower bound,
hence two continuous zero crossings per mode.
static void mdlSetWorkWidths(SimStruct *S)
{
int nModes;
int nNonsampledZCs;

nModes = numOutput;
nNonsampledZCs = 2 * numOutput;

ssSetNumModes(S,nModes);
ssSetNumNonsampledZCs(S,nNonsampledZCs);
}

Next, mdlOutputs determines which mode the simulation is running in at the beginning of each
major time step. The method stores this information in the mode vector so it is available when
calculating outputs at both major and minor time steps.
/* Get the mode vector */
int_T *mode = ssGetModeVector(S);

/* Specify three possible mode values.*/


enum { UpperLimitEquation, NonLimitEquation, LowerLimitEquation };

/* Update the mode vector at the beginning of a major time step */


if ( ssIsMajorTimeStep(S) ) {

9-31
9 Implementing Block Features for C/C++ S-Functions

for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {


if ( *uPtrs[uIdx] > *upperLimit ) {
/* Upper limit is reached. */
mode[iOutput] = UpperLimitEquation;

} else if ( *uPtrs[uIdx] < *lowerLimit ) {


/* Lower limit is reached. */
mode[iOutput] = LowerLimitEquation;

} else {
/* Output is not limited. */
mode[iOutput] = NonLimitEquation;
}

/* Adjust indices to give scalar expansion. */


uIdx += uInc;
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
}
/* Reset index to input and limits. */
uIdx = 0;
upperLimit = mxGetPr( P_PAR_UPPER_LIMIT );
lowerLimit = mxGetPr( P_PAR_LOWER_LIMIT );

} /* end IsMajorTimeStep */

Output calculations in mdlOutputs are done based on the values stored in the mode vector.
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
if ( mode[iOutput] == UpperLimitEquation ) {
/* Output upper limit. */
*y++ = *upperLimit;

} else if ( mode[iOutput] == LowerLimitEquation ) {


/* Output lower limit. */
*y++ = *lowerLimit;

} else {
/* Output is equal to input */
*y++ = *uPtrs[uIdx];
}

After outputs are calculated, the Simulink engine calls mdlZeroCrossings to determine if a zero
crossing has occurred. A zero crossing is detected if any element of the continuous zero-crossing
vector switches from negative to positive, or positive to negative. If this occurs, the simulation
modifies the step size and recalculates the outputs to try to locate the exact zero crossing. For this
example, the values for the continuous zero-crossing vectors are calculated as shown below.
static void mdlZeroCrossings(SimStruct *S)
{
int_T iOutput;
int_T numOutput = ssGetOutputPortWidth(S,0);
real_T *zcSignals = ssGetNonsampledZCs(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

/* Set index and increment for the input signal, upper limit, and lower
* limit parameters so that each gives scalar expansion if needed. */
int_T uIdx = 0;
int_T uInc = ( ssGetInputPortWidth(S,0) > 1 );
const real_T *upperLimit = mxGetPr( P_PAR_UPPER_LIMIT );
int_T upperLimitInc = ( mxGetNumberOfElements( P_PAR_UPPER_LIMIT ) > 1 );
const real_T *lowerLimit = mxGetPr( P_PAR_LOWER_LIMIT );
int_T lowerLimitInc = ( mxGetNumberOfElements( P_PAR_LOWER_LIMIT ) > 1 );

/*Check if the input has crossed an upper or lower limit */


for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
zcSignals[2*iOutput] = *uPtrs[uIdx] - *upperLimit;

9-32
Zero Crossings

zcSignals[2*iOutput+1] = *uPtrs[uIdx] - *lowerLimit;

/* Adjust indices to give scalar expansion if needed */


uIdx += uInc;
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
}
}

See Also
ssGetNumNonsampledZCs

More About
• “SimStruct Macros and Functions Listed by Usage” on page 12-2

9-33
9 Implementing Block Features for C/C++ S-Functions

S-Function Compliance with the ModelOperatingPoint

ModelOperatingPoint Compliance Specification for C MEX S-Functions


As with the MATLAB S-function, your C MEX S-function code must inform Simulink of the S-function
compliance with the ModelOperatingPoint feature. You can accomplish this task by using the S-
function API, ssSetOperatingPointCompliance.

In most cases, specifying the compliance to be default is sufficient to save and restore the necessary
state data. To specify the default compliance, add this line:

ssSetOperatingPointCompliance(S, USE_DEFAULT_OPERATING_POINT).

The options are as follows:

Setting Result
OPERATING_POINT_COMPLIANCE_UNK This is the default setting for all S-functions. For S-functions that do
NOWN not use PWorks, Simulink saves and restores the default simulation
operating point (see USE_DEFAULT_OPERATING_POINT) and issues a
warning to inform the user of this assumption. On the other hand,
Simulink reports an error during the save and restore if it encounters
an S-function that uses PWorks.
USE_DEFAULT_OPERATING_POINT This setting instructs Simulink to treat the S-function like a built-in
block when saving and restoring the ModelOperatingPoint object.
This setting saves continuous states, non-scratch Dworks, and zero
crossing signal information
USE_EMPTY_OPERATING_POINT This setting informs Simulink that the S-function does not have any
simulation state. With this setting, no state information is saved for
this block. This setting is primarily useful for "sink" blocks (i.e.,
blocks with no output ports) that use PWorks or DWorks to store
handles to files or figure windows.

Note This setting is not allowed if the S-function registers any


discrete or continuous states or zero crossing signals.
DISALLOW_OPERATING_POINT This setting informs Simulink that the S-function does not allow the
saving or restoring of its operating point. Simulink reports an error if
you try to save or restore the ModelOperatingPoint object of the
model that contains this S-function. You can use this setting if the S-
function communicates with a 3rd party library and state serialization
is not possible.
USE_CUSTOM_OPERATING_POINT This setting informs Simulink that the S-function has
mdlGetOperatingPoint and mdlSetOperatingPoint methods.

For S-functions that use PWork vectors or static variables to hold data that Simulink updates during
simulation, the S-function must use the custom mdlGetOperatingPoint and
mdlSetOperatingPoint methods. The following statements demonstrate the proper format.

mxArray* mdlGetOperatingPoint(SimStruct* S)
void mdlSetOperatingPoint(SimStruct* S, const mxArray* inSS)

9-34
S-Function Compliance with the ModelOperatingPoint

For an example of how to implement these methods, see “Custom Code and Hand Coded Blocks using
the S-function API”.

See Also
GetOperatingPoint | SetOperatingPoint | mdlGetOpeartingPoint |
mdlSetOperatingPoint | ssSetOperatingPointCompliance |
ssSetOperatingPointVisibility

More About
• “SimStruct Macros and Functions Listed by Usage” on page 12-2
• “Save and Restore Simulation Operating Point”
• “Operating Point Behavior”

9-35
9 Implementing Block Features for C/C++ S-Functions

MATLAB Data in C S-Functions


MATLAB data is represented as mxArrays in C/C++ language. The mxArray structure typically
contains type, dimension, data, data type, sparsity, and the field and field numbers of the MATLAB
array. In S-functions, “Pass Dialog Parameters to S-Functions” on page 9-2 values evaluated in
MATLAB are transferred into Simulink as an mxArray. See “C Matrix API” for a list of functions.

S-functions read MATLAB data mainly for the following purposes:

• Access block dialog parameters using mxArrays


• Pass arguments to or from a MATLAB Function using mexCallMATLAB

mxArray Manipulation
You can manipulate mxArrays in S-functions using the standard MATLAB API functions. In general, if
your S-function is declared exception free by passing the SS_OPTION_EXCEPTION_FREE_CODE
option to ssSetOptions (see Exception Free Code on page 9-45 in “Handle Errors in S-Functions”
on page 9-44), it should avoid MATLAB API functions that throw exceptions (i.e., long jump), such as
mxCreateDoubleMatrix. Otherwise, the S-function can use any of the listed functions.

Note S-function parameters are read-only within the S-function algorithm. You can modify parameter
values via the S-function block dialog or mask.

If you have Simulink Coder, it supports a subset of the mxArray manipulation functions when
generating noninlined code for an S-function. For a list of supported functions, see “Write Noninlined
S-Function” (Simulink Coder).

Calls to the macro ssGetSFcnParam return a pointer to an mxArray, which can be used with the
mxArray manipulation functions. If your S-function contains S-function parameters, use the mxArray
manipulation functions in the mdlCheckParameters method to check the S-function parameter
values. See the S-function sfun_runtime3.c for an example

In this S-function, the following lines check that the first S-function parameter is a character array
with a length greater than or equal to two.
if (!mxIsChar(ssGetSFcnParam(S, 0)) ||
(nu=mxGetNumberOfElements(ssGetSFcnParam(S, 0))) < 2) {
ssSetErrorStatus(S,"1st parameter to S-function must be a "
"string of at least 2 '+' and '-' characters");
return;
}

mxArrays Using 32-bit APIs


To write C/C++ programs that work with MATLAB mxArray data structure, use C matrix APIs. C
matrix APIs support 32-bit and 64-bit indexing. By default, your S-functions are built using 32-bit
APIs. Check “Upgrade MEX Files to Use 64-Bit API” to see how to upgrade your existing MEX files.

To check the use of 32-bit APIs, you can run the S-function upgrade advisor. In the Modeling tab,
select Model Advisor > Upgrade Advisor.

If you build your code with -largeArrayDims and your code populates the ssParamRec structure's
dimension field with mxArray dimensions, starting R2018a, you can no longer cast the return value

9-36
MATLAB Data in C S-Functions

of mxGetDimensions to an int_T pointer because mxGetDimensions now returns a size_T


pointer on 64-bit platforms. As a workaround, create a temporary copy of type int_T and assign it to
the dims field of ssParamRec structure.

Replace: With:
ssParamRec p; ssParamRec p;
p.dims = (int_T *) mxGetDimensions(ssGetSFcnParam(S,
const mxArray*
0)); mxPrm = ssGetSFcnParam(S, 0);
// Set up other fields of p const mwSize* mxDims = mxGetDimensions(mxPrm);
if (!ssSetRunTimeParamInfo(S, 0, &p)) { const mwSize mxNumDims = mxGetNumberOfDimensions(mxPr
free(dims); mwSize idx;
return; int_T * dims = malloc(sizeof(int)*mxNumDims);
} for (idx=0; idx < mxNumDims; idx++)
free(dims); // free memory allocated for dimensions
{
dims[idx] = mxDims[idx];
}

p.dims = dims;
// Set up other fields of p

if (!ssSetRunTimeParamInfo(S, 0, &p)) {
free(dims);
return;
}
free(dims); // free memory allocated for dimensions

mxArrays Using Interleaved Complex Representation


Until MATLAB version 9.4 (R2018a), mxArrays used separate complex representation where the real
and imaginary parts of a complex number were stored separately. Starting version 9.4 (R2018a),
MATLAB uses interleaved representation to store complex data, where the real and imaginary parts
of a complex numbers are stored together. For S-functions to manipulate mxArrays, there is no
longer need to convert data from separate to interleaved complex. See “MATLAB Support for
Interleaved Complex API in MEX Functions” for more information on how to update your code.

Note To store complex data for input and output signals or DWorks on page 8-2, Simulink uses
interleaved representation.

To automatically check your S-functions on potential interleaved complex data issues, in the
Modeling tab, select Model Advisor > Upgrade Advisor. In the Upgrade Advisor window, select
Check model for S-function upgrade issues.

The following code sample copies a parameter value from an mxArray to the output of the S-function.
In this example, the dimensions, data type, and the complexity of the parameter and the output
argument are ensured to be the same. For example, if the parameter is complex, the output signal is
complex. If the parameter is real, the output signal is real. For the example below, the data type of
both the parameter and the output is a double (real_T). Before R2018a, you could use mxGetPr and
mxGetPr to copy the real and imaginary parts of a complex data separately. With interleaved complex
representation, you can use memcpy, which is a single copy instruction.

9-37
9 Implementing Block Features for C/C++ S-Functions

Replace: With:
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *y = ssGetOutputPortRealSig
boolean_T yIsComplex = ssGetOutputPortComplexSignal(S,
boolean_T yIsComplex
0) == COMPLEX_YES;
= ssGetOutputPortComplexSi
int_T yWidth = ssGetOutputPortWidth(S,0);
int_T yWidth = ssGetOutputPortWidth(S

const real_T *pr = mxGetPr(ssGetSFcnParam(S,


mxComplexDouble
0)); *pc;
const real_T *pi = mxGetPi(ssGetSFcnParam(S,
mxDouble *pr;
0));

int i; if (yIsComplex) {

for (i = 0; i < yWidth; i++) { pc = mxGetComplexDoubles(ssGetSFcnParam(S, 0)


memcpy(y, pc, yWidth*sizeof(mxComplexDouble))
int_T idx = (yIsComplex) ? 2*i : i;
y[idx] = pr[idx]; } else {

if(yIsComplex){ pr = mxGetDoubles(ssGetSFcnParam(S, 0));


y[idx+1] = pi[idx]; memcpy(y, pr, yWidth*sizeof(mxDouble));
}
} }
a

a. sizeof(mxComplexDouble)=2*sizeof(mxDouble)=2*sizeof(real_T)

See Also
S-Function | S-Function Builder

More About
• “MATLAB Support for 64-Bit Indexing”
• “MATLAB Support for Interleaved Complex API in MEX Functions”
• “Upgrade MEX Files to Use Interleaved Complex API”

9-38
Implement Function-Call Subsystems with S-Functions

Implement Function-Call Subsystems with S-Functions


You can create a triggered subsystem whose execution is determined by logic internal to a C MEX S-
function instead of by the value of a signal. A subsystem so configured is called a function-call
subsystem. To implement a function-call subsystem:

• In the Trigger block, select function-call as the Trigger type parameter.


• In the S-function, use the ssEnableSystemWithTid and ssDisableSystemWithTid to enable
or disable the triggered subsystem and the ssCallSystemWithTid macro to call the triggered
subsystem.
• In the model, connect the S-Function block output directly to the trigger port.

Note Function-call connections can only be performed on the first output port.

Function-call subsystems are not executed directly by the Simulink engine; rather, the S-function
determines when to execute the subsystem. When the subsystem completes execution, control
returns to the S-function. This figure illustrates the interaction between a function-call subsystem and
an S-function.

In this figure, ssCallSystemWithTid executes the function-call subsystem that is connected to the
first output port element. ssCallSystemWithTid returns 0 if an error occurs while executing the
function-call subsystem or if the output is unconnected. After the function-call subsystem executes,
control is returned to your S-function.

Function-call subsystems can only be connected to S-functions that have been properly configured to
accept them.

To configure an S-function to call a function-call subsystem:

• In mdlInitializeSizes, set the data type of the S-function first output port to function-call by
specifying
ssSetOutputPortDataType(S, 0, SS_FCN_CALL);
• Specify the elements that are to execute the function-call subsystem in
mdlInitializeSampleTimes. For example:
ssSetCallSystemOutput(S,0); /* call on first element */
ssSetCallSystemOutput(S,1); /* call on second element */
• Specify in mdlInitializeSampleTimes whether you want the S-function to be able to enable or
disable the function-call subsystem. Only S-functions that explicitly enable and disable the
function-call subsystem can reset the states and outputs of the subsystem, as determined by the
function-call subsystem's Trigger and Outport blocks. For example, the code
ssSetExplicitFCSSCtrl(S, 1);

9-39
9 Implementing Block Features for C/C++ S-Functions

in mdlInitializeSampleTimes specifies that the S-function can enable and disable the
function-call subsystem. In this case, the S-function must invoke ssEnableSystemWithTid
before executing the subsystem using ssCallSystemWithTid.
• Execute the subsystem in the appropriate mdlOutputs or mdlUpdate S-function routine. For
example:

static void mdlOutputs(...)


{
if (((int)*uPtrs[0]) % 2 == 1) {
if (!ssCallSystemWithTid(S,0,tid)) {
/* Error occurred, which will be reported by */
/*the Simulink engine*/
return;
}
} else {
if (!ssCallSystemWithTid(S,1,tid)) {
/* Error occurred, which will be reported by */
/*the Simulink engine*/
return;
}
}
...
}

See sfun_fcncall.c for an example that executes a function-call subsystem on the first and second
elements of the first S-function output. The following Simulink model (sfcndemo_sfun_fcncall) uses
this S-function.

The first function-call subsystem provides a sine wave output. The second function-call subsystem is a
simple feedback loop containing a Unit Delay block.

9-40
Implement Function-Call Subsystems with S-Functions

When the Pulse Generator emits its upper value, the function-call subsystem connected to the first
element of the first S-function output port is triggered. Similarly, when the Pulse Generator emits its
lower value, the function-call subsystem connected to the second element is triggered. The simulation
output is shown on the following Scope.

9-41
9 Implementing Block Features for C/C++ S-Functions

Function-call subsystems are a powerful modeling construct. You can configure Stateflow blocks to
execute function-call subsystems, thereby extending the capabilities of the blocks. For more
information, see the Stateflow documentation.

See Also
ssCallSystemWithTid | ssDisableSystemWithTid | ssEnableSystemWithTid

More About
• “SimStruct Macros and Functions Listed by Usage” on page 12-2

9-42
Use C/C++ S-Functions as Sim Viewing Devices in External Mode

Use C/C++ S-Functions as Sim Viewing Devices in External


Mode
A sim viewing device encapsulates processing and viewing of signals received from the target system
in external mode. During simulation in external mode, the target system uploads the appropriate
input values to the sim viewing device in the Simulink model. The sim viewing device then conditions
the input signals as needed and renders the signals on the screen. A sim viewing device runs only on
the host, generating no code in the target system and, therefore, allowing extra processing of
displayed signals without burdening the generated code. You can use your S-function as a sim
viewing device in external mode if it satisfies the following conditions.

• The S-function has no output ports.


• The S-function contains no states.
• The generated code does not require the conditioned signals produced by the S-function.

To specify a C MEX S-function as a sim viewing device, set the SS_OPTION_SIM_VIEWING_DEVICE


option in the mdlInitializeSizes function. For example

ssSetOptions(S, SS_OPTION_SIM_VIEWING_DEVICE);

When simulating a model in Rapid Accelerator mode with signal logging enabled, no data is logged
for antenna elements or To Workspace blocks that are inside of a sim viewing device.

External mode compatible S-functions are selected, and the trigger is armed, by using the External
Signal & Triggering dialog box. For more information see “External Mode Simulation with TCP/IP or
Serial Communication” (Simulink Coder).

See Also
mdlInitializeSizes

More About
• “SimStruct Macros and Functions Listed by Usage” on page 12-2

9-43
9 Implementing Block Features for C/C++ S-Functions

Handle Errors in S-Functions


In this section...
“About Handling Errors” on page 9-44
“Exception Free Code” on page 9-45
“ssSetErrorStatus Termination Criteria” on page 9-45
“Checking Array Bounds” on page 9-46

About Handling Errors


When working with S-functions, it is important to handle unexpected events such as invalid
parameter values correctly.

If your C MEX S-function has parameters whose contents you need to validate, use the following
technique to report errors.

ssSetErrorStatus(S,"Error encountered due to ...");


return;

In most cases, the Simulink engine displays errors in the Diagnostic Viewer. If the error is
encountered in mdlCheckParameters as the S-function parameters are being entered into the block
dialog, the engine opens the error dialog shown below. In either case, the engine displays the error
message along with the name of the S-function and the associated S-function block that invoked the
error.

The second argument to ssSetErrorStatus must be persistent memory. It cannot be a local


variable in your function. For example, the following causes unpredictable errors.

mdlOutputs()
{
char msg[256]; /* ILLEGAL: should be "static char */
/*msg[256];"*/
sprintf(msg,"Error due to %s", string);
ssSetErrorStatus(S,msg);
return;
}

Because ssSetErrorStatus does not generate exceptions, using it to report errors in your S-
function is preferable to using mexErrMsgTxt. The mexErrMsgTxt function uses exception handling
to terminate S-function execution. To support exception handling in S-functions, the Simulink engine
must set up exception handlers prior to each S-function invocation. This introduces overhead into
simulation.

9-44
Handle Errors in S-Functions

Exception Free Code


You can avoid simulation overhead by ensuring that your C MEX S-function contains entirely
exception free code. Exception free code refers to code that never long-jumps. Your S-function is not
exception free if it contains any routine that, when called, has the potential of long-jumping. For
example, mexErrMsgTxt throws an exception (i.e., long-jumps) when called, thus ending execution of
your S-function. Using mxCalloc can cause unpredictable results in the event of a memory allocation
error, because mxCalloc long-jumps. If memory allocation is needed, use the stdlib.h calloc
routine directly and perform your own error handling.

If you do not call mexErrMsgTxt or other API routines that cause exceptions, use the
SS_OPTION_EXCEPTION_FREE_CODE S-function option. You do this by issuing the following
command in the mdlInitializeSizes function.

ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);

Setting this option increases the performance of your S-function by allowing the Simulink engine to
bypass the exception-handling setup that is usually performed prior to each S-function invocation.
You must take extreme care to verify that your code is exception free when using
SS_OPTION_EXCEPTION_FREE_CODE. If your S-function generates an exception when this option is
set, unpredictable results occur.

All mex* routines have the potential of long-jumping. Several mx* routines also have the potential of
long-jumping. To avoid any difficulties, use only the API routines that retrieve a pointer or determine
the size of parameters. For example, the following API routines never throw an exception: mxGetPr,
mxGetData, mxGetNumberOfDimensions, mxGetM, mxGetN, and mxGetNumberOfElements.

Code in run-time routines can also throw exceptions. Run-time routines refer to certain S-function
routines that the engine calls during the simulation loop (see “Simulink Engine Interaction with C S-
Functions” on page 4-50). The run-time routines include

• mdlGetTimeOfNextVarHit
• mdlOutputs
• mdlUpdate
• mdlDerivatives

If all run-time routines within your S-function are exception free, you can use this option:

ssSetOptions(S, SS_OPTION_RUNTIME_EXCEPTION_FREE_CODE);

The other routines in your S-function do not have to be exception free.

ssSetErrorStatus Termination Criteria


If one of your C MEX S-function callback methods invokes ssSetErrorStatus during a simulation,
the Simulink engine posts the error and terminates the simulation as soon as the callback method
returns. If your S-function SS_OPTION_CALL_TERMINATE_ON_EXIT option is enabled (see
ssSetOptions), The engine invokes your S-function mdlTerminate method as part of the
termination process. Otherwise, the engine invokes your S-function mdlTerminate method only if at
least one block mdlStart method has executed without error during the simulation.

9-45
9 Implementing Block Features for C/C++ S-Functions

Checking Array Bounds


If your C MEX S-function causes otherwise inexplicable errors, the reason might be that the S-
function is writing beyond its assigned areas in memory. You can verify this possibility by enabling the
array bounds checking feature. This feature detects any attempt by an S-Function block to write
beyond the areas assigned to it for the following types of block data:

• Work vectors (R, I, P, D, and mode)


• States (continuous and discrete)
• Outputs

To enable array bounds checking, select warning or error from the Array bounds exceeded
options list on the Configuration Parameters dialog box. Alternatively, enter the following
command at the MATLAB command prompt.

set_param(modelName, 'ArrayBoundsChecking', ValueStr)

where modelName is the name of the Simulink model and ValueStr is either 'none', 'warning',
or 'error'.

See Also
mdlInitializeSizes | ssSetErrorStatus

More About
• “SimStruct Macros and Functions Listed by Usage” on page 12-2

9-46
Guidelines for Writing Thread-Safe S-Functions

Guidelines for Writing Thread-Safe S-Functions


Simulink lets you run S-functions in parallel with multithreaded programming, which makes
simulations run more quickly than serial runs. Multithreaded programming with S-functions requires
you to make S-functions thread-safe. Creating thread-safe code involves ensuring that data shared
between multiple threads is protected so that data and results are as expected. Simulation with S-
functions that are not thread-safe might cause unexpected behavior.

Background
C/C++ S-functions are implemented in C or C++ and built into shared libraries known as MEX files.
When an S-function block refers to a shared library, MATLAB loads the S-function block into the
process. When multiple S-function blocks refer to the same shared library, they also refer to the initial
shared library copy. This process results in multiple S-function blocks sharing the same data owned
by the shared library. Thus, multithreaded S-function blocks access the same data at the same time.

In addition, if these S-functions refer to the same resources, multithreaded S-function blocks can
access the same resources (such as files) at the same time, even when the S-function blocks are
associated with different S-functions.

Guidelines
An S-function is generally considered thread-safe when it can safely execute concurrently using
multiple threads. To designate an S-function as thread-safe, use the
ssSetRuntimeThreadSafetyCompliance function. If you are not sure about the thread-safety of
your S-function, use these guidelines to investigate and make it thread-safe.

Data Share

Definition Problem Solution


S-function refers to data using Multiple threads can use Be cautious when accessing
pointers (for example, pointers to access the same data shared by multiple threads.
ssSetUserData, data. If the threads try to write
ssGetUserData, to the same memory location at • Make the data constant if it
ssSetPWorkValue and the same time, they violate is read-only.
ssGetPWorkValue). An S- thread-safety. Concurrent reads • To control access to data,
function easily shares data from multiple threads are safe consider using mutexes.
using these pointers. as long as there are no writes Mutexes can also ensure
before, during, or after the cache coherence.
reads, which can cause
incoherent caches.

9-47
9 Implementing Block Features for C/C++ S-Functions

Global Variables

Definition Problem Solution


Global variables are shared data Multiple threads writing to non- • If you are writing to global
accessible throughout an protected shared data is not data, localize it, control
application. safe. Reading is safe as long as access to it using mutexes,
there are no writes before, or change the semantics of
during, or after the reads, which your algorithm. To ensure
can cause incoherent caches. cache coherence, also
consider using memory
fences such as atomic or
mutex.
• If you are reading, make
your global variable
constant.

Local Static Variables Initialization

Definition Problem Solution


Local static variables are stored If multiple threads enter the • If you are using a C++
in one location. function scope at the same time, version prior to C++11,
the software makes multiple protect the initialization of
• In C++, local static variables attempts to write to the same local static variable using a
are initialized upon entering location. This issue holds even if mutex or thread-safe
the function scope and are the local static variable is initialization mechanism,
not thread-safe. constant. such as std::call_once or
• In C, local static variables boost::call_once.
are initialized at the start of • If you are using C++11 or
the application as long as the later, local static variable
value on the right of the initialization is guaranteed to
assignment (the value being be thread-safe.
assigned into the variable) is
compile-time constant. These
variables are thread-safe.

Resources

Definition Problem Solution


Resources are entities that are Accessing resources from Be cautious when managing a
explicitly requested from and multiple threads might not be resource. Thread-safety of a
returned to the system. Some thread-safe, such as reading and resource depends on its
examples of resources include writing to a file from multiple implementation. For more
dynamically allocated memory, threads. Even if these information about thread-safety
files, database connections, and operations are thread-safe, they specifications, see the resource
network sockets. Your might not produce the expected documentation. Optionally, you
application might need to results. can guard access to the
manage resources. resource using a mechanism
such as a mutex.

9-48
Guidelines for Writing Thread-Safe S-Functions

Reentrancy

Definition Problem Solution


A function is reentrant if it is Calling a nonreentrant function Make your function reentrant.
safe to call multiple times from from multiple threads might not For example:
the same thread (recursively) . be safe.
For example, the strtok • Eliminate the states the
function is not reentrant function holds.
because it keeps some internal • Replace nonreentrant
state of the string to be functions calls with
tokenized. A function is reentrant equivalents. For
reentrant if it does not call example, replace strtok
nonreentrant functions. with strtok_r.

mexCallMATLAB

Definition Problem Solution


An S-function might call Simulink code that handles Do not call mexCallMATLAB in
MATLAB using the mexCallMATLAB functionality is your S-function.
mexCallMATLAB function. not thread-safe.

Exception Free Code

Definition Problem Solution


An S-function is exception free When an S-function is not Examine your S-function for
as long as none of its exception free, its subroutines long jumps. If there are none,
subroutines, when called, has are indirectly called through mark the S-function as
the potential of long jumping. mexCallMATLAB, which is not exception free using the
For more information about thread-safe (see ssSetOptions(S,
exception free S-functions, see mexCallMATLAB). SS_OPTION_EXCEPTION_FREE
“Exception Free Code” on page _CODE) function. If the S-
9-45. function long jumps despite this
flag, unpredictable behavior
occurs.

If an S-function throws an
exception but uses a try/catch
block to catch the exception,
that S-function is safe.

Data Race

Definition Problem Solution


Data race occurs when the The application might have Consider one of the following:
output of your application unexpected behavior.
depends on the order of • Revise your algorithm to
execution such that the eliminate data races.
behavior of your application • Use locks to control the
changes between executions. order of execution in critical
parts of your code, or make
critical operations atomic.

9-49
9 Implementing Block Features for C/C++ S-Functions

Volatile

Definition Problem Solution


The volatile keyword tells the Applications might mistakenly Do not use the volatile
compiler not to optimize a use volatile to achieve keyword for thread-safety.
variable because its value might thread-safety. volatile does
be changed by some mechanism not provide atomicity or
that the compiler is not aware synchronization among threads.
of.

Error Status

Definition Problem Solution


S-functions use ssSetErrorStatus and Use the thread-safe
ssSetErrorStatus, ssGetErrorStatus are not ssSetLocalErrorStatus, and
ssGetErrorStatus, thread-safe. These functions can ssGetLocalErrorStatus
ssSetLocalErrorStatus, and overwrite existing errors and functions. Do not use
ssGetLocalErrorStatus to cause reporting of inaccurate ssSetErrorStatus and
access error status. errors. For example, Block A ssGetErrorStatus.
might report the error thrown
by Block B.

See Also
ssGetLocalErrorStatus | ssSetLocalErrorStatus

More About
• “Exception Free Code” on page 9-45
• “Run Co-Simulation Components on Multiple Cores”

9-50
C MEX S-Function Examples

C MEX S-Function Examples


In this section...
“About S-Function Examples” on page 9-51
“Continuous States” on page 9-51
“Discrete States” on page 9-55
“Continuous and Discrete States” on page 9-59
“Variable Sample Time” on page 9-63
“Array Inputs and Outputs” on page 9-67
“Zero-Crossing Detection” on page 9-73
“Discontinuities in Continuous States” on page 9-83

About S-Function Examples


All examples are based on the C MEX S-function templates sfuntmpl_basic.c and
sfuntmpl_doc.c. Open sfuntmpl_doc.c. for a detailed discussion of the S-function template.

Continuous States
The csfunc.c example shows how to model a continuous system with states using a C MEX S-
function. The following Simulink model uses this S-function.

sfcndemo_csfunc

In continuous state integration, the Simulink solvers integrate a set of continuous states using the
following equations.

S-functions that contain continuous states implement a state-space equation. The mdlOutputs
method contains the output portion and mdlDerivatives method contains the derivative portion of
the state-space equation. To visualize how the integration works, see the flowchart in “Simulink
Engine Interaction with C S-Functions” on page 4-50. The output equation corresponds to the
mdlOutputs in the major time step. Next, the example enters the integration section of the
flowchart. Here the Simulink engine performs a number of minor time steps during which it calls
mdlOutputs and mdlDerivatives. Each of these pairs of calls is referred to as an integration
stage. The integration returns with the continuous states updated and the simulation time moved
forward. Time is moved forward as far as possible, providing that error tolerances in the state are
met. The maximum time step is subject to constraints of discrete events such as the actual simulation
stop time and the user-imposed limit.

The csfunc.c example specifies that the input port has direct feedthrough. This is because matrix D
is initialized to a nonzero matrix. If D is set equal to a zero matrix in the state-space representation,

9-51
9 Implementing Block Features for C/C++ S-Functions

the input signal is not used in mdlOutputs. In this case, the direct feedthrough can be set to 0,
which indicates that csfunc.c does not require the input signal when executing mdlOutputs.

matlabroot/toolbox/simulink/simdemos/simfeatures/src/csfunc.c

The S-function csfunc.c begins with #define statements for the S-function name and level, and a
#include statement for the simstruc.h header. After these statements, the S-function can include
or define any other necessary headers, data, etc. The csfunc.c example defines the variable U as a
pointer to the first input port's signal and initializes static variables for the state-space matrices.
/* File : csfunc.c
* Abstract:
*
* Example C S-function for defining a continuous system.
*
* x' = Ax + Bu
* y = Cx + Du
*
* For more details about S-functions, see simulink/src/sfuntmpl_doc.c.
*
* Copyright 1990-2013 The MathWorks, Inc.
*/

#define S_FUNCTION_NAME csfunc


#define S_FUNCTION_LEVEL 2

#include "simstruc.h"

#define U(element) (*uPtrs[element]) /* Pointer to Input Port0 */

static real_T A[2][2]={ { -0.09, -0.01 } ,


{ 1 , 0 }
};

static real_T B[2][2]={ { 1 , -7 } ,


{ 0 , -2 }
};

static real_T C[2][2]={ { 0 , 2 } ,


{ 1 , -5 }
};

static real_T D[2][2]={ { -3 , 0 } ,


{ 1 , 0 }
};

The required S-function method mdlInitializeSizes then sets up the following S-function
characteristics.

• ssSetNumSFcnParams sets the number of expected S-function dialog parameters to zero.


• ssGetSFcnParamsCount determines how many parameters the user actually entered into the S-
function dialog. If the number of user-specified parameters does not match the number returned
by ssGetNumSFcnParams, the S-function errors out.
• If the S-function parameter count passes, mdlInitializeSizes sets the number of continuous
and discrete states using ssSetNumContStates and ssSetNumDiscStates, respectively. This
example has two continuous states and zero discrete states.
• Next, the method configures the S-function to have a single input and output port, each with a
width of two to match the dimensions of the state-space matrices. The method passes a value of 1
to ssSetInputPortDirectFeedThrough to indicate the input port has direct feedthrough.
• ssSetNumSampleTimes initializes one sample time, which the mdlInitializeSampleTimes
function configures later.
• The S-function indicates that no work vectors are used by passing a value of 0 to
ssSetNumRWork, ssSetNumIWork, etc. You can omit these lines because zero is the default value
for all of these macros. However, for clarity, the S-function explicitly sets the number of work
vectors.

9-52
C MEX S-Function Examples

• Lastly, ssSetOptions sets any applicable options. In this case, the only option is
SS_OPTION_EXCEPTION_FREE_CODE, which stipulates that the code is exception free.

The mdlInitializeSizes function for this example is shown below.


/*====================*
* S-function methods *
*====================*/

/* Function: mdlInitializeSizes ===============================================


* Abstract:
* Determine the S-function block's characteristics:
* number of inputs, outputs, states, etc.
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch reported by the Simulink engine*/
}

ssSetNumContStates(S, 2);
ssSetNumDiscStates(S, 0);

if (!ssSetNumInputPorts(S, 1)) return;


ssSetInputPortWidth(S, 0, 2);
ssSetInputPortDirectFeedThrough(S, 0, 1);

if (!ssSetNumOutputPorts(S, 1)) return;


ssSetOutputPortWidth(S, 0, 2);

ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);

/* Take care when specifying exception free code - see sfuntmpl_doc.c */


ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}

The required S-function method mdlInitializeSampleTimes specifies the S-function sample


rates. The value CONTINUOUS_SAMPLE_TIME passed to the ssSetSampleTime macro specifies that
the first S-function sample rate be continuous. ssSetOffsetTime then specifies an offset time of
zero for this sample rate. The call to ssSetModelReferenceSampleTimeDefaultInheritance
tells the solver to use the default rule to determine if referenced models containing this S-function
can inherit their sample times from the parent model.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* Specify that we have a continuous sample time.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}

The optional S-function method mdlInitializeConditions initializes the continuous state vector.
The #define statement before this method is required for the Simulink engine to call this function.
In the example below, ssGetContStates obtains a pointer to the continuous state vector. The for
loop then initializes each state to zero.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ========================================

9-53
9 Implementing Block Features for C/C++ S-Functions

* Abstract:
* Initialize both continuous states to zero.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x0 = ssGetContStates(S);
int_T lp;

for (lp=0;lp<2;lp++) {
*x0++=0.0;
}
}

The required mdlOutputs function computes the output signal of this S-function. The beginning of
the function obtains pointers to the first output port, continuous states, and first input port. The S-
function uses the data in these arrays to solve the output equation y=Cx+Du.
/* Function: mdlOutputs =======================================================
* Abstract:
* y = Cx + Du
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *x = ssGetContStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

UNUSED_ARG(tid); /* not used in single tasking mode */

/* y=Cx+Du */
y[0]=C[0][0]*x[0]+C[0][1]*x[1]+D[0][0]*U(0)+D[0][1]*U(1);
y[1]=C[1][0]*x[0]+C[1][1]*x[1]+D[1][0]*U(0)+D[1][1]*U(1);
}

The mdlDerivatives function calculates the continuous state derivatives. Because this function is
an optional method, a #define statement must precede the function. The beginning of the function
obtains pointers to the S-function continuous states, state derivatives, and first input port. The S-
function uses this data to solve the equation dx=Ax+Bu.
#define MDL_DERIVATIVES
/* Function: mdlDerivatives =================================================
* Abstract:
* xdot = Ax + Bu
*/
static void mdlDerivatives(SimStruct *S)
{
real_T *dx = ssGetdX(S);
real_T *x = ssGetContStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

/* xdot=Ax+Bu */
dx[0]=A[0][0]*x[0]+A[0][1]*x[1]+B[0][0]*U(0)+B[0][1]*U(1);
dx[1]=A[1][0]*x[0]+A[1][1]*x[1]+B[1][0]*U(0)+B[1][1]*U(1);
}

The required mdlTerminate function performs any actions, such as freeing memory, necessary at
the end of the simulation. In this example, the function is empty.
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */
}

The required S-function trailer includes the files necessary for simulation or code generation, as
follows.

9-54
C MEX S-Function Examples

#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX file? */


#include "simulink.c" /* MEX file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif

Note The mdlOutputs and mdlTerminate functions use the UNUSED_ARG macro to indicate that an
input argument the callback requires is not used. This optional macro is defined in
simstruc_types.h. If used, you must call this macro once for each input argument that a callback
does not use.

Discrete States
The dsfunc.c example shows how to model a discrete system in a C MEX S-function. The following
Simulink model uses this S-function.

sfcndemo_dsfunc

Discrete systems can be modeled by the following set of equations.

The dsfunc.c example implements a discrete state-space equation. The mdlOutputs method
contains the output portion and the mdlUpdate method contains the update portion of the discrete
state-space equation. To visualize how the simulation works, see the flowchart in “Simulink Engine
Interaction with C S-Functions” on page 4-50. The output equation above corresponds to the
mdlOutputs in the major time step. The preceding update equation corresponds to the mdlUpdate
in the major time step. If your model does not contain continuous elements, the Simulink engine skips
the integration phase and time is moved forward to the next discrete sample hit.

matlabroot/toolbox/simulink/simdemos/simfeatures/src/dsfunc.c

The S-function dsfunc.c begins with #define statements for the S-function name and level, along
with a #include statement for the simstruc.h header. After these statements, the S-function can
include or define any other necessary headers, data, etc. The dsfunc.c example defines U as a
pointer to the first input port's signal and initializes static variables for the state-space matrices.
/* File : dsfunc.c
* Abstract:
*
* Example C S-function for defining a discrete system.
*
* x(n+1) = Ax(n) + Bu(n)
* y(n) = Cx(n) + Du(n)
*
* For more details about S-functions, see simulink/src/sfuntmpl_doc.c.
*
* Copyright 1990-2013 The MathWorks, Inc.
*/

#define S_FUNCTION_NAME dsfunc

9-55
9 Implementing Block Features for C/C++ S-Functions

#define S_FUNCTION_LEVEL 2

#include "simstruc.h"

#define U(element) (*uPtrs[element]) /* Pointer to Input Port0 */

static real_T A[2][2]={ { -1.3839, -0.5097 } ,


{ 1 , 0 }
};

static real_T B[2][2]={ { -2.5559, 0 } ,


{ 0 , 4.2382 }
};

static real_T C[2][2]={ { 0 , 2.0761 } ,


{ 0 , 7.7891 }
};

static real_T D[2][2]={ { -0.8141, -2.9334 } ,


{ 1.2426, 0 }
};

The required S-function method mdlInitializeSizes then sets up the following S-function
characteristics.

• ssSetNumSFcnParams sets the number of expected S-function dialog parameters to zero.


• ssGetSFcnParamsCount determines how many parameters the user actually entered into the S-
function dialog. If the number of user-specified parameters does not match the number returned
by ssGetNumSFcnParams, the S-function errors out.
• If the S-function parameter count passes, mdlInitializeSizes next sets the number of
continuous and discrete states using ssSetNumContStates and ssSetNumDiscStates,
respectively. This example has zero continuous states and two discrete states.
• Next, the method configures the S-function to have a single input and output port, each with a
width of two to match the dimensions of the state-space matrices. The method passes a value of 1
to ssSetInputPortDirectFeedThrough to indicate the input port has direct feedthrough.
• ssSetNumSampleTimes initializes one sample time, which the mdlInitializeSampleTimes
function configures later.
• The S-function indicates that no work vectors are used by passing a value of 0 to
ssSetNumRWork, ssSetNumIWork, etc. You can omit these lines because zero is the default value
for all of these macros. However, for clarity, the S-function explicitly sets the number of work
vectors.
• Lastly, ssSetOptions sets any applicable options. In this case, the only option is
SS_OPTION_EXCEPTION_FREE_CODE, which stipulates that the code is exception free.

The mdlInitializeSizes function for this example is shown below.


/*====================*
* S-function methods *
*====================*/

/* Function: mdlInitializeSizes ===============================================


* Abstract:
* Determine the S-function block's characteristics:
* number of inputs, outputs, states, etc.
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch reported by the Simulink engine*/
}

9-56
C MEX S-Function Examples

ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 2);

if (!ssSetNumInputPorts(S, 1)) return;


ssSetInputPortWidth(S, 0, 2);
ssSetInputPortDirectFeedThrough(S, 0, 1);

if (!ssSetNumOutputPorts(S, 1)) return;


ssSetOutputPortWidth(S, 0, 2);

ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);

/* Take care when specifying exception free code - see sfuntmpl_doc.c */


ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}

The required S-function method mdlInitializeSampleTimes specifies the S-function sample


rates. A call to ssSetSampleTime sets this first S-function sample period to 1.0. ssSetOffsetTime
then specifies an offset time of zero for the first sample rate. The call to
ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to use the default rule
to determine if referenced models containing this S-function can inherit their sample times from the
parent model.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* Specify a sample time 0f 1.0.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, 1.0);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}

The optional S-function method mdlInitializeConditions initializes the discrete state vector.
The #define statement before this method is required for the Simulink engine to call this function.
In the example below, ssGetRealDiscStates obtains a pointer to the discrete state vector. The for
loop then initializes each discrete state to one.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ========================================
* Abstract:
* Initialize both discrete states to one.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x0 = ssGetRealDiscStates(S);
int_T lp;

for (lp=0;lp<2;lp++) {
*x0++=1.0;
}
}

The required mdlOutputs function computes the output signal of this S-function. The beginning of
the function obtains pointers to the first output port, discrete states, and first input port. The S-
function uses the data in these arrays to solve the output equation y=Cx+Du.
/* Function: mdlOutputs =======================================================
* Abstract:
* y = Cx + Du

9-57
9 Implementing Block Features for C/C++ S-Functions

*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *x = ssGetRealDiscStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

UNUSED_ARG(tid); /* not used in single tasking mode */

/* y=Cx+Du */
y[0]=C[0][0]*x[0]+C[0][1]*x[1]+D[0][0]*U(0)+D[0][1]*U(1);
y[1]=C[1][0]*x[0]+C[1][1]*x[1]+D[1][0]*U(0)+D[1][1]*U(1);
}

The Simulink engine calls the mdlUpdate function once every major integration time step to update
the discrete states' values. Because this function is an optional method, a #define statement must
precede the function. The beginning of the function obtains pointers to the S-function discrete states
and first input port. The S-function uses the data in these arrays to solve the equation dx=Ax+Bu,
which is stored in the temporary variable tempX before being assigned into the discrete state vector
x.
#define MDL_UPDATE
/* Function: mdlUpdate ======================================================
* Abstract:
* xdot = Ax + Bu
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
real_T tempX[2] = {0.0, 0.0};
real_T *x = ssGetRealDiscStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

UNUSED_ARG(tid); /* not used in single tasking mode */

/* xdot=Ax+Bu */
tempX[0]=A[0][0]*x[0]+A[0][1]*x[1]+B[0][0]*U(0)+B[0][1]*U(1);
tempX[1]=A[1][0]*x[0]+A[1][1]*x[1]+B[1][0]*U(0)+B[1][1]*U(1);

x[0]=tempX[0];
x[1]=tempX[1];
}

The required mdlTerminate function performs any actions, such as freeing memory, necessary at
the end of the simulation. In this example, the function is empty.
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */
}

The required S-function trailer includes the files necessary for simulation or code generation, as
follows.
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX file? */
#include "simulink.c" /* MEX file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif

Note The mdlOutputs and mdlTerminate functions use the UNUSED_ARG macro to indicate that an
input argument the callback requires is not used. This optional macro is defined in

9-58
C MEX S-Function Examples

simstruc_types.h. If used, you must call this macro once for each input argument that a callback
does not use.

Continuous and Discrete States


The mixedm.c example shows a hybrid (a combination of continuous and discrete states) system. The
mixedm.c example combines elements of csfunc.c and dsfunc.c. The following Simulink model
uses this S-function.

sfcndemo_mixedm

If you have a hybrid system, the mdlDerivatives method calculates the derivatives of the
continuous states of the state vector, x, and the mdlUpdate method contains the equations used to
update the discrete state vector, xD. The mdlOutputs method computes the S-function outputs after
checking for sample hits to determine at what point the S-function is being called.

In Simulink block diagram form, the S-function mixedm.c looks like

which implements a continuous integrator followed by a discrete unit delay.

matlabroot/toolbox/simulink/simdemos/simfeatures/src/mixedm.c

The S-function mixedm.c begins with #define statements for the S-function name and level, along
with a #include statement for the simstruc.h header. After these statements, the S-function can
include or define any other necessary headers, data, etc. The mixedm.c example defines U as a
pointer to the first input port's signal.
/* File : mixedm.c
* Abstract:
*
* An example S-function illustrating multiple sample times by implementing
* integrator -> ZOH(Ts=1second) -> UnitDelay(Ts=1second)
* with an initial condition of 1.
* (e.g. an integrator followed by unit delay operation).
*
* For more details about S-functions, see simulink/src/sfuntmpl_doc.c
*
* Copyright 1990-2007 The MathWorks, Inc.
*/

#define S_FUNCTION_NAME mixedm


#define S_FUNCTION_LEVEL 2

#include "simstruc.h"

#define U(element) (*uPtrs[element]) /* Pointer to Input Port0 */

The required S-function method mdlInitializeSizes then sets up the following S-function
characteristics.

• ssSetNumSFcnParams sets the number of expected S-function dialog parameters to zero.


• ssGetSFcnParamsCount determines how many parameters the user actually entered into the S-
function dialog. If the number of user-specified parameters does not match the number returned
by ssGetNumSFcnParams, the S-function errors out.

9-59
9 Implementing Block Features for C/C++ S-Functions

• If the S-function parameter count passes, mdlInitializeSizes next sets the number of
continuous and discrete states using ssSetNumContStates and ssSetNumDiscStates,
respectively. This example has one continuous state and one discrete state.
• The S-function initializes one floating-point work vector by passing a value of 1 to
ssSetNumRWork. No other work vectors are initialized.
• Next, the method uses ssSetNumInputPorts and ssSetNumOutputPorts to configure the S-
function to have a single input and output port, each with a width of one. The method passes a
value of 1 to ssSetInputPortDirectFeedThrough to indicate the input port has direct
feedthrough.
• This S-function assigns sample times using a hybrid block-based and port-based method. The
macro ssSetNumSampleTimes initializes two block-based sample times, which the
mdlInitializeSampleTimes function configures later. The macros
ssSetInputPortSampleTime and ssSetInputPortOffsetTime initialize the input port to
have a continuous sample time with an offset of zero. Similarly, ssSetOutputPortSampleTime
and ssSetOutputPortOffsetTime initialize the output port sample time to 1 with an offset of
zero.
• Lastly, ssSetOptions sets two S-function options. SS_OPTION_EXCEPTION_FREE_CODE
stipulates that the code is exception free and SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED
indicates a combination of block-based and port-based sample times.

The mdlInitializeSizes function for this example is shown below.


*====================*
* S-function methods *
*====================*/

/* Function: mdlInitializeSizes ===============================================


* Abstract:
* Determine the S-function block's characteristics:
* number of inputs, outputs, states, etc.
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch reported by the Simulink engine*/
}

ssSetNumContStates(S, 1);
ssSetNumDiscStates(S, 1);
ssSetNumRWork(S, 1); /* for zoh output feeding the delay operator */

if (!ssSetNumInputPorts(S, 1)) return;


ssSetInputPortWidth(S, 0, 1);
ssSetInputPortDirectFeedThrough(S, 0, 1);
ssSetInputPortSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetInputPortOffsetTime(S, 0, 0.0);

if (!ssSetNumOutputPorts(S, 1)) return;


ssSetOutputPortWidth(S, 0, 1);
ssSetOutputPortSampleTime(S, 0, 1.0);
ssSetOutputPortOffsetTime(S, 0, 0.0);

ssSetNumSampleTimes(S, 2);

/* Take care when specifying exception free code - see sfuntmpl_doc.c. */


ssSetOptions(S, (SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED));

} /* end mdlInitializeSizes */

The required S-function method mdlInitializeSampleTimes specifies the S-function block-based


sample rates. The first call to ssSetSampleTime specifies that the first sample rate is continuous,

9-60
C MEX S-Function Examples

with the subsequent call to ssSetOffsetTime setting the offset to zero. The second call to this pair
of macros sets the second sample time to 1 with an offset of zero. The S-function port-based sample
times set in mdlInitializeSizes must all be registered as a block-based sample time. The call to
ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to use the default rule
to determine if referenced models containing this S-function can inherit their sample times from the
parent model.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* Two tasks: One continuous, one with discrete sample time of 1.0.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);

ssSetSampleTime(S, 1, 1.0);
ssSetOffsetTime(S, 1, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
} /* end mdlInitializeSampleTimes */

The optional S-function method mdlInitializeConditions initializes the continuous and discrete
state vectors. The #define statement before this method is required for the Simulink engine to call
this function. In this example, ssGetContStates obtains a pointer to the continuous state vector
and ssGetRealDiscStates obtains a pointer to the discrete state vector. The method then sets all
states' initial conditions to one.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ==========================================
* Abstract:
* Initialize both continuous states to one.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *xC0 = ssGetContStates(S);
real_T *xD0 = ssGetRealDiscStates(S);

xC0[0] = 1.0;
xD0[0] = 1.0;

} /* end mdlInitializeConditions */

The required mdlOutputs function performs computations based on the current task. The macro
ssIsContinuousTask checks if the continuous task is executing. If this macro returns true,
ssIsSpecialSampleHit then checks if the discrete sample rate is also executing. If this macro also
returns true, the method sets the value of the floating-point work vector to the current value of the
continuous state, via pointers obtained using ssGetRWork and ssGetContStates, respectively. The
mdlUpdate method later uses the floating-point work vector as the input to the zero-order hold.
Updating the work vector in mdlOutputs ensures that the correct values are available during
subsequent calls to mdlUpdate. Finally, if the S-function is running at its discrete rate, i.e., the call to
ssIsSampleHit returns true, the method sets the output to the value of the discrete state.
/* Function: mdlOutputs =======================================================
* Abstract:
* y = xD, and update the zoh internal output.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
/* update the internal "zoh" output */
if (ssIsContinuousTask(S, tid)) {
if (ssIsSpecialSampleHit(S, 1, 0, tid)) {
real_T *zoh = ssGetRWork(S);
real_T *xC = ssGetContStates(S);
*zoh = *xC;
}

9-61
9 Implementing Block Features for C/C++ S-Functions

/* y=xD */
if (ssIsSampleHit(S, 1, tid)) {
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *xD = ssGetRealDiscStates(S);
y[0]=xD[0];
}

} /* end mdlOutputs */

The Simulink engine calls the mdlUpdate function once every major integration time step to update
the discrete states' values. Because this function is an optional method, a #define statement must
precede the function. The call to ssIsSampleHit ensures the body of the method is executed only
when the S-function is operating at its discrete rate. If ssIsSampleHit returns true, the method
obtains pointers to the S-function discrete state and floating-point work vector and updates the
discrete state's value using the value stored in the work vector.
#define MDL_UPDATE
/* Function: mdlUpdate ======================================================
* Abstract:
* xD = xC
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
UNUSED_ARG(tid); /* not used in single tasking mode */

/* xD=xC */
if (ssIsSampleHit(S, 1, tid)) {
real_T *xD = ssGetRealDiscStates(S);
real_T *zoh = ssGetRWork(S);
xD[0]=*zoh;
}
} /* end mdlUpdate */

The mdlDerivatives function calculates the continuous state derivatives. Because this function is
an optional method, a #define statement must precede the function. The function obtains pointers
to the S-function continuous state derivative and first input port then sets the continuous state
derivative equal to the value of the first input.
#define MDL_DERIVATIVES
/* Function: mdlDerivatives =================================================
* Abstract:
* xdot = U
*/
static void mdlDerivatives(SimStruct *S)
{
real_T *dx = ssGetdX(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

/* xdot=U */
dx[0]=U(0);

} /* end mdlDerivatives */

The required mdlTerminate function performs any actions, such as freeing memory, necessary at
the end of the simulation. In this example, the function is empty.
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */
}

9-62
C MEX S-Function Examples

The S-function trailer includes the files necessary for simulation or code generation, as follows.
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX file? */
#include "simulink.c" /* MEX file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif

Note The mdlUpdate and mdlTerminate functions use the UNUSED_ARG macro to indicate that an
input argument the callback requires is not used. This optional macro is defined in
simstruc_types.h. If used, you must call this macro once for each input argument that a callback
does not use.

Variable Sample Time


The example S-function vsfunc.c uses a variable-step sample time. The following Simulink model
uses this S-function.

sfcndemo_vsfunc

Variable step-size functions require a call to mdlGetTimeOfNextVarHit, which is an S-function


routine that calculates the time of the next sample hit. S-functions that use the variable-step sample
time can be used only with variable-step solvers. The vsfunc.c example is a discrete S-function that
delays its first input by an amount of time determined by the second input.

The vsfunc.c example outputs the input u delayed by a variable amount of time. mdlOutputs sets
the output y equal to state x. mdlUpdate sets the state vector x equal to u, the input vector. This
example calls mdlGetTimeOfNextVarHit to calculate and set the time of the next sample hit, that
is, the time when vsfunc.c is next called. In mdlGetTimeOfNextVarHit, the macro
ssGetInputPortRealSignalPtrs gets a pointer to the input u. Then this call is made:

ssSetTNext(S, ssGetT(S) + U(1));

The macro ssGetT gets the simulation time t. The second input to the block, U(1), is added to t,
and the macro ssSetTNext sets the time of the next hit equal to t+U(1), delaying the output by the
amount of time set in (U(1)).

matlabroot/toolbox/simulink/simdemos/simfeatures/src/vsfunc.c

The S-function vsfunc.c begins with #define statements for the S-function name and level, along
with a #include statement for the simstruc.h header. After these statements, the S-function can
include or define any other necessary headers, data, etc. The vsfunc.c example defines U as a
pointer to the first input port's signal.
/* File : vsfunc.c
* Abstract:
*
* Variable step S-function example.
* This example S-function illustrates how to create a variable step
* block. This block implements a variable step delay
* in which the first input is delayed by an amount of time determined
* by the second input:
*
* dt = u(2)
* y(t+dt) = u(t)
*
* For more details about S-functions, see simulink/src/sfuntmpl_doc.c.
*

9-63
9 Implementing Block Features for C/C++ S-Functions

* Copyright 1990-2007 The MathWorks, Inc.


*/

#define S_FUNCTION_NAME vsfunc


#define S_FUNCTION_LEVEL 2

#include "simstruc.h"

#define U(element) (*uPtrs[element]) /* Pointer to Input Port0 */

The required S-function method mdlInitializeSizes then sets up the following S-function
characteristics.

• ssSetNumSFcnParams sets the number of expected S-function dialog parameters to zero.


• ssGetSFcnParamsCount determines how many parameters the user actually entered into the S-
function dialog. If the number of user-specified parameters does not match the number returned
by ssGetNumSFcnParams, the S-function errors out.
• If the S-function parameter count passes, mdlInitializeSizes next sets the number of
continuous and discrete states using ssSetNumContStates and ssSetNumDiscStates,
respectively. This example has no continuous states and one discrete state.
• Next, the method uses ssSetNumInputPorts and ssSetNumOutputPorts to configure the S-
function to have a single input and output port. Calls to ssSetInputPortWidth and
ssSetOutputPortWidth assign widths to these input and output ports. The method passes a
value of 1 to ssSetInputPortDirectFeedThrough to indicate the input port has direct
feedthrough.
• ssSetNumSampleTimes then initializes one sample time, which the
mdlInitializeSampleTimes function configures later.
• The S-function indicates that no work vectors are used by passing a value of 0 to
ssSetNumRWork, ssSetNumIWork, etc. You can omit these lines because zero is the default value
for all of these macros. However, for clarity, the S-function explicitly sets the number of work
vectors.
• Next, ssGetSimMode checks if the S-function is being run in a simulation or by the Simulink
Coder product. If ssGetSimMode returns SS_SIMMODE_RTWGEN and ssIsVariableStepSolver
returns false, indicating use with the Simulink Coder product and a fixed-step solver, then the S-
function errors out.
• Lastly, ssSetOptions sets any applicable options. In this case, the only option is
SS_OPTION_EXCEPTION_FREE_CODE, which stipulates that the code is exception free.

The mdlInitializeSizes function for this example is shown below.


/* Function: mdlInitializeSizes ===============================================
* Abstract:
* Determine the S-function block's characteristics:
* number of inputs, outputs, states, etc.
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch reported by the Simulink engine*/
}

ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 1);

if (!ssSetNumInputPorts(S, 1)) return;


ssSetInputPortWidth(S, 0, 2);
ssSetInputPortDirectFeedThrough(S, 0, 1);

9-64
C MEX S-Function Examples

if (!ssSetNumOutputPorts(S, 1)) return;


ssSetOutputPortWidth(S, 0, 1);

ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);

if (ssGetSimMode(S) == SS_SIMMODE_RTWGEN && !ssIsVariableStepSolver(S)) {


ssSetErrorStatus(S, "S-function vsfunc.c cannot be used with RTW "
"and Fixed-Step Solvers because it contains variable"
" sample time");
}

/* Take care when specifying exception free code - see sfuntmpl_doc.c */


ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}

The required S-function method mdlInitializeSampleTimes specifies the S-function sample


rates. The input argument VARIABLE_SAMPLE_TIME passed to ssSetSampleTime specifies that this
S-function has a variable-step sample time and ssSetOffsetTime specifies an offset time of zero.
The call to ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to use the
default rule to determine if referenced models containing this S-function can inherit their sample
times from the parent model. Because the S-function has a variable-step sample time, vsfunc.c
must calculate the time of the next sample hit in the mdlGetTimeOfNextVarHit method, shown
later.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* Variable-Step S-function
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, VARIABLE_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}

The optional S-function method mdlInitializeConditions initializes the discrete state vector.
The #define statement before this method is required for the Simulink engine to call this function.
In the example, the method uses ssGetRealDiscStates to obtain a pointer to the discrete state
vector and sets the state's initial value to zero.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ========================================
* Abstract:
* Initialize discrete state to zero.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x0 = ssGetRealDiscStates(S);

x0[0] = 0.0;
}

The optional mdlGetTimeOfNextVarHit method calculates the time of the next sample hit. Because
this method is optional, a #define statement precedes it. First, this method obtains a pointer to the
first input port's signal using ssGetInputPortRealSignalPtrs. If the input signal's second
element is positive, the macro ssGetT gets the simulation time t. The macro ssSetTNext sets the
time of the next hit equal to t+(*U[1]), delaying the output by the amount of time specified by the
input's second element (*U[1]).
#define MDL_GET_TIME_OF_NEXT_VAR_HIT
static void mdlGetTimeOfNextVarHit(SimStruct *S)

9-65
9 Implementing Block Features for C/C++ S-Functions

{
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

/* Make sure input will increase time */


if (U(1) <= 0.0) {
/* If not, abort simulation */
ssSetErrorStatus(S,"Variable step control input must be "
"greater than zero");
return;
}
ssSetTNext(S, ssGetT(S)+U(1));
}

The required mdlOutputs function computes the S-function output signal. The function obtains
pointers to the first output port and discrete state and then assigns the state's current value to the
output.
/* Function: mdlOutputs =======================================================
* Abstract:
* y = x
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *x = ssGetRealDiscStates(S);

/* Return the current state as the output */


y[0] = x[0];
}

The mdlUpdate function updates the discrete state's value. Because this method is optional, a
#define statement precedes it. The function first obtains pointers to the S-function discrete state
and first input port then assigns the value of the first element of the first input port signal to the
state.
#define MDL_UPDATE
/* Function: mdlUpdate ========================================================
* Abstract:
* This function is called once for every major integration time step.
* Discrete states are typically updated here, but this function is useful
* for performing any tasks that should only take place once per integration
* step.
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
real_T *x = ssGetRealDiscStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

x[0]=U(0);
}

The required mdlTerminate function performs any actions, such as freeing memory, necessary at
the end of the simulation. In this example, the function is empty.
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
}

The required S-function trailer includes the files necessary for simulation or code generation, as
follows.
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX file? */
#include "simulink.c" /* MEX file interface mechanism */
#else

9-66
C MEX S-Function Examples

#include "cg_sfun.h" /* Code generation registration function */


#endif

Array Inputs and Outputs


The example S-function sfun_matadd.c demonstrates how to implement a matrix addition block.
The following Simulink model uses this S-function.

sfcndemo_matadd

The S-function adds signals of various dimensions to a parameter value entered in the S-function. The
S-function accepts and outputs 2-D or n-D signals.

matlabroot/toolbox/simulink/simdemos/simfeatures/src/sfun_matadd.c

The S-function sfun_matadd.c begins with #define statements for the S-function name and level,
along with a #include statement for the simstruc.h header. After these statements, the S-function
includes or defines any other necessary headers, data, etc. This example defines additional variables
for the number of S-function parameters, the S-function parameter value, and the flag EDIT_OK that
indicates if the parameter value can be edited during simulation.
/* SFUN_MATADD matrix support example.
* C MEX S-function for matrix addition with one input port,
* one output port, and one parameter.
*
* Input Signal: 2-D or n-D array
* Parameter: 2-D or n-D array
* Output Signal: 2-D or n-D array
*
* Input parameter output
* --------------------------------
* scalar scalar scalar
* scalar matrix matrix (input scalar expansion)
* matrix scalar matrix (parameter scalar expansion)
* matrix matrix matrix
*
* Copyright 1990-2007 The MathWorks, Inc.
*/
#define S_FUNCTION_NAME sfun_matadd
#define S_FUNCTION_LEVEL 2

#include "simstruc.h"

enum {PARAM = 0, NUM_PARAMS};

#define PARAM_ARG ssGetSFcnParam(S, PARAM)

#define EDIT_OK(S, ARG) \


(!((ssGetSimMode(S) == SS_SIMMODE_SIZES_CALL_ONLY) \
&& mxIsEmpty(ARG)))

The S-function next implements the mdlCheckParameters method to validate the S-function dialog
parameters. The #ifdef statement checks that the S-function is compiled as a MEX file, instead of
for use with the Simulink Coder product. Because mdlCheckParameters is optional, the S-function
code contains a #define statement to register the method. The body of the function checks that the
S-function parameter value is not empty. If the parameter check fails, the S-function errors out with a
call to ssSetErrorStatus.
#ifdef MATLAB_MEX_FILE
#define MDL_CHECK_PARAMETERS
/* Function: mdlCheckParameters ================================

* Abstract:
* Verify parameter settings.

9-67
9 Implementing Block Features for C/C++ S-Functions

*/
static void mdlCheckParameters(SimStruct *S)
{
if(EDIT_OK(S, PARAM_ARG)){
/* Check that parameter value is not empty*/
if( mxIsEmpty(PARAM_ARG) ) {
ssSetErrorStatus(S, "Invalid parameter specified. The"
"parameter must be non-empty");
return;
}
}
} /* end mdlCheckParameters */
#endif

The required S-function method mdlInitializeSizes then sets up the following S-function
characteristics.

• ssSetNumSFcnParams sets the number of expected S-function dialog parameters to one, as


defined by the variable NUM_PARAMS.
• If this S-function is compiled as a MEX file, ssGetSFcnParamsCount determines how many
parameters the user actually entered into the S-function dialog. If the number of user-specified
parameters matches the number returned by ssGetNumSFcnParams, the method calls
mdlCheckParameters to validate the user-entered data. Otherwise, the S-function errors out.
• If the parameter check passes, the S-function specifies that all S-function parameters are tunable
using ssSetSFcnParamTunable.
• The S-function then invokes ssAllowSignalsWithMoreThan2D to allow the S-function to accept
n-D signals.
• Next, ssSetNumOutputPorts and ssSetNumInputPorts specify that the S-function has a
single output port and a single input port.
• The S-function uses ssSetInputPortDimensionInfo to specify that the input port is
dynamically sized. In this case, the S-function needs to implement an
mdlSetInputPortDimensionInfo method to set the actual input dimension.
• The output dimensions depend on the dimensions of the S-function parameter. If the parameter is
a scalar, the call to ssSetOutputPortDimensionInfo specifies that the output port dimensions
are dynamically sized. If the parameter is a matrix, the output port dimensions are initialized to
the dimensions of the S-function parameter. In this case, the macro DECL_AND_INIT_DIMSINFO
initializes a dimsInfo structure. The S-function assigns the width, size, and dimensions of the S-
function parameter into the dimsInfo structure and then passes this structure to
ssSetOutputPortDimensionInfo in order to set the output port dimensions accordingly.
• The S-function specifies that the input port has direct feedthrough by passing a value of 1 to
ssSetInputPortDirectFeedThrough.
• ssSetNumSampleTimes initializes one sample time, to be configured later in the
mdlInitializeSampleTimes method.
• Lastly, ssSetOptions sets any applicable options. In this case,
SS_OPTION_EXCEPTION_FREE_CODE stipulates that the code is exception free and
SS_OPTION_WORKS_WITH_CODE_REUSE signifies that this S-function is compatible with the
subsystem code reuse feature of the Simulink Coder product.
/* Function: mdlInitializeSizes ================================
* Abstract:
* Initialize the sizes array
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, NUM_PARAMS);

9-68
C MEX S-Function Examples

#if defined(MATLAB_MEX_FILE)
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; }
mdlCheckParameters(S);
if (ssGetErrorStatus(S) != NULL) return;
#endif

{
int iParam = 0;
int nParam = ssGetNumSFcnParams(S);

for ( iParam = 0; iParam < nParam; iParam++ )


{
ssSetSFcnParamTunable( S, iParam, SS_PRM_TUNABLE );
}
}

/* Allow signal dimensions greater than 2 */


ssAllowSignalsWithMoreThan2D(S);

/* Set number of input and output ports */


if (!ssSetNumInputPorts( S,1)) return;
if (!ssSetNumOutputPorts(S,1)) return;

/* Set dimensions of input and output ports */


{
int_T pWidth = mxGetNumberOfElements(PARAM_ARG);
/* Input can be a scalar or a matrix signal. */
if(!ssSetInputPortDimensionInfo(S,0,DYNAMIC_DIMENSION)) {
return; }

if( pWidth == 1) {
/* Scalar parameter: output dimensions are unknown. */
if(!ssSetOutputPortDimensionInfo(S,0,DYNAMIC_DIMENSION)){
return; }
}
else{
/*
* Non-scalar parameter: output dimensions are the same
* as the parameter dimensions. To support n-D signals,
* must use a dimsInfo structure to specify dimensions.
*/
DECL_AND_INIT_DIMSINFO(di); /*Initializes structure*/
int_T pSize = mxGetNumberOfDimensions(PARAM_ARG);
const int_T *pDims = mxGetDimensions(PARAM_ARG);
di.width = pWidth;
di.numDims = pSize;
di.dims = pDims;
if(!ssSetOutputPortDimensionInfo(S, 0, &di)) return;
}
}
ssSetInputPortDirectFeedThrough(S, 0, 1);

ssSetNumSampleTimes(S, 1);
ssSetOptions(S,
SS_OPTION_WORKS_WITH_CODE_REUSE |
SS_OPTION_EXCEPTION_FREE_CODE);
} /* end mdlInitializeSizes */

The required S-function method mdlInitializeSampleTimes specifies the S-function sample


rates. To specify that this S-function inherits its sample time from its driving block, the S-function
calls ssSetSampleTime with the input argument INHERITED_SAMPLE_TIME. The call to
ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to use the default rule
to determine if referenced models containing this S-function can inherit their sample times from the
parent model.
/* Function: mdlInitializeSampleTimes ==========================
* Abstract:
* Initialize the sample times array.
*/

9-69
9 Implementing Block Features for C/C++ S-Functions

static void mdlInitializeSampleTimes(SimStruct *S)


{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
} /* end mdlInitializeSampleTimes */

The S-function calls the mdlSetWorkWidths method to register its run-time parameters. Because
mdlSetWorkWidths is an optional method, a #define statement precedes it. The method first
initializes a name for the run-time parameter and then uses
ssRegAllTunableParamsAsRunTimeParams to register the run-time parameter.
/* Function: mdlSetWorkWidths ==================================
* Abstract:
* Set up run-time parameter.
*/
#define MDL_SET_WORK_WIDTHS
static void mdlSetWorkWidths(SimStruct *S)
{
const char_T *rtParamNames[] = {"Operand"};
ssRegAllTunableParamsAsRunTimeParams(S, rtParamNames);
} /* end mdlSetWorkWidths */

The S-function mdlOutputs method uses a for loop to calculate the output as the sum of the input
and S-function parameter. The S-function handles n-D arrays of data using a single index into the
array.
/* Function: mdlOutputs ========================================
* Abstract:
* Compute the outputs of the S-function.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
InputRealPtrsType uPtr = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
const real_T *p = mxGetPr(PARAM_ARG);

int_T uWidth = ssGetInputPortWidth(S,0);


int_T pWidth = mxGetNumberOfElements(PARAM_ARG);
int_T yWidth = ssGetOutputPortWidth(S,0);
int i;

UNUSED_ARG(tid); /* not used in single tasking mode */

/*
* Note1: Matrix signals are stored in column major order.
* Note2: Access each matrix element by one index not two
* indices. For example, if the output signal is a
* [2x2] matrix signal,
* - -
* | y[0] y[2] |
* | y[1] y[3] |
* - -
* Output elements are stored as follows:
* y[0] --> row = 0, col = 0
* y[1] --> row = 1, col = 0
* y[2] --> row = 0, col = 1
* y[3] --> row = 1, col = 1
*/

for (i = 0; i < yWidth; i++) {


int_T uIdx = (uWidth == 1) ? 0 : i;
int_T pIdx = (pWidth == 1) ? 0 : i;

y[i] = *uPtr[uIdx] + p[pIdx];


}
} /* end mdlOutputs */

9-70
C MEX S-Function Examples

During signal propagation, the S-function calls the optional mdlSetInputPortDimensionInfo


method with the candidate input port dimensions stored in dimsInfo. The #if defined statement
checks that the S-function is compiled as a MEX file. Because mdlSetInputPortDimensionInfo is
an optional method, a #define statement precedes it. In mdlSetInputPortDimensionInfo, the S-
function uses ssSetInputPortDimensionInfo to set the dimensions of the input port to the
candidate dimensions. If the call to this macro succeeds, the S-function further checks the candidate
dimensions to ensure that the input signal is either a 2-D scalar or a matrix. If this condition is met
and the output port dimensions are still dynamically sized, the S-function calls
ssSetOutputPortDimensionInfo to set the dimension of the output port to the same candidate
dimensions. The ssSetOutputPortDimensionInfo macro cannot modify the output port
dimensions if they are already specified.
#if defined(MATLAB_MEX_FILE)
#define MDL_SET_INPUT_PORT_DIMENSION_INFO
/* Function: mdlSetInputPortDimensionInfo ======================
* Abstract:
* This routine is called with the candidate dimensions for
* an input port with unknown dimensions. If the proposed
* dimensions are acceptable, the routine should go ahead and
* set the actual port dimensions. If they are unacceptable
* an error should be generated via ssSetErrorStatus.
* Note that any other input or output ports whose dimensions
* are implicitly defined by virtue of knowing the dimensions
* of the given port can also have their dimensions set.
*/
static void mdlSetInputPortDimensionInfo(SimStruct *S,
int_T port,
const DimsInfo_T *dimsInfo)
{
int_T pWidth = mxGetNumberOfElements(PARAM_ARG);
int_T pSize = mxGetNumberOfDimensions(PARAM_ARG);
const int_T *pDims = mxGetDimensions(PARAM_ARG);

int_T uNumDims = dimsInfo->numDims;


int_T uWidth = dimsInfo->width;
int_T *uDims = dimsInfo->dims;

int_T numDims;
boolean_T isOk = true;
int iParam = 0;
int_T outWidth = ssGetOutputPortWidth(S, 0);

/* Set input port dimension */


if(!ssSetInputPortDimensionInfo(S, port, dimsInfo)) return;

/*
* The block only accepts 2-D or higher signals. Check
* number of dimensions. If the parameter and the input
* signal are non-scalar, their dimensions must be the same.
*/
isOk = (uNumDims >= 2) && (pWidth == 1 || uWidth == 1 ||
pWidth == uWidth);
numDims = (pSize != uNumDims) ? numDims : uNumDims;

if(isOk && pWidth > 1 && uWidth > 1){


for ( iParam = 0; iParam < numDims; iParam++ ) {
isOk = (pDims[iParam] == uDims[iParam]);
if(!isOk) break;
}
}

if(!isOk){
ssSetErrorStatus(S,"Invalid input port dimensions. The "
"input signal must be a 2-D scalar signal, or it must "
"be a matrix with the same dimensions as the parameter "
"dimensions.");
return;
}

9-71
9 Implementing Block Features for C/C++ S-Functions

/* Set the output port dimensions */


if (outWidth == DYNAMICALLY_SIZED){
if(!ssSetOutputPortDimensionInfo(S,port,dimsInfo)) return;
}
} /* end mdlSetInputPortDimensionInfo */

During signal propagation, if any output ports have unknown dimensions, the S-function calls the
optional mdlSetOutputPortDimensionInfo method. Because this method is optional, a #define
statement precedes it. In mdlSetOutputPortDimensionInfo, the S-function uses
ssSetOutputPortDimensionInfo to set the dimensions of the output port to the candidate
dimensions dimsInfo. If the call to this macro succeeds, the S-function further checks the candidate
dimensions to ensure that the input signal is either a 2-D or n-D matrix. If this condition is not met,
the S-function errors out with a call to ssSetErrorStatus. Otherwise, the S-function calls
ssSetInputPortDimensionInfo to set the dimension of the input port to the same candidate
dimensions.
# define MDL_SET_OUTPUT_PORT_DIMENSION_INFO
/* Function: mdlSetOutputPortDimensionInfo =====================
* Abstract:
* This routine is called with the candidate dimensions for
* an output port with unknown dimensions. If the proposed
* dimensions are acceptable, the routine should go ahead and
* set the actual port dimensions. If they are unacceptable
* an error should be generated via ssSetErrorStatus.
* Note that any other input or output ports whose dimensions
* are implicitly defined by virtue of knowing the dimensions
* of the given port can also have their dimensions set.
*/
static void mdlSetOutputPortDimensionInfo(SimStruct *S,
int_T port,
const DimsInfo_T *dimsInfo)
{
/*
* If the block has scalar parameter, the output dimensions
* are unknown. Set the input and output port to have the
* same dimensions.
*/
if(!ssSetOutputPortDimensionInfo(S, port, dimsInfo)) return;

/* The block only accepts 2-D or n-D signals.


* Check number of dimensions.
*/
if (!(dimsInfo->numDims >= 2)){
ssSetErrorStatus(S, "Invalid output port dimensions. "
"The output signal must be a 2-D or n-D array (matrix) "
"signal.");
return;
}else{
/* Set the input port dimensions */
if(!ssSetInputPortDimensionInfo(S,port,dimsInfo)) return;
}
} /* end mdlSetOutputPortDimensionInfo */

Because the S-function has ports that are dynamically sized, it must provide an
mdlSetDefaultPortDimensionInfo method. The Simulink engine invokes this method during
signal propagation when it cannot determine the dimensionality of the signal connected to the block's
input port. This situation can happen, for example, if the input port is unconnected. In this example,
the mdlSetDefaultPortDimensionInfo method sets the input and output ports dimensions to a
scalar.
# define MDL_SET_DEFAULT_PORT_DIMENSION_INFO
/* Function: mdlSetDefaultPortDimensionInfo ====================
* This routine is called when the Simulink engine is not able
* to find dimension candidates for ports with unknown dimensions.
* This function must set the dimensions of all ports with
* unknown dimensions.

9-72
C MEX S-Function Examples

*/
static void mdlSetDefaultPortDimensionInfo(SimStruct *S)
{
int_T outWidth = ssGetOutputPortWidth(S, 0);
/* Input port dimension must be unknown. Set it to scalar.*/
if(!ssSetInputPortMatrixDimensions(S, 0, 1, 1)) return;
if(outWidth == DYNAMICALLY_SIZED){
/* Output dimensions are unknown. Set it to scalar. */
if(!ssSetOutputPortMatrixDimensions(S, 0, 1, 1)) return;
}
} /* end mdlSetDefaultPortDimensionInfo */
#endif

The required mdlTerminate function performs any actions, such as freeing memory, necessary at
the end of the simulation. In this example, the function is empty.
/* Function: mdlTerminate ======================================
* Abstract:
* Called when the simulation is terminated.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */

} /* end mdlTerminate */

The required S-function trailer includes the files necessary for simulation or code generation.
#ifdef MATLAB_MEX_FILE
#include "simulink.c"
#else
#include "cg_sfun.h"
#endif

/* [EOF] sfun_matadd.c */

Note The mdlOutputs and mdlTerminate functions use the UNUSED_ARG macro to indicate that an
input argument the callback requires is not used. This optional macro is defined in
simstruc_types.h. You must call this macro once for each input argument that a callback does not
use.

Zero-Crossing Detection
The example S-function sfun_zc_sat.c demonstrates how to implement a Saturation block. The
following Simulink model uses this S-function.

sfcndemo_sfun_zc_sat

The S-function works with either fixed-step or variable-step solvers. When this S-function inherits a
continuous sample time and uses a variable-step solver, it uses a zero-crossings algorithm to locate
the exact points at which the saturation occurs.

matlabroot/toolbox/simulink/simdemos/simfeatures/src/sfun_zc_sat.c

The S-function sfun_zc_sat.c begins with #define statements for the S-function name and level,
along with a #include statement for the simstruc.h header. After these statements, the S-function
includes or defines any other necessary headers, data, etc. This example defines various parameters
associated with the upper and lower saturation bounds.
/* File : sfun_zc_sat.c
* Abstract:

9-73
9 Implementing Block Features for C/C++ S-Functions

*
* Example of an S-function which has nonsampled zero crossings to
* implement a saturation function. This S-function is designed to be
* used with a variable or fixed step solver.
*
* A saturation is described by three equations
*
* (1) y = UpperLimit
* (2) y = u
* (3) y = LowerLimit
*
* and a set of inequalities that specify which equation to use
*
* if UpperLimit < u then use (1)
* if LowerLimit <= u <= UpperLimit then use (2)
* if u < LowerLimit then use (3)
*
* A key fact is that the valid equation 1, 2, or 3, can change at
* any instant. Nonsampled zero crossing support helps the variable step
* solvers locate the exact instants when behavior switches from one equation
* to another.
*
* Copyright 1990-2007 The MathWorks, Inc.
*/

#define S_FUNCTION_NAME sfun_zc_sat


#define S_FUNCTION_LEVEL 2

#include "simstruc.h"

/*========================*
* General Defines/macros *
*========================*/

/* index to Upper Limit */


#define I_PAR_UPPER_LIMIT 0

/* index to Lower Limit */


#define I_PAR_LOWER_LIMIT 1

/* total number of block parameters */


#define N_PAR 2

/*
* Make access to mxArray pointers for parameters more readable.
*/
#define P_PAR_UPPER_LIMIT ( ssGetSFcnParam(S,I_PAR_UPPER_LIMIT) )
#define P_PAR_LOWER_LIMIT ( ssGetSFcnParam(S,I_PAR_LOWER_LIMIT) )

This S-function next implements the mdlCheckParameters method to check the validity of the S-
function dialog parameters. Because this method is optional, a #define statement precedes it. The
#if defined statement checks that this function is compiled as a MEX file, instead of for use with
the Simulink Coder product. The body of the function performs basic checks to ensure that the user
entered real vectors of equal length for the upper and lower saturation limits. If the parameter
checks fail, the S-function errors out.
#define MDL_CHECK_PARAMETERS
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)

/* Function: mdlCheckParameters =============================================


* Abstract:
* Check that parameter choices are allowable.
*/
static void mdlCheckParameters(SimStruct *S)
{
int_T i;
int_T numUpperLimit;
int_T numLowerLimit;
const char *msg = NULL;

9-74
C MEX S-Function Examples

/*
* check parameter basics
*/
for ( i = 0; i < N_PAR; i++ ) {
if ( mxIsEmpty( ssGetSFcnParam(S,i) ) ||
mxIsSparse( ssGetSFcnParam(S,i) ) ||
mxIsComplex( ssGetSFcnParam(S,i) ) ||
!mxIsNumeric( ssGetSFcnParam(S,i) ) ) {
msg = "Parameters must be real vectors.";
goto EXIT_POINT;
}
}

/*
* Check sizes of parameters.
*/
numUpperLimit = mxGetNumberOfElements( P_PAR_UPPER_LIMIT );
numLowerLimit = mxGetNumberOfElements( P_PAR_LOWER_LIMIT );

if ( ( numUpperLimit != 1 ) &&
( numLowerLimit != 1 ) &&
( numUpperLimit != numLowerLimit ) ) {
msg = "Number of input and output values must be equal.";
goto EXIT_POINT;
}

/*
* Error exit point
*/
EXIT_POINT:
if (msg != NULL) {
ssSetErrorStatus(S, msg);
}
}
#endif /* MDL_CHECK_PARAMETERS */

The required S-function method mdlInitializeSizes sets up the following S-function


characteristics.

• ssSetNumSFcnParams sets the number of expected S-function dialog parameters to two, as


defined previously in the variable N_PAR.
• If this method is compiled as a MEX file, ssGetSFcnParamsCount determines how many
parameters the user actually entered into the S-function dialog. If the number of user-specified
parameters matches the number returned by ssGetNumSFcnParams, the method calls
mdlCheckParameters to check the validity of the user-entered data. Otherwise, the S-function
errors out.
• If the parameter check passes, the S-function determines the maximum number of elements
entered into either the upper or lower saturation limit parameter. This number is needed later to
determine the appropriate output width.
• Next, the number of continuous and discrete states is set using ssSetNumContStates and
ssSetNumDiscStates, respectively. This example has no continuous or discrete states.
• The method specifies that the S-function has a single output port using ssSetNumOutputPorts
and sets the width of this output port using ssSetOutputPortWidth. The output port width is
either the maximum number of elements in the upper or lower saturation limit or is dynamically
sized. Similar code specifies a single input port and indicates the input port has direct
feedthrough by passing a value of 1 to ssSetInputPortDirectFeedThrough.
• ssSetNumSampleTimes initializes one sample time, which the mdlInitializeSampleTimes
function configures later.
• The S-function indicates that no work vectors are used by passing a value of 0 to
ssSetNumRWork, ssSetNumIWork, etc. You can omit these lines because zero is the default value

9-75
9 Implementing Block Features for C/C++ S-Functions

for all of these macros. However, for clarity, the S-function explicitly sets the number of work
vectors.
• The method initializes the zero-crossing detection work vectors using ssSetNumModes and
ssSetNumNonsampledZCs. The mdlSetWorkWidths method specifies the length of these
dynamically sized vectors later.
• Lastly, ssSetOptions sets any applicable options. In this case,
SS_OPTION_EXCEPTION_FREE_CODE stipulates that the code is exception free and
SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION permits scalar expansion of the input without
having to provide an mdlSetInputPortWidth function.

The mdlInitializeSizes function for this example is shown below.


/* Function: mdlInitializeSizes ===============================================
* Abstract:
* Initialize the sizes array.
*/
static void mdlInitializeSizes(SimStruct *S)
{
int_T numUpperLimit, numLowerLimit, maxNumLimit;

/*
* Set and Check parameter count
*/
ssSetNumSFcnParams(S, N_PAR);

#if defined(MATLAB_MEX_FILE)
if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) {
mdlCheckParameters(S);
if (ssGetErrorStatus(S) != NULL) {
return;
}
} else {
return; /* Parameter mismatch reported by the Simulink engine*/
}
#endif

/*
* Get parameter size info.
*/
numUpperLimit = mxGetNumberOfElements( P_PAR_UPPER_LIMIT );
numLowerLimit = mxGetNumberOfElements( P_PAR_LOWER_LIMIT );

if (numUpperLimit > numLowerLimit) {


maxNumLimit = numUpperLimit;
} else {
maxNumLimit = numLowerLimit;
}

/*
* states
*/
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 0);

/*
* outputs
* The upper and lower limits are scalar expanded
* so their size determines the size of the output
* only if at least one of them is not scalar.
*/
if (!ssSetNumOutputPorts(S, 1)) return;

if ( maxNumLimit > 1 ) {
ssSetOutputPortWidth(S, 0, maxNumLimit);
} else {
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);
}

9-76
C MEX S-Function Examples

/*
* inputs
* If the upper or lower limits are not scalar then
* the input is set to the same size. However, the
* ssSetOptions below allows the actual width to
* be reduced to 1 if needed for scalar expansion.
*/
if (!ssSetNumInputPorts(S, 1)) return;

ssSetInputPortDirectFeedThrough(S, 0, 1 );

if ( maxNumLimit > 1 ) {
ssSetInputPortWidth(S, 0, maxNumLimit);
} else {
ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
}

/*
* sample times
*/
ssSetNumSampleTimes(S, 1);

/*
* work
*/
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);

/*
* Modes and zero crossings:
* If we have a variable-step solver and this block has a continuous
* sample time, then
* o One mode element will be needed for each scalar output
* in order to specify which equation is valid (1), (2), or (3).
* o Two ZC elements will be needed for each scalar output
* in order to help the solver find the exact instants
* at which either of the two possible "equation switches"
* One will be for the switch from eq. (1) to (2);
* the other will be for eq. (2) to (3) and vice versa.
* otherwise
* o No modes and nonsampled zero crossings will be used.
*
*/
ssSetNumModes(S, DYNAMICALLY_SIZED);
ssSetNumNonsampledZCs(S, DYNAMICALLY_SIZED);

/*
* options
* o No mexFunctions and no problematic mxFunctions are called
* so the exception free code option safely gives faster simulations.
* o Scalar expansion of the inputs is desired. The option provides
* this without the need to write mdlSetOutputPortWidth and
* mdlSetInputPortWidth functions.
*/
ssSetOptions(S, ( SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION));

} /* end mdlInitializeSizes */

The required S-function method mdlInitializeSampleTimes specifies the S-function sample


rates. The input argument INHERITED_SAMPLE_TIME passed to ssSetSampleTime specifies that
this S-function inherits its sample time from its driving block. The call to
ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to use the default rule
to determine if referenced models containing this S-function can inherit their sample times from the
parent model.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:

9-77
9 Implementing Block Features for C/C++ S-Functions

* Specify that the block is continuous.


*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}

The optional method mdlSetWorkWidths initializes the size of the zero-crossing detection work
vectors. Because this method is optional, a #define statement precedes it. The #if defined
statement checks that the S-function is being compiled as a MEX file. Zero-crossing detection can be
done only when the S-function is running at a continuous sample rate using a variable-step solver.
The if statement uses ssIsVariableStepSolver, ssGetSampleTime, and ssGetOffsetTime to
determine if this condition is met. If so, the method sets the number of modes equal to the width of
the first output port and the number of nonsampled zero crossings to twice this amount. Otherwise,
the method sets both values to zero.
#define MDL_SET_WORK_WIDTHS
#if defined(MDL_SET_WORK_WIDTHS) && defined(MATLAB_MEX_FILE)
/* Function: mdlSetWorkWidths ===============================================
* The width of the Modes and the ZCs depends on the width of the output.
* This width is not always known in mdlInitializeSizes so it is handled
* here.
*/
static void mdlSetWorkWidths(SimStruct *S)
{
int nModes;
int nNonsampledZCs;

if (ssIsVariableStepSolver(S) &&
ssGetSampleTime(S,0) == CONTINUOUS_SAMPLE_TIME &&
ssGetOffsetTime(S,0) == 0.0) {

int numOutput = ssGetOutputPortWidth(S, 0);

/*
* modes and zero crossings
* o One mode element will be needed for each scalar output
* in order to specify which equation is valid (1), (2), or (3).
* o Two ZC elements will be needed for each scalar output
* in order to help the solver find the exact instants
* at which either of the two possible "equation switches"
* One will be for the switch from eq. (1) to (2);
* the other will be for eq. (2) to (3) and vice versa.
*/
nModes = numOutput;
nNonsampledZCs = 2 * numOutput;
} else {
nModes = 0;
nNonsampledZCs = 0;
}
ssSetNumModes(S,nModes);
ssSetNumNonsampledZCs(S,nNonsampledZCs);
}
#endif /* MDL_SET_WORK_WIDTHS */

After declaring variables for the input and output signals, the mdlOutputs functions uses an if-
else statement to create blocks of code used to calculate the output signal based on whether the S-
function uses a fixed-step or variable-step solver. The if statement queries the length of the
nonsampled zero-crossing vector. If the length, set in mdlWorkWidths, is zero, then no zero-crossing
detection is done and the output signals are calculated directly from the input signals. Otherwise, the
function uses the mode work vector to determine how to calculate the output signal. If the simulation
is at a major time step, i.e., ssIsMajorTimeStep returns true, mdlOutputs determines which
mode the simulation is running in, either saturated at the upper limit, saturated at the lower limit, or
not saturated. Then, for both major and minor time steps, the function calculates an output based on

9-78
C MEX S-Function Examples

this mode. If the mode changed between the previous and current time step, then a zero crossing
occurred. The mdlZeroCrossings function, not mdlOutputs, indicates this crossing to the solver.
/* Function: mdlOutputs =======================================================
* Abstract:
*
* A saturation is described by three equations
*
* (1) y = UpperLimit
* (2) y = u
* (3) y = LowerLimit
*
* When this block is used with a fixed-step solver or it has a noncontinuous
* sample time, the equations are used as it
*
* Now consider the case of this block being used with a variable-step solver
* and it has a continuous sample time. Solvers work best on smooth problems.
* In order for the solver to work without chattering, limit cycles, or
* similar problems, it is absolutely crucial that the same equation be used
* throughout the duration of a MajorTimeStep. To visualize this, consider
* the case of the Saturation block feeding an Integrator block.
*
* To implement this rule, the mode vector is used to specify the
* valid equation based on the following:
*
* if UpperLimit < u then use (1)
* if LowerLimit <= u <= UpperLimit then use (2)
* if u < LowerLimit then use (3)
*
* The mode vector is changed only at the beginning of a MajorTimeStep.
*
* During a minor time step, the equation specified by the mode vector
* is used without question. Most of the time, the value of u will agree
* with the equation specified by the mode vector. However, sometimes u's
* value will indicate a different equation. Nonetheless, the equation
* specified by the mode vector must be used.
*
* When the mode and u indicate different equations, the corresponding
* calculations are not correct. However, this is not a problem. From
* the ZC function, the solver will know that an equation switch occurred
* in the middle of the last MajorTimeStep. The calculations for that
* time step will be discarded. The ZC function will help the solver
* find the exact instant at which the switch occurred. Using this knowledge,
* the length of the MajorTimeStep will be reduced so that only one equation
* is valid throughout the entire time step.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
int_T numOutput = ssGetOutputPortWidth(S,0);
int_T iOutput;

/*
* Set index and increment for input signal, upper limit, and lower limit
* parameters so that each gives scalar expansion if needed.
*/
int_T uIdx = 0;
int_T uInc = ( ssGetInputPortWidth(S,0) > 1 );
const real_T *upperLimit = mxGetPr( P_PAR_UPPER_LIMIT );
int_T upperLimitInc = ( mxGetNumberOfElements( P_PAR_UPPER_LIMIT ) > 1 );
const real_T *lowerLimit = mxGetPr( P_PAR_LOWER_LIMIT );
int_T lowerLimitInc = ( mxGetNumberOfElements( P_PAR_LOWER_LIMIT ) > 1 );

UNUSED_ARG(tid); /* not used in single tasking mode */

if (ssGetNumNonsampledZCs(S) == 0) {
/*
* This block is being used with a fixed-step solver or it has
* a noncontinuous sample time, so we always saturate.
*/
for (iOutput = 0; iOutput < numOutput; iOutput++) {

9-79
9 Implementing Block Features for C/C++ S-Functions

if (*uPtrs[uIdx] >= *upperLimit) {


*y++ = *upperLimit;
} else if (*uPtrs[uIdx] > *lowerLimit) {
*y++ = *uPtrs[uIdx];
} else {
*y++ = *lowerLimit;
}

upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
uIdx += uInc;
}

} else {
/*
* This block is being used with a variable-step solver.
*/
int_T *mode = ssGetModeVector(S);

/*
* Specify indices for each equation.
*/
enum { UpperLimitEquation, NonLimitEquation, LowerLimitEquation };

/*
* Update the Mode Vector ONLY at the beginning of a MajorTimeStep
*/
if ( ssIsMajorTimeStep(S) ) {
/*
* Specify the mode, ie the valid equation for each output scalar.
*/
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
if ( *uPtrs[uIdx] > *upperLimit ) {
/*
* Upper limit eq is valid.
*/
mode[iOutput] = UpperLimitEquation;
} else if ( *uPtrs[uIdx] < *lowerLimit ) {
/*
* Lower limit eq is valid.
*/
mode[iOutput] = LowerLimitEquation;
} else {
/*
* Nonlimit eq is valid.
*/
mode[iOutput] = NonLimitEquation;
}
/*
* Adjust indices to give scalar expansion if needed.
*/
uIdx += uInc;
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
}

/*
* Reset index to input and limits.
*/
uIdx = 0;
upperLimit = mxGetPr( P_PAR_UPPER_LIMIT );
lowerLimit = mxGetPr( P_PAR_LOWER_LIMIT );

} /* end IsMajorTimeStep */

/*
* For both MinorTimeSteps and MajorTimeSteps calculate each scalar
* output using the equation specified by the mode vector.
*/
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
if ( mode[iOutput] == UpperLimitEquation ) {
/*

9-80
C MEX S-Function Examples

* Upper limit eq.


*/
*y++ = *upperLimit;
} else if ( mode[iOutput] == LowerLimitEquation ) {
/*
* Lower limit eq.
*/
*y++ = *lowerLimit;
} else {
/*
* Nonlimit eq.
*/
*y++ = *uPtrs[uIdx];
}

/*
* Adjust indices to give scalar expansion if needed.
*/
uIdx += uInc;
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
}
}
} /* end mdlOutputs */

The mdlZeroCrossings method determines if a zero crossing occurred between the previous and
current time step. The method obtains a pointer to the input signal using
ssGetInputPortRealSignalPtrs. A comparison of this signal's value to the value of the upper
and lower saturation limits determines values for the elements of the nonsampled zero-crossing
vector. If any element of the nonsampled zero-crossing vector switches from negative to positive, or
positive to negative, a zero crossing occurred. In the event of a zero crossing, the Simulink engine
modifies the step size and recalculates the outputs to try to locate the exact zero crossing.
#define MDL_ZERO_CROSSINGS
#if defined(MDL_ZERO_CROSSINGS) && (defined(MATLAB_MEX_FILE) || defined(NRT))

/* Function: mdlZeroCrossings =================================================


* Abstract:
* This will only be called if the number of nonsampled zero crossings is
* greater than 0 which means this block has a continuous sample time and the
* model is using a variable-step solver.
*
* Calculate zero crossing (ZC) signals that help the solver find the
* exact instants at which equation switches occur:
*
* if UpperLimit < u then use (1)
* if LowerLimit <= u <= UpperLimit then use (2)
* if u < LowerLimit then use (3)
*
* The key words are help find. There is no choice of a function that will
* direct the solver to the exact instant of the change. The solver will
* track the zero crossing signal and do a bisection style search for the
* exact instant of equation switch.
*
* There is generally one ZC signal for each pair of signals that can
* switch. The three equations above would break into two pairs (1)&(2)
* and (2)&(3). The possibility of a "long jump" from (1) to (3) does
* not need to be handled as a separate case. It is implicitly handled.
*
* When ZCs are calculated, the value is normally used twice. When it is
* first calculated, it is used as the end of the current time step. Later,
* it will be used as the beginning of the following step.
*
* The sign of the ZC signal always indicates an equation from the pair. For
* S-functions, which equation is associated with a positive ZC and which is
* associated with a negative ZC doesn't really matter. If the ZC is positive
* at the beginning and at the end of the time step, this implies that the
* "positive" equation was valid throughout the time step. Likewise, if the
* ZC is negative at the beginning and at the end of the time step, this
* implies that the "negative" equation was valid throughout the time step.

9-81
9 Implementing Block Features for C/C++ S-Functions

* Like any other nonlinear solver, this is not foolproof, but it is an


* excellent indicator. If the ZC has a different sign at the beginning and
* at the end of the time step, then a equation switch definitely occurred
* during the time step.
*
* Ideally, the ZC signal gives an estimate of when an equation switch
* occurred. For example, if the ZC signal is -2 at the beginning and +6 at
* the end, then this suggests that the switch occurred
* 25% = 100%*(-2)/(-2-(+6)) of the way into the time step. It will almost
* never be true that 25% is perfectly correct. There is no perfect choice
* for a ZC signal, but there are some good rules. First, choose the ZC
* signal to be continuous. Second, choose the ZC signal to give a monotonic
* measure of the "distance" to a signal switch; strictly monotonic is ideal.
*/
static void mdlZeroCrossings(SimStruct *S)
{
int_T iOutput;
int_T numOutput = ssGetOutputPortWidth(S,0);
real_T *zcSignals = ssGetNonsampledZCs(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

/*
* Set index and increment for the input signal, upper limit, and lower
* limit parameters so that each gives scalar expansion if needed.
*/
int_T uIdx = 0;
int_T uInc = ( ssGetInputPortWidth(S,0) > 1 );
real_T *upperLimit = mxGetPr( P_PAR_UPPER_LIMIT );
int_T upperLimitInc = ( mxGetNumberOfElements( P_PAR_UPPER_LIMIT ) > 1 );
real_T *lowerLimit = mxGetPr( P_PAR_LOWER_LIMIT );
int_T lowerLimitInc = ( mxGetNumberOfElements( P_PAR_LOWER_LIMIT ) > 1 );

/*
* For each output scalar, give the solver a measure of "how close things
* are" to an equation switch.
*/
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {

/* The switch from eq (1) to eq (2)


*
* if UpperLimit < u then use (1)
* if LowerLimit <= u <= UpperLimit then use (2)
*
* is related to how close u is to UpperLimit. A ZC choice
* that is continuous, strictly monotonic, and is
* u - UpperLimit
* or it is negative.
*/
zcSignals[2*iOutput] = *uPtrs[uIdx] - *upperLimit;

/* The switch from eq (2) to eq (3)


*
* if LowerLimit <= u <= UpperLimit then use (2)
* if u < LowerLimit then use (3)
*
* is related to how close u is to LowerLimit. A ZC choice
* that is continuous, strictly monotonic, and is
* u - LowerLimit.
*/
zcSignals[2*iOutput+1] = *uPtrs[uIdx] - *lowerLimit;

/*
* Adjust indices to give scalar expansion if needed.
*/
uIdx += uInc;
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
}
}

#endif /* end mdlZeroCrossings */

9-82
C MEX S-Function Examples

The S-function concludes with the required mdlTerminate function. In this example, the function is
empty.
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */
}

The required S-function trailer includes the files necessary for simulation or code generation, as
follows.
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX file? */
#include "simulink.c" /* MEX file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif

Note The mdlOutputs and mdlTerminate functions use the UNUSED_ARG macro to indicate that an
input argument the callback requires is not used. This optional macro is defined in
simstruc_types.h. If used, you must call this macro once for each input argument that a callback
does not use.

Discontinuities in Continuous States


The example S-function stvctf.c demonstrates a time-varying continuous transfer function. The
following Simulink model uses this S-function.

sfcndemo_stvctf

The S-function demonstrates how to work with the solvers so that the simulation maintains
consistency, which means that the block maintains smooth and consistent signals for the integrators
although the equations that are being integrated are changing.

matlabroot/toolbox/simulink/simdemos/simfeatures/src/stvctf.c

The S-function stvctf.c begins with #define statements for the S-function name and level, along
with a #include statement for the simstruc.h header. After these statements, the S-function
includes or defines any other necessary headers, data, etc. This example defines parameters for the
transfer function's numerator and denominator, which are entered into the S-function dialog. The
comments at the beginning of this S-function provide additional information on the purpose of the
work vectors in this example.
/*
* File : stvctf.c
* Abstract:
* Time Varying Continuous Transfer Function block
*
* This S-function implements a continuous time transfer function
* whose transfer function polynomials are passed in via the input
* vector. This is useful for continuous time adaptive control
* applications.
*
* This S-function is also an example of how to use banks to avoid
* problems with computing derivatives when a continuous output has
* discontinuities. The consistency checker can be used to verify that
* your S-function is correct with respect to always maintaining smooth

9-83
9 Implementing Block Features for C/C++ S-Functions

* and consistent signals for the integrators. By consistent we mean that


* two mdlOutputs calls at major time t and minor time t are always the
* same. The consistency checker is enabled on the diagnostics page of the
* Configuraion parameters dialog box. The update method of this S-function
* modifies the coefficients of the transfer function, which cause the
* output to "jump." To have the simulation work properly, we need to let
* the solver know of these discontinuities by setting
* ssSetSolverNeedsReset and then we need to use multiple banks of
* coefficients so the coefficients used in the major time step output
* and the minor time step outputs are the same. In the simulation loop
* we have:
* Loop:
* o Output in major time step at time t
* o Update in major time step at time t
* o Integrate (minor time step):
* o Consistency check: recompute outputs at time t and compare
* with current outputs.
* o Derivatives at time t
* o One or more Output,Derivative evaluations at time t+k
* where k <= step_size to be taken.
* o Compute state, x
* o t = t + step_size
* End_Integrate
* End_Loop
* Another purpose of the consistency checker is to verify that when
* the solver needs to try a smaller step_size, the recomputing of
* the output and derivatives at time t doesn't change. Step size
* reduction occurs when tolerances aren't met for the current step size.
* The ideal ordering would be to update after integrate. To achieve
* this we have two banks of coefficients. And the use of the new
* coefficients, which were computed in update, is delayed until after
* the integrate phase is complete.
*
* This block has multiple sample times and will not work correctly
* in a multitasking environment. It is designed to be used in
* a single tasking (or variable step) simulation environment.
* Because this block accesses the input signal in both tasks,
* it cannot specify the sample times of the input and output ports
* (SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED).
*
* See simulink/src/sfuntmpl_doc.c.
*
* Copyright 1990-7 The MathWorks, Inc.
*/

#define S_FUNCTION_NAME stvctf


#define S_FUNCTION_LEVEL 2

#include "simstruc.h"

/*
* Defines for easy access to the numerator and denominator polynomials
* parameters
*/
#define NUM(S) ssGetSFcnParam(S, 0)
#define DEN(S) ssGetSFcnParam(S, 1)
#define TS(S) ssGetSFcnParam(S, 2)
#define NPARAMS 3

This S-function implements the mdlCheckParameters method to check the validity of the S-function
dialog parameters. Because this method is optional, a #define statement precedes it. The #if
defined statement checks that this function is compiled as a MEX file, instead of for use with the
Simulink Coder product. The body of the function performs basic checks to ensure that the user
entered real vectors for the numerator and denominator, and that the denominator has a higher order
than the numerator. If the parameter check fails, the S-function errors out.
#define MDL_CHECK_PARAMETERS
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)
/* Function: mdlCheckParameters =============================================
* Abstract:

9-84
C MEX S-Function Examples

* Validate our parameters to verify:


* o The numerator must be of a lower order than the denominator.
* o The sample time must be a real positive nonzero value.
*/
static void mdlCheckParameters(SimStruct *S)
{
int_T i;

for (i = 0; i < NPARAMS; i++) {


real_T *pr;
int_T el;
int_T nEls;
if (mxIsEmpty( ssGetSFcnParam(S,i)) ||
mxIsSparse( ssGetSFcnParam(S,i)) ||
mxIsComplex( ssGetSFcnParam(S,i)) ||
!mxIsNumeric( ssGetSFcnParam(S,i)) ) {
ssSetErrorStatus(S,"Parameters must be real finite vectors");
return;
}
pr = mxGetPr(ssGetSFcnParam(S,i));
nEls = mxGetNumberOfElements(ssGetSFcnParam(S,i));
for (el = 0; el < nEls; el++) {
if (!mxIsFinite(pr[el])) {
ssSetErrorStatus(S,"Parameters must be real finite vectors");
return;
}
}
}

if (mxGetNumberOfElements(NUM(S)) > mxGetNumberOfElements(DEN(S)) &&


mxGetNumberOfElements(DEN(S)) > 0 && *mxGetPr(DEN(S)) != 0.0) {
ssSetErrorStatus(S,"The denominator must be of higher order than "
"the numerator, nonempty and with first "
"element nonzero");
return;
}

/* xxx verify finite */


if (mxGetNumberOfElements(TS(S)) != 1 || mxGetPr(TS(S))[0] <= 0.0) {
ssSetErrorStatus(S,"Invalid sample time specified");
return;
}
}
#endif /* MDL_CHECK_PARAMETERS */

The required S-function method mdlInitializeSizes then sets up the following S-function
characteristics.

• ssSetNumSFcnParams sets the number of expected S-function dialog parameters to three, as


defined previously in the variable NPARAMS.
• If this method is compiled as a MEX file, ssGetSFcnParamsCount determines how many
parameters the user entered into the S-function dialog. If the number of user-specified parameters
matches the number returned by ssGetNumSFcnParams, the method calls
mdlCheckParameters to check the validity of the user-entered data. Otherwise, the S-function
errors out.
• If the parameter check passes, the S-function specifies the number of continuous and discrete
states using ssSetNumContStates and ssSetNumDiscStates, respectively. This example has
no discrete states and sets the number of continuous states based on the number of coefficients in
the transfer function's denominator.
• Next, ssSetNumInputPorts specifies that the S-function has a single input port and sets its
width to one plus twice the length of the denominator using ssSetInputPortWidth. The method
uses the value provided by the third S-function dialog parameter as the input port's sample time.
This parameter indicates the rate at which the transfer function is modified during simulation. The

9-85
9 Implementing Block Features for C/C++ S-Functions

S-function specifies that the input port has direct feedthrough by passing a value of 1 to
ssSetInputPortDirectFeedThrough.
• ssSetNumOutputPorts specifies that the S-function has a single output port. The method uses
ssSetOutputPortWidth to set the width of this output port, ssSetOutputPortSampleTime to
specify that the output port has a continuous sample time, and ssSetOutputPortOffsetTime to
set the offset time to zero.
• ssSetNumSampleTimes then initializes two sample times, which the
mdlInitializeSampleTimes function configures later.
• The method passes a value of four times the number of denominator coefficients to
ssSetNumRWork in order to set the length of the floating-point work vector. ssSetNumIWork then
sets the length of the integer work vector to two. The RWork vectors store two banks of transfer
function coefficients, while the IWork vector indicates which bank in the RWork vector is currently
in use. The S-function sets the length of all other work vectors to zero. You can omit these lines
because zero is the default value for these macros. However, for clarity, the S-function explicitly
sets the number of work vectors.
• Lastly, ssSetOptions sets any applicable options. In this case,
SS_OPTION_EXCEPTION_FREE_CODE stipulates that the code is exception free.

The mdlInitializeSizes function for this example is shown below.


/* Function: mdlInitializeSizes ===============================================
* Abstract:
* Determine the S-function block's characteristics:
* number of inputs, outputs, states, etc.
*/
static void mdlInitializeSizes(SimStruct *S)
{
int_T nContStates;
int_T nCoeffs;

/* See sfuntmpl_doc.c for more details on the macros below. */

ssSetNumSFcnParams(S, NPARAMS); /* Number of expected parameters. */


#if defined(MATLAB_MEX_FILE)
if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) {
mdlCheckParameters(S);
if (ssGetErrorStatus(S) != NULL) {
return;
}
} else {
return; /* Parameter mismatch reported by the Simulink engine*/
}
#endif

/*
* Define the characteristics of the block:
*
* Number of continuous states: length of denominator - 1
* Inputs port width 2 * (NumContStates+1) + 1
* Output port width 1
* DirectFeedThrough: 0 (Although this should be computed.
* We'll assume coefficients entered
* are strictly proper).
* Number of sample times: 2 (continuous and discrete)
* Number of Real work elements: 4*NumCoeffs
* (Two banks for num and den coeff's:
* NumBank0Coeffs
* DenBank0Coeffs
* NumBank1Coeffs
* DenBank1Coeffs)
* Number of Integer work elements: 2 (indicator of active bank 0 or 1
* and flag to indicate when banks
* have been updated).

9-86
C MEX S-Function Examples

*
* The number of inputs arises from the following:
* o 1 input (u)
* o the numerator and denominator polynomials each have NumContStates+1
* coefficients
*/
nCoeffs = mxGetNumberOfElements(DEN(S));
nContStates = nCoeffs - 1;

ssSetNumContStates(S, nContStates);
ssSetNumDiscStates(S, 0);

if (!ssSetNumInputPorts(S, 1)) return;


ssSetInputPortWidth(S, 0, 1 + (2*nCoeffs));
ssSetInputPortDirectFeedThrough(S, 0, 0);
ssSetInputPortSampleTime(S, 0, mxGetPr(TS(S))[0]);
ssSetInputPortOffsetTime(S, 0, 0);

if (!ssSetNumOutputPorts(S,1)) return;
ssSetOutputPortWidth(S, 0, 1);
ssSetOutputPortSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetOutputPortOffsetTime(S, 0, 0);

ssSetNumSampleTimes(S, 2);

ssSetNumRWork(S, 4 * nCoeffs);
ssSetNumIWork(S, 2);
ssSetNumPWork(S, 0);

ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);

/* Take care when specifying exception free code - see sfuntmpl_doc.c */


ssSetOptions(S, (SS_OPTION_EXCEPTION_FREE_CODE));

} /* end mdlInitializeSizes */

The required S-function method mdlInitializeSampleTimes specifies the S-function sample


rates. The first call to ssSetSampleTime specifies that the first sample rate is continuous and the
subsequent call to ssSetOffsetTime sets the offset to zero. The second call to this pair of macros
sets the second sample time to the value of the third S-function parameter with an offset of zero. The
call to ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to use the default
rule to determine if referenced models containing this S-function can inherit their sample times from
the parent model.
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* This function is used to specify the sample time(s) for the
* S-function. This S-function has two sample times. The
* first, a continuous sample time, is used for the input to the
* transfer function, u. The second, a discrete sample time
* provided by the user, defines the rate at which the transfer
* function coefficients are updated.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
/*
* the first sample time, continuous
*/
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);

/*
* the second, discrete sample time, is user provided
*/
ssSetSampleTime(S, 1, mxGetPr(TS(S))[0]);
ssSetOffsetTime(S, 1, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);

9-87
9 Implementing Block Features for C/C++ S-Functions

} /* end mdlInitializeSampleTimes */

The optional S-function method mdlInitializeConditions initializes the continuous state vector
and the initial numerator and denominator vectors. The #define statement before this method is
required for the Simulink engine to call this function. The function initializes the continuous states to
zero. The numerator and denominator coefficients are initialized from the first two S-function
parameters, normalized by the first denominator coefficient. The function sets the value stored in the
IWork vector to zero, to indicate that the first bank of numerator and denominator coefficients stored
in the RWork vector is currently in use.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ==========================================
* Abstract:
* Initialize the states, numerator and denominator coefficients.
*/
static void mdlInitializeConditions(SimStruct *S)
{
int_T i;
int_T nContStates = ssGetNumContStates(S);
real_T *x0 = ssGetContStates(S);
int_T nCoeffs = nContStates + 1;
real_T *numBank0 = ssGetRWork(S);
real_T *denBank0 = numBank0 + nCoeffs;
int_T *activeBank = ssGetIWork(S);

/*
* The continuous states are all initialized to zero.
*/
for (i = 0; i < nContStates; i++) {
x0[i] = 0.0;
numBank0[i] = 0.0;
denBank0[i] = 0.0;
}
numBank0[nContStates] = 0.0;
denBank0[nContStates] = 0.0;

/*
* Set up the initial numerator and denominator.
*/
{
const real_T *numParam = mxGetPr(NUM(S));
int numParamLen = mxGetNumberOfElements(NUM(S));

const real_T *denParam = mxGetPr(DEN(S));


int denParamLen = mxGetNumberOfElements(DEN(S));
real_T den0 = denParam[0];

for (i = 0; i < denParamLen; i++) {


denBank0[i] = denParam[i] / den0;
}

for (i = 0; i < numParamLen; i++) {


numBank0[i] = numParam[i] / den0;
}
}

/*
* Normalize if this transfer function has direct feedthrough.
*/
for (i = 1; i < nCoeffs; i++) {
numBank0[i] -= denBank0[i]*numBank0[0];
}

/*
* Indicate bank0 is active (i.e. bank1 is oldest).
*/
*activeBank = 0;

} /* end mdlInitializeConditions */

9-88
C MEX S-Function Examples

The mdlOutputs function calculates the S-function output signals when the S-function is simulating
in a continuous task, i.e., ssIsContinuousTask is true. If the simulation is also at a major time
step, mdlOutputs checks if the numerator and denominator coefficients need to be updated, as
indicated by a switch in the active bank stored in the IWork vector. At both major and minor time
steps, the S-function calculates the output using the numerator coefficients stored in the active bank.
/* Function: mdlOutputs =======================================================
* Abstract:
* The outputs for this block are computed by using a controllable state-
* space representation of the transfer function.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
if (ssIsContinuousTask(S,tid)) {
int i;
real_T *num;
int nContStates = ssGetNumContStates(S);
real_T *x = ssGetContStates(S);
int_T nCoeffs = nContStates + 1;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
int_T *activeBank = ssGetIWork(S);

/*
* Switch banks because we've updated them in mdlUpdate and we're no
* longer in a minor time step.
*/
if (ssIsMajorTimeStep(S)) {
int_T *banksUpdated = ssGetIWork(S) + 1;
if (*banksUpdated) {
*activeBank = !(*activeBank);
*banksUpdated = 0;
/*
* Need to tell the solvers that the derivatives are no
* longer valid.
*/
ssSetSolverNeedsReset(S);
}
}
num = ssGetRWork(S) + (*activeBank) * (2*nCoeffs);

/*
* The continuous system is evaluated using a controllable state space
* representation of the transfer function. This implies that the
* output of the system is equal to:
*
* y(t) = Cx(t) + Du(t)
* = [ b1 b2 ... bn]x(t) + b0u(t)
*
* where b0, b1, b2, ... are the coefficients of the numerator
* polynomial:
*
* B(s) = b0 s^n + b1 s^n-1 + b2 s^n-2 + ... + bn-1 s + bn
*/
*y = *num++ * (*uPtrs[0]);
for (i = 0; i < nContStates; i++) {
*y += *num++ * *x++;
}
}

} /* end mdlOutputs */

Although this example has no discrete states, the method still implements the mdlUpdate function to
update the transfer function coefficients at every major time step. Because this method is optional, a
#define statement precedes it. The method uses ssGetInputPortRealSignalPtrs to obtain a
pointer to the input signal. The input signal's values become the new transfer function coefficients,
which the S-function stores in the bank of the inactive RWork vector. When the mdlOutputs function

9-89
9 Implementing Block Features for C/C++ S-Functions

is later called at this major time step, it updates the active bank to be this updated bank of
coefficients.
#define MDL_UPDATE
/* Function: mdlUpdate ========================================================
* Abstract:
* Every time through the simulation loop, update the
* transfer function coefficients. Here we update the oldest bank.
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
if (ssIsSampleHit(S, 1, tid)) {
int_T i;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
int_T uIdx = 1;/*1st coeff is after signal input*/
int_T nContStates = ssGetNumContStates(S);
int_T nCoeffs = nContStates + 1;
int_T bankToUpdate = !ssGetIWork(S)[0];
real_T *num = ssGetRWork(S)+bankToUpdate*2*nCoeffs;
real_T *den = num + nCoeffs;

real_T den0;
int_T allZero;

/*
* Get the first denominator coefficient. It will be used
* for normalizing the numerator and denominator coefficients.
*
* If all inputs are zero, we probably could have unconnected
* inputs, so use the parameter as the first denominator coefficient.
*/
den0 = *uPtrs[uIdx+nCoeffs];
if (den0 == 0.0) {
den0 = mxGetPr(DEN(S))[0];
}

/*
* Grab the numerator.
*/
allZero = 1;
for (i = 0; (i < nCoeffs) && allZero; i++) {
allZero &= *uPtrs[uIdx+i] == 0.0;
}

if (allZero) { /* if numerator is all zero */


const real_T *numParam = mxGetPr(NUM(S));
int_T numParamLen = mxGetNumberOfElements(NUM(S));
/*
* Move the input to the denominator input and
* get the denominator from the input parameter.
*/
uIdx += nCoeffs;
num += nCoeffs - numParamLen;
for (i = 0; i < numParamLen; i++) {
*num++ = *numParam++ / den0;
}
} else {
for (i = 0; i < nCoeffs; i++) {
*num++ = *uPtrs[uIdx++] / den0;
}
}

/*
* Grab the denominator.
*/
allZero = 1;
for (i = 0; (i < nCoeffs) && allZero; i++) {
allZero &= *uPtrs[uIdx+i] == 0.0;
}

if (allZero) { /* If denominator is all zero. */

9-90
C MEX S-Function Examples

const real_T *denParam = mxGetPr(DEN(S));


int_T denParamLen = mxGetNumberOfElements(DEN(S));

den0 = denParam[0];
for (i = 0; i < denParamLen; i++) {
*den++ = *denParam++ / den0;
}
} else {
for (i = 0; i < nCoeffs; i++) {
*den++ = *uPtrs[uIdx++] / den0;
}
}

/*
* Normalize if this transfer function has direct feedthrough.
*/
num = ssGetRWork(S) + bankToUpdate*2*nCoeffs;
den = num + nCoeffs;
for (i = 1; i < nCoeffs; i++) {
num[i] -= den[i]*num[0];
}

/*
* Indicate oldest bank has been updated.
*/
ssGetIWork(S)[1] = 1;
}

} /* end mdlUpdate */

The mdlDerivatives function calculates the continuous state derivatives. The function uses the
coefficients from the active bank to solve a controllable state-space representation of the transfer
function.
#define MDL_DERIVATIVES
/* Function: mdlDerivatives ===================================================
* Abstract:
* The derivatives for this block are computed by using a controllable
* state-space representation of the transfer function.
*/
static void mdlDerivatives(SimStruct *S)
{
int_T i;
int_T nContStates = ssGetNumContStates(S);
real_T *x = ssGetContStates(S);
real_T *dx = ssGetdX(S);
int_T nCoeffs = nContStates + 1;
int_T activeBank = ssGetIWork(S)[0];
const real_T *num = ssGetRWork(S) + activeBank*(2*nCoeffs);
const real_T *den = num + nCoeffs;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

/*
* The continuous system is evaluated using a controllable state-space
* representation of the transfer function. This implies that the
* next continuous states are computed using:
*
* dx = Ax(t) + Bu(t)
* = [-a1 -a2 ... -an] [x1(t)] + [u(t)]
* [ 1 0 ... 0] [x2(t)] + [0]
* [ 0 1 ... 0] [x3(t)] + [0]
* [ . . ... .] . + .
* [ . . ... .] . + .
* [ . . ... .] . + .
* [ 0 0 ... 1 0] [xn(t)] + [0]
*
* where a1, a2, ... are the coefficients of the numerator polynomial:
*
* A(s) = s^n + a1 s^n-1 + a2 s^n-2 + ... + an-1 s + an
*/
dx[0] = -den[1] * x[0] + *uPtrs[0];

9-91
9 Implementing Block Features for C/C++ S-Functions

for (i = 1; i < nContStates; i++) {


dx[i] = x[i-1];
dx[0] -= den[i+1] * x[i];
}

} /* end mdlDerivatives */

The required mdlTerminate function performs any actions, such as freeing memory, necessary at
the end of the simulation. In this example, the function is empty.
/* Function: mdlTerminate =====================================================
* Abstract:
* Called when the simulation is terminated.
* For this block, there are no end of simulation tasks.
*/
static void mdlTerminate(SimStruct *S)
{
UNUSED_ARG(S); /* unused input argument */
} /* end mdlTerminate */

The required S-function trailer includes the files necessary for simulation or code generation, as
follows.
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX file? */
#include "simulink.c" /* MEX file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif

Note The mdlTerminate function uses the UNUSED_ARG macro to indicate that an input argument
the callback requires is not used. This optional macro is defined in simstruc_types.h. If used, you
must call this macro once for each input argument that a callback does not use.

See Also
mdlInitializeSizes | mdlTerminate | mdlUpdate

More About
• “Create a Basic C MEX S-Function” on page 4-2
• “S-Function Callback Methods” on page 1-14
• “SimStruct Macros and Functions Listed by Usage” on page 12-2

9-92
10

Implement Block Features for MATLAB


S-Functions

• “Pass Dialog Parameters to S-Functions” on page 10-2


• “Create and Update S-Function Run-Time Parameters” on page 10-4
• “Create Input and Output Ports” on page 10-6
• “Inherit Custom Data Types” on page 10-8
• “Specify S-Function Sample Times” on page 10-9
• “S-Function Compliance with ModelOperatingPoint” on page 10-12
• “Use DWork Vectors in S-Functions” on page 10-14
• “Use MATLAB S-Functions as Sim Viewing Devices in External Mode” on page 10-18
10 Implement Block Features for MATLAB S-Functions

Pass Dialog Parameters to S-Functions


In this section...
“Using Level-2 MATLAB S-Function Dialog Parameters” on page 10-2
“Tunable Parameters” on page 10-2

You can pass parameters to an S-function at the start of and during the simulation, using the S-
function parameters field of the Block Parameters dialog box. Such parameters are called dialog
box parameters to distinguish them from run-time parameters created by the S-function to facilitate
code generation (see “Create and Update S-Function Run-Time Parameters” on page 9-5).

Note You cannot use the Model Explorer, the S-function Block Parameters dialog box, or a mask to
tune the parameters of a source S-function, i.e., an S-function that has outputs but no inputs, while a
simulation is running. For more information, see “Tune and Experiment with Block Parameter
Values”.

Using Level-2 MATLAB S-Function Dialog Parameters


The Simulink engine stores Level-2 MATLAB S-function dialog parameters in the block run-time
object. To use dialog parameters in a Level-2 MATLAB S-function, perform the following steps when
you create the S-function:

1 Determine the order in which the parameters are to be specified in the block's dialog box.
2 In the setup method, set the run-time object's NumDialogPrms property to indicate to the
engine how many parameters the S-function accepts, for example:

block.NumDialogPrms = 2;
3 Access the dialog box parameters in the S-function using the run-time object's DialogPrm
method. The dialog parameter's Data property stores its current value, for example:

param1 = block.DialogPrm(1).Data;
param2 = block.DialogPrm(2).Data;

When running a simulation, you must specify the parameters in the Parameters field of the Level-2
MATLAB S-Function Block Parameters dialog in the same order that you defined them in step 1.

Tunable Parameters
Dialog parameters can be either tunable or nontunable. A tunable parameter is a parameter that a
user can change while the simulation is running.

Note Dialog box parameters are tunable by default. Nevertheless, it is good programming practice to
set the tunability of every parameter, even those that are tunable. If you enable the simulation
diagnostic S-function upgrades needed, the Simulink engine issues the diagnostic whenever it
encounters an S-function that fails to specify the tunability of all its parameters.

In a Level-2 MATLAB S-function, set the run-time object DialogPrmsTunable property in the setup
method to specify the tunability of each S-function dialog box parameter. For example, the following

10-2
Pass Dialog Parameters to S-Functions

line sets the first parameter of an S-function with three dialog parameters to tunable, and the second
and third parameters to nontunable.

block.DialogPrmsTunable = {'Tunable','Nontunable','Nontunable'};

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “Use S-Functions in Models” on page 1-3

10-3
10 Implement Block Features for MATLAB S-Functions

Create and Update S-Function Run-Time Parameters


You can create internal representations of external S-function dialog box parameters called run-time
parameters. Every run-time parameter corresponds to one or more dialog box parameters and can
have the same value and data type as its corresponding external parameters or a different value or
data type. If a run-time parameter differs in value or data type from its external counterpart, the
dialog parameter is said to have been transformed to create the run-time parameter. The value of a
run-time parameter that corresponds to multiple dialog parameters is typically a function of the
values of the dialog parameters. The Simulink engine allocates and frees storage for run-time
parameters and provides functions for updating and accessing them, thus eliminating the need for S-
functions to perform these tasks. Run-time parameters facilitate the following kinds of S-function
operations:

• Computed parameters

Often the output of a block is a function of the values of several dialog parameters. For example,
suppose a block has two parameters, the volume and density of some object, and the output of the
block is a function of the input signal and the mass of the object. In this case, the mass can be
viewed as a third internal parameter computed from the two external parameters, volume and
density. An S-function can create a run-time parameter corresponding to the computed weight,
thereby eliminating the need to provide special case handling for weight in the output
computation. See “Creating Run-Time Parameters from Multiple S-Function Parameters” on page
9-7 for more information.
• Data type conversions

Often a block needs to change the data type of a dialog parameter to facilitate internal processing.
For example, suppose that the output of the block is a function of the input and a dialog parameter
and the input and dialog parameter are of different data types. In this case, the S-function can
create a run-time parameter that has the same value as the dialog parameter but has the data
type of the input signal, and use the run-time parameter in the computation of the output.
• Code generation

During code generation, the Simulink Coder product writes all run-time parameters automatically
to the model.rtw file, eliminating the need for the S-function to perform this task via an mdlRTW
method.

Create Run-Time Parameters


In a Level-2 MATLAB S-function, you create run-time parameters associated with all the tunable
dialog parameters. Use the run-time object's AutoRegRuntimePrms method in the
PostPropagationSetup callback method to register the block's run-time parameters. For example:

block.AutoRegRuntimePrms;

Update Run-Time Parameters


In a Level-2 MATLAB S-function, update the run-time parameters using the
AutoUpdateRuntimePrms method in the ProcessParameters callback method. For example:

block.AutoUpdateRuntimePrms;

10-4
Create and Update S-Function Run-Time Parameters

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “Use S-Functions in Models” on page 1-3

10-5
10 Implement Block Features for MATLAB S-Functions

Create Input and Output Ports

Creating Input Ports for Level-2 MATLAB S-Functions


To create and configure input ports, the setup method should first specify the number of S-function
input ports, using the run-time object NumInputPorts property. Next, if all input ports inherit their
functional properties (data type, dimensions, complexity, and sampling mode) from their input
signals, include the following line in the setup method:

block.SetPreCompInpPortInfoToDynamic;

Then, for each input port, the setup method can specify

• The dimensions of the input port, using block.InputPort(n).Dimensions.

To individually specify that an input port's dimensions are dynamically sized, assign a value of -1
to the dimensions. In this case, you can implement the SetInputPortDimensions method to set
the dimensions during signal propagation.

• Whether the input port has direct feedthrough, using


block.InputPort(n).DirectFeedthrough.

A port has direct feedthrough if the input is used in the Outputs functions to calculate either the
outputs or the next sample time hit. The direct feedthrough flag for each input port can be set to
either 1=yes or 0=no. Setting the direct feedthrough flag to 0 tells the Simulink engine that u is
not used to calculate the outputs or next sample time hit. Violating this leads to unpredictable
results.
• The data type of the input port, using block.InputPort(n).DatatypeID. See the explanation
for the “DatatypeID” property in the Simulink.BlockData data object reference page for a list
of valid data type IDs.

If you want the data type of the port to depend on the data type of the port to which it is
connected, specify the data type as -1. In this case, you can implement the
SetInputPortDataType method to set the data type during signal propagation.
• The numeric type of the input port, if the port accepts complex-valued signals, using
block.InputPort(n).Complexity.

If you want the numeric type of the port to depend on the numeric type of the port to which it is
connected, specify the numeric type as 'Inherited'. In this case, you can implement the
SetInputPortComplexSignal method to set the numeric type during signal propagation.

For an example that configures a Level-2 MATLAB S-function with multiple input and output ports,
open the model sldemo_msfcn_lms and inspect the S-function adapt_lms.m.

Creating Output Ports for Level-2 MATLAB S-Functions


To create output ports for Level-2 MATLAB S-functions the setup method should first specify the
number of S-function output ports, using the run-time object NumOutputPorts property. Next, if all
output ports inherit their functional properties (data type, dimensions, complexity, and sampling
mode), include the following line in the setup method:

block.SetPreCompOutPortInfoToDynamic;

10-6
Create Input and Output Ports

Configure the output ports exactly as you configure input ports. See “Creating Input Ports for Level-2
MATLAB S-Functions” on page 10-6 for a list of properties you can specify for each output port,
substituting OutputPort for InputPort in each call to the run-time object.

Scalar Expansion of Inputs


Scalar expansion of inputs refers conceptually to the process of expanding scalar input signals to the
same dimensions as wide input signals connected to other S-function input ports. This is done by
setting each element of the expanded signal to the value of the scalar input.

A C MEX S-function's mdlInitializeSizes method enables scalar expansion of inputs by setting


the SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION option, using ssSetOptions.

Masked Multiport S-Functions


If you are developing masked multiport S-function blocks whose number of ports varies based on
some parameter, and want to place them in a Simulink library, you must specify that the mask
modifies the appearance of the block. To do this, execute the command

set_param(blockname,'MaskSelfModifiable','on')

at the MATLAB command prompt before saving the library, where blockname is the full path to the
block. Failure to specify that the mask modifies the appearance of the block means that an instance of
the block in a model reverts to the number of ports in the library whenever you load the model or
update the library link.

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “Use S-Functions in Models” on page 1-3

10-7
10 Implement Block Features for MATLAB S-Functions

Inherit Custom Data Types


Level-2 MATLAB S-functions do not support defining custom data types within the S-function.
However, input and output ports can inherit their data types from a Simulink.NumericType or
Simulink.AliasType object. For example, the S-function in the following model inherits its input
data type from the Constant block:

The Constant block's Output data type field contains the value MyDouble, which is a
Simulink.AliasType defined in the MATLAB workspace with the following line of code:

MyDouble = Simulink.AliasType('double');

The input and output ports of the Level-2 MATLAB S-function msfcn_inheritdt.m inherit their data
types. When the Simulink engine performs data type propagation, it assigns the data type MyDouble
to these ports.

You can define a fixed-point data type within a Level-2 MATLAB S-function, using one of the following
three methods:

• RegisterDataTypeFxpBinaryPoint registers a fixed-point data type with binary point-only


scaling
• RegisterDataTypeFxpFSlopeFixExpBias registers a fixed-point data type with [Slope Bias]
scaling specified in terms of fractional slope, fixed exponent, and bias
• RegisterDataTypeFxpSlopeBias registers a data type with [Slope Bias] scaling

Note If the registered data type is not one of the Simulink built-in data types, you must have a Fixed-
Point Designer™ license.

If you have Fixed-Point Designer, inspect the example models and S-functions provided with the
software for examples using the macros for defining fixed-point data types.

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “Use S-Functions in Models” on page 1-3
• “Write Level-2 MATLAB S-Functions” on page 3-2

10-8
Specify S-Function Sample Times

Specify S-Function Sample Times

About Sample Times


You can specify the sample-time behavior of your S-functions in mdlInitializeSampleTimes. Your
S-function can inherit its rates from the blocks that drive it or define its own rates.

You can specify your S-function rates (i.e., sample times) as

• Block-based sample times


• Port-based sample times
• Hybrid block-based and port-based sample times

With block-based sample times, the S-function specifies a set of operating rates for the block as a
whole during the initialization phase of the simulation. With port-based sample times, the S-function
specifies a sample time for each input and output port individually during initialization. During the
simulation phase, with block-based sample times, the S-function processes all inputs and outputs
each time a sample hit occurs for the block. By contrast, with port-based sample times, the block
processes a particular port only when a sample hit occurs for that port.

For example, consider two sample rates, 0.5 and 0.25 seconds, respectively:

• In the block-based method, selecting 0.5 and 0.25 directs the block to execute inputs and outputs
at 0.25 second increments.
• In the port-based method, setting the input port to 0.5 and the output port to 0.25 causes the
block to process inputs at 2 Hz and outputs at 4 Hz.

You should use port-based sample times if your application requires unequal sample rates for input
and output execution or if you do not want the overhead associated with running input and output
ports at the highest sample rate of your block.

In some applications, an S-Function block might need to operate internally at one or more sample
rates while inputting or outputting signals at other rates. The hybrid block- and port-based method of
specifying sample rates allows you to create such blocks.

In typical applications, you specify only one block-based sample time. Advanced S-functions might
require the specification of port-based or multiple block sample times.

Block Based Sample Times


Level-2 MATLAB S-functions specify block-based sample times in their setup method. Use the line

block.SampleTimes = [sampleTime offsetTime];

to specify the sample time. Use a value of [-1 0] to indicate an inherited sample time. See “Specify
Sample Time” in Using Simulink for a complete list of valid sample times.

Specifying Port-Based Sample Times


To use port-based sample times in a Level-2 MATLAB S-function:

10-9
10 Implement Block Features for MATLAB S-Functions

• Specify the sample and offset times for each S-function port in the setup method. For example:

block.InputPort(1).SampleTime = [-1 0];


block.OutputPort(1).SampleTime = [-1 0];

The setup method must not specify a sample time for the block when using port-based sample
times.
• Provide SetInputPortSampleTime and SetOutputPortSampleTime methods, even if your S-
function does not inherit its port-based sample times.

Specifying Inherited Sample Time for a Port

In a Level-2 MATLAB S-function, use a value of [-1 0] for the SampleTime property of each port to
specify that the port inherits its sample time.

Specifying Constant Sample Time (Inf) for a Port

If your S-function uses port-based sample times, it can set a sample time of Inf on any of its ports. A
port-based sample time of Inf means that the signal entering or leaving the port stays constant.

In a Level-2 MATLAB S-function, use this code to specify a sample time of Inf for a port:

block.OutputPort(1).SampleTime = [inf 0];


block.SetAllowConstantSampleTime(true);

Configuring Port-Based Sample Times for Use in Triggered Subsystems

Level-2 MATLAB S-functions with port-based sample times cannot be placed in a triggered
subsystem. You must modify your S-function to use block-based sample times if you need to include it
in a triggered subsystem.

Hybrid Block-Based and Port-Based Sample Times


Level-2 MATLAB S-functions support port-based sample times, but do not support hybrid block-based
sample times. See Port-Based Sample Times for more information

Multirate S-Function Blocks


In a Level-2 MATLAB S-function, use the IsSampleHit method to determine whether the current
simulation time is one at which a task handled by this block is active.

If you write TLC code to generate inlined code from an S-function, and if the TLC code contains an
Outputs function, you must modify the TLC code if all of these conditions are true:

• The output port has a constant value. It uses or inherits a sample time of Inf.
• The S-function is a multi-rate S-function.

In this case, the TLC code must generate code for the constant-valued output port by using the
function OutputsForTID instead of the function Outputs. For more information, see “Specifying
Constant Sample Time (Inf) for a Port” on page 9-24.

10-10
Specify S-Function Sample Times

Synchronizing Multirate S-Function Blocks


If tasks running at different rates need to share data, you must ensure that data generated by one
task is valid when accessed by another task running at a different rate.

Suppose, for example, that your model has an input port operating at one rate (with a sample time
index of 0) and an output port operating at a slower rate (with a sample time index of 1). Further,
suppose that you want the output port to output the value currently on the input. Note that higher-
rate tasks always run before slower-rate tasks. Thus, the input task always runs before the output
task, ensuring that valid data is always present at the output port.

In a Level-2 MATLAB S-function, use the IsSpecialSampleHit method to determine whether the
current simulation time is one at which multiple tasks implemented by this block are active.

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “Use S-Functions in Models” on page 1-3

10-11
10 Implement Block Features for MATLAB S-Functions

S-Function Compliance with ModelOperatingPoint

ModelOperatingPoint Compliance Specification for Level-2 MATLAB S-


Functions
For the default ModelOperatingPoint compliance setting, Simulink saves and restores the
following data for a block:

• Continuous state values


• Values stored in non-scratch DWork vectors (this includes IWork, RWork and Mode DWorks)
• Values of Zero Crossing signals

In order for a Level-2 MATLAB S-function to work with the ModelOperatingPoint feature, you
must specify the simStateCompliance of the block using the method,

block.simStateCompliance = setting

where the permissible setting values are:

Setting Result
OPERATING_POINT_COM This default setting instructs Simulink to use the
PLIANCE_UNKNOWN USE_DEFAULT_OPERATING_POINT to save and restore theModelOperatingPoint
and issues a warning.
USE_DEFAULT_OPERATI This setting instructs Simulink to treat the S-function like a built-in block when
NG_POINT saving and restoring the ModelOperatingPoint.
USE_EMPTY_OPERATING This setting informs Simulink that the S-function does not have any simulation state.
_POINT With this setting, no state information is saved for the block. This setting is
primarily useful for "sink" blocks (i.e., blocks with no output ports) that use PWorks
or DWorks to store handles to files or figure windows.

Note This setting is not allowed if the S-function registers any discrete or
continuous states or zero crossing signals.
USE_CUSTOM_OPERATIN This setting informs Simulink that the S-function has custom GetOperatingPoint
G_POINT and SetOperatingPoint methods.
DISALLOW_OPERATING_ This setting informs Simulink that the S-function does not allow saving or restoring
POINT its simulation state. Simulink reports an error if you save and restore the
ModelOperatingPoint of the model that contains this S-function.

For an S-function with custom methods ('USE_CUSTOM_OPERATING_POINT'), you can use the
following statements to respectively get and set the ModelOperatingPoint:

function outSS = GetOperatingPoint(block)


function SetOperatingPoint(block, inSS)

For an example of how to implement these custom methods, see msfcn_varpulse.m.

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

10-12
S-Function Compliance with ModelOperatingPoint

More About
• “S-Function Concepts” on page 1-7
• “Use S-Functions in Models” on page 1-3

10-13
10 Implement Block Features for MATLAB S-Functions

Use DWork Vectors in S-Functions

What is a DWork Vector?


DWork vectors are blocks of memory that an S-function asks the Simulink engine to allocate to each
instance of the S-function in a model. If multiple instances of your S-function can occur in a model,
your S-function must use DWork vectors instead of global or static memory to store instance-specific
values of S-function variables. Otherwise, your S-function runs the risk of one instance overwriting
data needed by another instance, causing a simulation to fail or produce incorrect results. The ability
to keep track of multiple instances of an S-function is called reentrancy.

You can create an S-function that is reentrant by using DWork vectors that the engine manages for
each particular instance of the S-function.

DWork vectors have several advantages:

• Provide instance-specific storage for block variables


• Support floating-point, integer, pointer, and general data types
• Eliminate static and global variables
• Interact directly with the Simulink engine to perform memory allocation, initialization, and
deallocation
• Facilitate inlining the S-function during code generation
• Provide more control over how data appears in the generated code

Note DWork vectors are the most generalized and versatile type of work vector and the following
sections focus on their use. The Simulink product provides additional elementary types of work
vectors that support floating-point, integer, pointer, and mode data. You can find a discussion of these
work vectors in “Elementary Work Vectors” on page 8-15.

DWork vectors provide the most flexibility for setting data types, names, etc., of the data in the
simulation and during code generation. The following list describes all the properties that you can set
on a DWork vector:

• Data type
• Size
• Numeric type, either real or complex
• Name
• Usage type (see “Types of DWork Vectors” on page 8-4)
• Simulink Coder identifier
• Simulink Coder storage class
• Simulink Coder C type qualifier

See “How to Use DWork Vectors” on page 8-5 for instructions on how to set these properties. The
three Simulink Coder properties pertain only to code generation and have no effect during
simulation.

10-14
Use DWork Vectors in S-Functions

How to use DWork Vectors in Level-2 MATLAB S-Functions


Using DWork Vectors in Level-2 MATLAB S-Functions

The following steps show how to initialize and use DWork vectors in Level-2 MATLAB S-functions.
These steps use the S-function msfcn_unit_delay.m.

1 In the PostPropagationSetup method, initialize the number of DWork vectors and the
attributes of each vector. For example, the following PostPropagationSetup callback method
configures one DWork vector used to store a discrete state.

function PostPropagationSetup(block)

%% Setup Dwork
block.NumDworks = 1;
block.Dwork(1).Name = 'x0';
block.Dwork(1).Dimensions = 1;
block.Dwork(1).DatatypeID = 0;
block.Dwork(1).Complexity = 'Real';
block.Dwork(1).UsedAsDiscState = true;

The reference pages for Simulink.BlockCompDworkData and the parent class


Simulink.BlockData list the properties you can set for Level-2 MATLAB S-function DWork
vectors.
2 Initialize the DWork vector values in either the Start or InitializeConditions methods. Use
the Start method for values that are initialized only at the beginning of the simulation. Use the
InitializeConditions method for values that need to be reinitialized whenever a disabled
subsystem containing the S-function is reenabled.

For example, the following InitializeConditions method initializes the value of the DWork
vector configured in the previous step to the value of the first S-function dialog parameter.

function InitializeConditions(block)

%% Initialize Dwork
block.Dwork(1).Data = block.DialogPrm(1).Data;
3 In the Outputs, Update, etc. methods, use or update the DWork vector values, as needed. For
example, the following Outputs method sets the S-function output equal to the value stored in
the DWork vector. The Update method then changes the DWork vector value to the current value
of the first S-function input port.

%% Outputs callback method


function Outputs(block)

block.OutputPort(1).Data = block.Dwork(1).Data;

%% Update callback method


function Update(block)

block.Dwork(1).Data = block.InputPort(1).Data;

Note Level-2 MATLAB S-functions do not support MATLAB sparse matrices. Therefore, you cannot
assign a sparse matrix to the value of a DWork vector. For example, the following line of code
produces an error

10-15
10 Implement Block Features for MATLAB S-Functions

block.Dwork(1).Data = speye(10);

where the speye command produces a sparse identity matrix.

Level-2 MATLAB S-Function DWork Vector Example


The example S-function msfcn_varpulse.m models a variable width pulse generator. The S-function
uses two DWork vectors. The first DWork vector stores the pulse width value, which is modified at
every major time step in the Update method. The second DWork vector stores the handle of the pulse
generator block in the Simulink model. The value of this DWork vector does not change over the
course of the simulation.

The PostPropagationSetup method, called DoPostPropSetup in this S-function, sets up the two
DWork vectors.
function DoPostPropSetup(block)

% Initialize the Dwork vector


block.NumDworks = 2;

% Dwork(1) stores the value of the next pulse width


block.Dwork(1).Name = 'x1';
block.Dwork(1).Dimensions = 1;
block.Dwork(1).DatatypeID = 0; % double
block.Dwork(1).Complexity = 'Real'; % real
block.Dwork(1).UsedAsDiscState = true;

% Dwork(2) stores the handle of the Pulse Geneator block


block.Dwork(2).Name = 'BlockHandle';
block.Dwork(2).Dimensions = 1;
block.Dwork(2).DatatypeID = 0; % double
block.Dwork(2).Complexity = 'Real'; % real
block.Dwork(2).UsedAsDiscState = false;

The Start method initializes the DWork vector values.


function Start(block)

% Populate the Dwork vector


block.Dwork(1).Data = 0;

% Obtain the Pulse Generator block handle


pulseGen = find_system(gcs,'BlockType','DiscretePulseGenerator');
blockH = get_param(pulseGen{1},'Handle');
block.Dwork(2).Data = blockH;

The Outputs method uses the handle stored in the second DWork vector to update the pulse width of
the Pulse Generator block.
function Outputs(block)

% Update the pulse width value


set_param(block.Dwork(2).Data, 'PulseWidth', num2str(block.InputPort(1).data));

The Update method then modifies the first DWork vector with the next value for the pulse width,
specified by the input signal to the S-Function block.
function Update(block)

% Store the input value in the Dwork(1)


block.Dwork(1).Data = block.InputPort(1).Data;

%endfunction

10-16
Use DWork Vectors in S-Functions

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “Use S-Functions in Models” on page 1-3

10-17
10 Implement Block Features for MATLAB S-Functions

Use MATLAB S-Functions as Sim Viewing Devices in External


Mode
A sim viewing device encapsulates processing and viewing of signals received from the target system
in external mode. During simulation in external mode, the target system uploads the appropriate
input values to the sim viewing device in the Simulink model. The sim viewing device then conditions
the input signals as needed and renders the signals on the screen. A sim viewing device runs only on
the host, generating no code in the target system and, therefore, allowing extra processing of
displayed signals without burdening the generated code. You can use your S-function as a sim
viewing device in external mode if it satisfies the following conditions.

• The S-function has no output ports.


• The S-function contains no states.
• The generated code does not require the conditioned signals produced by the S-function.

To specify a Level-2 MATLAB S-function as a sim viewing device, call the run-time object's
SetSimViewingDevice method in the S-function setup callback method.

See Also
Level-2 MATLAB S-Function | MATLAB Function | S-Function | S-Function Builder

More About
• “S-Function Concepts” on page 1-7
• “Use S-Functions in Models” on page 1-3

10-18
11

S-Function Callback Methods

Every user-written S-function must implement a set of methods, called callback methods or simply
callbacks, that the Simulink engine invokes when simulating a model that contains the S-function.
Some callback methods are optional. The engine invokes an optional callback only if the S-function
defines the callback. This topic describes the purpose and syntax of all callback methods that an S-
function can implement. In each case, the documentation for a callback method indicates whether it
is required or optional. For a list of required callback methods, see “Callback Methods That an S-
Function Must Implement” on page 4-29.
11 S-Function Callback Methods

CheckParameters
Check the validity of a MATLAB S-Function's parameters

Required
No

Language
MATLAB

Syntax
CheckParameters(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing a Level-2 MATLAB S-Function
block.

Description
Verifies new parameter settings whenever parameters change or are reevaluated during a simulation.

When a simulation is running, changes to S-function parameters can occur at any time during the
simulation loop, that is, either at the start of a simulation step or during a simulation step. When the
change occurs during a simulation step, the Simulink engine calls this routine twice to handle the
parameter change. The first call during the simulation step is used to verify that the parameters are
correct. After verifying the new parameters, the simulation continues using the original parameter
values until the next simulation step, at which time the new parameter values are used. Redundant
calls are needed to maintain simulation consistency.

Note You cannot access the work, state, input, output, and other vectors in this routine. Use this
routine only to validate the parameters. Additional processing of the parameters should be done in
ProcessParameters.

Example
In a Level-2 MATLAB S-function, the setup method registers the CheckParameters method as
follows
s.RegBlockMethod('CheckParameters', @CheckParam);

The local function CheckParam then verifies the S-function parameters. In this example, the function
checks that the second parameter, an upper limit value, is greater than the first S-function parameter,
a lower limit value.

11-2
CheckParameters

function CheckParam(s)

% Check that upper limit is greater than lower limit


lowerLim = s.DialogPrm(1).Data;
upperLim = s.DialogPrm(2).Data;

if upperLim <= lowerLim,


error('The upper limit must be greater than the lower limit.');
end

See Also
ProcessParameters, Simulink.RunTimeBlock, Simulink.MSFcnRunTimeBlock,
mdlCheckParameters

Introduced in R2012b

11-3
11 S-Function Callback Methods

Derivatives
Compute a MATLAB S-Function's derivatives

Required
No

Language
MATLAB

Syntax
Derivatives(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block

Description
The Simulink engine invokes this optional method at each time step to compute the derivatives of the
S-function's continuous states. This method should store the derivatives in the S-function's state
derivatives vector. In a Level-2 MATLAB S-function, use the run-time object's Derivatives method.

Each time the Derivatives routine is called, it must explicitly set the values of all derivatives. The
derivative vector does not maintain the values from the last call to this routine. The memory allocated
to the derivative vector changes during execution.

Example
For a Level-2 MATLAB S-function example, see msfcn_limintm.m.

See Also
Simulink.RunTimeBlock, Simulink.MSFcnRunTimeBlock, mdlDerivatives

Introduced in R2012b

11-4
Disable

Disable
Respond to disabling of an enabled system containing this MATLAB S-Function block

Required
No

Language
MATLAB

Syntax
Disable(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
The Simulink engine invokes this optional method if this block resides in an enabled subsystem and
the enabled subsystem changes from an enabled to a disabled state at the current time step. Your S-
function can use this method to perform any actions required by the disabling of the containing
subsystem.

See Also
Enable, Simulink.MSFcnRunTimeBlock, mdlDisable

Introduced in R2012b

11-5
11 S-Function Callback Methods

Enable
Respond to enabling of an enabled system containing this MATLAB S-Function block

Required
No

Language
MATLAB

Syntax
Enable(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
The Simulink engine invokes this optional method if this block resides in an enabled subsystem and
the enabled subsystem changes from a disabled to an enabled state at the current time step. Your S-
function can use this method to perform any actions required by the enabling of the containing
subsystem.

See Also
Disable, Simulink.MSFcnRunTimeBlock, mdlEnable

Introduced before R2006a

11-6
GetOperatingPoint

GetOperatingPoint
Return MATLAB S-function's simulation operating point as a MATLAB data structure

Required
No

Language
MATLAB

Syntax
GetOperatingPoint(s)

Arguments
s
Instance of the Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-
Function block.

Description
The Simulink engine invokes this custom method to get the simulation snapshot (operating point) of
the model containing S. A call to this method should occur after Start and before Terminate to
ensure that all of the S-function data structures (states, DWork vectors, and outputs) are available.

See Also
“Custom Code and Hand Coded Blocks using the S-function API”| SetOperatingPoint |
Simulink.MSFcnRunTimeBlock | mdlGetOpeartingPoint |
ssSetOperatingPointCompliance

Introduced in R2019a

11-7
11 S-Function Callback Methods

GetSimState
(Not recommended) Return the MATLAB S-function simulation state as a valid MATLAB data
structure, such as a matrix structure or a cell array.

Note GetSimState is not recommended. Use mdlGetOperatingPoint instead.

Required
No

Language
MATLAB

Syntax
GetSimState(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
The Simulink engine invokes this custom method to get the simulation state (SimState) of the model
containing S. A call to this method should occur after Start and before Terminate to ensure that all
of the S-function data structures (e.g., states, DWork vectors, and outputs) are available.

See Also
SetSimState, Simulink.MSFcnRunTimeBlock, mdlGetSimState

Introduced in R2015b

11-8
InitializeConditions

InitializeConditions
Initialize the state vectors of this MATLAB S-function

Required
No

Language
MATLAB

Syntax
InitializeConditions(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
The Simulink engine invokes this optional method at the beginning of a simulation. It should initialize
the continuous and discrete states, if any, of this S-Function block. In a Level-2 MATLAB S-function,
use the ContStates or Dwork run-time object methods to access the continuous and discrete states.
This method can also perform any other initialization activities that this S-function requires.

Note If you have Simulink Coder, and you need to ensure that the initialization code in the
InitializeConditions function is run only once, then move this initialization code into the Start
method. MathWorks recommends this code change as a best practice.

If this S-function resides in an enabled subsystem configured to reset states, the Simulink engine also
calls this method when the enabled subsystem restarts execution.

The Simulink engine calls InitializeConditions prior to calculating the S-function's input
signals. Therefore, since the input signal values are not yet available, InitializeConditions
should not use the input signal values to set initial conditions. If your S-function needs to initialize
internal values using the block's input signals, perform the initialization in Outputs.

For example, in a C MEX S-function, initializes an IWork vector with one element in the
mdlInitializeSizes method.
ssSetNumIWork(S, 1);

The IWork vector holds a flag indicating if initial values have been specified. Initialize the flag's value
in the mdlInitializeCondition method.

11-9
11 S-Function Callback Methods

static void mdlInitializeConditions(SimStruct *S)


{
/* The mdlInitializeConditions method is called when the simulation
start and every time an enabled subsystem is re-enabled.

Reset the IWork flag to 1 when values need to be reinitialized.*/

ssSetIWorkValue(S, 0, 1);
}

Check the value of the IWork vector flag in the mdlOutputs method, to determine if initial values
need to be set. Since the engine has calculated input values at this point in the simulation, the
mdlOutputs method can use them to initialize internal values.

static void mdlOutputs(SimStruct *S, int_T tid)


{
// Initialize values if the IWork vector flag is true. //
if (ssGetIWorkValue(S, 0) == 1) {
// Enter initialization code here //
}

// Remainder of mdlOutputs function //


}

For a Level-2 MATLAB S-function, use a DWork vector instead of an IWork vector in the previous
example.

Example
This example initializes both a continuous and discrete state to 1.0. Level-2 MATLAB S-functions
store discrete states in their DWork vectors.

function InitializeConditions(s)

s.ContStates.Data(1) = 1;
s.Dwork(1).Data = 1;

% endfunction

See Also
Start, Outputs, Simulink.RunTimeBlock, Simulink.MSFcnRunTimeBlock,
mdlInitializeConditions

Introduced in R2012b

11-10
mdlCheckParameters

mdlCheckParameters
Check the validity of a C MEX S-function's parameters

Required
No

Languages
C, C++

Syntax
#define MDL_CHECK_PARAMETERS

void mdlCheckParameters(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
Verifies new parameter settings whenever parameters change or are reevaluated during a simulation.
If you have Simulink Coder, for C MEX S-functions, this method is only valid for simulation, and must
be enclosed in a #if defined(MATLAB_MEX_FILE) statement to be compatible with code
generation targets that support noninlined S-functions.

When a simulation is running, changes to S-function parameters can occur at any time during the
simulation loop, that is, either at the start of a simulation step or during a simulation step. When the
change occurs during a simulation step, the Simulink engine calls this routine twice to handle the
parameter change. The first call during the simulation step is used to verify that the parameters are
correct. After verifying the new parameters, the simulation continues using the original parameter
values until the next simulation step, at which time the new parameter values are used. Redundant
calls are needed to maintain simulation consistency.

Note You cannot access the work, state, input, output, and other vectors in this routine. Use this
routine only to validate the parameters. Additional processing of the parameters should be done in
mdlProcessParameters.

Example
This example checks the first S-function parameter to verify that it is a real nonnegative scalar.

Note Since mdlCheckParameters is an optional method, a #define MDL_CHECK_PARAMETERS


statement precedes the function. Also, since the Simulink Coder product does not support code

11-11
11 S-Function Callback Methods

generation for mdlCheckParameters, the function is wrapped in a #if


defined(MATLAB_MEX_FILE) statement.

#define PARAM1(S) ssGetSFcnParam(S,0)


#define MDL_CHECK_PARAMETERS /* Change to #undef to remove function */
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)
static void mdlCheckParameters(SimStruct *S)
{
if (mxGetNumberOfElements(PARAM1(S)) != 1) {
ssSetErrorStatus(S,"Parameter to S-function must be a scalar");
return;
} else if (mxGetPr(PARAM1(S))[0] < 0) {
ssSetErrorStatus(S, "Parameter to S-function must be nonnegative");
return;
}
}
#endif /* MDL_CHECK_PARAMETERS */

In addition to the preceding routine, you must add a call to this method from mdlInitializeSizes
to check parameters during initialization, because mdlCheckParameters is only called while the
simulation is running. To do this, after setting the number of parameters you expect in your S-
function by using ssSetNumSFcnParams, use this code in mdlInitializeSizes:

static void mdlInitializeSizes(SimStruct *S)


{
ssSetNumSFcnParams(S, 1); /* Number of expected parameters */
#if defined(MATLAB_MEX_FILE)
if(ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S) {
mdlCheckParameters(S);
if(ssGetErrorStatus(S) != NULL) return;
} else {
return; /* The Simulink engine reports a mismatch error. */
}
#endif
...
}

Note The macro ssGetSFcnParamsCount returns the actual number of parameters entered in the
dialog box.

See sfun_errhdl.c for an example.

See Also
mdlProcessParameters, ssGetSFcnParamsCount, CheckParameters

Introduced before R2006a

11-12
mdlDerivatives

mdlDerivatives
Compute the C MEX S-function's derivatives

Required
No

Languages
C, C++

Syntax
#define MDL_DERIVATIVES

void mdlDerivatives(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine invokes this optional method at each time step to compute the derivatives of the
S-function's continuous states. This method should store the derivatives in the S-function's state
derivatives vector. In a C MEX S-function, use ssGetdX to get a pointer to the derivatives vector.

Each time the mdlDerivatives routine is called, it must explicitly set the values of all derivatives.
The derivative vector does not maintain the values from the last call to this routine. The memory
allocated to the derivative vector changes during execution.

Note If you have Simulink Coder, when generating code for a noninlined C MEX S-function that
contains this method, make sure the method is not wrapped in a #if defined(MATLAB_MEX_FILE)
statement. For example:

#define MDL_DERIVATIVES
#if defined(MDL_DERIVATIVES) && defined(MATLAB_MEX_FILE)
static void mdlDerivatives(SimStruct *S)
{
/* Add mdlDerivatives code here *
}
#endif

The define statement makes the mdlDerivatives method available only to a MATLAB MEX file. If
the S-function is not inlined, the Simulink Coder product cannot use this method, resulting in link or
run-time errors.

11-13
11 S-Function Callback Methods

Example
For a C MEX S-function example, see csfunc.c.

See Also
ssGetdx, Derivatives

Introduced before R2006a

11-14
mdlDisable

mdlDisable
Respond to disabling of an enabled system containing this block

Required
No

Languages
C, C++

Syntax
#define MDL_DISABLE

void mdlDisable(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine invokes this optional method if this block resides in an enabled subsystem and
the enabled subsystem changes from an enabled to a disabled state at the current time step. Your S-
function can use this method to perform any actions required by the disabling of the containing
subsystem.

See Also
mdlEnable, Disable

Introduced before R2006a

11-15
11 S-Function Callback Methods

mdlEnable
Respond to enabling of a enabled system containing this block

Required
No

Languages
C, C++

Syntax
#define MDL_ENABLE

void mdlEnable(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine invokes this optional method if this block resides in an enabled subsystem and
the enabled subsystem changes from a disabled to an enabled state at the current time step. Your S-
function can use this method to perform any actions required by the enabling of the containing
subsystem.

See Also
mdlDisable, Enable

Introduced before R2006a

11-16
mdlGetOperatingPoint

mdlGetOperatingPoint
Return C MEX S-function's simulation operating point as a MATLAB data structure

Required
No

Languages
C, C++

Syntax
mxArray* mdlGetOperatingPoint(SimStruct* S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine invokes this custom method to get the simulation operating point (snapshot) of
the model containing S. A call to this method occurs after mdlStart and before mdlTerminate to
ensure that all of the S-function data structures (states, DWork vectors, and outputs) are available.
mdlGetOperatingPoint is required when the operating point compliance is custom,

Example
/* Function: mdlGetOperatingPoint
* Abstract:
* Package the RunTimeData structure as a MATLAB structure
* and return it.
*/
static mxArray* mdlGetOperatingPoint(SimStruct* S)
{
RunTimeData_T* rtd =
(RunTimeData_T*)ssGetPWorkValue(S, 0);
const char* fieldNames[] = {"Count"};

/* Create a MATLAB structure to hold the run-time data */


mxArray* simSnap =
mxCreateStructMatrix(1, 1, 1, fieldNames);
mxSetField(simSnap, 0, fieldNames[0],
mxCreateDoubleScalar(rtd->cnt));
return simSnap;
}

11-17
11 S-Function Callback Methods

See Also
“Custom Code and Hand Coded Blocks using the S-function API”| mdlSetOperatingPoint|
GetOperatingPoint

Introduced in R2019a

11-18
mdlGetSimState

mdlGetSimState
(Not recommended) Return the C MEX S-function simulation state as a valid MATLAB data structure,
such as a matrix structure or a cell array.

Note mdlGetSimState is not recommended. Use mdlGetOpeartingPoint instead.

Required
No

Languages
C, C++

Syntax
#define MDL_SIM_STATE

mxArray* mdlGetSimState(SimStruct* S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine invokes this custom method to get the simulation state (SimState) of the model
containing S. A call to this method should occur after mdlStart and before mdlTerminate to ensure
that all of the S-function data structures (e.g., states, DWork vectors, and outputs) are available.

Example
/* Function: mdlGetSimState
* Abstract:
* Package the RunTimeData structure as a MATLAB structure
* and return it.
*/
static mxArray* mdlGetSimState(SimStruct* S)
{
RunTimeData_T* rtd =
(RunTimeData_T*)ssGetPWorkValue(S, 0);
const char* fieldNames[] = {"Count"};

/* Create a MATLAB structure to hold the run-time data */


mxArray* simSnap =

11-19
11 S-Function Callback Methods

mxCreateStructMatrix(1, 1, 1, fieldNames);
mxSetField(simSnap, 0, fieldNames[0],
mxCreateDoubleScalar(rtd->cnt));
return simSnap;
}

See Also
mdlSetSimState, GetSimState

Introduced in R2009a

11-20
mdlGetTimeOfNextVarHit

mdlGetTimeOfNextVarHit
Determine the time of the next sample time hit

Required
No

Languages
C, C++

Syntax
#define MDL_GET_TIME_OF_NEXT_VAR_HIT

void mdlGetTimeOfNextVarHit(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine invokes this optional method at a major time step when the variable sample time
registered by this S-function has a hit. This method is used by the Simulink engine to determine the
time of the next sample hit for variable sample time. The S-function should set this next sample hit
using ssSetTNext macro in this method. The time of the next hit must be greater than the current
simulation time as returned by ssGetT. The S-function must implement mdlGetTimeOfNextVarHit
if it operates at a variable sample time.

For Level-2 MATLAB S-functions, use a sample time of -2 to specify a variable sample time. The S-
function's output method should then update the NextTimeHit property of the instance of the
Simulink.MSFcnRunTimeBlock class representing the S-Function block to set the time of the next
sample time hit. See /msfcn_vs.m for an example.

For Level-1 MATLAB S-functions, a flag of 4 is passed to the S-function when the next sample time
hit needs to be calculated.

Note The time of the next hit can be a function of the input signals.

Example
static void mdlGetTimeOfNextVarHit(SimStruct *S)
{
time_T offset = getOffset();
time_T timeOfNextHit = ssGetT(S) + offset;

11-21
11 S-Function Callback Methods

ssSetTNext(S, timeOfNextHit);
}

See Also
mdlInitializeSampleTimes, ssGetT, ssSetTNext

Introduced before R2006a

11-22
mdlInitializeConditions

mdlInitializeConditions
Initialize the state vectors of this C MEX S-function

Required
No

Languages
C, C++

Syntax
#define MDL_INITIALIZE_CONDITIONS

void mdlInitializeConditions(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine invokes this optional method at the beginning of a simulation. It should initialize
the continuous and discrete states, if any, of this S-Function block. In a C MEX S-function, use
ssGetContStates and/or ssGetDiscStates to access the states. This method can also perform
any other initialization activities that this S-function requires.

Note If you have Simulink Coder and you need to ensure that the initialization code in the
mdlInitializeConditions function is run only once, then move this initialization code into the
mdlStart method. MathWorks recommends this code change as a best practice.

If this S-function resides in an enabled subsystem configured to reset states, the Simulink engine also
calls this method when the enabled subsystem restarts execution. C MEX S-functions can use the
ssIsFirstInitCond macro to determine whether the time at which mdlInitializeCondition is called
is equal to the simulation start time.

Note If you have Simulink Coder, when generating code for a noninlined C MEX S-function that
contains this method, make sure the method is not wrapped in a #if defined(MATLAB_MEX_FILE)
statement. For example:

#define MDL_INITIALIZE_CONDITIONS
#if defined(MDL_INITIALIZE_CONDITIONS) && defined(MATLAB_MEX_FILE)
static void mdlInitializeConditions(SimStruct *S)
{

11-23
11 S-Function Callback Methods

/* Add mdlInitializeConditions code here */


}
#endif

The define statement makes the mdlInitializeConditions method available only to a MATLAB
MEX file. If the S-function is not inlined, the Simulink Coder product cannot use this method,
resulting in link or run-time errors.

The Simulink engine calls mdlInitializeConditions prior to calculating the S-function's input
signals. Therefore, since the input signal values are not yet available, mdlInitializeConditions
should not use the input signal values to set initial conditions. If your S-function needs to initialize
internal values using the block's input signals, perform the initialization in mdlOutputs.

For example, in a C MEX S-function, initializes an IWork vector with one element in the
mdlInitializeSizes method.

ssSetNumIWork(S, 1);

The IWork vector holds a flag indicating if initial values have been specified. Initialize the flag's value
in the mdlInitializeCondition method.

static void mdlInitializeConditions(SimStruct *S)


{
/* The mdlInitializeConditions method is called when the simulation
start and every time an enabled subsystem is re-enabled.

Reset the IWork flag to 1 when values need to be reinitialized.*/

ssSetIWorkValue(S, 0, 1);
}

Check the value of the IWork vector flag in the mdlOutputs method, to determine if initial values
need to be set. Since the engine has calculated input values at this point in the simulation, the
mdlOutputs method can use them to initialize internal values.

static void mdlOutputs(SimStruct *S, int_T tid)


{
// Initialize values if the IWork vector flag is true. //
if (ssGetIWorkValue(S, 0) == 1) {
// Enter initialization code here //
}

// Remainder of mdlOutputs function //


}

For a Level-2 MATLAB S-function, use a DWork vector instead of an IWork vector in the previous
example.

Example
This example initializes both a continuous and discrete state to 1.0.

#define MDL_INITIALIZE_CONDITIONS /*Change to #undef to remove */


/*function*/
#if defined(MDL_INITIALIZE_CONDITIONS)

11-24
mdlInitializeConditions

static void mdlInitializeConditions(SimStruct *S)


{
int i;
real_T *xcont = ssGetContStates(S);
int_T nCStates = ssGetNumContStates(S);
real_T *xdisc = ssGetRealDiscStates(S);
int_T nDStates = ssGetNumDiscStates(S);

for (i = 0; i < nCStates; i++) {


*xcont++ = 1.0;
}

for (i = 0; i < nDStates; i++) {


*xdisc++ = 1.0;
}

}
#endif /* MDL_INITIALIZE_CONDITIONS */

For another example that initializes only the continuous states, see resetint.c.

See Also
mdlStart, mdlOutputs, ssIsFirstInitCond, ssGetContStates, ssGetDiscStates,
ssGetTStart, ssGetT, InitializeConditions

Introduced before R2006a

11-25
11 S-Function Callback Methods

mdlInitializeSampleTimes
Specify the sample rates at which this C MEX S-function operates

Required
Yes

Languages
C, C++

Syntax
#define MDL_INITIALIZE_SAMPLE_TIMES

void mdlInitializeSampleTimes(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
This method should specify the sample time and offset time for each sample rate at which this S-
function operates via the following paired macros

ssSetSampleTime(S, sampleTimeIndex, sample_time)


ssSetOffsetTime(S, offsetTimeIndex, offset_time)

where sampleTimeIndex runs from 0 to one less than the number of sample times specified in
mdlInitializeSizes via ssSetNumSampleTimes.

If the S-function operates at one or more sample rates, this method can specify any of the following
sample time and offset values for a given sample time:

• [CONTINUOUS_SAMPLE_TIME, 0.0]
• [CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]
• [discrete_sample_period, offset]
• [VARIABLE_SAMPLE_TIME, 0.0]

The uppercase values are macros defined in sl_sample_time_defs.h.

If the S-function operates at one rate, this method can alternatively set the sample time to one of the
following sample/offset time pairs.

• [INHERITED_SAMPLE_TIME, 0.0]

11-26
mdlInitializeSampleTimes

• [INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]

If the number of sample times is 0, the Simulink engine assumes that the S-function inherits its
sample time from the block to which it is connected, i.e., that the sample time is
[INHERITED_SAMPLE_TIME, 0.0]

This method can therefore return without doing anything.

Use the following guidelines when specifying sample times.

• A continuous function that changes during minor integration steps should set the sample time to
[CONTINUOUS_SAMPLE_TIME, 0.0]
• A continuous function that does not change during minor integration steps should set the sample
time to
[CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]
• A discrete function that changes at a specified rate should set the sample time to
[discrete_sample_period, offset]

where
discrete_sample_period > 0.0

and
0.0 <= offset < discrete_sample_period
• A discrete function that changes at a variable rate should set the sample time to
[VARIABLE_SAMPLE_TIME, 0.0]

The Simulink engine invokes the mdlGetTimeOfNextVarHit function to get the time of the next
sample hit for the variable-step discrete task.

Note that VARIABLE_SAMPLE_TIME requires a variable-step solver.


• To operate correctly in a triggered subsystem or a periodic system, a discrete S-function should

• Specify a single sample time set to


[INHERITED_SAMPLE_TIME, 0.0]
• Use ssSetOptions to set the SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME simulation
option in mdlInitializeSizes
• Verify that it was assigned a discrete or triggered sample time in mdlSetWorkWidths:
if (ssGetSampleTime(S, 0) == CONTINUOUS_SAMPLE_TIME) {
ssSetErrorStatus(S,
"This block cannot be assigned a continuous sample
time");
}

After propagating sample times throughout the block diagram, the engine assigns the sample time
[INHERITED_SAMPLE_TIME, INHERITED_SAMPLE_TIME]

to discrete blocks residing in triggered subsystems.

11-27
11 S-Function Callback Methods

If this function has no intrinsic sample time, it should set its sample time to inherited according to the
following guidelines:

• A function that changes as its input changes, even during minor integration steps, should set its
sample time to

[INHERITED_SAMPLE_TIME, 0.0]

A function that changes as its input changes, but doesn't change during minor integration steps
(i.e., is held during minor steps) should set its sample time to

[INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]

The S-function should use the ssIsSampleHit or ssIsContinuousTask macros to check for a
sample hit during execution (in mdlOutputs or mdlUpdate). For example, if the block's first sample
time is continuous, the function can use the following code fragment to check for a sample hit.

if (ssIsContinuousTask(S,tid)) {
}

Note The function receives incorrect results if it uses ssIsSampleHit(S,0,tid).

If the function wants to determine whether the third (discrete) task has a hit, it can use the following
code fragment.

if (ssIsSampleHit(S,2,tid) {
}

Note If you have Simulink Coder, when generating code for a noninlined S-function that contains
this method, make sure the method is not wrapped in a #if defined(MATLAB_MEX_FILE)
statement. For example:

#if defined(MATLAB_MEX_FILE)
static void mdlInitializeSampleTimes(SimStruct *S)
{
/* Add mdlInitializeSampleTimes code here *
}
#endif

The define statement makes the mdlInitializeSampleTimes method available only to a


MATLAB MEX file. If the S-function is not inlined, the Simulink Coder product cannot use this
method, resulting in link or run-time errors.

See Also
mdlSetInputPortSampleTime, mdlSetOutputPortSampleTime

Introduced before R2006a

11-28
mdlInitializeSizes

mdlInitializeSizes
Specify the number of inputs, outputs, states, parameters, and other characteristics of the C MEX S-
function

Required
Yes

Languages
C, C++

Syntax
#define MDL_INITIAL_SIZES

void mdlInitializeSizes(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
This is the first S-function callback methods that the Simulink engine calls. This method performs the
following tasks:

• Specify the number of parameters that this S-function supports, using ssSetNumSFcnParams.

Use ssSetSFcnParamTunable(S,paramIdx, 0) when a parameter cannot change during


simulation, where paramIdx starts at 0. When a parameter has been specified as not tunable, the
engine issues an error during simulation (or when in external mode when using the Simulink
Coder product) if an attempt is made to change the parameter.
• Specify the number of states that this function has, using ssSetNumContStates and
ssSetNumDiscStates.
• Configure the block's input ports, including:

• Specify the number of input ports that this S-function has, using ssSetNumInputPorts.
• Specify the dimensions of the input ports.

See ssSetInputPortDimensionInfo for more information.


• For each input port, specify whether it has direct feedthrough, using
ssSetInputPortDirectFeedThrough.

A port has direct feedthrough if the input is used in either the mdlOutputs or
mdlGetTimeOfNextVarHit function. The direct feedthrough flag for each input port can be

11-29
11 S-Function Callback Methods

set to either 1=yes or 0=no. It should be set to 1 if the input, u, is used in the mdlOutputs or
mdlGetTimeOfNextVarHit routine. Setting the direct feedthrough flag to 0 tells the Simulink
engine that u is not used in either of these S-function routines. Violating this leads to
unpredictable results.
• Configure the block's output ports, including:

• Specify the number of output ports that the block has, using ssSetNumOutputPorts.
• Specify the dimensions of the output ports.

See mdlSetOutputPortDimensionInfo for more information.

If your S-function outputs are discrete (for example, the outputs only take specific values such as
0, 1, and 2), specify SS_OPTION_DISCRETE_VALUED_OUTPUT.
• Set the number of sample times (i.e., sample rates) at which the block operates.

There are two ways of specifying sample times:

• Port-based sample times


• Block-based sample times

See “Specify S-Function Sample Times” on page 9-20 for a complete discussion of sample time
issues.

For multirate S-functions, the suggested approach to setting sample times is via the port-based
sample times method. When you create a multirate S-function, you must take care to verify that,
when slower tasks are preempted, your S-function correctly manages data so as to avoid race
conditions. When port-based sample times are specified, the block cannot inherit a sample time of
Inf at any port.
• Set the size of the block's work vectors, using ssSetNumRWork, ssSetNumIWork,
ssSetNumPWork, ssSetNumModes, ssSetNumNonsampledZCs.
• Set the simulation options that this block implements, using ssSetOptions.

All options have the form SS_OPTION_<name>. See “Configure C/C++ S-Function Features” for
information on each option. Use a bitwise OR operator to set multiple options, as in

ssSetOptions(S, (SS_OPTION_name1 | SS_OPTION_name2))

Note If you have Simulink Coder, when generating code for a noninlined S-function that contains
this method, make sure the method is not wrapped in a #if defined(MATLAB_MEX_FILE)
statement. For example:

#if defined(MATLAB_MEX_FILE)
static void mdlInitializeSizes(SimStruct *S)
{
/* Add mdlInitializeSizes code here *
}
#endif

The define statement makes the mdlInitializeSizes method available only to a MATLAB MEX
file. If the S-function is not inlined, the Simulink Coder product cannot use this method, resulting in
link or run-time errors.

11-30
mdlInitializeSizes

Dynamically Sized Block Features

You can set the parameters NumContStates, NumDiscStates, NumInputs, NumOutputs,


NumRWork, NumIWork, NumPWork, NumModes, and NumNonsampledZCs to a fixed nonnegative
integer or tell the Simulink engine to size them dynamically:

• DYNAMICALLY_SIZED -- Sets lengths of states, work vectors, and so on to values inherited from
the driving block. It sets widths to the actual input widths, according to the scalar expansion rules
unless you use mdlSetWorkWidths to set the widths.
• 0 or positive number -- Sets lengths (or widths) to the specified values. The default is 0.

Initialization for MATLAB S-Functions

The Level-2 MATLAB S-function setup method performs nearly the same tasks as the C MEX S-
function mdlInitializeSizes method.

Example
static void mdlInitializeSizes(SimStruct *S)
{
int_T nInputPorts = 1; /* number of input ports */
int_T nOutputPorts = 1; /* number of output ports */
int_T needsInput = 1; /* direct feedthrough */

int_T inputPortIdx = 0;
int_T outputPortIdx = 0;

ssSetNumSFcnParams(S, 0); /* Number of expected parameters */


if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
/*
* If the number of expected input parameters is not
* equal to the number of parameters entered in the
* dialog box, return. The Simulink engine generates an
* error indicating that there is aparameter mismatch.
*/
return;
}else {
mdlCheckParameters(S);

if (ssGetErrorStatus(S) != NULL)
return;
}

ssSetNumContStates( S, 0);
ssSetNumDiscStates( S, 0);

/*
* Configure the input ports. First set the number of input
* ports.
*/
if (!ssSetNumInputPorts(S, nInputPorts)) return;
/*
* Set input port dimensions for each input port index
* starting at 0.

11-31
11 S-Function Callback Methods

*/
if(!ssSetInputPortDimensionInfo(S, inputPortIdx,
DYNAMIC_DIMENSION)) return;
/*
* Set direct feedthrough flag (1=yes, 0=no).
*/
ssSetInputPortDirectFeedThrough(S, inputPortIdx, needsInput);

/*
* Configure the output ports. First set the number of
* output ports.
*/
if (!ssSetNumOutputPorts(S, nOutputPorts)) return;

/*
* Set output port dimensions for each output port index
* starting at 0.
*/
if(!ssSetOutputPortDimensionInfo(S,outputPortIdx,
DYNAMIC_DIMENSION)) return;

/*
* Set the number of sample times. */
ssSetNumSampleTimes(S, 1);

/*
* Set size of the work vectors.
*/
ssSetNumRWork(S, 0); /* real vector */
ssSetNumIWork(S, 0); /* integer vector */
ssSetNumPWork(S, 0); /* pointer vector */
ssSetNumModes(S, 0); /* mode vector */
ssSetNumNonsampledZCs(S, 0); /* zero crossings */

ssSetOptions(S, 0);

} /* end mdlInitializeSizes */

See Also
setup, mdlInitializeSampleTimes

Introduced before R2006a

11-32
mdlOutputs

mdlOutputs
Compute the signals that this block emits

Required
Yes

Languages
C, C++

Syntax
#define MDL_OUTPUTS

void mdlOutputs(SimStruct *S, int_T tid)

Arguments
S
SimStruct representing an S-Function block.
tid
Task ID.

Description
The Simulink engine invokes this required method at each simulation time step. The method should
compute the S-function's outputs at the current time step and store the results in the S-function's
output signal arrays.

The tid (task ID) argument specifies the task running when the mdlOutputs routine is invoked. You
can use this argument in the mdlOutputs routine of a multirate S-Function block to encapsulate
task-specific blocks of code (see “Multirate S-Function Blocks” on page 9-27).

Use the UNUSED_ARG macro if the S-function does not contain task-specific blocks of code to indicate
that the tid input argument is required but not used in the body of the callback. To do this, insert the
line
UNUSED_ARG(tid)

after the declarations in mdlOutputs.

Note If you have Simulink Coder, when generating code for a noninlined S-function that contains
this method, make sure the method is not wrapped in a #if defined(MATLAB_MEX_FILE)
statement. For example:
#if defined(MATLAB_MEX_FILE)
static void mdlOutputs(SimStruct *S)

11-33
11 S-Function Callback Methods

{
/* Add mdlOutputs code here *
}
#endif

The define statement makes the mdlOutputs method available only to a MATLAB MEX file. If the
S-function is not inlined, the Simulink Coder product cannot use this method, resulting in link or run-
time errors.

Example
For an example of an mdlOutputs routine that works with multiple input and output ports, see
sfun_multiport.c.

See Also
ssGetOutputPortComplexSignal, ssGetOutputPortRealSignal, ssGetOutputPortSignal,
Outputs

Introduced before R2006a

11-34
mdlProcessParameters

mdlProcessParameters
Process the C MEX S-function's parameters

Required
No

Languages
C, C++

Syntax

#define MDL_PROCESS_PARAMETERS

void mdlProcessParameters(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
This is an optional routine that the Simulink engine calls after mdlCheckParameters changes and
verifies parameters. The processing is done at the top of the simulation loop when it is safe to process
the changed parameters. This function is only valid for simulation. C MEX S-functions must enclose
the method in a #if defined(MATLAB_MEX_FILE) statement.

The purpose of this routine is to process newly changed parameters. An example is to cache
parameter changes in work vectors. The engine does not call this routine when it is used with the
Simulink Coder product. Therefore, if you use this routine in an S-function designed for use with the
Simulink Coder product, you must write your S-function so that it doesn't rely on this routine. To do
this, you must inline your S-function by using the Target Language Compiler. For information on
inlining S-functions, see “Inlining S-Functions” (Simulink Coder).

Example
This example processes a character vector parameter that mdlCheckParameters has verified to be
of the form '+++' (where there could be any number of '+' or '-' characters).

#define MDL_PROCESS_PARAMETERS /* Change to #undef to remove function */


#if defined(MDL_PROCESS_PARAMETERS) && defined(MATLAB_MEX_FILE)
static void mdlProcessParameters(SimStruct *S)
{
int_T i;
char_T *plusMinusStr;

11-35
11 S-Function Callback Methods

int_T nInputPorts = ssGetNumInputPorts(S);


int_T *iwork = ssGetIWork(S);
if ((plusMinusStr=(char_T*)malloc(nInputPorts+1)) == NULL) {
ssSetErrorStatus(S,"Memory allocation error in mdlStart");
return;
}
if (mxGetString(SIGNS_PARAM(S),plusMinusStr,nInputPorts+1) != 0) {
free(plusMinusStr);
ssSetErrorStatus(S,"mxGetString error in mdlStart");
return;
}
for (i = 0; i < nInputPorts; i++) {
iwork[i] = plusMinusStr[i] == '+'? 1: -1;
}
free(plusMinusStr);

}
#endif /* MDL_PROCESS_PARAMETERS */

mdlProcessParameters is called from mdlStart to load the signs character vector prior to the
start of the simulation loop.

#define MDL_START
#if defined(MDL_START)
static void mdlStart(SimStruct *S)
{
mdlProcessParameters(S);
}
#endif /* MDL_START */

See Also
mdlCheckParameters, ProcessParameters

Introduced before R2006a

11-36
mdlProjection

mdlProjection
Perturb the solver's solution of a system's states to better satisfy time-invariant solution relationships

Required
No

Languages
C, C++

Syntax
#define MDL_PROJECTION

void mdlProjection(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
This method is intended for use with S-functions that model dynamic systems whose states satisfy
time-invariant relationships, such as those resulting from mass or energy conservation or other
physical laws. The Simulink engine invokes this method at each time step after the model's solver has
computed the S-function's states for that time step. Typically, slight errors in the numerical solution
of the states cause the solutions to fail to satisfy solution invariants exactly. Your mdlProjection
method can compensate for the errors by perturbing the states so that they more closely approximate
solution invariants at the current time step. As a result, the numerical solution adheres more closely
to the ideal solution as the simulation progresses, producing a more accurate overall simulation of
the system modeled by your S-function.

Your mdlProjection method's perturbations of system states must fall within the solution error
tolerances specified by the model in which the S-function is embedded. Otherwise, the perturbations
may invalidate the solver's solution. It is up to your mdlProjection method to ensure that the
perturbations meet the error tolerances specified by the model. See “Perturbing a System's States
Using a Solution Invariant” on page 11-38 for a simple method for perturbing a system's states. The
following articles describe more sophisticated perturbation methods that your mdlProjection
method can use.

• C.W. Gear, “Maintaining Solution Invariants in the Numerical Solution of ODEs,” Journal on
Scientific and Statistical Computing, Vol. 7, No. 3, July 1986.
• L.F. Shampine, “Conservation Laws and the Numerical Solution of ODEs I,” Computers and
Mathematics with Applications, Vol. 12B, 1986, pp. 1287–1296.
• L.F. Shampine, “Conservation Laws and the Numerical Solution of ODEs II,” Computers and
Mathematics with Applications, Vol. 38, 1999, pp. 61–72.

11-37
11 S-Function Callback Methods

Example
Perturbing a System's States Using a Solution Invariant

Here is a simple, Taylor-series-based approach to perturbing a system's states. Suppose your S-


function models a dynamic system having a solution invariant, g(X, t), i.e., g is a continuous,
differentiable function of the system states, X , and time, t, whose value is constant with time. Then

−1
Xn ≅ Xn* + JnT ( Jn JnT ) Rn

where

• Xn is the system's ideal state vector at the solver's current time step
• Xn* is the approximate state vector computed by the solver at the current time step
• Jn is the Jacobian of the invariant function evaluated at the point in state space specified by the
approximate state vector at the current time step:

∂g
Jn = (X *, t )
∂X n n
• tn is the time at the current time step
• Rn is the residual (difference) between the invariant function evaluated at Xn and Xn* at the
current time step:

Rn = g(Xn, tn) − g(Xn*, tn)

Note The value of g(Xn, tn) is the same at each time step and is known by definition.

Given a continuous, differentiable invariant function for the system that your S-function models, this
formula allows your S-function's mdlProjection method to compute a perturbation

−1
JnT ( Jn JnT ) Rn

of the solver's numerical solution, Xn*, that more closely matches the ideal solution, Xn, keeping the S-
function's solution from drifting from the ideal solution as the simulation progresses.

MATLAB Example

This example illustrates how the perturbation method outlined in the previous section can keep a
model's numerical solution from drifting from the ideal solution as a simulation progresses. Consider
the following model,mdlProjectionEx1:

The PredPrey block references an S-function that uses the Lotka-Volterra equations:

11-38
mdlProjection

predprey_noproj.m

function predprey_noproj(block)
% This is one of a pair S-functions that model a population of predators
% and preys, the other being predprey.m.

% Both S-functions use the Lotka-Volterra equations


%
% xdot = ax(1-y)
% ydot = -cy(1-x)
%
% to model changes in the population where x(t) and y(t) are
% the number of predators and prey, respectively, and
% a and c are constants. Both S-functions assume a=1 and c=2 and
% that the population consists initially of one predator and three prey.
%
% The solution to the Lotka-Volterra equations obeys the time-invariant
% relationship
%
% (x^-c)*exp(cx)*(y^-a)*exp(ay) = d
%
% where d is a constant.
%
% The other S-function uses this relationship to compensate for
% numerical errors in the solver's solution of the model's state
% equations. This S-function does not attempt to compensate for the
% numerical errors. As a result, the solution drifts from the ideal
% solution as the simulation progresses.
%
% For more information, see the documentation for the mdlProjection
% method in the online Simulink documentation.
%
% Copyright 2006 The MathWorks, Inc.

setup(block);

%endfunction

function setup(block)

%% Register number of input and output ports


block.NumInputPorts = 0;
block.NumOutputPorts = 2;

block.OutputPort(1).Dimensions = 1;
block.OutputPort(1).SamplingMode = 'Sample';

block.OutputPort(2).Dimensions = 1;
block.OutputPort(2).SamplingMode = 'Sample';

%% Set block sample time to variable sample time


block.SampleTimes = [0 0];

%% Setup Dwork
block.NumContStates = 2;

%% Register methods

11-39
11 S-Function Callback Methods

block.RegBlockMethod('InitializeConditions', @InitConditions);
block.RegBlockMethod('Outputs', @Output);
block.RegBlockMethod('Derivatives', @Derivatives);
block.RegBlockMethod('Projection', @Projection);
block.RegBlockMethod('Update', @Update);

%endfunction

function InitConditions(block)

%% Initialize Dwork: 1 predator, 3 prey.


block.ContStates.Data = [1 3]';

%endfunction

function Output(block)

% Output number of predators.


block.OutputPort(1).Data = block.ContStates.Data(1);

% Output number of prey.


block.OutputPort(2).Data = block.ContStates.Data(2);

block.ContStates.Data;

%endfunction

function Derivatives(block)

states = block.ContStates.Data;
% System ODEs
block.Derivatives.Data = [2*states(1)*(1-states(2)); -states(2)*(1-states(1))];

%endfunction

function Projection(block)
%not implemented
%endfunction

function Update(block)

%endfunction

ẋ = ax(1 − y)
ẏ = − cy(1 − x)

to model predator-prey population dynamics, where x(t) is the population density of the predators and
y(t) is the population density of prey. The ideal solution to the predator-prey ODEs satisfies the time-
invariant function

x−cecx y−aeay = d

where a, c, and d are constants. The S-function assumes a = 1, c = 2, and d = 121.85.

The Invariant Residual block in this model computes the residual between the invariant function
evaluated along the system's ideal trajectory through state space and its simulated trajectory:

11-40
mdlProjection

cxn −a ayn
Rn = d − xn−ce yn e

where xnand ynare the values computed by the model's solver for the predator and prey population
densities, respectively, at the current time step. Ideally, the residual should be zero throughout
simulation of the model, but simulating the model reveals that the residual actually strays
considerably from zero:

Now consider the following model, mdlProjectionEx2:

This model is the same as the previous model, except that its S-function, predprey.m, includes a
mdlProjection method that uses the perturbation approach outlined in “Perturbing a System's
States Using a Solution Invariant” on page 11-38 to compensate for numerical drift. As a result, the
numerical solution more closely tracks the ideal solution as the simulation progresses as
demonstrated by the residual signal, which remains near or at zero throughout the simulation:

11-41
11 S-Function Callback Methods

predprey.m

function predprey(block)
% This is one of a pair S-functions that model a population of predators
% and preys, the other being predprey_noproj.m.

% Both S-functions use the Lotka-Volterra equations


%
% xdot = ax(1-y)
% ydot = -cy(1-x)
%
% to model changes in the population where x(t) and y(t) are
% the number of predators and prey, respectively, and
% a and c are constants. Both S-functions assume a=1 and c=2 and
% that the population consists initially of one predator and three prey.
%
% The solution to the Lotka-Volterra equations obeys the time-invariant
% relationship
%
% (x^-c)*exp(cx)*(y^-a)*exp(ay) = d
%
% where d is a constant.
%
% This S-function uses this relationship to compensate for
% numerical errors in the solver's solution of the model's state
% equations. The other S-function does not. As a result,
% the other's solution drifts from the ideal solution as the
% simulation progresses.
%
% For more information, see the documentation for the mdlProjection
% method in the online Simulink documentation.
%
% Copyright 2006 The MathWorks, Inc.
setup(block);

%endfunction

function setup(block)

%% Register number of input and output ports


block.NumInputPorts = 0;
block.NumOutputPorts = 2;

block.OutputPort(1).Dimensions = 1;
block.OutputPort(1).SamplingMode = 'Sample';

block.OutputPort(2).Dimensions = 1;
block.OutputPort(2).SamplingMode = 'Sample';

%% Set block sample time to variable sample time


block.SampleTimes = [0 0];

%% Setup Dwork
block.NumContStates = 2;

%% Register methods
block.RegBlockMethod('InitializeConditions', @InitConditions);

11-42
mdlProjection

block.RegBlockMethod('Outputs', @Output);
block.RegBlockMethod('Derivatives', @Derivatives);
block.RegBlockMethod('Projection', @Projection);

%endfunction

function InitConditions(block)

%% Initialize Dwork: 1 predator, 3 prey.


block.ContStates.Data = [1 3]';

%endfunction

function Output(block)

% Output number of predators.


block.OutputPort(1).Data = block.ContStates.Data(1);

% Output number of prey.


block.OutputPort(2).Data = block.ContStates.Data(2);
block.ContStates.Data;

%endfunction

function Derivatives(block)

states = block.ContStates.Data;
% System ODEs
block.Derivatives.Data = [2*states(1)*(1-states(2)); -states(2)*(1-states(1))];

%endfunction

function Projection(block)

states = block.ContStates.Data;

%Computing the Jacobian of the Invariant


J = localInvJac(states);

%Computing the residual of the invariant


R = localInvRes(states);

%Perturbing the states


states = states + (J')*((J*(J'))^-1) * R;

%Update the states


block.ContStates.Data = states;

%endfunction

function J = localInvJac(states)

%Computing the Jacobian of the invariant


L = 1/states(1) * exp(states(1));
R = 1/(states(2)^2)*exp(2*states(2));
Lx = (states(1)-1)/(states(1)^2)*exp(states(1));
Ry = 2*(states(2)-1)/(states(2)^3)*exp(2*states(2));

11-43
11 S-Function Callback Methods

J = [Lx*R L*Ry];

%endfunction

function R = localInvRes(states)

%Invariant value
inv0 = 1.218481287142732e+002;

%Computing invariant
L = 1/states(1) * exp(states(1));
R = 1/(states(2)^2)*exp(2*states(2));
inv = L*R;

%Residual
R = inv0-inv;

%endfunction

See Also
Projection

11-44
mdlProjection

Introduced in R2006b

11-45
11 S-Function Callback Methods

mdlRTW
Generate code generation data for a C MEX S-function

Required
No

Languages
C, C++

Syntax
#define MDL_RTW

void mdlRTW(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
This function is called when the Simulink Coder product is generating the model.rtw file. In C MEX
S-functions, you can call the following functions that add fields to the model.rtw file:

• ssWriteRTWParameters
• ssWriteRTWParamSettings
• ssWriteRTWWorkVect
• ssWriteRTWStr
• ssWriteRTWStrParam
• ssWriteRTWScalarParam
• ssWriteRTWStrVectParam
• ssWriteRTWVectParam
• ssWriteRTW2dMatParam
• ssWriteRTWMxVectParam
• ssWriteRTWMx2dMatParam

In C MEX S-functions, this function must be enclosed in a #if defined(MATLAB_MEX_FILE)


statement.

11-46
mdlRTW

Example
See the S-function sfun_multiport.c in the Simulink model sfcndemo_sfun_multiport for an
example.

See Also
ssSetErrorStatus, WriteRTW

Introduced before R2006a

11-47
11 S-Function Callback Methods

mdlSetDefaultPortComplexSignals
Set the numeric types (real, complex, or inherited) of ports whose numeric types cannot be
determined from block connectivity

Required
No

Languages
C, C++

Syntax
#define MDL_SET_DEFAULT_PORT_COMPLEX_SIGNALS

void mdlSetDefaultPortComplexSignals(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine invokes this method if the block has ports whose numeric types cannot be
determined from connectivity. (This usually happens when the block is unconnected or is part of a
feedback loop.) This method must set the numeric types of all ports whose numeric types are not set.
This method is only valid for simulation, and must be enclosed in a #if
defined(MATLAB_MEX_FILE) statement.

If the block does not implement this method and at least one port is known to be complex, the engine
sets the unknown ports to COMPLEX_YES; otherwise, it sets the unknown ports to COMPLEX_NO.

See Also
ssSetOutputPortComplexSignal, ssSetInputPortComplexSignal

Introduced before R2006a

11-48
mdlSetDefaultPortDataTypes

mdlSetDefaultPortDataTypes
Set the data types of ports whose data types cannot be determined from block connectivity

Required
No

Languages
C, C++

Syntax
#define MDL_SET_DEFAULT_PORT_DATA_TYPES

void mdlSetDefaultPortDataTypes(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine invokes this method if the block has ports whose data types cannot be
determined from block connectivity. (This usually happens when the block is unconnected or is part of
a feedback loop.) This method must set the data types of all ports whose data types are not set. This
method is only valid for simulation, and must be enclosed in a #if defined(MATLAB_MEX_FILE)
statement.

If the block does not implement this method and the engine cannot determine the data types of any of
its ports, the engine sets the data types of all the ports to double. If the block does not implement
this method and the engine cannot determine the data types of some, but not all, of its ports, the
engine sets the unknown ports to the data type of the port whose data type has the largest size.

The engine invokes an error if the mdlSetDefaultPortDataType method attempts to modify the
data type of a port when the data type was previously specified by mdlSetInputPortDataType or
mdlSetOutputPortDataType. If an S-function has multiple input or output ports,
mdlSetDefaultPortDataType should check if the data type of a port is still dynamic before
attempting to set the type. For example, the mdlSetDefaultPortDataType uses the following lines
to check if the data type of the second input port is still unknown.
if (ssGetInputPortDataType(S, 1) == DYNAMICALLY_TYPED) {
ssSetInputPortDataType(S, 1, SS_UINT8 );
}

See Also
ssSetOutputPortDataType, ssSetInputPortDataType

11-49
11 S-Function Callback Methods

Introduced before R2006a

11-50
mdlSetDefaultPortDimensionInfo

mdlSetDefaultPortDimensionInfo
Set the default dimensions of the signals accepted or emitted by a C MEX S-function's ports

Required
No

Languages
C, C++

Syntax
#define MDL_SET_DEFAULT_PORT_DIMENSION_INFO

void mdlSetDefaultPortDimensionInfo(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine calls this method during signal dimension propagation when a model does not
supply enough information to determine the dimensionality of signals that can enter or leave the
block represented by S. This method should set the dimensions of any input and output ports that are
dynamically sized to default values. This method is only valid for simulation, and must be enclosed in
a #if defined(MATLAB_MEX_FILE) statement.

If the S-function does not implement this method, the engine tries to find a set of dimensions that will
satisfy the dimension propagation rules implemented using mdlSetInputPortDimensionInfo and
mdlSetOutputPortDimensionInfo. This process might not be able to produce a valid set of
dimensions for S-functions with special dimension requirements.

The engine invokes an error if the mdlSetDefaultPortDimensionInfo method attempts to modify


the dimensions of a port when the dimensions were previously specified by
mdlSetInputPortDimensionInfo or mdlSetOutputPortDimensionInfo. If an S-function has
multiple input or output ports, mdlSetDefaultPortDimensionInfo should check if the dimensions
of the port are still dynamic before attempting to set the dimensions. For example, the
mdlSetDefaultPortDimensionInfo uses the following lines to check if the dimensions of the first
output port are still unknown.

if (ssGetOutputPortWidth(S, 0) == DYNAMICALLY_SIZED) {
ssSetOutputPortMatrixDimensions(S, 0, 1, 1 );
}

11-51
11 S-Function Callback Methods

Example
See sfun_matadd.c for an example of how to use this function.

See Also
ssSetErrorStatus, ssSetOutputPortMatrixDimensions

Introduced before R2006a

11-52
mdlSetInputPortComplexSignal

mdlSetInputPortComplexSignal
Set the numeric types (real, complex, or inherited) of the signals accepted by an input port

Required
No

Languages
C, C++

Syntax
#define MDL_SET_INPUT_PORT_COMPLEX_SIGNAL

void mdlSetInputPortComplexSignal(SimStruct *S, int_T port,


CSignal_T csig)

Arguments
S
SimStruct representing an S-Function block.
port
Index of a port.
csig
Numeric type of signal, either COMPLEX_NO (real) or COMPLEX_YES (complex).

Description
The Simulink engine calls this routine to set the input port numeric type for inputs that have this
attribute set to COMPLEX_INHERITED. The input csig is the proposed numeric type for this input
port. This method is only valid for simulation. C MEX S-functions must enclosed this method in a #if
defined(MATLAB_MEX_FILE) statement.

The S-function must check whether the proposed numeric type is a valid type for the specified port. If
it is valid, a C MEX S-function sets the numeric type of the specified input port using
ssSetInputPortComplexSignal. Otherwise, it reports an error using ssSetErrorStatus.

The S-function can also set the numeric types of other input and output ports with inherited numeric
types. The engine reports an error if the S-function changes the numeric type of a port whose
numeric type is known.

If the S-function does not implement this routine, the engine assumes that the S-function accepts a
real or complex signal and sets the input port numeric type to the specified value.

The engine calls this method until all input ports with inherited numeric types have their numeric
types specified.

11-53
11 S-Function Callback Methods

Example
See sdotproduct.c for an example of how to use this function.

See Also
ssSetErrorStatus, ssSetInputPortComplexSignal, SetInputPortComplexSignal

Introduced before R2006a

11-54
mdlSetInputPortDataType

mdlSetInputPortDataType
Set the data types of the signals accepted by an input port

Required
No

Languages
C, C++

Syntax
#define MDL_SET_INPUT_PORT_DATA_TYPE

void mdlSetInputPortDataType(SimStruct *S, int_T port,


DTypeId id)

Arguments
S
SimStruct representing an S-Function block.
port
Index of a port.
id
Data type ID.

Description
The Simulink engine calls this routine to set the data type of port when port has an inherited data
type. The data type id is the proposed data type for this port. Data type IDs for the built-in data types
can be found in simstruc_types.h. This method is only valid for simulation. C MEX S-functions
must enclose this method in a #if defined(MATLAB_MEX_FILE) statement.

The S-function must check whether the specified data type is a valid data type for the specified port.
If it is a valid data type, a C MEX S-functions sets the data type of the input port using
ssSetInputPortDataType. Otherwise, it reports an error using ssSetErrorStatus.

The S-function can also set the data types of other input and output ports if they are unknown. The
engine reports an error if the S-function changes the data type of a port whose data type has been
set.

If the block does not implement this routine, the engine assumes that the block accepts any data type
and sets the input port data type to the specified value.

The engine calls this method until all input ports with inherited data types have their data types
specified.

11-55
11 S-Function Callback Methods

See Also
ssSetErrorStatus, ssSetInputPortDataType, SetInputPortDataType

Introduced before R2006a

11-56
mdlSetInputPortDimensionInfo

mdlSetInputPortDimensionInfo
Set the dimensions of the signals accepted by an input port

Required
No

Languages
C, C++

Syntax
#define MDL_SET_INPUT_PORT_DIMENSION_INFO

void mdlSetInputPortDimensionInfo(SimStruct *S, int_T port,


const DimsInfo_T *dimsInfo)

Arguments
S
SimStruct representing an S-Function block.
port
Index of a port.
dimsInfo
Structure that specifies the signal dimensions supported by the port.

See ssSetInputPortDimensionInfo for a description of this structure.

Description
The Simulink engine calls this method during dimension propagation with candidate dimensions
dimsInfo for port. In C MEX S-functions, if the proposed dimensions are acceptable, the method
sets the actual port dimensions, using ssSetInputPortDimensionInfo. If they are unacceptable,
the method generates an error via ssSetErrorStatus.

This method is only valid for simulation. A C MEX S-function must enclose the method in a #if
defined(MATLAB_MEX_FILE) statement.

Note This method can set the dimensions of any other input or output port whose dimensions derive
from the dimensions of port.

By default, the engine calls this method only if it can fully determine the dimensionality of port from
the port to which it is connected. For C MEX S-functions, if the engine cannot completely determine
the dimensionality from port connectivity, it invokes mdlSetDefaultPortDimensionInfo. If an S-

11-57
11 S-Function Callback Methods

function can fully determine the port dimensionality from partial information, set the option
SS_OPTION_ALLOW_PARTIAL_DIMENSIONS_CALL in mdlInitializeSizes, using ssSetOptions.
If this option is set, the engine invokes mdlSetInputPortDimensionInfo even if it can only
partially determine the dimensionality of the input port from connectivity.

The engine calls this method until all input ports with inherited dimensions have their dimensions
specified.

Example
See sfun_matadd.c for an example of how to use this function.

See Also
ssSetErrorStatus, mdlSetOutputPortDimensionInfo, SetInputPortDimensions

Introduced before R2006a

11-58
mdlSetInputPortDimensionsModeFcn

mdlSetInputPortDimensionsModeFcn
Propagate the dimensions mode

Required
No

Languages
C, C++

Syntax
void mdlSetInputPortDimensionsModeFcn(SimStruct *S, int_T portIdx,
DimensionsMode_T dimsMode)

Arguments
S
SimStruct representing an S-Function block.
portIdx
Index of a port.
dimsMode
Current dimensions mode. Possible values are INHERIT_DIMS_MODE, FIXED_DIMS_MODE, and
VARIABLE_DIMS_MODE

Description
The Simulink engine calls this optional method to enable this S-function to set the dimensions mode
of the input port indexed by portIdx.

C Example
See sfun_varsize_holdStatesUntilReset.c for an example of how to use this function.

See Also
mdlSetInputPortDimensionInfo, SetInputPortDimensionsMode

Introduced in R2009b

11-59
11 S-Function Callback Methods

mdlSetInputPortSampleTime
Set the sample time of an input port that inherits its sample time from the port to which it is
connected

Required
No

Languages
C, C++

Syntax
#define MDL_SET_INPUT_PORT_SAMPLE_TIME

void mdlSetInputPortSampleTime(SimStruct *S, int_T port,


real_T sampleTime, real_T offsetTime)

Arguments
S
SimStruct representing an S-Function block.
port
Index of a port.
sampleTime
Inherited sample time for port.
offsetTime
Inherited offset time for port.

Description
The Simulink engine invokes this method with the sample time that port inherits from the port to
which it is connected.

For C MEX S-functions, if the inherited sample time is acceptable, this method sets the sample time
of port to the inherited time, using ssSetInputPortSampleTime and
ssSetInputPortOffsetTime. If the sample time is unacceptable, this method generates an error
via ssSetErrorStatus. Note that any other input or output ports whose sample times are implicitly
defined by virtue of knowing the sample time of the given port can also have their sample times set
via calls to ssSetInputPortSampleTime or ssSetOutputPortSampleTime. This method is only
valid for simulation, and must be enclosed in a #if defined(MATLAB_MEX_FILE) statement.

The engine calls this method until all input ports with inherited sample times are specified.

When inherited port-based sample times are specified, the sample time is guaranteed to be one of the
following where 0.0 < period < inf and 0.0 <= offset < period.

11-60
mdlSetInputPortSampleTime

Sample Time Offset Time


Continuous 0.0 0.0
Discrete period offset

Constant, triggered, and variable-step sample times are not propagated to S-functions with port-
based sample times.

Generally mdlSetInputPortSampleTime is called once per port with the input port sample time.
However, there can be cases where this function is called more than once. This happens when the
simulation engine is converting continuous sample times to continuous but fixed in minor steps
sample times. When this occurs, the original values of the sample times specified in
mdlInitializeSizes are restored before this method is called again.

The final sample time specified at the port can be different from (but equivalent to) the sample time
specified by this method. This occurs when

• The model uses a fixed-step solver and the port has a continuous but fixed in minor step sample
time. In this case, the Simulink engine converts the sample time to the fundamental sample time
for the model.
• The engine adjusts the sample time to be as numerically sound as possible. For example, the
engine converts [0.2499999999999, 0] to [0.25, 0].

The S-function can examine the final sample times in mdlInitializeSampleTimes.

See Also
ssSetInputPortSampleTime, ssSetOutputPortSampleTime, mdlInitializeSampleTimes,
SetInputPortSampleTime

Introduced before R2006a

11-61
11 S-Function Callback Methods

mdlSetInputPortWidth
Set the width of an input port that accepts 1-D (vector) signals

Required
No

Languages
C, C++

Syntax
#define MDL_SET_INPUT_PORT_WIDTH

void mdlSetInputPortWidth(SimStruct *S, int_T port, int_T width)

Arguments
S
SimStruct representing an S-Function block.
port
Index of a port.
width
Width of signal.

Description
This method is called with the candidate width for a dynamically sized port. If the proposed width is
acceptable, the method should set the actual port width using ssSetInputPortWidth. If the size is
unacceptable, an error should be generated via ssSetErrorStatus. Note that any other
dynamically sized input or output ports whose widths are implicitly defined by virtue of knowing the
width of the given port can also have their widths set via calls to ssSetInputPortWidth or
ssSetOutputPortWidth. This method is only valid for simulation, and must be enclosed in a #if
defined(MATLAB_MEX_FILE) statement.

The Simulink engine invokes this method until all dynamically sized input ports are configured.

See Also
ssSetInputPortWidth, ssSetOutputPortWidth, ssSetErrorStatus

Introduced before R2006a

11-62
mdlSetOperatingPoint

mdlSetOperatingPoint
Restore operating point of C MEX S-function

Required
No

Languages
C, C++

Syntax
#define MDL_OPERATING_POINT

void mdlSetOperatingPoint(SimStruct* S, const mxArray* in)

Arguments
S
SimStruct representing an S-Function block.
const mxArray* in
Operating point of S-Function created by mdlGetOperatingPoint.

Description
The Simulink engine invokes this custom method at the beginning of a simulation of the model
containing S (SimStruct representing an S-Function block). mdlSetOperatingPoint sets the initial
simulation state of the S-function to the operating point of the model.

Example
/* Function: mdlSetOperatingPoint
* Abstract:
* Unpack the MATLAB structure passed and restore it to
* the RunTimeData structure
*/
static void mdlSetOperatingPoint(SimStruct* S,
const mxArray* simSnap)
{
RunTimeData_T* rtd =
(RunTimeData_T*)ssGetPWorkValue(S, 0);

/* Check and load the count value */


{
const mxArray* cnt =
mxGetField(simSnap, 0, fieldNames[0]);
ERROR_IF_NULL(S,cnt,

11-63
11 S-Function Callback Methods

"Count field not found in simulation state");


if ( mxIsComplex(cnt) ||
!mxIsUint64(cnt) ||
mxGetNumberOfElements(cnt) != 1 ) {
ssSetErrorStatus(S, "Count field is invalid");
return;
}
rtd->cnt = ((uint64_T*)(mxGetData(cnt)))[0];
}
}

See Also
“Custom Code and Hand Coded Blocks using the S-function API”| mdlInitializeConditions|
mdlGetOperatingPoint |SetOperatingPoint

Introduced in R2019a

11-64
mdlSetOutputPortComplexSignal

mdlSetOutputPortComplexSignal
Set the numeric types (real, complex, or inherited) of the signals accepted by an output port

Required
No

Languages
C, C++

Syntax
#define MDL_SET_OUTPUT_PORT_COMPLEX_SIGNAL

void mdlSetOutputPortComplexSignal(SimStruct *S, int_T port,


CSignal_T csig)

Arguments
S
SimStruct representing an S-Function block.
port
Index of a port.
csig
Numeric type of signal, either COMPLEX_NO (real) or COMPLEX_YES (complex).

Description
The Simulink engine calls this routine to set the output port numeric type for outputs that have this
attribute set to COMPLEX_INHERITED. The input argument csig is the proposed numeric type for
this output port. The S-function must check whether the specified numeric type is a valid type for the
specified port.

If it is valid, C MEX S-functions set the numeric type of the specified output port using
ssSetOutputPortComplexSignal. Otherwise, the S-function reports an error, using
ssSetErrorStatus. This method is only valid for simulation. C MEX S-functions must enclose the
method in a #if defined(MATLAB_MEX_FILE) statement.

The S-function can also set the numeric types of other input and output ports with unknown numeric
types. The engine reports an error if the S-function changes the numeric type of a port whose
numeric type is known.

If the S-function does not implement this routine, the engine assumes that the S-function accepts a
real or complex signal and sets the output port numeric type to the specified value.

The engine calls this method until all output ports with inherited numeric types have their numeric
types specified.

11-65
11 S-Function Callback Methods

Example
See sdotproduct.c for an example of how to use this function.

See Also
ssSetOutputPortComplexSignal, ssSetErrorStatus, SetOutputPortComplexSignal

Introduced before R2006a

11-66
mdlSetOutputPortDataType

mdlSetOutputPortDataType
Set the data type of the signals emitted by an output port

Required
No

Languages
C, C++

Syntax
#define MDL_SET_OUTPUT_PORT_DATA_TYPE

void mdlSetOutputPortDataType(SimStruct *S, int_T port,


DTypeId id)

Arguments
S
SimStruct representing an S-Function block.
port
Index of an output port.
id
Data type ID.

Description
The Simulink engine calls this routine to set the data type of port when port has an inherited data
type. The data type ID id is the proposed data type for this port. Data type IDs for the built-in data
types can be found in simstruc_types.h. The S-function must check whether the specified data
type is a valid data type for the specified port.

If it is a valid data type, a C MEX S-function sets the data type of port using
ssSetOutputPortDataType. Otherwise, the S-function reports an error, using
ssSetErrorStatus. This method is only valid for simulation. C MEX S-functions must enclose the
method in a #if defined(MATLAB_MEX_FILE) statement.

The S-function can also set the data types of other input and output ports if their data types have not
been set. The engine reports an error if the S-function changes the data type of a port whose data
type has been set.

If the block does not implement this method, the engine assumes that the block supports any data
type and sets the output port data type to the specified value.

The engine calls this method until all output ports with inherited data types have their data types
specified.

11-67
11 S-Function Callback Methods

See Also
ssSetOutputPortDataType, ssSetErrorStatus, SetOutputPortDataType

Introduced before R2006a

11-68
mdlSetOutputPortDimensionInfo

mdlSetOutputPortDimensionInfo
Set the dimensions of the signals accepted by an output port

Required
No

Languages
C, C++

Syntax
#define MDL_SET_OUTPUT_PORT_DIMENSION_INFO

void mdlSetOutputPortDimensionInfo(SimStruct *S, int_T port,


const DimsInfo_T *dimsInfo)

Arguments
S
SimStruct representing an S-Function block.
port
Index of a port.
dimsInfo
Structure that specifies the signal dimensions supported by port.

See ssSetInputPortDimensionInfo for a description of this structure.

Description
The Simulink engine calls this method with candidate dimensions dimsInfo for port. In C MEX S-
functions, if the proposed dimensions are acceptable, the method sets the actual port dimensions,
using ssSetOutputPortDimensionInfo. If they are unacceptable, the method generates an error
via ssSetErrorStatus. This method is only valid for simulation. C MEX S-functions must enclose
the method in a #if defined(MATLAB_MEX_FILE) statement.

Note This method can set the dimensions of any other input or output port whose dimensions derive
from the dimensions of port.

By default, the engine calls this method only if it can fully determine the dimensionality of port from
the port to which it is connected. In C MEX S-functions, if the engine cannot completely determine
the dimensionality from port connectivity, it invokes mdlSetDefaultPortDimensionInfo. If an S-
function can fully determine the port dimensionality from partial information, set the option
SS_OPTION_ALLOW_PARTIAL_DIMENSIONS_CALL in mdlInitializeSizes, using ssSetOptions.

11-69
11 S-Function Callback Methods

If this option is set, the engine invokes mdlSetOutputPortDimensionInfo even if it can only
partially determine the dimensionality of the output port from connectivity.

The engine calls this method until all output ports with inherited dimensions have their dimensions
specified.

Example
See sfun_matadd.c for an example of how to use this function.

See Also
ssSetErrorStatus, ssSetOutputPortDimensionInfo, SetOutputPortDimensions

Introduced before R2006a

11-70
mdlSetOutputPortSampleTime

mdlSetOutputPortSampleTime
Set the sample time of an output port that inherits its sample time from the port to which it is
connected

Required
No

Languages
C, C++

Syntax
#define MDL_SET_OUTPUT_PORT_SAMPLE_TIME

void mdlSetOutputPortSampleTime(SimStruct *S, int_T port,


real_T sampleTime, real_T offsetTime)

Arguments
S
SimStruct representing an S-Function block.
port
Index of a port.
sampleTime
Inherited sample time for port.
offsetTime
Inherited offset time for port.

Description
The Simulink engine calls this method with the sample time that port inherits from the port to which
it is connected.

For C MEX S-functions, if the inherited sample time is acceptable, this method should set the sample
time of port to the inherited sample time and offset time, using ssSetOutputPortSampleTime and
ssSetOutputPortOffsetTime. If the sample time is unacceptable, this method generates an error
via ssSetErrorStatus. This method is only valid for simulation, and must be enclosed in a #if
defined(MATLAB_MEX_FILE) statement.

This method can set the sample time of any other input or output port whose sample time derives
from the sample time of port, using ssSetInputPortSampleTime or
ssSetOutputPortSampleTime in C MEX S-functions.

Normally, sample times are propagated forward; however, if sources feeding this block have inherited
sample times, the engine might choose to back-propagate known sample times to this block. When

11-71
11 S-Function Callback Methods

back-propagating sample times, this method is called in succession for all inherited output port
signals.

See mdlSetInputPortSampleTime for more information about when this method is called.

See Also
ssSetErrorStatus, ssSetInputPortSampleTime, ssSetOutputPortSampleTime,
mdlSetInputPortSampleTime, SetOutputPortSampleTime

Introduced before R2006a

11-72
mdlSetOutputPortWidth

mdlSetOutputPortWidth
Set the width of an output port that outputs 1-D (vector) signals

Required
No

Languages
C, C++

Syntax
#define MDL_SET_OUTPUT_PORT_WIDTH

void mdlSetOutputPortWidth(SimStruct *S, int_T port,


int_T width)

Arguments
S
SimStruct representing an S-Function block.
port
Index of a port.
width
Width of signal.

Description
This method is called with the candidate width for a dynamically sized port. If the proposed width is
acceptable, the method should go ahead and set the actual port width, using
ssSetOutputPortWidth. If the size is unacceptable, an error should be generated via
ssSetErrorStatus. Note that any other dynamically sized input or output ports whose widths are
implicitly defined by virtue of knowing the width of the given port can also have their widths set via
calls to ssSetInputPortWidth or ssSetOutputPortWidth. This method is only valid for
simulation, and must be enclosed in a #if defined(MATLAB_MEX_FILE) statement.

See Also
ssSetInputPortWidth, ssSetOutputPortWidth, ssSetErrorStatus

Introduced before R2006a

11-73
11 S-Function Callback Methods

mdlSetSimState
(Not recommended) Set the simulation state of the C MEX S-function by restoring the SimState.

Note mdlSetSimState is not recommended. Use mdlSetOperatingPoint instead.

Required
No

Languages
C, C++

Syntax
#define MDL_SIM_STATE

void mdlSetSimState(SimStruct* S, const mxArray* in)

Arguments
S
SimStruct representing an S-Function block.
const mxArray* in
Any valid MATLAB data.

Description
The Simulink engine invokes this custom method at the beginning of a simulation of the model
containing S . Simulink sets the initial simulation state of the S-function to the SimState of the model.

Example
/* Function: mdlSetSimState
* Abstract:
* Unpack the MATLAB structure passed and restore it to
* the RunTimeData structure
*/
static void mdlSetSimState(SimStruct* S,
const mxArray* simSnap)
{
RunTimeData_T* rtd =
(RunTimeData_T*)ssGetPWorkValue(S, 0);

/* Check and load the count value */


{

11-74
mdlSetSimState

const mxArray* cnt =


mxGetField(simSnap, 0, fieldNames[0]);
ERROR_IF_NULL(S,cnt,
"Count field not found in simulation state");
if ( mxIsComplex(cnt) ||
!mxIsUint64(cnt) ||
mxGetNumberOfElements(cnt) != 1 ) {
ssSetErrorStatus(S, "Count field is invalid");
return;
}
rtd->cnt = ((uint64_T*)(mxGetData(cnt)))[0];
}
}

See Also
mdlInitializeConditions, mdlGetSimState, SetSimState

Introduced in R2009a

11-75
11 S-Function Callback Methods

mdlSetWorkWidths
Specify the sizes of the work vectors and create the run-time parameters required by this C MEX S-
function

Required
No

Languages
C, C++

Syntax
#define MDL_SET_WORK_WIDTHS

void mdlSetWorkWidths(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine calls this optional method to enable this S-function to set the sizes of state and
work vectors that it needs to store global data and to create run-time parameters (see “Create and
Update S-Function Run-Time Parameters” on page 9-5). The engine invokes this method after it has
determined the input port width, output port width, and sample times of the S-function. This allows
the S-function to size the state and work vectors based on the number and sizes of inputs and outputs
and/or the number of sample times. This method specifies the state and work vector sizes via the
macros ssGetNumContStates, ssSetNumDiscStates, ssSetNumRWork, ssSetNumIWork,
ssSetNumPWork, ssSetNumModes, and ssSetNumNonsampledZCs.

A C-MEX S-function needs to implement this method only if it does not know the sizes of all the work
vectors it requires when the engine invokes the function's mdlInitializeSizes method. If this S-
function implements mdlSetWorkWidths, it should initialize the sizes of any work vectors that it
needs to DYNAMICALLY_SIZED in mdlInitializeSizes, even for those whose exact size it knows
at that point. The S-function should then specify the actual size in mdlSetWorkWidths. This method
is only valid for simulation, and must be enclosed in a #if defined(MATLAB_MEX_FILE)
statement.

Example
For a full example of a C MEX S-function using DWork vectors, see the file sfun_rtwdwork.c used
in the Simulink model sfcndemo_sfun_rtwdwork.

11-76
mdlSetWorkWidths

See Also
mdlInitializeSizes, PostPropagationSetup

Introduced before R2006a

11-77
11 S-Function Callback Methods

mdlSimStatusChange
Respond to a pause or resumption of the simulation of the model that contains this C MEX S-function

Required
No

Languages
C, C++

Syntax
#define MDL_SIM_STATUS_CHANGE

void mdlSimStatusChange(SimStruct *S,


ssSimStatusChangeType simStatus)

Arguments
S
SimStruct representing an S-Function block.
simStatus
Status of the simulation, either SIM_PAUSE or SIM_CONTINUE.

Description
The Simulink engine calls this routine when a simulation of the model containing S pauses or
resumes. This method is only valid for simulation. C MEX S-functions must enclose the method in a
#if defined(MATLAB_MEX_FILE) statement.

Example
#if defined(MATLAB_MEX_FILE)
#define MDL_SIM_STATUS_CHANGE
static void mdlSimStatusChange(SimStruct *S,
ssSimStatusChangeType simStatus) {
if (simStatus == SIM_PAUSE) {
ssPrintf("Pause has been called! \n");
} else if (simStatus == SIM_CONTINUE) {
ssPrintf("Continue has been called! \n");
}
}
#endif

See Also
SimStatusChange

11-78
mdlSimStatusChange

Introduced before R2006a

11-79
11 S-Function Callback Methods

mdlStart
Initialize the state vectors of this C MEX S-function

Required
No

Languages
C, C++

Syntax
#define MDL_START

void mdlStart(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine invokes this optional method at the beginning of a simulation. The method
performs initialization activities that this S-function requires only once, such as setting up user data
or initializing states.

This method is called at the start of every successive simulation in Fast Restart mode, and it performs
tasks that are required for every run. In contrast, mdlSetupRuntimeResources performs tasks
once in Fast Restart mode and the results of those tasks done are reused by successive simulations.

If your S-function resides in an enabled subsystem and needs to reinitialize its states whenever the
subsystem is enabled, use mdlInitializeConditions to initialize the state values, instead of
mdlStart.

In C MEX S-functions, use ssGetContStates and/or ssGetDiscStates to get the states.

Example
See sfun_directlook.c for an example of how to use this function.

See Also
mdlInitializeConditions, ssGetContStates, ssGetDiscStates, Start

11-80
mdlStart

Introduced before R2006a

11-81
11 S-Function Callback Methods

mdlSetupRuntimeResources
Perform any actions required once at the start of the simulation

Required
No

Languages
C, C++

Syntax
#define MDL_SETUP_RUNTIME_RESOURCES

void mdlSetupRuntimeResources(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
The Simulink engine invokes this optional method at the end of compilation. The method performs
setup activities that this S-function requires only once irrespective of the number of simulations that
follow (such as in a Fast Restart scenario).

See Also
mdlInitializeConditions, ssGetContStates, ssGetDiscStates, Start, “Get Started with
Fast Restart”

Introduced in R2016b

11-82
mdlTerminate

mdlTerminate
Perform any actions required at termination of the simulation

Required
Yes

Languages
C, C++

Syntax
void mdlTerminate(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
This method performs any actions, such as freeing of memory, that must be performed when the
simulation is terminated or when an S-Function block is destroyed (e.g., when it is deleted from a
model). This method is called at the end of every simulation in Fast Restart mode.

In C MEX S-functions, the mdlTerminate method is called after a simulation (mdlStart is called).

In addition, if the SS_OPTION_CALL_TERMINATE_ON_EXIT option is set for a given S-function, and if


mdlInitializeSizes is called, then the user is guaranteed that Simulink will call mdlTerminate.
One reason to set the SS_OPTION_CALL_TERMINATE_ON_EXIT option is to allocate memory in
mdlInitializeSizes rather than wait until mdlStart.

Note that Simulink calls mdlInitializeSizes under a number of circumstances, including


compilation and simulation. Simulink will also call mdlInitializeSizes during model editing if you
perform an operation such as the setting of parameters.

In C MEX S-functions, use the UNUSED_ARG macro if the mdlTerminate function does not perform
any actions that require the SimStruct S to indicate that the S input argument is required, but not
used in the body of the callback. To do this, insert the line

UNUSED_ARG(S)

after any declarations in mdlTerminate.

Note If you have Simulink Coder, when generating code for a noninlined C MEX S-function that
contains this method, make sure the method is not wrapped in a #if defined(MATLAB_MEX_FILE)
statement. For example:

11-83
11 S-Function Callback Methods

#if defined(MATLAB_MEX_FILE)
static void mdlTerminate(SimStruct *S)
{
/* Add mdlTerminate code here *
}
#endif

The define statement makes the mdlTerminate method available only to a MATLAB MEX file. If
the S-function is not inlined, Simulink Coder cannot use this method, resulting in link or run-time
errors.

Example
Suppose your S-function allocates blocks of memory in mdlStart and saves pointers to the blocks in
a PWork vector. The following code fragment would free this memory.

{
int i;
for (i = 0; i<ssGetNumPWork(S); i++) {
if (ssGetPWorkValue(S,i) != NULL) {
free(ssGetPWorkValue(S,i));
}
}
}

See Also
ssSetOptions, Terminate

Introduced before R2006a

11-84
mdlCleanupRuntimeResources

mdlCleanupRuntimeResources
Perform any actions required once at termination of the simulation

Required
Yes

Languages
C, C++

Syntax
void mdlCleanupRuntimeResources(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
This method performs any actions, such as freeing of memory, that must be performed when the
simulation is terminated or when an S-Function block is destroyed (e.g., when it is deleted from a
model).

In C MEX S-functions, the mdlCleanupRuntimeResources method is called after a simulation


(mdlSetupRuntimeResources is called), and it reverses the actions performed by
mdlSetupRuntimeResources.

Note If you have Simulink Coder, when generating code for a noninlined C MEX S-function that
contains this method, make sure the method is not wrapped in a #if defined(MATLAB_MEX_FILE)
statement. For example:

#if defined(MATLAB_MEX_FILE)
static void mdlCleanupRuntimeResources(SimStruct *S)
{
/* Add mdlCleanupRuntimeResources code here *
}
#endif

The define statement makes the mdlCleanupRuntimeResources method available only to a


MATLAB MEX file. If the S-function is not inlined, Simulink Coder cannot use this method, resulting
in link or run-time errors.

11-85
11 S-Function Callback Methods

Example
Suppose your S-function allocates blocks of memory in mdlSetupRuntimeResources and saves
pointers to the blocks in a PWork vector. The following code fragment would free this memory.

#define MDL_CLEANUP_RUNTIME_RESOURCES
static void mdlCleanupRuntimeResources(SimStruct *S)
{
int i;
for (i = 0; i<ssGetNumPWork(S); i++) {
if (ssGetPWorkValue(S,i) != NULL) {
free(ssGetPWorkValue(S,i));
}
}
}

See Also
mdlSetupRuntimeResources,ssSetOptions, Terminate, “Get Started with Fast Restart”

Introduced in R2016b

11-86
mdlUpdate

mdlUpdate
Update a block's states

Required
No

Languages
C, C++

Syntax
#define MDL_UPDATE

void mdlUpdate(SimStruct *S, int_T tid)

Arguments
S
SimStruct representing an S-Function block.
tid
Task ID.

Description
The Simulink engine invokes this optional method at each major simulation time step. The method
should compute the S-function's states at the current time step and store the states in the S-
function's state vector. The method can also perform any other tasks that the S-function needs to
perform at each major time step.

Use this code if your S-function has one or more discrete states or does not have direct feedthrough.

The reason for this is that most S-functions that do not have discrete states but do have direct
feedthrough do not have update functions. Therefore, the engine is able to eliminate the need for the
extra call in these circumstances.

If your C MEX S-function needs to have its mdlUpdate routine called and it does not satisfy either of
the above two conditions, specify that it has a discrete state, using the ssSetNumDiscStates macro
in the mdlInitializeSizes function.

In C MEX S-functions, the tid (task ID) argument specifies the task running when the mdlOutputs
routine is invoked. You can use this argument in the mdlUpdate routine of a multirate S-Function
block to encapsulate task-specific blocks of code (see “Multirate S-Function Blocks” on page 9-27).

Use the UNUSED_ARG macro if your C MEX S-function does not contain task-specific blocks of code to
indicate that the tid input argument is required but not used in the body of the callback. To do this,
insert the line

11-87
11 S-Function Callback Methods

UNUSED_ARG(tid)

after the declarations in mdlUpdate.

Note If you have Simulink Coder, when generating code for a noninlined C MEX S-function that
contains this method, make sure the method is not wrapped in a #if defined(MATLAB_MEX_FILE)
statement. For example:

#define MDL_UPDATE
#if defined(MDL_UPDATE) && defined(MATLAB_MEX_FILE)
static void mdlUpdate(SimStruct *S, int_T tid)
{
/* Add mdlUpdate code here */
}
#endif

The define statement makes the mdlUpdate method available only to a MATLAB MEX file. If the S-
function is not inlined, Simulink Coder cannot use this method, resulting in link or run-time errors.

Example
For an example that uses this function to update discrete states, see dsfunc.c. For an example that
uses this function to update the transfer function coefficients of a time-varying continuous transfer
function, see stvctf.c.

See Also
mdlDerivatives, ssGetContStates, ssGetDiscStates, Update

Introduced before R2006a

11-88
mdlZeroCrossings

mdlZeroCrossings
Update zero-crossing vector

Required
No

Languages
C, C++

Syntax
#define MDL_ZERO_CROSSINGS

void mdlZeroCrossings(SimStruct *S)

Arguments
S
SimStruct representing an S-Function block.

Description
An S-function needs to provide this optional method only if it does zero-crossing detection.
Implementing zero-crossing detection typically requires using the zero-crossing and mode work
vectors to determine when a zero crossing occurs and how the S-function's outputs should respond to
this event. The mdlZeroCrossings method should update the S-function's zero-crossing vector,
using ssGetNonsampledZCs.

You can use the optional mdlZeroCrossings routine when your S-function has registered the
CONTINUOUS_SAMPLE_TIME and has nonsampled zero crossings (ssGetNumNonsampledZCs(S) >
0). The mdlZeroCrossings routine is used to provide the Simulink engine with signals that are to
be tracked for zero crossings. These are typically

• Continuous signals entering the S-function


• Internally generated signals that cross zero when a discontinuity would normally occur in
mdlOutputs

Thus, the zero-crossing signals are used to locate the discontinuities and end the current time step at
the point of the zero crossing. To provide the engine with zero-crossing signals, mdlZeroCrossings
updates the ssGetNonsampleZCs(S) vector.

Example
For an example, see sfun_zc_sat.c. A detailed description of this example can be found in “Zero
Crossings” on page 9-31.

11-89
11 S-Function Callback Methods

See Also
mdlInitializeSizes, ssGetNonsampledZCs

Introduced before R2006a

11-90
Outputs

Outputs
Compute the signals that this MATLAB S-function block emits

Required
Yes

Language
MATLAB

Syntax
Outputs(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
The Simulink engine invokes this required method at each simulation time step. In a Level-2 MATLAB
S-function, the Outputs method calculates the S-function's outputs at the current time step and
store the results in the run-time object's OutputPort(n).Data property. In addition, for S-functions
with a variable sample time, the Outputs method computes the next sample time hit.

Use the run-time object method IsSampleHit to determine if the current simulation time is one at
which a task handled by this block is active. For port-based sample times, use the IsSampleHit
property of the run-time object's InputPort or OutputPort methods to determine if the port
produces outputs or accepts inputs at the current simulation time step.

Set the run-time object's NextTimeHit property to specify the time of the next sample hit for
variable sample-time S-functions.

See Also
Simulink.RunTimeBlock, Simulink.MSFcnRunTimeBlock, mdlOutputs

Introduced in R2012b

11-91
11 S-Function Callback Methods

PostPropagationSetup
Specify the sizes of the work vectors and create the run-time parameters required by this MATLAB S-
function

Required
No

Language
MATLAB

Syntax
PostPropagationSetup(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
The Simulink engine calls this optional method to enable this S-function to set the sizes of state and
work vectors that it needs to store global data and to create run-time parameters (see “Create and
Update S-Function Run-Time Parameters” on page 9-5). The engine invokes this method after it has
determined the input port width, output port width, and sample times of the S-function. This allows
the S-function to size the state and work vectors based on the number and sizes of inputs and outputs
and/or the number of sample times.

A Level-2 MATLAB S-function must implement this method if any DWork vectors are used in the S-
function. In the case of MATLAB S-functions, this method sets the number of DWork vectors and
initializes their attributes. For example, the following code in the PostPropagationSetup method
specifies the usage for the first DWork vector:

s.DWork(1).Usage = type;

where s is an instance of the Simulink.MSFcnRunTimeBlock class representing the Level-2


MATLAB S-Function block and type is one of the following:

• DWork
• DState
• Scratch
• Mode

11-92
PostPropagationSetup

Example
For a full example of a Level-2 MATLAB S-function using DWork vectors, see the file adapt_lms.m
used in the Simulink model sldemo_msfcn_lms.

See Also
setup, Simulink.RunTimeBlock, mdlSetWorkWidths

Introduced in R2012b

11-93
11 S-Function Callback Methods

ProcessParameters
Process the MATLAB S-function's parameters

Required
No

Language
MATLAB

Syntax
ProcessParameters(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
This is an optional routine that the Simulink engine calls after CheckParameters changes and
verifies parameters. The processing is done at the top of the simulation loop when it is safe to process
the changed parameters. This function is only valid for simulation.

The purpose of this routine is to process newly changed parameters. An example is to cache
parameter changes in work vectors. The engine does not call this routine when it is used with the
Simulink Coder product. Therefore, if you use this routine in an S-function designed for use with the
Simulink Coder product, you must write your S-function so that it doesn't rely on this routine. To do
this, you must inline your S-function by using the Target Language Compiler. For information on
inlining S-functions, see “Inlining S-Functions” (Simulink Coder).

See Also
CheckParameters, Simulink.MSFcnRunTimeBlock, mdlProcessParameters

Introduced in R2012b

11-94
Projection

Projection
Perturb the solver's solution of a system's states to better satisfy time-invariant solution relationships

Required
No

Language
MATLAB

Syntax
Projection(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
This method is intended for use with S-functions that model dynamic systems whose states satisfy
time-invariant relationships, such as those resulting from mass or energy conservation or other
physical laws. The Simulink engine invokes this method at each time step after the model's solver has
computed the S-function's states for that time step. Typically, slight errors in the numerical solution
of the states cause the solutions to fail to satisfy solution invariants exactly. Your Projection
method can compensate for the errors by perturbing the states so that they more closely approximate
solution invariants at the current time step. As a result, the numerical solution adheres more closely
to the ideal solution as the simulation progresses, producing a more accurate overall simulation of
the system modeled by your S-function.

Your Projection method's perturbations of system states must fall within the solution error
tolerances specified by the model in which the S-function is embedded. Otherwise, the perturbations
may invalidate the solver's solution. It is up to your Projection method to ensure that the
perturbations meet the error tolerances specified by the model. See “Perturb System States Using a
Solution Invariant” on page 11-96 for a simple method for perturbing a system's states. The
following articles describe more sophisticated perturbation methods that your mdlProjection
method can use.

• C.W. Gear, “Maintaining Solution Invariants in the Numerical Solution of ODEs,” Journal on
Scientific and Statistical Computing, Vol. 7, No. 3, July 1986.
• L.F. Shampine, “Conservation Laws and the Numerical Solution of ODEs I,” Computers and
Mathematics with Applications, Vol. 12B, 1986, pp. 1287–1296.
• L.F. Shampine, “Conservation Laws and the Numerical Solution of ODEs II,” Computers and
Mathematics with Applications, Vol. 38, 1999, pp. 61–72.

11-95
11 S-Function Callback Methods

Example
Perturb System States Using a Solution Invariant

Here is a simple, Taylor-series-based approach to perturbing a system's states. Suppose your S-


function models a dynamic system having a solution invariant, g(X, t), i.e., g is a continuous,
differentiable function of the system states, X , and time, t, whose value is constant with time. Then
−1
Xn ≅ Xn* + JnT ( Jn JnT ) Rn

where

• Xn is the system's ideal state vector at the solver's current time step
• Xn* is the approximate state vector computed by the solver at the current time step
• Jn is the Jacobian of the invariant function evaluated at the point in state space specified by the
approximate state vector at the current time step:

∂g
Jn = (X *, t )
∂X n n
• tn is the time at the current time step
• Rn is the residual (difference) between the invariant function evaluated at Xn and Xn* at the
current time step:

Rn = g(Xn, tn) − g(Xn*, tn)

Note The value of g(Xn, tn) is the same at each time step and is known by definition.

Given a continuous, differentiable invariant function for the system that your S-function models, this
formula allows your S-function's mdlProjection method to compute a perturbation
−1
JnT ( Jn JnT ) Rn

of the solver's numerical solution, Xn*, that more closely matches the ideal solution, Xn, keeping the S-
function's solution from drifting from the ideal solution as the simulation progresses.

MATLAB Example

This example illustrates how the perturbation method outlined in the previous section can keep a
model's numerical solution from drifting from the ideal solution as a simulation progresses. Consider
the following model,mdlProjectionEx1:

The PredPrey block references an S-function, predprey_noproj.m, that uses the Lotka-Volterra
equations

11-96
Projection

ẋ = ax(1 − y)
ẏ = − cy(1 − x)

to model predator-prey population dynamics, where x(t) is the population density of the predators and
y(t) is the population density of prey. The ideal solution to the predator-prey ODEs satisfies the time-
invariant function

x−cecx y−aeay = d

where a, c, and d are constants. The S-function assumes a = 1, c = 2, and d = 121.85.

The Invariant Residual block in this model computes the residual between the invariant function
evaluated along the system's ideal trajectory through state space and its simulated trajectory:
cxn −a ayn
Rn = d − xn−ce yn e

where xnand ynare the values computed by the model's solver for the predator and prey population
densities, respectively, at the current time step. Ideally, the residual should be zero throughout
simulation of the model, but simulating the model reveals that the residual actually strays
considerably from zero:

Now consider the following model, mdlProjectionEx2:

11-97
11 S-Function Callback Methods

This model is the same as the previous model, except that its S-function, predprey.m, includes a
mdlProjection method that uses the perturbation approach outlined in “Perturb System States
Using a Solution Invariant” on page 11-96 to compensate for numerical drift. As a result, the
numerical solution more closely tracks the ideal solution as the simulation progresses as
demonstrated by the residual signal, which remains near or at zero throughout the simulation:

See Also
Simulink.MSFcnRunTimeBlock, mdlProjection,

Introduced in R2012b

11-98
SetAllowConstantSampleTime

SetAllowConstantSampleTime
Specify sample time behavior and tunability for S-function blocks with port-based sample times

Language
MATLAB

Syntax
SetAllowConstantSampleTime(s,flag)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
flag
Logical value to allow a sample time of Inf for ports in the S-function. The default is False.

Description
Use this macro to specify sample time behavior for your S-function with port-based sample times. If
you set flag to False, the Simulink engine does not allow a sample time of Inf for this S-function.
If you set flag to True, the S-function block is tunable and its ports can have a sample time of Inf.
To set the sample time for ports in the S-function, use SetInputPortSampleTime and
SetOutputPortSampleTime. These ports execute every time you tune any parameter in your model
during simulation.

See Also
SetOutputPortSampleTime, SetInputPortSampleTime

11-99
11 S-Function Callback Methods

SetInputPortComplexSignal
Set the numeric types (real, complex, or inherited) of the signals accepted by an input port

Required
No

Language
MATLAB

Syntax
SetInputPortComplexSignal(s, port, csig)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
port
Integer value specifying index of port to be set.
csig
Integer value specifying whether the port accepts real (false or 0) or complex (true or 1)
signals.

Description
The Simulink engine calls this routine to set the input port numeric type for inputs that have this
attribute set to COMPLEX_INHERITED. The input csig is the proposed numeric type for this input
port. This method is only valid for simulation.

The S-function must check whether the proposed numeric type is a valid type for the specified port. If
it is valid, level-2 MATLAB S-functions set the numeric type of the specified input port using the line:

s.InputPort(port).Complexity = csig;

The S-function can also set the numeric types of other input and output ports with inherited numeric
types. The engine reports an error if the S-function changes the numeric type of a port whose
numeric type is known.

If the S-function does not implement this routine, the engine assumes that the S-function accepts a
real or complex signal and sets the input port numeric type to the specified value.

The engine calls this method until all input ports with inherited numeric types have their numeric
types specified.

11-100
SetInputPortComplexSignal

See Also
Simulink.MSFcnRunTimeBlock, Simulink.BlockPortData,
mdlSetInputPortComplexSignal

Introduced in R2012b

11-101
11 S-Function Callback Methods

SetInputPortDataType
Set the data types of the signals accepted by an input port

Required
No

Language
MATLAB

Syntax
SetInputPortDataType(s, port, id)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
port
Integer value specifying index of port to be set.
id
Integer value specifying ID of port's data type. Use s.DatatypeName(id) to get the data type's
name.

Description
The Simulink engine calls this routine to set the data type of port when port has an inherited data
type. The data type id is the proposed data type for this port. Data type IDs for the built-in data types
can be found in simstruc_types.h. This method is only valid for simulation.

The S-function must check whether the specified data type is a valid data type for the specified port.
If it is a valid data type, Level-2 MATLAB S-functions set the data type of the input port using the line:

s.InputPort(port).DatatypeID = id;

The S-function can also set the data types of other input and output ports if they are unknown. The
engine reports an error if the S-function changes the data type of a port whose data type has been
set.

If the block does not implement this routine, the engine assumes that the block accepts any data type
and sets the input port data type to the specified value.

The engine calls this method until all input ports with inherited data types have their data types
specified.

11-102
SetInputPortDataType

See Also
Simulink.MSFcnRunTimeBlock, Simulink.BlockPortData, mdlSetInputPortDataType

Introduced in R2012b

11-103
11 S-Function Callback Methods

SetInputPortDimensions
Set the dimensions of the signals accepted by an input port

Required
No

Languages
MATLAB

Syntax
SetInputPortDimensions(s, port, dimsInfo)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
port
Integer value specifying index of port to be set.
dimsInfo
Array that specifies the signal dimensions supported by the port, e.g., [5] for a 5-element vector
signal or [3 3] for a 3-by-3 matrix signal.

Description
The Simulink engine calls this method during dimension propagation with candidate dimensions
dimsInfo for port.

A Level-2 MATLAB S-function sets the input port dimensions using the line

s.InputPort(port).Dimensions = dimsInfo;

This method is only valid for simulation.

Note This method can set the dimensions of any other input or output port whose dimensions derive
from the dimensions of port.

By default, the engine calls this method only if it can fully determine the dimensionality of port from
the port to which it is connected.

The engine calls this method until all input ports with inherited dimensions have their dimensions
specified.

11-104
SetInputPortDimensions

See Also
SetOutputPortDimensions, Simulink.MSFcnRunTimeBlock, Simulink.BlockPortData,
mdlSetInputPortDimensionInfo

Introduced in R2012b

11-105
11 S-Function Callback Methods

SetInputPortDimensionsMode
Propagate the dimensions mode

Required
No

Language
MATLAB

Syntax
SetInputPortDimensionsMode(s, port, dm)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
port
Integer value specifying index of port to be set.
dm
Integer value representing the dimensions mode of the port.

Description
The Simulink engine calls this optional method to enable this S-function to set the dimensions mode
of the input port indexed by portIdx.

See Also
SetInputPortDimensions, Simulink.MSFcnRunTimeBlock, Simulink.BlockPortData,
mdlSetInputPortDimensionsModeFcn

Introduced in R2012b

11-106
SetInputPortSampleTime

SetInputPortSampleTime
Set the sample time of an input port that inherits its sample time from the port to which it is
connected

Required
No

Language
MATLAB

Syntax
SetInputPortSampleTime(s, port, time)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
port
Integer value specifying the index of port whose sampling mode is to be set.
time
Two-element array, [period offset], that specifies the period and offset of the times that this
port samples its input.

Description
The Simulink engine invokes this method with the sample time that port inherits from the port to
which it is connected.

For Level-2 MATLAB S-functions, if the inherited sample time is acceptable, this method sets the
sample time and offset time using the line

s.InputPort(port).SampleTime = time;

The engine calls this method until all input ports with inherited sample times are specified.

When inherited port-based sample times are specified, the sample time is guaranteed to be one of the
following where 0.0 < period < inf and 0.0 <= offset < period.

Sample Time Offset Time


Continuous 0.0 0.0
Discrete period offset

11-107
11 S-Function Callback Methods

Constant, triggered, and variable-step sample times are not propagated to S-functions with port-
based sample times.

Generally SetInputPortSampleTime is called once per port with the input port sample time.
However, there can be cases where this function is called more than once. This happens when the
simulation engine is converting continuous sample times to continuous but fixed in minor steps
sample times. When this occurs, the original values of the sample times specified in setup are
restored before this method is called again.

The final sample time specified at the port can be different from (but equivalent to) the sample time
specified by this method. This occurs when

• The model uses a fixed-step solver and the port has a continuous but fixed in minor step sample
time. In this case, the Simulink engine converts the sample time to the fundamental sample time
for the model.
• The engine adjusts the sample time to be as numerically sound as possible. For example, the
engine converts [0.2499999999999, 0] to [0.25, 0].

The S-function can examine the final sample times in setup.

See Also
setup, Simulink.MSFcnRunTimeBlock , mdlSetInputPortSampleTime

Introduced in R2012b

11-108
SetOperatingPoint

SetOperatingPoint
Restore operating point of MATLAB S-function

Required
No

Language
MATLAB

Syntax
SetOperatingPoint(s, in)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
in
The MATLAB data of type returned by GetOperatingPoint.

Description
The Simulink engine invokes this custom method at the beginning of a simulation of the model
containing S. Simulink sets the initial simulation state of the S-function to the operating point of the
model.

See Also
“Custom Code and Hand Coded Blocks using the S-function API”| InitializeConditions|
GetOperatingPoint|mdlGetOpeartingPoint

Introduced in R2019a

11-109
11 S-Function Callback Methods

SetOutputPortComplexSignal
Set the numeric types (real, complex, or inherited) of the signals accepted by an output port

Required
No

Language
MATLAB

Syntax
SetOutputPortComplexSignal(s, port, csig)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
port
Integer value specifying the index of port to be set.

csig
Integer value specifying whether the port produces real (0) or complex (1) signals.

Description
The Simulink engine calls this routine to set the output port numeric type for outputs that have this
attribute set to COMPLEX_INHERITED. The input argument csig is the proposed numeric type for
this output port. The S-function must check whether the specified numeric type is a valid type for the
specified port.

If it is valid, Level-2 MATLAB S-functions set the numeric type of the specified output port using the
line

s.OutputPort(port).Complexity = csig;

The S-function can also set the numeric types of other input and output ports with unknown numeric
types. The engine reports an error if the S-function changes the numeric type of a port whose
numeric type is known.

If the S-function does not implement this routine, the engine assumes that the S-function accepts a
real or complex signal and sets the output port numeric type to the specified value.

The engine calls this method until all output ports with inherited numeric types have their numeric
types specified.

11-110
SetOutputPortComplexSignal

Example
See sdotproduct.c for an example of how to use this function.

See Also
Simulink.MSFcnRunTimeBlock , Simulink.BlockPortData,
mdlSetOutputPortComplexSignal

Introduced in R2012b

11-111
11 S-Function Callback Methods

SetOutputPortDataType
Set the data type of the signals emitted by an output port

Required
No

Language
MATLAB

Syntax
SetOutputPortDataType(s, port, id)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
port
Integer value specifying index of port to be set.
id
Integer value specifying ID of port's data type. Use s.DatatypeName(id) to get the data type's
name.

Description
The Simulink engine calls this routine to set the data type of port when port has an inherited data
type. The data type ID id is the proposed data type for this port. Data type IDs for the built-in data
types can be found in simstruc_types.h. The S-function must check whether the specified data
type is a valid data type for the specified port.

If it is a valid data type, Level-2 MATLAB S-functions set the data type of the output port using the
line

s.OutputPort(port).DatatypeID = id;

The S-function can also set the data types of other input and output ports if their data types have not
been set. The engine reports an error if the S-function changes the data type of a port whose data
type has been set.

If the block does not implement this method, the engine assumes that the block supports any data
type and sets the output port data type to the specified value.

The engine calls this method until all output ports with inherited data types have their data types
specified.

11-112
SetOutputPortDataType

See Also
Simulink.MSFcnRunTimeBlock , Simulink.BlockPortData, mdlSetOutputPortDataType

Introduced in R2012b

11-113
11 S-Function Callback Methods

SetOutputPortDimensions
Set the dimensions of the signals accepted by an output port

Required
No

Language
MATLAB

Syntax
SetOutputPortDimensions(s, port, dimsInfo)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
port
Integer value specifying the index of the port to be set.
dimsInfo
Array that specifies the signal dimensions supported by the port, e.g., [5] for a 5-element vector
signal or [3 3] for a 3-by-3 matrix signal.

Description
The Simulink engine calls this method with candidate dimensions dimsInfo for port.

A Level-2 MATLAB S-function sets the output port dimensions using the line

s.OutputPort(port).Dimensions = dimsInfo;

Note This method can set the dimensions of any other input or output port whose dimensions derive
from the dimensions of port.

By default, the engine calls this method only if it can fully determine the dimensionality of port from
the port to which it is connected.

The engine calls this method until all output ports with inherited dimensions have their dimensions
specified.

11-114
SetOutputPortDimensions

Example
See sfun_matadd.c for an example of how to use this function.

See Also
SetInputPortDimensions, Simulink.MSFcnRunTimeBlock , Simulink.BlockPortData,
mdlSetOutputPortDimensionInfo

Introduced in R2012b

11-115
11 S-Function Callback Methods

SetOutputPortSampleTime
Set the sample time of an output port that inherits its sample time from the port to which it is
connected

Required
No

Language
MATLAB

Syntax
SetOutputPortSampleTime(s, port, time)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the S-Function block.
port
Integer value specifying the index of port whose sampling mode is to be set.
time
Two-element array, [period offset], that specifies the period and offset of the times that this
port produces output.

Description
The Simulink engine calls this method with the sample time that port inherits from the port to which
it is connected.

For Level-2 MATLAB S-functions, if the inherited sample time is acceptable, this method sets the
sample time and offset time using the line

s.OutputPort(port).SampleTime = time;

This method can set the sample time of any other input or output port whose sample time derives
from the sample time of port, setting the SampleTime property of the Simulink.BlockPortData
object associated with the port in Level-2 MATLAB S-functions.

Normally, sample times are propagated forward; however, if sources feeding this block have inherited
sample times, the engine might choose to back-propagate known sample times to this block. When
back-propagating sample times, this method is called in succession for all inherited output port
signals.

See SetInputPortSampleTime for more information about when this method is called.

11-116
SetOutputPortSampleTime

See Also
SetInputPortSampleTime, Simulink.MSFcnRunTimeBlock, Simulink.BlockPortData,
mdlSetOutputPortSampleTime

Introduced in R2012b

11-117
11 S-Function Callback Methods

SetSimState
(Not recommended) Set the simulation state of the MATLAB S-function by restoring the SimState.

Note SetSimState is not recommended. Use SetOperatingPoint instead.

Required
No

Language
MATLAB

Syntax
SetSimState(s, in)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
in
The MATLAB data of type returned by GetSimState.

Description
The Simulink engine invokes this custom method at the beginning of a simulation of the model
containing S . Simulink sets the initial simulation state of the S-function to the SimState of the model.

See Also
InitializeConditions, GetSimState, mdlSetSimState

Introduced in R2012b

11-118
setup

setup
Specify the number of inputs, outputs, states, parameters, and other characteristics of the MATLAB
S-function

Required
Yes

Language
MATLAB

Syntax
setup(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
This is the first S-function callback methods that the Simulink engine calls.

The Level-2 MATLAB S-function setup method performs nearly the same tasks as the C MEX S-
function mdlInitializeSizes method, with two significant differences. The setup method does
not initialize discrete state information, but it does specify the block sample times, eliminating the
need for an mdlInitializeSampleTimes method. Use the following properties and methods of the
run-time object s to configure the S-function:

• Specify the number of parameters that this S-function supports, using s.NumDialogPrms.

Use s.DialogPrmsTunable to set the tunability of each dialog parameter. When a parameter
has been specified as not tunable, the Simulink engine issues an error during simulation (or when
in external mode when using the Simulink Coder product) if an attempt is made to change the
parameter.
• Specify the number of continuous states that this function has, using s.NumContStates. Specify
discrete state information in the PostPropagationSetup method using a DWork vector.
• Configure the block's input ports, including:

• Specify the number of input ports that this S-function has, using s.NumInputPorts.
• Specify the dimensions of the ith input port, using s.InputPort(i).Dimensions.
• If using port-based sample times, specify the sample time of the ith input port, using
s.InputPort(i).SampleTime.

11-119
11 S-Function Callback Methods

• For each input port, specify whether it has direct feedthrough, using
s.InputPort(i).DirectFeedthrough.

A port has direct feedthrough if the input is used in the Outputs method to calculate the
output or the next sample time, for an S-function with a variable sample time. The direct
feedthrough flag for each input port can be set to either 1=yes or 0=no. It should be set to 1 if
the input, u, is used in the Outputs method. Setting the direct feedthrough flag to 0 tells the
engine that u is not used in this S-function method. Violating this leads to unpredictable
results.

See Simulink.BlockData and its parent and children classes for a list of all the properties and
methods associated with a Level-2 MATLAB S-function input port.
• Configure the block's output ports, including:

• Specify the number of output ports that the block has, using s.NumOutputPorts.
• Specify the dimensions of the ith output port, using s.OutputPort(i).Dimensions.
• If using port-based sample times, specify the sample time of the ith output port, using
s.OutputPort(i).SampleTime.
• Set the block-based sample times (i.e., sample rates), using s.SampleTimes.

See “Specify S-Function Sample Times” on page 9-20 for a complete discussion of sample time
issues.

For multirate S-functions, the suggested approach to setting sample times is via the port-based
sample times method. When you create a multirate S-function, you must take care to verify that,
when slower tasks are preempted, your S-function correctly manages data so as to avoid race
conditions. When port-based sample times are specified, the block cannot inherit a sample time of
Inf at any port.

See “Using the setup Method” on page 3-4 for additional information and examples using the setup
method.

Dynamically Sized Block Features

You can set the parameters NumContStates, NumDworkDiscStates, NumInputPorts, and


NumOutputPorts to a fixed non-negative integer or tell the Simulink engine to size them
dynamically:

• DYNAMICALLY_SIZED -- Sets lengths of states, work vectors, and so on to values inherited from
the driving block. It sets widths to the actual input widths, according to the scalar expansion rules
unless you use mdlSetWorkWidths to set the widths.
• 0 or positive number -- Sets lengths (or widths) to the specified values. The default is 0.

See Also
Simulink.BlockData, Simulink.MSFcnRunTimeBlock, mdlInitializeSizes,
mdlInitializeSampleTimes

Introduced in R2012b

11-120
SimStatusChange

SimStatusChange
Respond to a pause or resumption of the simulation of the model that contains this MATLAB S-
function

Required
No

Languages
MATLAB

Syntax
SimStatusChange(s, status)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.
status
Status of the simulation, either 0 when paused or 1 when continued.

Description
The Simulink engine calls this routine when a simulation of the model containing S pauses or
resumes. This method is only valid for simulation.

See Also
Simulink.MSFcnRunTimeBlock, mdlSimStatusChange

Introduced in R2012b

11-121
11 S-Function Callback Methods

Start
Initialize the state vectors of this MATLAB S-function

Required
No

Language
MATLAB

Syntax
Start(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
The Simulink engine invokes this optional method at the beginning of a simulation. The method
performs initialization activities that this S-function requires only once, such as allocating memory
and setting up user data. Use InitializeConditions to initialize state values

If your S-function resides in an enabled subsystem and needs to reinitialize its states whenever the
subsystem is enabled, use InitializeConditions to initialize the state values, instead of Start.

Use the properties of Simulink.RunTimeBlock to get the states.

Example
See msfcn_varpulse.m for an example of how to use this function.

See Also
InitializeConditions, Simulink.RunTimeBlock, mdlStart

Introduced in R2012b

11-122
Terminate

Terminate
Perform any actions required at termination of the simulation

Required
Yes

Language
MATLAB

Syntax
Terminate(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
This method performs any actions, such as freeing of memory, that must be performed when the
simulation is terminated or when an S-Function block is destroyed (e.g., when it is deleted from a
model).

See Also
mdlTerminate

Introduced in R2012b

11-123
11 S-Function Callback Methods

Update
Update a block's states

Required
No

Language
MATLAB

Syntax
Update(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
The Simulink engine invokes this optional method at each major simulation time step. The method
should compute the S-function's states at the current time step and store the states in the S-
function's state vector. The method can also perform any other tasks that the S-function needs to
perform at each major time step.

Use this code if your S-function has one or more discrete states or does not have direct feedthrough.

The reason for this is that most S-functions that do not have discrete states but do have direct
feedthrough do not have update functions. Therefore, the engine is able to eliminate the need for the
extra call in these circumstances.

In Level-2 MATLAB S-functions, use the run-time object method IsSampleHit to determine if the
current simulation time is one at which a task handled by this block is active. For port-based sample
times, use the IsSampleHit property of the run-time object's InputPort or OutputPort to
determine if the port produces outputs or accepts inputs at the current simulation time step.

Example
For an example that uses this function to update discrete states, see msfcn_unit_delay.m.

See Also
Derivatives, Simulink.RunTimeBlock, Simulink.MSFcnRunTimeBlock, mdlUpdate

11-124
Update

Introduced in R2012b

11-125
11 S-Function Callback Methods

WriteRTW
Generate code generation data for the MATLAB S-function

Required
No

Language
MATLAB

Syntax
WriteRTW(s)

Arguments
s
Instance of Simulink.MSFcnRunTimeBlock class representing the Level-2 MATLAB S-Function
block.

Description
This function is called when the Simulink Coder product is generating the model.rtw file.

In Level-2 MATLAB S-functions, use the run-time object's WriteRTWParam method to write custom
parameters to the model.rtw file.

Example
See the S-function adapt_lms.m in the Simulink model sldemo_msfcn_lms for an example.

See Also
Simulink.MSFcnRunTimeBlock, mdlRTW

Introduced in R2012b

11-126
12

S-Function SimStruct Functions


Reference
12 S-Function SimStruct Functions Reference

S-Function SimStruct Functions


In this section...
“About SimStruct Functions” on page 12-2
“Language Support” on page 12-2
“The SimStruct” on page 12-2
“SimStruct Macros and Functions Listed by Usage” on page 12-2

About SimStruct Functions


The Simulink software provides a set of functions for accessing the fields of an S-function's simulation
data structure (SimStruct). S-function callback methods use these functions to store and retrieve
information about an S-function.

Language Support
Some SimStruct functions are available only in some of the languages supported by the Simulink
software. The reference page for each SimStruct macro or function lists the languages in which it is
available and gives the syntax for these languages.

Note Most SimStruct functions available in C are implemented as C macros. Individual reference
pages indicate any SimStruct macro that becomes a function when you compile your S-function in
debug mode (mex -g).

The SimStruct
The file simstruc.h is a C language header file that defines the Simulink data structure and the
SimStruct access macros. It encapsulates all the data relating to the model or S-function, including
block parameters and outputs.

There is one SimStruct data structure allocated for the Simulink model. Each S-function in the
model has its own SimStruct associated with it. The organization of these SimStructs is much like
a directory tree. The SimStruct associated with the model is the root SimStruct. The SimStructs
associated with the S-functions are the child SimStructs.

SimStruct Macros and Functions Listed by Usage


• “Buses” on page 12-3
• “Data Type” on page 12-3
• “Dialog Box Parameters” on page 12-4
• “Error Handling and Status” on page 12-4
• “Function Call” on page 12-4
• “Input and Output Ports” on page 12-6
• “Model Reference” on page 12-11
• “Run-Time Parameters” on page 12-11

12-2
S-Function SimStruct Functions

• “Sample Time” on page 12-12


• “Simulation Information” on page 12-13
• “State and Work Vector” on page 12-14
• “Code Generation” on page 12-16
• “Miscellaneous” on page 12-17

Buses

Macro Description
ssGetBusElementComplexSignal Get the signal complexity for a bus element.
ssGetBusElementDataType Get the data type identifier for a bus element.
ssGetBusElementDimensions Get the dimensions of a bus element.
ssGetBusElementName Get the name of a bus element.
ssGetBusElementNumDimensions Get the number of dimensions for a bus element.
ssGetBusElementOffset Get the offset from the start of the bus data type
to a bus element.
ssGetNumBusElements Get the number of elements in a bus signal.
ssGetSFcnParamName Get the value of a block parameter for an S-
function block.
ssIsDataTypeABus Determine whether a data type identifier
represents a bus signal.
ssRegisterTypeFromParameter Register a data type that a parameter in the
Simulink data type table specifies.
ssSetBusInputAsStruct Specify whether to convert the input bus signal
for an S-function from virtual to nonvirtual.
ssSetBusOutputAsStruct Specify whether the output bus signal from an S-
function must be virtual or nonvirtual.
ssSetBusOutputObjectName Specify the name of the bus object that defines
the structure and type of the output bus signal.

Data Type

Macro Description
ssGetDataTypeId Get the ID for a data type.
ssGetDataTypeIdAliasedThruTo Get the ID for the built-in data type associated
with a data type alias.
ssGetDataTypeName Get a data type's name.
ssGetDataTypeSize Get a data type's size.
ssGetDataTypeZero Get the zero representation of a data type.
ssGetInputPortDataType Get the data type of an input port.
ssGetNumDataTypes Get the number of data types defined by an S-
function or the model.
ssGetOutputPortDataType Get the data type of an output port.

12-3
12 S-Function SimStruct Functions Reference

Macro Description
ssGetOutputPortSignal Get an output signal of any type except double.
ssGetSFcnParamDataType Get the data type of a parameter.
ssRegisterDataType Register a data type.
ssSetDataTypeSize Specify the size of a data type.
ssSetDataTypeZero Specify the zero representation of a data type.
ssSetInputPortDataType Specify the data type of signals accepted by an
input port.
ssSetOutputPortDataType Specify the data type of an output port.

Dialog Box Parameters

Macro Description
ssGetDTypeIdFromMxArray Get the Simulink data type of a dialog parameter.
ssGetNumSFcnParams Get the number of parameters that an S-function
expects.
ssGetSFcnParam Get a parameter entered by a user in the S-
Function block dialog box.
ssGetSFcnParamsCount Get the actual number of parameters specified by
the user.
ssSetNumSFcnParams Set the number of parameters that an S-function
expects.
ssSetSFcnParamTunable Specify the tunability of a dialog box parameter.

Error Handling and Status

Macro Description
ssGetErrorStatus Get a character vector that identifies the last
error.
ssGetLocalErrorStatus Get a character vector that identifies the last
error in a thread-safe manner.
ssPrintf Print a variable-content msg.
ssSetErrorStatus Report errors.
ssSetLocalErrorStatus Report errors in a thread-safe manner.
ssWarning Display a warning message.

Function Call

Macro Description
ssCallSystemWithTid Execute a function-call subsystem connected to
an S-function.
ssDisableSystemWithTid Disable a function-call subsystem connected to
this S-function block.

12-4
S-Function SimStruct Functions

Macro Description
ssEnableSystemWithTid Enable a function-call subsystem connected to
this S-function.
ssGetCallSystemNumFcnCall- Get the number of function-call destinations.
Destinations
ssGetExplicitFCSSCtrl Determine whether this S-function explicitly
enables and disables the function-call subsystem
that it invokes.
ssSetCallSystemOutput Specify that an output port element issues a
function call.
ssSetExplicitFCSSCtrl Specify whether an S-function explicitly enables
and disables the function-call subsystem that it
calls.

12-5
12 S-Function SimStruct Functions Reference

Input and Output Ports

I/O Port — Signal Specification

Macro Description
ssAllowSignalsWithMoreThan2D Enable S-function to work with
multidimensional input and output signals.
ssGetInputPortComplexSignal Get the numeric type (complex or real) of an
input port.
ssGetInputPortDataType Get the data type of an input port.
ssGetInputPortDirectFeedThrough Determine whether an input port has direct
feedthrough.
ssGetInputPortFrameData Determine whether a port accepts signal
frames.
ssGetInputPortOffsetTime Determine the offset time of an input port.
ssGetInputPortRequiredContiguous Determine whether the signal elements
entering a port must be contiguous.
ssGetInputPortSampleTime Determine the sample time of an input port.
ssGetInputPortSampleTimeIndex Get the sample time index of an input port.
ssGetInputPortUnit Get unit of input port
ssGetOutputPortComplexSignal Get the numeric type (complex or real) of an
output port.
ssGetOutputPortDataType Get the data type of an output port.
ssGetOutputPortFrameData Determine whether a port outputs signal
frames.
ssGetOutputPortOffsetTime Determine the offset time of an output port.
ssGetOutputPortSampleTime Determine the sample time of an output
port.
ssGetOutputPortUnit Get unit of output port
ssRegisterUnitFromExpr Register unit from unit expression
ssSetInputPortComplexSignal Set the numeric type (real or complex) of an
input port.
ssSetInputPortDataType Set the data type of an input port.
ssSetInputPortDirectFeedThrough Specify that an input port is a direct-
feedthrough port.
ssSetInputPortOffsetTime Specify the sample time offset for an input
port.
ssSetInputPortRequiredContiguous Specify that the signal elements entering a
port must be contiguous.
ssSetInputPortSampleTime Set the sample time of an input port.
ssSetInputPortUnit Specify unit of input port
ssSetNumInputPorts Set the number of input ports on an S-
Function block.

12-6
S-Function SimStruct Functions

Macro Description
ssSetNumOutputPorts Specify the number of output ports on an S-
Function block.
ssSetOneBasedIndexInputPort Specify that an input port expects one-based
indices.
ssSetOneBasedIndexOutputPort Specify that an output port emits one-based
indices.
ssSetOutputPortComplexSignal Specify the numeric type (real or complex)
of this port.
ssSetOutputPortDataType Specify the data type of an output port.
ssSetOutputPortOffsetTime Specify the sample time offset value of an
output port.
ssSetOutputPortSampleTime Specify the sample time of an output port.
ssSetOutputPortUnit Specify unit of output port
ssSetZeroBasedIndexInputPort Specify that an input port expects zero-
based indices.
ssSetZeroBasedIndexOutputPort Specify that an output port emits zero-based
indices.

12-7
12 S-Function SimStruct Functions Reference

I/O Port — Signal Dimensions

Macro Description
ssAddOutputDimsDependencyRule Register a method to handle current
dimensions update.
ssAddVariableSizeSignalsRuntimeChecker Register a method to check the current
input dimensions.
ssAllowSignalsWithMoreThan2D Enable S-function to work with
multidimensional signals.
ssGetCurrentInputPortDimensions Gets the current size of dimension dIdx of
input port pIdx.
ssGetCurrentInputPortWidth Gets the total width (total number of
elements) of the signal at input port pIdx
ssGetCurrentOutputPortDimensions Gets the current size of dimension dIdx of
the signal at output port pIdx.
ssGetCurrentOutputPortWidth Gets the total width (total number of
elements) of the signal at output port pIdx.
ssGetInputPortDimensions Get the dimensions of the signal accepted by
an input port.
ssGetInputPortDimensionSize Get the size of one dimension of the signal
entering an input port.
ssGetInputPortDimensionsMode Gets the dimensions mode of the input port
indexed by pIdx,
ssGetInputPortNumDimensions Get the dimensionality of the signals
accepted by an input port.
ssGetInputPortWidth Determine the width of an input port.
ssGetOutputPortDimensions Get the dimensions of the signal leaving an
output port.
ssGetOutputPortDimensionSize Get the size of one dimension of the signal
leaving an output port.
ssGetOutputPortDimensionsMode Sets the dimensions mode of the output port
indexed by pIdx.
ssGetOutputPortNumDimensions Get the number of dimensions of an output
port.
ssGetOutputPortWidth Determine the width of an output port.
ssSetCurrentOutputPortDimensions Sets the current size corresponding to
dimension dIdx of the output signal at port
pIdx.
ssSetDWorkRequireResetForSignalSize Set the block flag for resetting the dIndex
DWork size upon subsystem reset.
ssSetInputPortDimensionInfo Set the dimensionality of an input port.
ssSetInputPortDimensionsMode Sets the dimensions mode of the input port
indexed by pIdx.

12-8
S-Function SimStruct Functions

Macro Description
ssSetInputPortDimsSameAsOutputPortDims Set the dimensions of output port outIdx to
be equal than the dimensions of input port
inpIdx.
ssSetInputPortMatrixDimensions Specify dimension information for an input
port that accepts matrix signals.
ssSetInputPortVectorDimension Specify dimension information for an input
port that accepts vector signals.
ssSetInputPortWidth Set the width of a 1-D (vector) input port.
ssSetOutputPortDimensionInfo Specify the dimensionality of an output port.
ssSetOutputPortDimensionsMode Sets the dimensions mode of the output port
indexed by pIdx.
ssSetOutputPortMatrixDimensions Specify dimension information for an output
port that emits matrix signals.
ssSetOutputPortVectorDimension Specify dimension information for an output
port that emits vector signals.
ssSetOutputPortWidth Specify the width of a 1-D (vector) output
port.
ssSetOutputPortMatrixDimensions Specify the dimensions of a 2-D (matrix)
signal.
ssRegMdlSetInputPortDimensionsModeFcn Register the method to handle dimensions
mode propagation for each input port.
ssSetSignalSizesComputeType Set the type of output dependency on the
input signal.
ssSetVectorMode Specify the vector mode that an S-function
supports.

12-9
12 S-Function SimStruct Functions Reference

I/O Port — Signal Access

Macro Description
ssGetInputPortBufferDstPort Determine the output port that is
overwriting an input port's memory buffer.
ssGetInputPortConnected Determine whether an S-Function block port
is connected to a nonvirtual block.
ssGetInputPortOptimOpts Determine the reusability setting of the
memory allocated to the input port of an S-
function.
ssGetInputPortOverWritable Determine whether an input port can be
overwritten.
ssGetInputPortRealSignal Get the address of a real, contiguous signal
entering an input port.
ssGetInputPortRealSignalPtrs Access the signal elements connected to an
input port.
ssGetInputPortSignal Get the address of a contiguous signal
entering an input port.
ssGetInputPortSignalPtrs Get pointers to input signal elements of type
other than double.
ssGetNumInputPorts Can be used in any routine (except
mdlInitializeSizes) to determine how
many input ports a block has.
ssGetNumOutputPorts Can be used in any routine (except
mdlInitializeSizes) to determine how
many output ports a block has.
ssGetOutputPortConnected Determine whether an output port is
connected to a nonvirtual block.
ssGetOutputPortBeingMerged Determine whether the output of this block
is connected to a Merge block.
ssGetOutputPortOptimOpts Determine the reusability of the memory
allocated to the output port of an S-function.
ssGetOutputPortRealSignal Access the elements of a signal connected to
an output port.
ssGetOutputPortSignal Get the vector of signal elements emitted by
an output port.
ssSetInputPortOptimOpts Specify the reusability of the memory
allocated to the input port of an S-function.
ssSetInputPortOverWritable Specify whether an input port is
overwritable by an output port.
ssSetOutputPortOptimOpts Specify the reusability of the memory
allocated to the output port of an S-function.
ssSetOutputPortOverwritesInputPort Specify whether an output port can share its
memory buffer with an input port.

12-10
S-Function SimStruct Functions

Model Reference

Macro Description
ssRTWGenIsModelReferenceRTW- Target Determine if the model reference Simulink Coder
target is generating.
ssRTWGenIsModelReferenceSIM- Target Determine if the model reference simulation
target is generating.
ssSetModelReferenceNormalMode- Support Specify if S-function can be used in referenced
model simulating in normal mode.
ssSetModelReferenceSampleTime- Specify that a referenced model containing this
DefaultInheritance S-function can inherit its sample time from its
parent model.
ssSetModelReferenceSampleTime- Specify that the use of this S-function in a
DisallowInheritance referenced model prevents the referenced model
from inheriting its sample time from its parent
model.
ssSetModelReferenceSampleTime- Specify whether use of an S-function in a
InheritanceRule referenced model prevents the referenced model
from inheriting its sample time from the parent
model.

Run-Time Parameters

These macros allow you to create, update, and access run-time parameters corresponding to a block's
dialog parameters.

Macro Description
ssGetNumRunTimeParams Get the number of run-time parameters created
by this S-function.
ssGetRunTimeParamInfo Get the attributes of a specified run-time
parameter.
Register all tunable dialog parameters as run-
time parameters.
ssRegDlgParamAsRunTimeParam Register a run-time parameter.
ssSetNumRunTimeParams Specify the number of run-time parameters to be
created by this S-function.
ssSetRunTimeParamInfo Specify the attributes of a specified run-time
parameter.
Update all run-time parameters corresponding to
tunable dialog parameters.
ssUpdateDlgParamAsRunTimeParam Update a run-time parameter.
ssUpdateRunTimeParamData Update the value of a specified run-time
parameter.
ssUpdateRunTimeParamInfo Update the attributes of a specified run-time
parameter from the attributes of the
corresponding dialog parameters.

12-11
12 S-Function SimStruct Functions Reference

Sample Time

Macro Description
ssGetInputPortSampleTime Determine the sample time of an input port.
ssGetInputPortSampleTimeIndex Get the sample time index of an input port.
ssGetNumSampleTimes Get the number of sample times an S-function
has.
ssGetOffsetTime Determine one of an S-function's sample time
offsets.
ssGetOutputPortSampleTime Determine the sample time of an output port.
ssGetPortBasedSampleTimeBlock- Determine whether a block that uses port-based
IsTriggered sample times resides in a triggered subsystem.
ssGetSampleTime Determine one of an S-function's sample times.
ssGetTNext Get the time of the next sample hit in a discrete
S-function with a variable sample time.
ssIsContinuousTask Determine whether a specified rate is the
continuous rate.
ssIsSampleHit Determine the sample rate at which an S-function
is operating.
ssIsSpecialSampleHit Determine whether the current sample time hits
two specified rates.
ssSampleAndOffsetAreTriggered Determine whether a sample time and offset
value pair indicate a triggered sample time.
ssSetInputPortSampleTime Set the sample time of an input port.
ssSetModelReferenceSampleTime- Specify that a referenced model containing this
DefaultInheritance S-function can inherit its sample time from its
parent model.
ssSetModelReferenceSampleTime- Specify that the use of this S-function in a
DisallowInheritance referenced model prevents the referenced model
from inheriting its sample time from its parent
model.
ssSetModelReferenceSampleTime- Specify whether use of an S-function in a
InheritanceRule referenced model prevents the referenced model
from inheriting its sample time from the parent
model.
ssSetNumSampleTimes Set the number of sample times an S-function
has.
ssSetOffsetTime Specify the offset of a sample time.
ssSetSampleTime Specify a sample time for an S-function.
ssSetTNext Specify the time of the next sample hit in an S-
function.

12-12
S-Function SimStruct Functions

Simulation Information

Macro Description
ssGetBlockReduction Determine whether a block has requested block
reduction before the simulation has begun and
whether it has actually been reduced after the
simulation loop has begun.
ssGetErrorStatus Get a character vector that identifies the last
error.
ssGetFixedStepSize Get the fixed step size of the model containing
the S-function.
ssGetMaxStepSize Get the maximum step size of the model
containing the S-function.
ssGetMinStepSize Get the minimum step size of the model
containing the S-function.
ssGetSimMode Determine the context in which an S-function is
being invoked: normal simulation, external-mode
simulation, model editor, etc.
ssGetSimStatus Determine the current simulation status.
ssGetSolverMode Get the solver mode being used to solve the S-
function.
ssGetSolverName Get the name of the solver being used for the
simulation.
ssGetStateAbsTol Get the absolute tolerance used by the model's
variable-step solver for a specified state.
ssGetStopRequested Get the value of the simulation stop requested
flag.
ssGetT Get the current base simulation time.
ssGetTaskTime Get the current time for a task.
ssGetTFinal Get the end time of the current simulation.
ssGetTNext Get the time of the next sample hit.
ssGetTStart Get the start time of the current simulation.
ssIsExternalSim Determine if the model is running in external
mode.
ssIsFirstInitCond Determine whether the current simulation time is
equal to the simulation start time.
ssIsMajorTimeStep Determine whether the current time step is a
major time step.
ssIsMinorTimeStep Determine whether the current time step is a
minor time step.
ssIsVariableStepSolver Determine whether the current solver is a
variable-step solver.

12-13
12 S-Function SimStruct Functions Reference

Macro Description
ssRTWGenIsAccelerator Determine if the model is running in Accelerator
mode.
ssSetStateAbsTol Set the values of the absolute tolerances that the
variable-step solver will apply to the S-function
states.
ssSetBlockReduction Request that Simulink attempt to reduce a block.
ssSetOperatingPointCompliance Specify how Simulink treats an S-function when
saving and restoring the simulation state of a
model containing the S-function.
ssSetOperatingPointVisibility Specify whether or not the simulation state of the
S-function is visible (accessible) in the simulation
state of the model.
ssSetSolverNeedsReset Ask Simulink to reset the solver.
ssSetStopRequested Ask Simulink to terminate the simulation at the
end of the current time step.

State and Work Vector

Macro Description
ssGetContStates Get an S-function's continuous states.
ssGetDiscStates Get an S-function's discrete states.
ssGetDWork Get a DWork vector.
ssGetDWorkComplexSignal Determine whether the elements of a DWork
vector are real or complex numbers.
ssGetDWorkDataType Get the data type of a DWork vector.
ssGetDWorkName Get the name of a DWork vector.
ssGetDWorkUsageType Determine how the DWork vector is used in S-
function.
ssGetDWorkUsedAsDState Determine whether a DWork vector is used as a
discrete state vector.
ssGetDWorkWidth Get the size of a DWork vector.
ssGetdX Get the derivatives of the continuous states of an
S-function.
ssGetIWork Get an S-function's integer-valued (int_T) work
vector.
ssGetIWorkValue Get a value from a block's integer work vector.
ssGetModeVector Get an S-function's mode work vector.
ssGetModeVectorValue Get an element of a block's mode vector.
ssGetNonsampledZCs Get an S-function's zero-crossing signals vector.
ssGetNumContStates Determine the number of continuous states that
an S-function has.

12-14
S-Function SimStruct Functions

Macro Description
ssGetNumDiscStates Determine the number of discrete states that an
S-function has.
ssGetNumDWork Get the number of Dwork vectors used by a block.
ssGetNumIWork Get the size of an S-function's integer work
vector.
ssGetNumModes Determine the size of an S-function's mode
vector.
ssGetNumNonsampledZCs Determine the number of nonsampled zero
crossings that an S-function detects.
ssGetNumPWork Determine the size of an S-function's pointer
work vector.
ssGetNumRWork Determine the size of an S-function's real-valued
(real_T) work vector.
ssGetPWork Get an S-function's pointer (void *) work vector.
ssGetPWorkValue Get a pointer from a pointer work vector.
ssGetRealDiscStates Get the real (real_T) values of an S-function's
discrete state vector.
ssGetRWork Get an S-function's real-valued (real_T) work
vector.
ssGetRWorkValue Get an element of an S-function's real-valued
work vector.
ssSetDWorkComplexSignal Specify whether the elements of a Dwork vector
are real or complex.
ssSetDWorkDataType Specify the data type of a Dwork vector.
ssSetDWorkName Specify the name of a Dwork vector.
ssSetDWorkUsageType Specify how the DWork vector is used in S-
function.
ssSetDWorkUsedAsDState Specify that a Dwork vector is used as a discrete
state vector.
ssSetDWorkWidth Specify the width of a Dwork vector.
ssSetIWorkValue Set an element of a block's integer work vector.
ssSetModeVectorValue Set an element of a block's mode vector.
ssSetNumContStates Specify the number of continuous states that an
S-function has.
ssSetNumDiscStates Specify the number of discrete states that an S-
function has.
ssSetNumDWork Specify the number of Dwork vectors used by a
block.
ssSetNumIWork Specify the size of an S-function's integer
(int_T) work vector.

12-15
12 S-Function SimStruct Functions Reference

Macro Description
ssSetNumModes Specify the number of operating modes that an S-
function has.
ssSetNumNonsampledZCs Specify the number of zero crossings that an S-
function detects.
ssSetNumPWork Specify the size of an S-function's pointer (void
*) work vector.
ssSetNumRWork Specify the size of an S-function's real (real_T)
work vector.
ssSetPWorkValue Set an element of a block's pointer work vector.
ssSetRWorkValue Set an element of a block's floating-point work
vector.

Code Generation

Macro Description
ssGetDWorkRTWIdentifier Get the identifier used to declare a DWork vector
in code generated from the associated S-function.
ssGetDWorkRTWIdentifierMust- Get a flag indicating if a DWork vector resolves to
ResolveToSignalObject a Simulink.Signal object.
ssGetDWorkRTWStorageClass Get the storage class of a DWork vector in code
generated from the associated S-function.
ssGetDWorkRTWTypeQualifier Get the C type qualifier (e.g., const) used to
declare a DWork vector in code generated from
the associated S-function.
ssGetNumInputPorts Get the number of input ports that a block has
ssGetNumOutputPorts Get the number of output ports that a block has
ssGetPlacementGroup Get the name of the placement group of a block.
ssRTWGenIsCodeGen Identify any code generation that is not used by
the Accelerator.
ssSetArrayLayoutForCodeGen Specify array layout of the S-function.
ssSetDWorkRTWIdentifier Set the identifier used to declare a DWork vector
in code generated from the associated S-function.
ssSetDWorkRTWIdentifierMust- Specify if a DWork vector resolves to a
ResolveToSignalObject Simulink.Signal object.
ssSetDWorkRTWStorageClass Set the storage class of a DWork vector in code
generated from the associated S-function.
ssSetDWorkRTWTypeQualifier Set the C type qualifier (e.g., const) used to
declare a DWork vector in code generated from
the associated S-function.
ssSetPlacementGroup Specify the name of the placement group of a
block.

12-16
S-Function SimStruct Functions

Macro Description
ssWriteRTW2dMatParam Write a Simulink matrix parameter to the S-
function's model.rtw file.
ssWriteRTWMx2dMatParam Write a MATLAB matrix parameter to the S-
function's model.rtw file.
ssWriteRTWMxVectParam Write a MATLAB vector parameter to the S-
function's model.rtw file.
ssWriteRTWParameters Write tunable parameters to the S-function's
model.rtw file.
ssWriteRTWParamSettings Write settings for the S-function's parameters to
the model.rtw file.
ssWriteRTWScalarParam Write a scalar parameter to the S-function's
model.rtw file.
ssWriteRTWStr Write a character vector to the S-function's
model.rtw file.
ssWriteRTWStrParam Write a character vector parameter to the S-
function's model.rtw file.
ssWriteRTWStrVectParam Write a character vector vector parameter to the
S-function's model.rtw file.
ssWriteRTWVectParam Write a Simulink vector parameter to the S-
function's model.rtw file.
ssWriteRTWWorkVect Write the S-function's work vectors to the
model.rtw file.

Miscellaneous

Macro Description
ssCallExternalModeFcn Invoke the external mode function for an S-
function.
ssGetModelName Get the name of an S-Function block or model
containing the S-function.
ssGetParentSS Get the parent of an S-function.
ssGetPath Get the path of an S-function or the model
containing the S-function.
ssGetRootSS Return the root (model) SimStruct.
ssGetUserData Access user data.
ssSetExternalModeFcn Specify the external mode function for an S-
function.
ssSetOptions Set various simulation options.
ssSetPlacementGroup Specify the execution order of a sink or source S-
function.
ssSetUserData Specify user data.

12-17
12 S-Function SimStruct Functions Reference

Macro Description
ssSupportsMultipleExecInstances Allow an S-function to operate within a For Each
Subsystem.

12-18
13

S-Function Options

This section describes the S-function options available through ssSetOptions. Each S-function sets
its applicable options at the end of its mdlInitializeSizes method. Use the OR operator (|) to set
multiple options. For example:

ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_DISCRETE_VALUED_OUTPUT);
13 S-Function Options

SS_OPTION_ALLOW_CONSTANT_PORT_SAMPLE_TIM
E
Allow a sample time of Inf for a port

Description
Allows an S-function with port-based sample times to specify or inherit a sample time of Inf. This
setting allows the port to have a constant value, and tells the Simulink engine that all input and
output ports support a sample time of Inf. See “Specifying Constant Sample Time (Inf) for a Port” on
page 9-24 for more information.

Example
See sfun_port_constant.c, the source file for the sfcndemo_port_constant example, for an
example.

See Also
SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME

Introduced in R2007b

13-2
SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION

SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION
Allow scalar expansion of input ports

Description
Specifies that the input to your S-function input ports can have a width of either 1 or the size
specified by the port, usually referred to as the block width. The S-function expands scalar inputs to
the same dimensions as the block width. See “Scalar Expansion of Inputs” on page 9-15 for more
information.

Example
See sfun_multiport.c, the source file for the sfcndemo_sfun_multiport example, for an
example.

Introduced in R2007b

13-3
13 S-Function Options

SS_OPTION_ALLOW_PARTIAL_DIMENSIONS_CALL
Allow calls to mdlSetInputPortDimensionInfo and mdlSetOutputPortDimensionInfo with
partial dimension information

Description
Indicates the S-function can handle dynamically dimensioned signals. By default, the Simulink engine
calls the mdlSetInputPortDimensionInfo or mdlSetOutputPortDimensionInfo methods if
the number of dimensions and size of each dimension for the candidate port are fully known. If
SS_OPTION_ALLOW_PARTIAL_DIMENSIONS_CALLS is set, the engine may also call these methods
with partial dimension information. For example, the methods may be called when the port width is
known, but the actual 2-D dimensions are unknown. See mdlSetDefaultPortDimensionInfo for
more information.

See Also
mdlSetDefaultPortDimensionInfo

Introduced in R2007b

13-4
SS_OPTION_ALLOW_PORT_SAMPLE_TIME_IN_TRIGSS

SS_OPTION_ALLOW_PORT_SAMPLE_TIME_IN_TRIGSS
Allow an S-function with port-based sample times to operate in a triggered subsystem

Description
Allows an S-function that uses port-based sample times to operate in a triggered subsystem. During
sample time propagation, use the macro ssSampleAndOffsetAreTriggered to determine if the
sample and offset times correspond to the block being in a triggered subsystem. If the block is
triggered, all port sample times must be either triggered or constant. See “Configuring Port-Based
Sample Times for Use in Triggered Subsystems” on page 9-25 for more information.

Example
See sfun_port_triggered.c, the source file for the sfcndemo_port_triggered example, for an
example.

See Also
ssSampleAndOffsetAreTriggered

Introduced in R2007b

13-5
13 S-Function Options

SS_OPTION_ASYNC_RATE_TRANSITION
Create a read-write pair of blocks that ensure correct data transfer

Description
Creates a read-write pair of blocks intended to guarantee correct data transfers between a
synchronously (periodic) and an asynchronously executing subsystem or between two asynchronously
executing subsystems. Both the read S-function and write S-function should set this option.

An asynchronously executed function-call subsystem is a function-call subsystem driven by an S-


function with the SS_OPTION_ASYNCHRONOUS specified.

The Simulink engine defines two classes of asynchronous rate transitions.

• Read-write pairs. In this class, two blocks, using a technique such as double buffering, ensure data
integrity in a multitasking environment. When creating the read-write pair of blocks, the S-
functions for these blocks should set the SS_OPTION_ASYNC_RATE_TRANSITION option.
Furthermore, the MaskType property of the read block, must include the character vector read
and the MaskType property of write block must include the character vector write.
• A single protected or unprotected block. To create a single Protected Rate Transition block, create
a subsystem that contains the following

and set the Tag value of the Outport block to AsyncRateTransition. The S-function then
provides the code for the protected transition. Note, this S-function does not set the
SS_OPTION_ASYNC_RATE_TRANSITION option.

See Also
SS_OPTION_ASYNCHRONOUS

Introduced in R2007b

13-6
SS_OPTION_ASYNCHRONOUS

SS_OPTION_ASYNCHRONOUS
Specify this S-function drives a function-call subsystem attached to interrupt service routines

Description
Specifies that the S-function is driving function-call subsystems attached to interrupt service
routines. This option applies only to S-functions that have no input ports during code generation and
1 output port. During simulation, the S-function may have an input port to provide a condition on
which to execute. The output port must be configured to perform function calls on every element. If
any of these requirements is not met, the SS_OPTION_ASYNCHRONOUS option is ignored. Specifying
this option

• Informs the Simulink engine that there is no implied data dependency involving the data sources
or destinations of the function-call subsystem called by the S-function.
• Causes the function-call subsystem attached to the S-function to be colored purple, indicating that
it does not execute at a periodic rate.
• Enables additional checks to verify that the model is constructed correctly.

1 The engine validates that the appropriate asynchronous rate transition blocks reside between
the purple function-call subsystem. The engine also checks that period tasks exists. You can
directly read and write from the function-call subsystem by using a block that has no
computational overhead. To ensure safe task transitions between period and asynchronous
tasks, use the SS_OPTION_ASYNC_RATE_TRANSITION option.
2 For data transfers between two asynchronously executed (purple) function-call subsystem, the
engine validates that the appropriate asynchronous task transition blocks exits.

See Also
SS_OPTION_ASYNC_RATE_TRANSITION

Introduced in R2007b

13-7
13 S-Function Options

SS_OPTION_CALL_TERMINATE_ON_EXIT
Force call to mdlTerminate

Description
Guarantees the Simulink engine calls the S-function's mdlTerminate method before destroying a
block that references the S-function. Calling mdlTerminate allows your S-function to clean up after
itself, for example, by freeing memory it allocated during a simulation. The engine destroys an S-
function block under the following circumstances.

1 A simulation ends either normally or as a result of invoking ssSetErrorStatus.


2 A user deletes the block.
3 The engine eliminates the block as part of a block reduction optimization (see “Block reduction”).

If this option is not set, the engine calls your S-function's mdlTerminate method only if the
mdlStart method of at least one block in the model containing the S-function executed without
error.

Example
See the S-function sfun_runtime3.c for an example.

See Also
mdlTerminate

Introduced in R2007b

13-8
SS_OPTION_CAN_BE_CALLED_CONDITIONALLY

SS_OPTION_CAN_BE_CALLED_CONDITIONALLY
Specify this S-function can be called conditionally

Description
Specifies that the S-function can be called conditionally by other blocks. Simulink uses this option to
determine if the S-Function block can participate in “Conditional input branch execution” for Switch
and Multiport Switch blocks.

Example
See the S-function sdotproduct.c used in the Simulink model sfcndemo_sdotproduct for an
example.

Introduced in R2007b

13-9
13 S-Function Options

SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME
Disallow inheritance of Inf sample time

Description
Prohibits the S-Function block that references this S-function from inheriting a sample time of Inf.
The SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME option applies only to S-functions that use
block-based sample times.

Note If you have Simulink Coder, and the S-function declares the number of sample times as
PORT_BASED_SAMPLE_TIMES, it will not inherit a sample time of Inf unless it specifies the
SS_OPTION_ALLOW_CONSTANT_PORT_SAMPLE_TIME option.

If you have Simulink Coder, note:

• If the S-function specifies this option and inherits a sample time of Inf, the Simulink Coder
product determines how to generate code for the block based on if the block is invariant.
• A block is invariant if all of its ports' signals are invariant. A signal is invariant if it has a constant
value during the entire simulation. A constant block sample time does not guarantee all the ports'
signals are invariant. For more information, see “Inline Invariant Signals” (Simulink Coder).
• If the block is not invariant, the Simulink Coder product generates code only in the
model_initialize function. If the block is invariant, the Simulink Coder product eliminates the
block’s code altogether.

Example
See sfix_fir.cpp for an example.

See Also
SS_OPTION_ALLOW_CONSTANT_PORT_SAMPLE_TIME

Introduced in R2007b

13-10
SS_OPTION_DISCRETE_VALUED_OUTPUT

SS_OPTION_DISCRETE_VALUED_OUTPUT
Specify this S-function has discrete valued output

Description
Specifies this S-function has discrete valued outputs. With this option set, the Simulink engine does
not assign algebraic variables to this S-function when it appears in an algebraic loop.

Introduced in R2007b

13-11
13 S-Function Options

SS_OPTION_EXCEPTION_FREE_CODE
Improve performance of exception-free S-functions

Description
Improves performance of S-functions that do not use mexErrMsgTxt, mxCalloc, or any other
routines that can throw an exception. An S-function is not exception free if it contains any routine
that, when called, has the potential of long-jumping out of a block of code and into another scope. See
“Exception Free Code” on page 9-45 for more information.

Example
See vsfunc.c for an example.

See Also
SS_OPTION_RUNTIME_EXCEPTION_FREE_CODE

Introduced in R2007b

13-12
SS_OPTION_FORCE_NONINLINED_FCNCALL

SS_OPTION_FORCE_NONINLINED_FCNCALL
Specify generated code format for function-call subsystems called by this S-function

Description
If you have Simulink Coder, indicates that the software should generate procedures for all function-
call subsystems called by this S-function, instead of possibly inlining the subsystem code. If an S-
function sets this option, Simulink Coder ignores the Inline setting for the Code generation
function packaging option in the Subsystem Parameters dialog box for the Subsystem block. For
more information, see “About Nonvirtual Subsystem Code Generation” (Embedded Coder).

Introduced in R2007b

13-13
13 S-Function Options

SS_OPTION_NONVOLATILE
Enable the Simulink engine to remove unnecessary S-Function blocks

Description
Specifies this S-function has no side effects. Setting this option enables the Simulink engine to
remove the S-Function block referencing this S-function during dead branch elimination, if it is not
needed.

Example
See the S-function sdotproduct.c used in the Simulink model sfcndemo_sdotproduct for an
example.

Introduced in R2007b

13-14
SS_OPTION_PLACE_ASAP

SS_OPTION_PLACE_ASAP
Specify this S-function should be placed as soon as possible

Description
Specifies that this S-function should be placed as soon as possible in the block sorted order. The
SS_OPTION_PLACE_ASAP option uses a hierarchical sorted order such as that used by blocks. Within
a subsystem, the Simulink engine places an S-function block using this option as far up in the sorted
order as possible without changing the model's semantics. If the S-function's Priority block property
is set, and other blocks in the same subsystem have the same priority, the engine places S-functions
with this option before the other blocks in the same subsystem with the same priority. This option is
typically used by devices connecting to hardware when you want to ensure the hardware connection
is completed first.

Note Simulink honors the SS_OPTION_PLACE_ASAP option, relative to other blocks, only if this
block and the other blocks are in the same subsystem. As a result, Simulink does not compare two
blocks set with SS_OPTION_PLACE_ASAP if they exist in different subsystems. In addition, Simulink
might not place blocks with SS_OPTION_PLACE_ASAP set before blocks without
SS_OPTION_PLACE_ASAP set if they are in different subsystems.

For more information on block sorted orders, see “Control and Display Execution Order”.

Introduced in R2007b

13-15
13 S-Function Options

SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED
Specify this S-function uses port-based sample times

Description
Indicates the S-function registers multiple sample times (ssSetNumSampleTimes > 1) to specify the
rate at which each input and output port is running. The simulation engine needs this information
when checking for illegal rate transitions. If an S-function uses this option, it cannot inherit its
sample times. See “Hybrid Block-Based and Port-Based Sample Times” on page 9-27 for more
information.

Example
See mixedm.c for an example.

Introduced in R2007b

13-16
SS_OPTION_REQ_INPUT_SAMPLE_TIME_MATCH

SS_OPTION_REQ_INPUT_SAMPLE_TIME_MATCH
Specify sample times of input signal and port must match

Description
Specifies that the input signal sample times must match the sample time assigned to the block input
port. For example:

generates an error if this option is set. The Simulink engine does not generate an error if the block or
input port sample time is inherited.

Introduced in R2007b

13-17
13 S-Function Options

SS_OPTION_RUNTIME_EXCEPTION_FREE_CODE
Improve performance of run-time exception-free S-functions

Description
Improves performance of S-functions that do not use mexErrMsgTxt, mxCalloc, or any other
routines that can throw an exception inside of a run-time routines. Applicable run-time routines
include mdlGetTimeOfNextVarHit, mdlOutputs, mdlUpdate, and mdlDerivatives.

See Also
SS_OPTION_EXCEPTION_FREE_CODE

Introduced in R2007b

13-18
SS_OPTION_SIM_VIEWING_DEVICE

SS_OPTION_SIM_VIEWING_DEVICE
Indicate S-Function block is a SimViewingDevice

Description
Indicates the S-Function block referencing this S-function is a SimViewingDevice. As long as the
block meets the other requirements for a SimViewingDevice, i.e., no states, no outputs, etc., the
Simulink engine considers the block to be an external mode block. As an external mode block, the
block appears in the external mode user interface and the Simulink Coder product does not generate
code for it. During an external mode simulation, the engine runs the block only on the host. See “Use
C/C++ S-Functions as Sim Viewing Devices in External Mode” on page 9-43 in Writing S-Functions
for more information.

Introduced in R2007b

13-19
13 S-Function Options

SS_OPTION_SFUNCTION_INLINED_FOR_RTW
Specify use of TLC file during code generation

Description
Indicates the S-function has an associated TLC file and does not contain an mdlRTW method. Setting
this option has no effect if the S-function contains an mdlRTW method. During code generation, if
SS_OPTION_SFUNCTION_INLINED_FOR_RTW is set and the Simulink Coder product cannot locate
the S-function's TLC file, the Simulink Coder product generates an error. If
SS_OPTION_SFUNCTION_INLINED_FOR_RTW is not set but the Simulink Coder product does locate a
TLC file for the S-function, it uses the TLC file.

Introduced in R2007b

13-20
SS_OPTION_SUPPORTS_ALIAS_DATA_TYPES

SS_OPTION_SUPPORTS_ALIAS_DATA_TYPES
Support data type aliases

Description
Specifies how the S-function handles signals whose data types are aliases (see
Simulink.AliasType for more information about data type aliases). If this option is set and the S-
function's inputs and outputs use data type aliases, SimStruct macros such as
ssGetInputPortDataType and ssGetOutputPortDataType return the data type IDs of those
aliases. However, if this option is not set, the SimStruct macros return the data type IDs associated
with the equivalent built-in data types instead. For a list of built-in values for the data type ID, see
ssGetInputPortDataType.

Note If you have Simulink Coder, and this option is not set and the S-function's inputs use data type
aliases, the Simulink engine attempts to propagate the aliases to the S-function's outputs. However,
this process can fail, in which case the engine propagates the equivalent built-in data types instead.
To explicitly control the propagation of data type aliases through an S-function, enable the
SS_OPTION_SUPPORTS_ALIAS_DATA_TYPES option.

Introduced in R2007b

13-21
13 S-Function Options

SS_OPTION_USE_TLC_WITH_ACCELERATOR
Use TLC file when simulating in accelerated mode

Description
Forces the Simulink Accelerator mode to use the Target Language Compiler (TLC) inlining code for
the S-function, which speeds up execution of the S-function. If this option is not set, the Simulink
Accelerator mode uses the MEX version of the S-function even if a TLC file for the S-function exists.
This option should not be set for device driver blocks (A/D) or when there is an incompatibility
between running the MEX mdlStart or mdlInitializeConditions functions together with the
TLC Outputs, Update, or Derivatives functions. Also, this option indicates that the TLC inlining
code should be used when generating a simulation target for a referenced model that contains this S-
function.

Note The Simulink Accelerator mode does not require the Simulink Coder product to run an inlined
S-function. However, to ensure that the inlined S-function can run in accelerated mode in current and
future Simulink releases, the TLC file for the S-function must use documented TLC functions to
access the CompiledModel structure.

Example
See the S-function timestwo.c used in the Simulink model sfcndemo_timestwo for an example.

Introduced in R2007b

13-22
SS_OPTION_WORKS_WITH_CODE_REUSE

SS_OPTION_WORKS_WITH_CODE_REUSE
Specify this S-function supports code reuse

Description
Signifies that this S-function is compatible with the Simulink Coder product subsystem code reuse
feature. See “S-Functions for Code Reuse” (Simulink Coder) in the "Simulink Coder User's Guide" for
more information. If this option is not set, the Simulink Coder product will not reuse any subsystem
containing this S-Function.

Example
See timestwo.c for an example.

Introduced in R2007b

13-23

You might also like