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. User script to generate S-Functions from Header

Maybe follow also the video: ../../mp4/GenSfn-C.mp4

As example for generation, the script in

src/main/Smlk/libSfn_Ctrl/+mkSfn/lib_T1FB_genSfH.m

is explained here.

%% Generates Sfunction wrapper and tlc file from information in the header file. 
% NOTE: This file needs a licensed script file see include statement in this script
% Please contact hartmut.schorrig@vishia.de to get more information.
% The generated wrapper and tlc should be stored additionally, so you can% compile 
% mex files nevertheless with unchanged S-function wrapper (unchanged interface of the Sfunction)
%
rootPath = evalin('base', 'rootPath');
display(rootPath);
clc;
clear mex;
currdir_ = pwd;
cd(rootPath);
disp('Generate S-Fuctions for lib_T1FB ...');
system('java.exe -cp ../tools/vishiaBase.jar;../tools/vishiaFBcl.jar org.vishia.jztxtcmd.JZtxtcmd main/Smlk/libSfn_Ctrl/+mkSfn/lib_T1FB_genSfH.m');
%genBus.lib_T1FB_genBus();  %just generated script
genSfn.lib_T1FB_mexdbg();    %just generated script.
%genSfn.lib_T1FB_mex();    %just generated script.
disp('successfull');
system('time /T');
cd(currdir_);
clear mex;

end

As you can see the first part of the generation script is a matlab script. The matlab script calls firstly a Java process from the operation system which generates the S-Function wrapper.c and tlc files.

Then with the generated files, the mex compiler is invoked, either for debug, or only release, or both. Newly generating for debug is necessary if you want to debug internally in the S-Functions for example with Visual-Studio "Attache to process". For running the S-Functions in Simulink only the release version is necessary. That is independent of an installed compiler, only dependent on the operation system (usual generated for MS-Windows, all 64 bit versions).

The scripts for generating the SFunction second source files and also the vishiaFBcl.jar are not open source. You can use it as 'Shareware' getting the compiled version of the vishiaFBcl.jar which includes also the scripts for generating. If you are interest how does it work, you should offer your name, institute and your approach.

You can also follow to the GenSfnFromHeader_HowDoesItWork.html. There partially it is explained how does the generation works, with showing the script and somewhat from the vishiaFBcl.jar java files to get an impression.

But follow the genration script, which you can use as template or pattern for your own with the 'Shareware' approach:

