1. Approach

As written in GenSfn_SmlkApproaches.html the best way to use all capabilities of S-Functions are manual written ones. Because: All operations can be used.

But manuel writing is an hi effort. Generally, instead manual writing also generating is possible. The generator can be adapted to use the necessary features. Instead, using a standard ready to use generator for S-Functions, it may not have the required features.

Because of many years of experience with header files and textual code generations I have written a generator which takes informations annotated in header files as input for S-Functions. The tool basic are only one Java jar file with about 1.2 MByte size, nor more is necessary. It uses the ZBNF-Parser to parse the header files, the JZtxtcmd as generator, and some script files.

The script files are the complex ones. They are not open source, but available with a basic fee. You can study generated files using the downloaded zip file, see #example.

2. Definition and Annotations in header files - Principle and first example.

The header files uses (should use) Javadoc approaches. The C sources should use an Object Oriented approach, binding operations ("C-functions") to data. Internally C++ can be used, but the interface for the S-Functions is yet C only.

For example the data for a simple T1 FBlock is defined as (see emC/Ctrl/T1_Ctrl_emC.h):

 /**Data definition for a T1 smoothing FBlock in integer artithmetic. */
 typedef struct T1i_Ctrl_emC_T {

  /**This value is only stored while param call. It is not used. */
  float Ts;

  /**The difference q-x for usage as smoothed differentiator.
   * dxhi is the representative int16-part regarded to input.
   */
  union{ int32 dx32; struct { int16 dxlo, dxhi; } dx16;} dx;

  /**The output value and state variable.
   * qhi is the representative int16-part regarded to input.
   */
  union{ int32 q32; struct { int16 qlo, qhi; } q16;} q;

  /**Factor to multiply the difference (x-q) for one step.
   * This factor is calculated as 65536 * (1.0f - expf(-Tstep / Ts))
   * to expand the 16-bit-x-value to 32 bit for q.
   * A value Ts = 0, it means without smoothing, results in 0xffff here.
   * The largest Ts is 65000 * Tstep, results in 1 for this value.
   * Larger Ts does not work.
   */
  uint16 fTs;

 } T1i_Ctrl_emC_s;

This FBlock has three operations, the constructor, the init routine and the step routine. It is possible to have more as one step routine either to get slightly different functionality with the same data definition (for example a DT1-FBlock) or to get access routines:

/**Constructor for the T1i FBlock.
 * @simulink ctor.
 */
extern_C T1i_Ctrl_emC_s* ctor_T1i_Ctrl_emC(T1i_Ctrl_emC_s* thiz);

Here the Description has a @simulink anotation. This constructor is called after creation of the FBlock in Simulink (mdlStart(…​)), respectively it is created in the startup routine of generated code. It is possible that the routine has non-tunable parameter, given in Simulink by the parameter dialog.

/**Initialization for the T1i FBlock.
 * @param Ts_param smoothing time in units of Tstep, secons.
 * @param Tstep step time for this FBlock.
 * @simulink init
 */
extern_C bool param_T1i_Ctrl_emC(T1i_Ctrl_emC_s* thiz, float Ts_param, float Tstep);

The initialization routine, designated with @simulink init in the comment, is called in the target code in a loop till initialization is done, and in simulink in the first step routines. It is possible to get values from other FBlocks in the model. Hence a loop is necessary to initialize all FBlocks independent of a sophisticated calling order. It is a concept. In this example only non-tunable parameters are used and the initialization can be done in the first call.

This routine is not inline, because it should not be fast executed. It contains some more complex stuff. For example calculate the fTs with an e-function: 1.0f - expf(-Tstep / Ts_param)). Hence it is written in the associated C-File.

/**Step routine for the T1 FBlock for 16 bit interger smoothing. */
static int16 step_T1i_Ctrl_emC(T1i_Ctrl_emC_s* thiz, int16 x);

This is the step routine to call in manual programming as forward declaration.

