The Swiss army knife – S-functions

S-functions are the most powerful way of defining custom blocks. There's no limit to what you can do: re-use an existing code, implement a functionality not available in your current blocksets, exchange data with external applications and devices, and so on. If you can code it, you can use it.

S-functions can be implemented using the following languages: the MATLAB scripting language, C/C++, and Fortran. S-functions written with the latter two need to be compiled first in order to produce a shared library loadable by the Simulink engine, while a MATLAB S-function will be interpreted and executed during the simulation.

In this chapter we'll focus on Level 2 MATLAB S-functions, since they are the easiest to develop even for people without programming knowledge. But the final part of this chapter, aimed at those who already know the C language, will explain how to obtain the same results with C MEX S-functions.

Note

There are Level 1 MATLAB S-functions too, with a different (older and likely to be deprecated soon) API to work with. New MATLAB S-functions should be developed using the Level 2 API.

In order to code an S-function, we'll have to learn some concepts: how a simulation is done, what information the Simulink engine requires, how S-functions provide that information, and how to store persistent data during the simulation.

The simulation phases

The whole simulation process can be divided into three phases: initialization, simulation, and cleanup.

The initialization phase takes place before the simulation starts: Simulink needs to know the number of inputs and outputs for each block (with their dimensions), the parameters, the sample time, and the amount of memory to be reserved. While some properties are read during model development (without knowing the number of input ports and output ports, the block could not be drawn), the required memory is allocated only once, as soon as the user starts the simulation.

The simulation loop is executed until the simulation reaches its end time, or the user manually stops it. Since block properties and memories have been properly set up in the previous phase, all that Simulink needs is to know how the outputs have to be calculated and update the memories.

Finally, the cleanup phase is executed only once, that is, after the simulation is stopped. During this phase, Simulink will free every system resource that has been allocated in the initialization phase, asking custom blocks what the appropriate method to free their resources is.

Level 2 MATLAB S-function callbacks

The S-function block must implement a certain number of predefined routines, or callbacks, that the Simulink engine will call during the three simulation phases.

Only two callbacks are required, the other ones being optional (if you don't need them, you don't have to declare them).

The mandatory callbacks

The first mandatory callback to have is the setup function called during the initialization phase. The block characteristics—inputs, outputs, sample times, and number of parameters—must be defined here. This function is called during model updates too (while developing the model).

Note

There is an important configuration related to input ports: the direct feedthrough. If, at a given simulation time step, the current input port value is required to calculate one or more output, that port must have the direct feedthrough flag set.

If, on the other hand, the outputs can be calculated using only the previous states (memories, integrators), the direct feedthrough flag must be cleared for every input port.

This will allow the Simulink engine to determine the block execution order and detect the presence of algebraic loops: they occur when an input port with direct feedthrough is driven by the output of the same block, either directly, or by a feedback path through other blocks which have direct feedthrough. Memory and integrator blocks are capable of breaking the loop.

The second mandatory callback is the Outputs function, called in the simulation loop at each time step. This function implements the core logic of the block; its purpose is to calculate the output ports values using the block's memories (and input values, if the block has direct feedthrough).

That's it. Those are the required callbacks. But if you want to store some variables in a persistent memory (that will be available to every callback during the simulation), we'll need to use some optional callbacks too.

The most useful optional callbacks

These callbacks are the ones we should know about in order to save persistent data during the simulation.

The first optional callback, PostPropagationSetup, is the one where you declare the necessary memory. It will be executed once after the setup routine, when Simulink has calculated the datatype and the dimensions for each signal in the model.

The second optional callback, Start, is called right before the beginning of the simulation loop. It's used to assign an initial value to the memory declared in PostPropagationSetup.

The third optional callback, Update, serves the purpose of updating the persistent memory and it is called at every step in the simulation loop, immediately after Outputs.

The fourth optional callback, Terminate, belongs to the cleanup phase and must be used when some system resources allocated in PostPropagationSetup can't be freed automatically by Simulink (common examples are pipes, network sockets, and devices like an RS232 serial port; they have to be closed first).

Tip

The Documentation center, as always, has a complete list of the available callbacks, with their description and example usage. It's under this path: Simulink | Block creation | Host-specific code | MATLAB S-Functions | S-Function Callback Methods.

The work vector – DWork

Let's suppose we would like to write a custom log-to-file block, which would save each input value to a line in a log file.

The implementation would be as follows:

  • Open a file for writing in Start
  • Write to the file each time Outputs is called
  • Close the file in Terminate

Where could we store the file handle?

We can't store it in a local variable. A local variable will disappear as soon as the Start callback is completed, and the Outputs callback will throw an error at the first attempt to write some data.

We could store it in a workspace (global) variable. The file handle will be accessible to the Outputs and Terminate callbacks. But there's a huge problem: if we had more than one log-to-file blocks in the same model, they will use the same variable to store the file handles, therefore writing everything to the same file. To overcome this problem we may use a vector variable holding a list of open file handles, and a boilerplate code determining the right index to use for each log-to-file block. Quite difficult, not to mention memory management issues.

There's a much better, cleaner way: work vectors. Yes, The MathWorks wrote the boilerplate code for us. The DWork vector is a portion of memory the Simulink engine allocates for each S-function block that needs to store persistent data during simulation, solving the problem of concurrent access; every S-function block will use only its own memory. And that memory is managed directly by the Simulink engine. Isn't it sweet?

There are four types of DWork vectors:

  • Generic DWork type vectors are the most generic, allowing to store variables, discrete states, and mode data
  • DState type vectors are specifically made to store discrete states (memories), enabling Simulink's data logging facilities
  • Scratch type vectors are persistent for only one time step; the data stored in them will disappear as soon as the next time step begins
  • Mode type vectors can store switches modifying the S-function operating mode

Note

To understand what an S-function mode is, imagine a block calculating the absolute value of the input. It has two modes of operation: copy the unchanged input to the output or change the input sign before copying it to the output. By using zero-crossing detection, we could set the mode in a single-width Mode DWork vector, used later in the Outputs callback.

Using DWork vectors is simple. We must use the PostPropagationSetup callback to define:

  • How many DWork vectors we need
  • The type of each DWork vector
  • The width of each DWork vector, equal to the number of elements we want to store
  • The characteristics (datatype, complexity) of the DWork vector elements

The theory is over. We know everything we need to develop our first MATLAB S-functions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.142.166