%{
==JZtxtcmd==
currdir = <:><&scriptdir>/../../../..<.>;   ##the currdir should be the simulink root dir.
include ../../SmlkSfunc_jzTc/simulinkSfuncBusGen.jzTc;  ##out of the box, see below
##include %1:org.vishia.fblock.SmlkSfn.simulinkSfuncBusGen_jzTc();  ##out of the box, see below

##Important note: To complile the Sfunctions newly you need three things outside of this simulink box.
##They should be placed in file system parallel to this this current dir 'smlk/SMLK_PRJ/Simulink'.
##1. The JZtxtcmd script engine. It is contained in 'Java/vishiajar/zbnf.jar'.
##   It is only used in the Java invocation as class path.
##2. Sources of Library C-functions. 
##3. The scripts to generate S-Funtion wrapper. This script is included above with relative path.
##   This scripts are not open source. you can get it with a low fee (one week coaching) from vishia.org.

The rest of the script is commented for Matlab, but it is recognized as JZtxtcmd script. You see an included file for this script. This is the internal script. It can be either loaded from the jar file, or, if you have the original, included as file.

Subtextvar mexAddCmd = <:>
system('copy "main\Smlk\lib_Sfn_Inspc\+mkSfn\emC_rtwmakecfg.m$" "<&dirMexWin>\rtwmakecfg.m"');
<.>;

The sub text written above is included in the mex generation file. In this case it contains a line for the matscript to copy a special file necessary for code generation into the mex destination folder.

##from this header Sfunctions are generated.
Fileset srcHeader =  
( main/cpp/src_emC:emC/Ctrl/T1_Ctrl_emC.h
);

This is one of the essential statements. A Fileset in JZtxtcmd defines some files which can be handled in subroutines. This Fileset contains only one file, here all header files should be named which are used to control the generation. This Header files should contain the annotations as described in chapter above Definition and Annotations in header files - Principle and first example..

The path is relative starting from the current directory in Simulink, which is the condition of starting this script. The Simulink current directory is in the working tree src where also setupMatlabPath.m is found.

The colon in the path is a special separator, after them starts the 'local path' of the file. The 'local path' is also that one which is used in include statements.

Fileset includepath = 
( main/cpp/src_emC
, main/cpp/src_emC/emC_inclApplSpec/stdef_SmlkSfuncExcH
, main/cpp/src_emC/emC_inclComplSpec/cc_SmlkSfunc_64bit
);

This is the include path used for the mex compiler. The directories for include are also given as Fileset, starting from the Simulink’s current directory, where also all source files should be able to find. It is the whole src tree of the working directory. For this concept you can also refer to ../../../SwEng/html/srcFileTree.html.

Here the base directory for all emC files are named as part of the include path, but also two directories which contains applstdef_emC.h (see ../../../emc/html/Base/applstdef_emC_h.html) and compl_adaption.h (see ../../../emc/html/Base/compl_adaption_h.html) especially for the S-Function mex compilation with Visual Studio.

## All sources for compilation to the named header file, for rapid accelerator, except the generated Sfunc-wrapper.
Fileset srcTlc_T1_Ctrl_emC = 
( main/cpp/src_emC/emC/Ctrl/T1i_Ctrl_emC.c     ##same c-file as header
);

The part above is a file set with the name srcTlc_ following with exact the name of the header file without extension .h. It means each header (more possible) should have one such entry. It lists the (usual one) source file to the header, which contains the implementation. The same source file should be used also for code generation and for the accelerator mode of Simulink as source for compilation.

Note that it is a good style guide that each user header file has exact one or some inplementation files, or revers: each implementation file should have exact one header associated to it which declares the content as interface. This proven style is presumed here.

## All sources for compilation to the named header file, except the generated Sfunc-wrapper.
Fileset src_T1_Ctrl_emC = 
( &srcTlc_T1_Ctrl_emC      ##Use the sources for rapid accelerator
, main/cpp/src_emC/emC_srcApplSpec/applConv/ApplThrowOnFatalError_emC.c
, main/cpp/src_emC/emC_srcApplSpec/applConv/ExceptionPrintStacktrace_emC.c
, main/cpp/src_emC/emC_srcApplSpec/applConv/ExcThCxtStacksize.c
, main/cpp/src_emC/emC_srcApplSpec/applConv/ObjectJc_allocStartup_emC.c
, main/cpp/src_emC/emC/Base/Assert_emC.c
, main/cpp/src_emC/emC/Base/CheckStack_emC.c
, main/cpp/src_emC/emC/Base/DefPortTypes_emC.c
, main/cpp/src_emC/emC/Base/ExceptionCpp_emC.cpp
, main/cpp/src_emC/emC/Base/ExcThreadCxt_emC.c
, main/cpp/src_emC/emC/Base/MemC_emC.c
, main/cpp/src_emC/emC/Base/Memfree_ThreadAndBlockHeap.c
, main/cpp/src_emC/emC/Base/ObjectSimple_emC.c
, main/cpp/src_emC/emC/Base/ObjectRefl_emC.c
, main/cpp/src_emC/emC/Base/Time_emC.c
, main/cpp/src_emC/emC/Base/Timeconversions_emC.c
, main/cpp/src_emC/emC/Base/Reflection_emC.c
, main/cpp/src_emC/emC/Base/ReflectionBaseTypes_emC.c
##, main/cpp/src_emC/emC/Base/String_emC.c
, main/cpp/src_emC/emC/Base/StringBase_emC.c
##, main/cpp/src_emC/emC/Base/StringBuilder_emC.c
, main/cpp/src_emC/emC/Base/Handle_ptr64_emC.c
, main/cpp/src_emC/emC/Base/Handle_ptr64_TimeMeas_emC.c
, main/cpp/src_emC/emC/Base/os_common.c
, main/cpp/src_emC/emC/OSAL/Environment_OSALemC.c
##, main/cpp/src_emC/emC_srcOSALspec/osal_Windows_Msc15/os_atomic.c
, main/cpp/src_emC/emC_srcOSALspec/osal_Windows/os_environment.c
, main/cpp/src_emC/emC_srcOSALspec/osal_Windows/os_file.c
, main/cpp/src_emC/emC_srcOSALspec/osal_Windows/os_mem.c
, main/cpp/src_emC/emC_srcOSALspec/osal_Windows/os_mutex.c
, main/cpp/src_emC/emC_srcOSALspec/osal_Windows/os_sharedmem.c
, main/cpp/src_emC/emC_srcOSALspec/osal_Windows/os_sync.c
, main/cpp/src_emC/emC_srcOSALspec/osal_Windows/os_thread.c
, main/cpp/src_emC/emC_srcOSALspec/osal_Windows/os_time.c
);

This is the file set especially for the S-Function or the mex compilation. The name of the file set should be built with src_ following with the name of the header file, adequate as the srcTlc_…​. It is presumed of course that all S-Functions which are declared in one header uses the same implementation files. Of course some system files are necessary for example to fulfill exception handling, allocation, etc. This files build the mex dll. For code generation this files are also present, but one time for the whole code generation, which is one executable. For the mex dll compilation each mex dll should contain this stuff, because all mex dll are primary independent (concept of dll in Windows).

If you have more as one header file, and some header files needs the same content here, you can build an extra file set src_emC or src_base and include it here. See some other +mkSfn/…​m files.

main() { ##main routine for the JZtxtcmd:

  zmake "main/Smlk/libSfn_Ctrl:lib_T1FB" := simulinkSfuncBusGen(&srcHeader
    , coptions="-TP -EHa"
    , dirRefl = null ##Reflections are part of the emC sources.
    ##, fileBus = ":+genBus/lib_T1FB_genBus.m"
    , dirMexDbg = "../mexDbg"
    , dirMex = "main/Smlk/libSmlk/mex"
    , dirMexcc=":+genSfn"
    , dirSfunc = ":+genSfn/Ctrl"
    , dirTlc = ":+genSfn/Ctrl"
    ##, html=":test.html"  ##only for internally data view
    );

}

==endJZcmd==
%}