static inline int16 step_T1i_Ctrl_emC(T1i_Ctrl_emC_s* thiz, int16 x) {
  thiz->dx.dx32 = (uint32)(thiz->fTs) * ( x - thiz->q.q16.qhi);  //
  thiz->q.q32 += thiz->dx.dx32;
  return thiz->q.q16.qhi; //hi part 16 bit
}

The step routine is really inline, hence fast. It is only a simple artithmetic. Inline allows fast execution. Hint: The prototype definition before is necessary to prevent warnings for some compiler.

 /**Get dx after step calculation. step_T1i_Ctrl_emC(...) should be called before. */
 static int16 dx_T1i_Ctrl_emC(T1i_Ctrl_emC_s* thiz, int16 x);

 static inline int16 dx_T1i_Ctrl_emC(T1i_Ctrl_emC_s* thiz, int16 x) {
   return thiz->dx.dx16.dxhi;
 }

This is another, an access routine for manual call, access to the differential part of a DT1 usage of the FBlock. Prototype and inline implementation.

/**Step routine wrapper for simulink for 16 bit T1 smoothing FBlock.
 * @simulink Object-FB, no-thizInit, no-thizStep.
 */
static inline void stepY_T1i_Ctrl_emC(T1i_Ctrl_emC_s* thiz, int16 x, int16* y_y) {
  *y_y = step_T1i_Ctrl_emC(thiz, x);
}

This routine determines the FBlock itself, with designation @simulink Object-FB. It uses the ctor and the init and maybe some more other routines inside the FBlock, see

The header contains here also the implementation of the step routine, for this simple functionality. The necessary annotations are shown already, only the @simulink …​ tag is necessary. But Javadoc-style is necessary.

Some informations are gotten from writing style of names: Tstep is always the first step time, not only the name of a parameter. The FBlock gets this determined step time per paramter value.

Names ending with _param are parameters (in dialog field in the S-Function). This parameter are tunable for step routines, and non tunable for parameters in the constructor and the init routine.

The data pointer have to be named thiz (not this which is a keyword in C++, but it has the adequate meaning for Object Orientation in C). The output’s names should end with _y. Unfortunately the return value is not able to use as only one output yet. More as one output should be a reference anytime. But that is the reason of an additonal inline operation. The compiler will be optimized it, it is usual not a disadvantage in runtime.

More informations are not necessary to designate the C data and operations to use as S-Function. Any operation with @simulink Object-FB build one S-Function.

3. Object Orientated relations between S-Function and ObjectJc

The possibility to wire some S-Functions with association or aggregation relations (UML) is another possibility of this S-Function approach. It enhances the Function block - Data flow modeling style. It is described for example in "Grafische objektorientierte C-Programmierung mit Simulink" (Fachartikel in Elektronik Praxis in german).

ModuleAggr ControllerParam Another example is written right side. The two modules Controller and Parameter Optimizer known one together. Both are aggregated, because the exchange some internal data. But the modules are separated, as an architecture decision. Maybe because there are different responsibilities in development, they run on different cores of a processor or something similar.

The necessary references are handles (32 bit) which are checked and replaces by the correct memory addresses inside the S-Function running Simulink-simulation. For code generation (Simulink coder) or manual programming the handles are immediately replaced by the real 32-bit addresses for a target controller.


For this approach the data struct should base on a ObjectJc part. This is a core feature of the ../../../emc/index.html (embedded multiplatform C/C++).

An header file content for a more complex FBlock, a PID controller with separated parameter FBlock (can hold the parameter for more as one PID) looks like:

/**Parameter of PID controller
 */
typedef struct Par_PIDf_Ctrl_emC_T
{
  union { ObjectJc obj; } base;
  /**for debugging and check: The used step time for calcualation of the factors. */
  float Tctrl;
  ....
} Par_PIDf_Ctrl_emC_s;
/**ctor of Par_PID controller
 * @param Tstep it is necessary as Simulink parameter to define a defined step time.
 *        It is the time to call the Operation-FB. It is [[set_Par_PIDf_Ctrl_emC(...)]].
 *        But the argument is not used here. The value is really only necessary for simulink:
 * * For 4diac, the step time is determined by the event connections.
 * * For C/++ usage the step time is determined by the calling sequence.
 * @simulink ctor.
 */
