Writing S-Functions | ![]() ![]() |
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 Simulink 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 in Simulink.
If instead the code needs to have continuous time states with support for variable-step solvers, the states must be registered and stored with Simulink 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.
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 Simulink 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.
PROGRAM
to SUBROUTINE subName
.
SUBROUTINE
argument list or in a COMMON
block.
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.
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 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.
![]() | Creating Level 2 Fortran S-Functions | Example C-MEX S-Function Calling Fortran Code | ![]() |