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).
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 for information
on setting up DWork vectors.
The following are some tips for creating the C-to-Fortran gateway S-function.
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
(open),
as well as S-function examples.matlabroot
/extern/examples/mex
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
cd(fullfile(matlabroot,'\extern\examples\mex')) mex yprimef.f yprimefg.f
For more information, see Build C MEX Function.
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 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 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 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
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
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.
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:
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
.
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.
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.
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.
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.
Change the line PROGRAM
to SUBROUTINE
subName
.
Now you can call it from C using C function syntax.
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.
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.
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.
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.
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.
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
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.
Run cd(matlabroot)
to go to your
MATLAB root.
Run mex -setup Fortran
to select
a Fortran compiler.
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
Run mex -setup C
to select a C
compiler.
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.
!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
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