extern_C Par_PIDf_Ctrl_emC_s* ctor_Par_PIDf_Ctrl_emC(ObjectJc* othiz, float Tstep);
/**init of parameter FBlock for the PID controller
 * @param Tstep_param It is the Tstep time of the controller, which should be regard on calculation of the factors.
 * @simulink init
 */
extern_C bool init_Par_PIDf_Ctrl_emC(Par_PIDf_Ctrl_emC_s* thiz, float Tctrl_param, float yMax_param
  , float kP, float Tn, float Td, float Tsd, ParFactors_PIDf_Ctrl_emC_s** parFactors_y );
/**step of parameter FBlock for the PID controller for actual changed parameter
 * @simulink Object-FB, no-thizStep.
 */
extern_C void set_Par_PIDf_Ctrl_emC(Par_PIDf_Ctrl_emC_s* thiz, float kP, float Tn, float Td, float Tsd, ParFactors_PIDf_Ctrl_emC_s** parFactors_y );

This are the data and routines of the parameter FBlock. Some parameters can be changed in runtime, via wired connections (not only via the parametrizing dialog box). But the parametrizing should use often an own slower step time. It is defined as the Tstep in the constructor.

The important but also simple data property is the base class ObjectJc. This class contains so named 'Reflection' of type ClassJc. The concept is adequate Java. Via the 'Reflection' the type is checked in the startup phase of the simulation. For a target software (well tested) a simple version of ObjectJc maybe without 'Reflection' is possible. It needs only 32 bit, proper for a poor target, for example 16 bit controller with less memory.

But the 'Reflection' property can be used both in Simulink as also in a target for symbolic access to data, using the ../../../Inspc/index.html approach.

The associated FBlock, the PID controller itself, has the following data structure (shortened):

/**Main data of PID controller
 * @simulink no-bus.
 */
typedef struct PIDf_Ctrl_emC_t
{
  union { ObjectJc obj; } base;

  ParFactors_PIDf_Ctrl_emC_s* parNew;  (1)

  /**Currently used factors. */
  ParFactors_PIDf_Ctrl_emC_s f;

  /**Current limitation of output. */
  float lim;

  ....

} PIDf_Ctrl_emC_s;

The init routine uses a reference (pointer):

/**init of PID controller
 * @param par the parameter Object, should be not null
 * @return false if par == null, true if initialized.
 * @simulink init.
 */
extern_C bool init_PIDf_Ctrl_emC(PIDf_Ctrl_emC_s* thiz, ParFactors_PIDf_Ctrl_emC_s* par);

This is matching to the parFactors_y output of the set_Par_PIDf_Ctrl_emC(…​) routine. The FBlock PIDctrl knows a defined part (only the factors) from the Param_PIDctrl FBlock. The connection between both is a simple number in uint32 format, a handle. Only that is able to handle in a model’s connection, and it is understandable from the view of a model owner without deep knowledge of C programming. The immediately address value need 64 bit, and it is a sensible number.

4. Life cycle: constructor, init step, terminate

Any SFunctionBlock has the four life cycle phases:

  • Allocate and construct: This is done in the mdlStart(…​) routine.

  • Init: All SFBlocks should be initialized. It is both the calculation if init states from parameter and inputs and get all aggregation references.

  • Run or step: Cyclically call

  • Terminate: The allocated data should be freed after stop simulation.

The problematic or interesting thing on init is: aggregation relation. The init phase should clarify ObjectOriented aggregation relation. It is a connection between ports. A Function Block should firstly initialized, than it can offer itself as reference to aggregate. Only if a FBlock has all aggregations and all aggregated blocks are initialized (have its aggregations and initial values), then the init phase is done. It means the init needs more as one steps. It is done in a specific step time, related with the Tinit time. For code generation this is an own step routine which is called only on startup till all is initialized. For the simulation run such multiple called startup is not intended. Hence it is done in a specific Tinit step time which is called cyclically but not used after initialization.

