Main Content

Create Level-2 Fortran S-Functions

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).

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 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. Create a working directory for your example and go to that directory in MATLAB using the cd command.

Then, use the following code:

copyfile(fullfile(matlabroot,'extern','examples','mex'), 'mex')
cd mex
mex yprime.c

If using a Fortran compiler, test the mex setup using the following commands and the example Fortran source code files, yprimef.F and yprimefg.F, in matlabroot/extern/examples/mex. You can use the same directory. Then, use the following code.

cd 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, PE, and Mach-O.

The compiler must also be configurable so that the caller cleans up the stack instead of the callee. Intel® Fortran Compiler 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. 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 Intel Fortran Compiler on 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 64-bit Windows machines, using Microsoft Visual C++® and Intel Parallel Studio XE 2020 for Fortran, this example can be compiled using the following two mex commands. Enter each command on one line. The mex -setup C and mex -setup Fortran commands configure the C and Fortran compilers and must be run before executing the second command. In the second command, replace the variable IFORT_COMPILER20 with the name of the system's environment variable pointing to the Intel Fortran Compiler root folder on your system.

mex('-v', '-c', fullfile(matlabroot,'toolbox','simulink','sfuntemplates','srcFortran','sfun_atmos_sub.F'))


mex('-v', ['-L"', fullfile(getenv('IFORT_COMPILER20'), 'compiler', 'lib', 'intel64_win'), '"'],
fullfile(matlabroot,'toolbox','simulink','sfuntemplates','srcFortran','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 Intel Fortran Compiler.

For an up-to-date list of all the supported compilers, see the MathWorks® supported and compatible compiler list at:

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.

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 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 Microsoft Visual C++ and Intel Fortran Compiler, 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. Create a working directory for your example and go to your directory using the cd command in MATLAB.

  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', fullfile(matlabroot,'toolbox','simulink','sfuntemplates','srcFortran','sfun_atmos_sub.F'))
  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_COMPILER20 is the name of the system's environment variable pointing to the Intel Parallel Studio XE 2020 root folder and may vary on your system.

    mex('-v', ['-L"', fullfile(getenv('IFORT_COMPILER20'), 'compiler', 'lib', 'intel64_win'), '"'], 
    fullfile(matlabroot,'toolbox','simulink','sfuntemplates','srcFortran','sfun_atmos.c'), 'sfun_atmos_sub.obj')

Building Gateway C MEX S-Functions on a Linux System

Build the gateway on a Linux system using the command

mex sfun_atmos.c sfun_atmos_sub.o

On some Linux 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