Writing S-Functions    

Porting Legacy Code

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 Simulink's 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 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 Simulink, 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 Simulink major 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 matlabroot/simulink/src/sfuntmpl_gate_fortran.c for an example that uses DWork vectors.

Sample Times

If the code has an implicit step size in its algorithm, coefficients, etc., ensure that you register the proper discrete sample time in the mdlInitializeSampleTimes() S-function method and only change the block's output values from the mdlUpdate() method.

Multiple Instances

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 matlabroot/simulink/include/simstruc.h and matlabroot/simulink/src/sfuntmpl_doc.c for details on allocating data-typed work vectors.

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.

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.


  Example C-MEX S-Function Calling Fortran Code Implementing Block Features