5. All annotations in the header file

  • @simulink bus for struct definitions: The struct is used to create a bus definition.

  • @simulink no-bus for struct definitions: From the struct no bus is created. This designation is not necessary, only comment.

  • @simulink ctor: This is the constructor routine for an Object-FB. The constructor is called in the mdlStart(…​) routine of the simulink engin, it is called in the init routine for code generation. The first argument should either be thiz or othiz. othiz should have the type ObjectJc* othiz, then the return value should have the type of the data. thiz can be used with the correct type of data, either based on ObjectJc or not. All following arguments are non-tunable parameters. The constructor is valid for more as one Object-FB.

If an new @simulink ctor definition is found in order of lines in the header file, all other routines except the @simulink dtor gets invalid for the following Object-FB. If a new Object-FB should be defined with this condition (all other operations invalid), but the same constructor should be used, the same prototype of the constructor with the @simulink ctor designation should be repeated.

  • @simulink dtor: This is the destructor routine for an Object-FB. The destructor is called in the mdlTerminate(…​) routine of the simulink engin. The destructor is valid for more as one Object-FB. It is the only one which is not removed if a new @simulink ctor is designated in the line order of a header file.

  • @simulink defTlcParam or @simulink defTlcParam_excl: This is a special routine which is called in the mdlRTW(…​) routine in the SFunction wrapper. It is used for a special preparation of parameters for code generation. See #TlcParam. This routine is used and valid with the @simulink ctor for all following Object-FB.

  • @simulink defPortTypes: This is a special routine which is called in the mdlInitializeSizes(…​) routine in the SFunction wrapper. It is used to determine port types which are derived from other port types defined by the wiring in the model. See #defPortTypes.

  • @simulink init: This is the initialization routine, called in the first step times of the simulation associated with a Tinit sampling time, respectively called in the Tinit-step routine in the code generation. The init definition should follow after the ctor and dtor, and but have to be the first routine which determines the Object-FB. See following order.

  • @simulink step2 or @simulink portStep: The arguments of this operation are associated to ports of the following Object-FB. This ports can have specific sample (step) times. They are designated to INHERITED_SAMPLE_TIME. It means the designation of the step time depends on the wiring in the model. This routines are called in the specificated step time of the simulation, respectively they are called in the associated step routine of code generation. It is similar a specific event chain of an event-wired FBlock diagram (such as https://www.eclipse.org/4diac).

  • @simulink upd: This operation is used for the mdlUpdate(…​) in the following Object-FB. The difference is: The input pins are not dedicated as ssSetInputPortDirectFeedThrough(.., 0). It breaks algorithm loops in the model. Outputs are not depending on this inputs. This routine is called in the associated step time.

  • @simulink Operation-FB: This tag forces the creation of a FB as 'Operation-FB' without own data, which gets its data from the first thiz input, in a Simulink model via handle. It is possible too that this is a static FBlock, without any data. All other operations are not used for this FBlock. This operation is not used for following Object-FB. An Operation-FB is like an operation to a Object-FB which can be disposed anywhere in the model.

  • @simulink Object-FB: This tag forces the creation of a FB as 'Object-FB'. The Object-FB has its internal data. It uses all designated operations from this list above, except the @simulink Operation-FB. This operation is used in the mdlOutputs(…​) routine in the SFunction wrapper. It is called in the associated step time. All inputs from the arguments of this routine are designated as ssSetInputPortDirectFeedThrough(.., 1). It means the outputs follows the inputs, determining the calculation order. See also @simulink upd above.

The @simulink Object-FB can be supplemented by some more tags separated with comma. It is:

  • step-in: It creates a first input designated to the Tstep sampling time without evaluating its associated data. This input is only used to determine the calculation order in the model. It is necessary if the Object-FB has no other inputs for the Tstep sampling time. The type of this input is uint32. It can be combined with thiz-step outputs.

  • step-out: It creates a first output designated to the Tstep sampling time without setting a value to this output, the output value remain 0. It is only desired to use determine the calculation order in the model. It is necessary if the Object-FB has no other outputs for the Tstep sampling time. The type of this input is uint32. It can be combined with step-in inputs or in calculations with an add operation. Because its value 0 it does not add.

  • no-thizStep: An Object-FB outputs its data handle with the Tstep sample time association on the last output pin before thiz with Tinit sample time association. This output can be used for connection to Operation-FB which should be calculated after the Object-FB or for associations (change able, not an aggregation) to other FBlocks. The designation no-thizStep prevents this default behaviour. It means, the data are not offered in the Tstep time. Independent of that it can be accessed from other FBlocks in the Tinit time, see next.

  • no-thizInit: An Object-FB outputs its data handle on the last output pin. It is to connect the data as aggregation to any other FBlock which can access this. The designation no-thizInit prevents this default behaviour. It means, the data cannot be accessed from other FBlocks, especially not from Operation_FB of the same struct. Hence this designation is usefull for FBlocks which’s data should not be accessed, especially simple isolated FBlocks. But the data can accessed with the thiz output in the Tstep time. See bullet above.

  • accel-tlc: This designation forces the ssSetOptions( simstruct, SS_OPTION_USE_TLC_WITH_ACCELERATOR, …​) see Simulink help. It means that the normal accelerator mode (not the "rapid accelerator") from the Simulink simulation engine uses the given tlc code, which results in a faster simulation. It depends of details whether this option should be used. The rapid accelerator always uses the tlc code. This designation is also used for Operation-FB.

  • try-catch: This designation forces additional try-catch statements on some executions. It helps to correct handling if errors in the C-sources occurs. But this option is not used because try-catch is always activated.

  • fnCallTrg: If this tag is set for an Object-FB the first output (before a possible step-in, usual not recommended) is an so named ssSetCallSystemOutput(simstruct, 0); which can be used to connect with a Triggered Subsystem. This is a special construct in Simulink for event processing.

6. defPortTypes operation: Specific defined port types.

7. Generated Wrapper-SFunction files and mex compilation

Some Wrapper for SFunction are already generated (see #example). They are necessary if the mex files should be newly created. They are an object of study to see how the system works.

In the example some libraries are contained:

 src/main/Smlk/libSmlk
                +-



[#TlcParam]
== TlcParam


[#example]
== Example in the `Smlk_ObjO_Inspc_20yy-mm-dd.zip`

You can find at link:https://vishia.org/smlk/Download/[] some versions of the zipped Simulink source tree. It has the structure of link:../SmlkFileTree/SmlkFileTree.html[].

=== Running the examples

Some examples are stored in `src/test/smlk/...`. The libraries and mex files for release are stored in `src/main/Smlk/libSmlk`:

src +-test/Smlk | +- +InspcCtrl | | +-ObjOModule_ExmplCtrl.slx …​ Example model | | +-ObjOModule_ExmplCtrl_init.m …​ init script | | + …​ etc. | +- +…​ etc. Note: Usage +directory for dot-usage in Simulink | +-main/Smlk +-libSmlk | +-libVishia_Inspc.mdl …​ Simulink model library with +-libVishia_CtrlSfn.mdl …​ generated SFunction, ready to use | +-mex …​ mex files for Windows-10, compiled with Visual Studio 2015 | +- tlc …​ tlc files for code generation

The mex files are ready to use for MS-Windows running the examples and working with the library.

The source for the mex files and the generation scripts are stored in:

=== Newly compile mex files, debug possibilities

In the folder `src/main/smlk/libSfn_Ctrl` and `src/main/smlk/libSfn_Inspc` you find

src +-main +-cpp | +- +gitclone_src_emC.sh …​ get emC sources from Github | +-src_emC …​ this directory is gotten from Github, not in zip | +-Smlk +-libSfn_XY | +- +mkSfn …​ scripts to generate the SFunction wrapper C sources | +- +genSfn …​ already generated sources and generating scripts | +-lib_XY_mex.m …​ compile script for mex +-lib_XY_mexdbg.m …​ compile script for mex debug version +-src_C/…​ …​ specific sources only for this lib, original C +-XY +-.c …​ generated wrapper for SFunctions. +-.tlc …​ generated tlc files, same as in libSmlk/mex/tlc

This folder is *NOT* necessary for modelling, but it contains the generated SFunction wrapper sources for the mex compilation and the compilation script.

The `src_emC` is not part of the zip because it is another independent component. You can simple synchronize it from github. The correct version (tag) is contained in the `gitclone_srx_emC.sh` script. Note: To execute shell scripts under windows you need MinGw, Cygwin or the shell execution capability of git. Because at least git should be given, it shouldn't be a problem for compilation- and script-affine users.

The `src/main/Smlk/libSfn_XY` contains the sources and generation scripts. The wrapper for the SFunction in the library are stored in `.../+genSfn/XY/...`. With them recompilation of the mex files can be done, especially for debug. You should resynchronize `src_emC` from github and then start the `...mexdbg.m` script. Most of original src files for the content of the SFunction (not the wrapper) are part of `src_emC` because they can be used outside of Simulink too. Only some specific Simulink sources are stored in the `src_C` sub folder.

With knowledge of the sources and the mex debug version you can attach also the debugging of Visual Studio. But for that the mex files should be newly compiled as debug version with your absolute paths stored in the appropriate NAME.mex64.pdb debug data base. The compilation does not need a newly generation of SFunction wrapper. The Sfunction wrapper and compile script are stored in the zip file.

=== Generate the wrapper of the SFunctions with new SFunction files

You can study the principles of SFunction wrapper with the given generated sources.

Only if you want to have more, your own SFunctions you need the generation scripts. These are not part of the zip file. You should write an email to hartmut.schorrig (at) vishia.de with short presentation of your approaches.

The matlab script files for the given libraries are part of `main/src/Smlk/libSfn_.../mkSfn/genscript.m`. This script is a matlab script, starts Java and reads the statements for generation from the same script. It includes that script which you should get via email, stored in `main/src/Smlk/SmlkSfunc_jzTc/...`.


== Code generation for Accelerator and Target

tlc files are beside mex, in `mex/tlc_c`

=== Settings for the model for sources for DataStruct_Inspc FBlocks

The model can contain Inspc data SFBlocks: link:./SmlkInspc/DataStruct_Inspc.html[]. They are essential to hold the data for a module. This FBlocks contains a parameter text to define which data with which type are stored. This FBlocks should be presented by a data `struct` in the generated code. This code is produced if the FBlocks are initialized.

To support this special feature some variables should be set in the MATLAB environment, as part of the initialization script of the module (callback - init):

dstPathGenDataStruct = ''; %%left empty if no code generation is used yet. %dstPathGenDataStruct = 'test/+InspcCtrl/genSrcInspc';

%Settings for the rapid accelerator mode, contribution for rtwmakecfg.m addIncludepath_rtwmakecfg = …​ { fullfile(rootPath, "test/Smlk/+InspcCtrl/srcC_Model") …​ , fullfile(rootPath, dstPathGenDataStruct) …​ %special sources for this model }; addSrcpath_rtwmakecfg = …​ { fullfile(rootPath, "test/Smlk/+InspcCtrl/srcC_Model") …​ %special sources for this model , fullfile(rootPath, dstPathGenDataStruct) …​ }; addSrc_rtwmakecfg = …​ { "Device_ObjOModules", "Controller_ObjOModules", "ParameterOptimizer_ObjOModules" …​ %special sources for this model };

* The `dstPathGenDataStruct` should be set if code generation is desired. Only then the `..._Inspc` SFBlocks generates code on startup of simulation, into the designated directory. If the code is generated already and the content of this SFBlocks is unchanged, it needs not be generated again. Possible mistakes are obviously on compilation the target, it is not obscure.

* `addIncludepath_rtwmakecfg` is used by the `mex/rtwmakecfg.m`. This file is used on code generation. It names additional include paths especially for accelerator code generation.

* `addSrcpath_rtwmakecfg` adequate, for additionally sources.

* `addSrc_rtwmakecfg` builds an array of all `DataStruct..._Inspc` SFBlocks, parameter "__Type of Datastruct__". This is the name of the created sources from this FBlocks.