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.

  1. Change the line PROGRAM to SUBROUTINE subName.
  1. Now you can call it from C using C function syntax.

  1. Identify variables that need to be inputs and outputs and put them in the SUBROUTINE argument list or in a COMMON block.
  1. 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.

  1. If you are converting a stand-alone simulation to work inside Simulink, identify the main loop of time integration and remove the loop and, if you want Simulink 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.

becomes

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