This is the main routine for JZtxtcmd and also the end of the script which closes the JZtxtcmd part and the mathlab comment.

The main routine contains the shown zmake call. zmake is a statement in ../../../JZtxtcmd/html/JZtxtcmd.html#chapter_7.7. It combines some arguments for a make.

  • One of them is the output path left of the called subroutine. Before the colon, it is the output directory as relative directory starting from the Simulink working directory. Right side from the colon the name of the generated mex file is written, but without the suffix _mex respectively _mexdbg. This should be related to the called mex file for generation.

  • The simulinkSfuncBusGen is the called sub routine in the include JZtxtcmd script for the generation, see docu GenSfnFromHeader_HowDoesItWork.html.

  • The first argument of this routine is exact that Fileset where the used headers are named.

  • coptions is an argument for additional options for the mex compilation. Here it is for Visual Studio.

    • The -TP option switches to using the C++ compiler instead the elsewhere used C compilation, which is a very good idea. You can also immediately use C++ sources in the S-Functions, though the interface for mex is a C interface

    • The -EHa option enables the so named 'asynchronous' Exceptions for runtime compiled with Visual Studio. This forces the normal Exception handling for memory protection faults, which can be occurred in development phases of a little bit more complex routines. This works only with C++ compilation, of course, and a proven Exception concept, which is given in ../../../emc/html/Base/ThCxtExc_emC.html. If a non expected exception is thrown, the SFunction and the whole simulation process will be proper terminated with an clean text message in Simulink, and Mathworks works furthermore. Without this exception concept an unexpected behavior (expectable in test situations with non ready overall software) forces an unexpected behavior of Mathworks, maybe it hangs and should be killled with the Task manager. This is prevented here.

  • The here mentioned dirRefl and fileBus are deprecated. The reflection information which are necessary in SFunctions which memory handle are used outside are generated and stored meanwhile with the emC sources. The fileBus came from the concept that data structures are offered to outside via Simulink buses. But meanwhile the concept of ObjectOrientation with handles are established. Buses in Simulink are not enough powerful (cannot contain referenced data) and are a special solution of Mathworks. This arguments can be also ommited, because it have this as default values in the called subroutine.

  • dirMexDebug and dirMex are the directories (relative from current dir in Simulink) where the mex files should be stored. That information are transferred to the mex compile output file.

  • dirSfunc and dirTlc are the directories where the generated source files for the S-Function wrapper and the tlc files are stored. The style guide says, store it in the own directory. The : as first character means: Use the output dir as first argument, destination for Zmake.

  • The html argument can name a file with path, here commented, where the internal data of the generation process are reported in. It helps for development, not necessary for usage.

How does it work, please refer to GenSfnFromHeader_HowDoesItWork.html. But the normal user may not know that details. Refer it if you are interest and you have the necessary time.

You should study the generated files to recognize which is generated. For that details you find helps on Mathworks. Especially all ss…​ routines in the S-Function wrapper are explain there, as also the whole S-Function concept.

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

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

6. 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 update: This operation is used for the mdlUpdate(…​) operation 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 update 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.

7. defPortTypes operation: Specific defined port types.

8. 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
                +-

9. TlcParam

10. Example in the Smlk_ObjO_Inspc_20yy-mm-dd.zip

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

10.1. 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:

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

10.3. 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/…​.

11. Code generation for Accelerator and Target

tlc files are beside mex, in mex/tlc_c

11.1. Settings for the model for sources for DataStruct_Inspc FBlocks

The model can contain Inspc data SFBlocks: ./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.