.. _legacy-code: =========================================== Integrating external code =========================================== Background ========== QGen supports interfacing generated code with manually written or code generated from another model by following means: * Using custom data types defined in external modules * Importing variables and constants defined in external modules * Calling C or Ada code using S-Function block Using custom data types defined in external modules ===================================================== Procedures for handling custom datatypes is described in :ref:`custom-data-types` .. _import-var: Importing variables and constants defined in external modules ================================================================= Variable or constant defined in an external module can be imported using Simulink Signal (https://www.mathworks.com/help/simulink/slref/signal.html) or Parameter (https://www.mathworks.com/help/simulink/slref/parameter.html) objects. Signal and Parameter objects with StorageClass 'ImportedExtern' or StorageClass 'Custom' and CustomStorageClass 'ImportFromFile' are treated as defined in an external module. In the case of StorageClass ‘Custom’, there is the possibility to indicate a specification file containing the symbol declaration. If a specification file is not defined, QGen generates an import declaration. Imported variable with no explicit declaration ---------------------------------------------------------------- When importing a variable with StorageClass 'ImportedExtern' or in case the StorageClass is 'Custom' and the HeaderFile parameter is not defined, QGen assumes that the variable was defined in an external module with C convention. Variable declaration is derived from the type specification in Matlab. .. code-block:: text % SIGNAL IntVarDefault % % External varible, no header specified % Assumed to be defined in C by default IntVarDefault = mpt.Signal; IntVarDefault.DataType = 'int32'; IntVarDefault.Min = []; IntVarDefault.Max = []; IntVarDefault.Description = ''; IntVarDefault.RTWInfo.StorageClass = 'Custom'; IntVarDefault.RTWInfo.Alias = ''; IntVarDefault.RTWInfo.Alignment = -1; IntVarDefault.RTWInfo.CustomStorageClass = 'ImportFromFile'; IntVarDefault.RTWInfo.CustomAttributes.ConcurrentAccess = 0; % SIGNAL IntVarDefault % When generating Ada code from the Signal object defined above the corresponding variable is declared in qgen_base_workspace package and imported with explicit "pragma Import" directive: .. code-block:: ada package qgen_base_workspace is IntVarDefault : Integer_16; pragma Import (C, IntVarDefault, "IntVarDefault"); end qgen_base_workspace; When generating C, the variable is declared with "extern" qualifier in qgen_base_workspace.h .. code-block:: c #ifndef QGEN_BASE_WORKSPACE_H #define QGEN_BASE_WORKSPACE_H #include "qgen_types.h" extern GAINT16 IntVarDefault; #endif Imported variable with explicit declaration ---------------------------------------------------------------- Adding a reference to specification file, where external variable is declared, allows qgen to import that specification directly and declaration in generated code is not required. .. code-block:: text % SIGNAL IntVarC % IntVarC = mpt.Signal; IntVarC.DataType = 'int16'; IntVarC.Min = []; IntVarC.Max = []; IntVarC.Description = ''; IntVarC.RTWInfo.StorageClass = 'Custom'; IntVarC.RTWInfo.Alias = ''; IntVarC.RTWInfo.Alignment = -1; IntVarC.RTWInfo.CustomStorageClass = 'ImportFromFile'; IntVarC.RTWInfo.CustomAttributes.MemorySection = 'Default'; IntVarC.RTWInfo.CustomAttributes.DataAccess = 'Direct'; IntVarC.RTWInfo.CustomAttributes.HeaderFile = 'ext_signal_c.h'; %IntVarC.RTWInfo.CustomAttributes.ConcurrentAccess = 0; % SIGNAL IntVarC % % SIGNAL IntVarAda % IntVarAda = mpt.Signal; IntVarAda.DataType = 'int16'; IntVarAda.Min = []; IntVarAda.Max = []; IntVarAda.Description = ''; IntVarAda.RTWInfo.StorageClass = 'Custom'; IntVarAda.RTWInfo.Alias = ''; IntVarAda.RTWInfo.Alignment = -1; IntVarAda.RTWInfo.CustomStorageClass = 'ImportFromFile'; IntVarAda.RTWInfo.CustomAttributes.MemorySection = 'Default'; IntVarAda.RTWInfo.CustomAttributes.DataAccess = 'Direct'; IntVarAda.RTWInfo.CustomAttributes.HeaderFile = 'ext_signal_ada.ads'; IntVarAda.RTWInfo.CustomAttributes.ConcurrentAccess = 0; % SIGNAL IntVarAda % When generating Ada code, ext_signal_ada.ads is included using "with" statement, an import pragma is defined in package ext_signal_c for variable declared in ext_signal_c.h. Note, that the initialization statement is generated only for variable IntVarAda2 defined in the model. All imported variables are expected to be initialized in their originating modules. .. code-block:: ada package ext_signal_c is IntVarC : Integer_16; pragma Import (C, IntVarC, "IntVarC"); end ext_signal_c; .. code-block:: ada with ext_signal_c; use ext_signal_c; with ext_signal_ada; use ext_signal_ada; with qgen_base_workspace; use qgen_base_workspace; package body ext_signal_c_ada is procedure initStates (State : in out ext_signal_c_ada_State) is begin -- Block 'ext_signal_c_ada/IntVarAda2Mem' State.IntVarAda2 := Integer_16 (0.0); -- End Block 'ext_signal_c_ada/IntVarAda2Mem' end initStates; procedure comp (Out1 : out Integer_16; Out2 : out Integer_16; Out3 : out Integer_16; Out4 : out Integer_16; State : in out ext_signal_c_ada_State) is begin Out1 := qgen_base_workspace.IntVarDefault; Out2 := ext_signal_c.IntVarC; Out3 := ext_signal_ada.IntVarAda; Out4 := State.IntVarAda2; end comp; end ext_signal_c_ada; In case of C as the output language the pattern is the same, except that now we generate "extern" declaration for originally declared in ext_signal_ada.ads and insert include statement for the one declared in ext_signal_c.h. .. code-block:: c #ifndef EXT_SIGNAL_ADA_H #define EXT_SIGNAL_ADA_H #include "qgen_types.h" extern GAINT16 IntVarAda; #endif .. code-block:: c #ifndef EXT_SIGNAL_C_ADA_H #define EXT_SIGNAL_C_ADA_H #include "ext_signal_c_ada_states.h" #include "qgen_types.h" #include "qgen_base_workspace.h" #include "ext_signal_c.h" #include "ext_signal_ada.h" extern void ext_signal_c_ada_initStates (ext_signal_c_ada_State* const State); extern void ext_signal_c_ada_comp (GAINT16* const Out1, GAINT16* const Out2, GAINT16* const Out3, GAINT16* const Out4, ext_signal_c_ada_State* const State); #endif .. code-block:: c #include "ext_signal_c_ada.h" void ext_signal_c_ada_initStates (ext_signal_c_ada_State* const State) { /* Block 'ext_signal_c_ada/IntVarAda2Mem' */ State->IntVarAda2 = (GAINT16) 0.0; /* End Block 'ext_signal_c_ada/IntVarAda2Mem' */ } void ext_signal_c_ada_comp (GAINT16* const Out1, GAINT16* const Out2, GAINT16* const Out3, GAINT16* const Out4, ext_signal_c_ada_State* const State) { *Out1 = IntVarDefault; *Out2 = IntVarC; *Out3 = IntVarAda; *Out4 = State->IntVarAda2; } Imported variables in subystem interface ---------------------------------------------------------------- QGen supports several modes of interfacing with generated code (see full list of modes in section :ref:`gen-code-interface`). By default each top level port is converted to an argument of corresponding compute function. The '--global-io' switch changes this behavior and generates global variables instead. If any of the input and output ports is connected to a signal with imported variable, the QGen-generated global variable is omitted and the imported one is used for IO. .. image:: img/simulink_signals_extern_io.png .. code-block:: text % SIGNAL ImportedSigIn % ImportedSigIn = Simulink.Signal; ImportedSigIn.DataType = 'int16'; ImportedSigIn.Dimensions = [2 2]; ImportedSigIn.Complexity = 'real'; ImportedSigIn.Min = -100; ImportedSigIn.Max = 100; ImportedSigIn.SamplingMode = 'Sample based'; ImportedSigIn.SampleTime = 5; ImportedSigIn.Description = 'Global input'; ImportedSigIn.RTWInfo.StorageClass = 'ImportedExtern'; % SIGNAL ImportedSigIn % % SIGNAL ImportedSigOut % ImportedSigOut = Simulink.Signal; ImportedSigOut.DataType = 'int16'; ImportedSigOut.Dimensions = [2 2]; ImportedSigOut.Complexity = 'real'; ImportedSigOut.Min = -100; ImportedSigOut.Max = 100; ImportedSigOut.SamplingMode = 'Sample based'; ImportedSigOut.SampleTime = 5; ImportedSigOut.Description = 'Global output'; ImportedSigOut.RTWInfo.StorageClass = 'ImportedExtern'; % SIGNAL ImportedSigOut % By default all ports have a corresponding function argument that, in case of an imported Signal object, is assigned to/from global variable .. code-block:: c void simulink_signals_extern_io_comp (GAINT16 const In1[2][2], GAINT16 const In2[2][2], GAINT16 Out1[2][2], GAINT16 Out2[2][2]) { GAUINT8 i; GAUINT8 j; /* Block 'simulink_signals_extern_io/In1' */ for (i = 0; i <= 1; i++) { for (j = 0; j <= 1; j++) { ImportedSigIn[i][j] = In1[i][j]; } } /* End Block 'simulink_signals_extern_io/In1' */ /* Block 'simulink_signals_extern_io/Gain' */ for (i = 0; i <= 1; i++) { for (j = 0; j <= 1; j++) { ImportedSigOut[i][j] = 2 * ImportedSigIn[i][j]; } } /* End Block 'simulink_signals_extern_io/Gain' */ /* Block 'simulink_signals_extern_io/Out1' */ for (i = 0; i <= 1; i++) { for (j = 0; j <= 1; j++) { Out1[i][j] = ImportedSigOut[i][j]; } } /* End Block 'simulink_signals_extern_io/Out1' */ /* Block 'simulink_signals_extern_io/Sum' */ /* Block 'simulink_signals_extern_io/In2' */ /* Block 'simulink_signals_extern_io/Out2' */ for (i = 0; i <= 1; i++) { for (j = 0; j <= 1; j++) { Out2[i][j] = ImportedSigOut[i][j] + In2[i][j]; } } /* End Block 'simulink_signals_extern_io/Out2' */ /* End Block 'simulink_signals_extern_io/In2' */ /* End Block 'simulink_signals_extern_io/Sum' */ } Running qgenc with '--global-io' switch removes the function arguments and generates global variables for In2 and Out2. External variables ImportedSignIn and ImportedSignOut are used for ports In1 and In2. .. code-block:: c void simulink_signals_extern_io_comp (void) { GAUINT8 i; GAUINT8 j; /* Block 'simulink_signals_extern_io/Gain' */ for (i = 0; i <= 1; i++) { for (j = 0; j <= 1; j++) { ImportedSigOut[i][j] = 2 * ImportedSigIn[i][j]; } } /* End Block 'simulink_signals_extern_io/Gain' */ /* Block 'simulink_signals_extern_io/Sum' */ /* Block 'simulink_signals_extern_io/In2' */ /* Block 'simulink_signals_extern_io/Out2' */ for (i = 0; i <= 1; i++) { for (j = 0; j <= 1; j++) { simulink_signals_extern_io_comp_Out2[i][j] = ImportedSigOut[i][j] + simulink_signals_extern_io_comp_In2[i][j]; } } /* End Block 'simulink_signals_extern_io/Out2' */ /* End Block 'simulink_signals_extern_io/In2' */ /* End Block 'simulink_signals_extern_io/Sum' */ } Calling C or Ada code using S-Function block ===================================================== Launching legacy code in Simulink is achieved using S-function blocks and mex files. Mex file (MATLAB EXecutable, http://www.mathworks.se/help/matlab/matlab_external/introducing-mex-files.html) is a compiled binary format, proprietary to Mathworks. The legacy code written in C, C++, Fortran or Ada can be compiled to this format and then linked to a model through S-function blocks (called "Legacy Function" blocks). For executing simulation no information on original sources is required. In case a model containing Legacy S-function blocks is used for code generation in Real Time Workshop one needs to provide a special template file for each mex function containing the function prototype, reference to file containing function prototype etc. ``qgenc`` does not support this template format and relies only on the information contained in mdl and slx files. The only type of supported s-function is "Legacy function". There are two options to generate code from this block: * Encode all information about the external library in s-function parameters using the legacy_code tool and let ``qgenc`` generate the calls or * Generate a wrapper function using ``qgenc`` and write the actual call to the legacy function manually in this wrapper. Option A: specify library information in Simulink ================================================= This option assumes that the external library with legacy code is fully specified in S-function parameters. At minimum the information we need is the name of the header file of the external library and a function signature specifying the mapping to block ports and data types of each argument. The workflow consist of the steps as follows: * create S-function block using the legacy code tool (see instructions below) * generate code using QGen. The generated code will contain call to the function in external library * while compiling the code provide the legacy code module in include and lib paths Creating S-function blocks in Simulink -------------------------------------- There are several methods for creating Legacy Function blocks in Simulink. The only method, that stores all information required for generation is that using the Legacy Code Tool (http://www.mathworks.se/help/simulink/slref/legacy_code.html or http://www.mathworks.se/help/rtw/block-authoring-with-legacy-code-tool.html). Let us consider creating a block for function "double myFun (in1 double)" with specification in file my_module.h: .. image:: img/my_module_1.png .. code-block:: c #ifndef MYMODULE_H #define MYMODULE_H double myFun (double in1); #endif The first step is to create a datastructure introducing this function to the legacy code tool: .. code-block:: text % define name of the function in Simulink (and name of the mex file) % this name does not need to be the same as the referenced legacy function def.SFunctionName = ('myFun_sim') % define source file (body) def.SourceFiles = {'my_module.c'} % define header file (spec) % NB! This file is not required for successful generation of mex files % however, it is mandatory for linking the generated code to correct module def.HeaderFiles = {'my_module.h'} % define function prototype (see syntax of the prototype specification in the % next section) def.OutputFcnSpec = 'double y1 = myFun (double u1)' To generate a block Simulink is able to execute we run the legacy code tool .. code-block:: text % generate wrapper for the legacy code % this will create file 'myFun_sim.c' which is a wrapper for simulation legacy_code ('sfcn_cmex_generate', def) As a result the wrapper file appears: .. image:: img/my_module_2.png This needs to be compiled for Simulink .. code-block:: text % create the mex file (compile the wrapper and the legacy code) legacy_code ('compile', def) .. image:: img/my_module_3.png .. code-block:: text % generate Simulink block % this will create a new model with a block linked to the new mex file legacy_code ('slblock_generate', def) The last command in the template above will create a new model that contains the generated S-function block. .. image:: img/my_module_4.png Drag this block to the model where the S-function was required. .. image:: img/my_module_5.png The final model: .. image:: img/my_module_6.png After exporting the model to XMI and generating code we can see that the code contains call to the myFun function: .. code-block:: c void legacy_demo_legacy_demo_comp (GAREAL In1, GAREAL *Out1) { GAREAL In1_out1; GAREAL myFun_sim_out1; /* Block legacy_demo/In1 */ In1_out1 = In1; /* End Block legacy_demo/In1 */ /* Block legacy_demo/myFun_sim */ myFun_sim_out1 = myFun (In1_out1); /* End Block legacy_demo/myFun_sim */ /* Block legacy_demo/Out1 */ *Out1 = myFun_sim_out1; /* End Block legacy_demo/Out1 */ } Function prototype syntax ------------------------- The goal of the function prototype is to define a mapping between the block interface and function arguments/return values. Special identifier patterns are used to denote block ports and mask parameters The syntax supported both by Simulink and ``qgenc`` contains the following elements: .. code-block:: text [ ] (, ... ) where: * ```` is a valid Simulink data type * ```` and ```` are references to inputs outputs or paramters in form * ``u`` -- input where in number of the input port * ``y`` -- output where is number of the output port * ``p`` -- parameter where is a parameter SParameter defined in MaskVariables * in case block input or output is an array then ```` may refer to its size: * ``size([u|y])`` -- length of the first dimension of input/output * ``size([u|y], 1)`` -- length of the first dimension of input/output * ``size([u|y], 2)`` -- length of the second dimension of input/output Option B: generate S-function wrappers ====================================== This option applies when the s-function block in not generated using the legacy code tool (or one did not provide the full prototype specification). When function prototype specification is not found in the block specification then ``qgenc`` will generate a module named _wrappers which contains a function for each generated block. The workflow is as follows: * Run ``qgenc`` on a Simulink model to generate code * ``qgenc`` will produce a module for each atomic subsystem + a special module containing function wrappers for each s-function with no prototype. * two files are generated for this special wrappers module: * ``_wrappers.[h|ads]`` -- the prototypes of wrapper functions * ``_wrappers.[c|adb].template`` -- empty function stubs for each wrapper function * the wrappers module will contain a function for each s-function block in form `` (, , ..., , , , ..., )`` * if this was the first time to generate code for this model then rename the file "_wrappers.[c|adb].template" to "_wrappers.[c|adb]" and write an appropriate function call in each wrapper function * in case of repeated code generation simply check that the function calls in the wrapper module generated earlier are not changed (the h/ads file is always overwritten, c/adb file remains inteact as the new file created by ``qgenc`` has ".template" suffix) * while compiling the code provide the legacy code module in include and lib paths Example ------- Let us consider a model mutlvect.mdl that calls a function mult_vect from library my_vect_ops multiplying a vector with constant. .. image:: img/my_vect_ops_1.png Library my_vect_ops ................... **my_vect_ops.h** .. code-block:: c #ifndef MY_VECT_OPS_H #define MY_VECT_OPS_H /** * Copies contents of vector to vector2 and multiplies each element * with multiplier. **/ void mult_vect (int *vector, int vector_len, int multiplier, int *vector2); #endif .. image:: img/my_vect_ops_3.png Generated wrapper functions ........................... The model contains a block named "sfun_mult_vect" with one input port of type int8[6], one input of type int8 and one output of type int8[6]. The wrappers generated by ``qgenc`` are as follows: **no_prototype_demo_wrappers.h** .. code-block:: c /* Copyright (C) Project P Consortium */ /* * @generated with GNAT Model Compiler 1.0w * Command line arguments: * -l c no_prototype_demo.xmi * --clean --pre-process-xmi */ #ifndef NO_PROTOTYPE_DEMO_WRAPPERS_H #define NO_PROTOTYPE_DEMO_WRAPPERS_H #include "qgen_types.h" extern void no_prototype_demo_wrappers_sfun_mult_vect (GAINT8 In1_out1[6], GAINT8 In2_out1, GAINT8 sfun_mult_vect_out1[6]); #endif /* @EOF */ **no_prototype_demo_wrappers.c.template** .. code-block:: c /* Copyright (C) Project P Consortium */ /* * @generated with GNAT Model Compiler 1.0w * Command line arguments: * -l c no_prototype_demo.xmi * --clean --pre-process-xmi */ #include "no_prototype_demo_wrappers.h" void no_prototype_demo_wrappers_sfun_mult_vect (GAINT8 In1_out1[6], GAINT8 In2_out1, GAINT8 sfun_mult_vect_out1[6]) { } /* @EOF */ Call to s-function .................. **no_prototype_demo.c** .. code-block:: c /* Copyright (C) Project P Consortium */ /* * @generated with GNAT Model Compiler 1.0w * Command line arguments: * -l c no_prototype_demo.xmi * --clean --pre-process-xmi */ #include "no_prototype_demo.h" void no_prototype_demo_no_prototype_demo_init (void) { } void no_prototype_demo_no_prototype_demo_comp (GAINT8 In1[6], GAINT8 In2, GAINT8 Out1[6]) { GAINT8 In1_out1[6]; GAINT8 In2_out1; GAINT8 sfun_mult_vect_out1[6]; /* Block no_prototype_demo/In1 */ for (GAUINT8 i = 0; i <= 5; i++) { In1_out1[i] = In1[i]; } /* End Block no_prototype_demo/In1 */ /* Block no_prototype_demo/In2 */ In2_out1 = In2; /* End Block no_prototype_demo/In2 */ /* Block no_prototype_demo/sfun_mult_vect */ (void) no_prototype_demo_wrappers_sfun_mult_vect (In1_out1, In2_out1, sfun_mult_vect_out1); /* End Block no_prototype_demo/sfun_mult_vect */ /* Block no_prototype_demo/Out1 */ for (GAUINT8 i_1 = 0; i_1 <= 5; i_1++) { Out1[i_1] = sfun_mult_vect_out1[i_1]; } /* End Block no_prototype_demo/Out1 */ } /* @EOF */ Manual code ........... The code written manually inside of the generated multvect_template stubs: **no_prototype_demo_wrappers.c** .. code-block:: c /* Copyright (C) Project P Consortium */ /* * @generated with GNAT Model Compiler 1.0w * Command line arguments: * -l c no_prototype_demo.xmi * --clean --pre-process-xmi */ #include "no_prototype_demo_wrappers.h" /* the legacy code specs */ #include "my_vect_ops.h" void no_prototype_demo_wrappers_sfun_mult_vect (GAINT8 In1_out1[6], GAINT8 In2_out1, GAINT8 sfun_mult_vect_out1[6]) { /* call external function. At this point the vector length is statically known, so we pass the length argument as hard-coded constant */ mult_vect (In1_out1, 6, In2_out1, sfun_mult_vect_out1); } /* @EOF */ From this point on, the no_prototype_demo_wrappers.c shall be made available at link time and it remains intact when new code is generated (unless the block interface changes). Compiling and linking the generated code ======================================== * The header file referenced in the HeaderFiles parameter shall be available at compile time. * The source file referenced in SourceFiles shall be available to the linker Calling C or Ada codebases using QGen-SFun block ====================================================== The QGen-SFun block is available in the Simulink Library Browser under the QGen Toolset Library. To use it simply drag it to the model from which you want to call your existing C or Ada code. To configure the code, double click on the block. The following dialog will appear: .. image:: img/QGen-Sfun-mask.png The first step is to choose the language that you are calling from the Language dropdown list. Then, click on ``Setup S-Function`` and the S-Function builder panel documented here https://www.mathworks.com/help/simulink/sfg/s-function-builder-dialog-box.html will pop up. .. image:: img/S-Function-builder.png Type the name of your S-Function binary within the ``S-Function name`` field. Keep the language within that panel set to C, even if you chose Ada in the first screen. This language corresponds to the language called by Simulink and we need it to be C. In the ``Initialization`` screen, set the number of discrete states and an array initializing them, note that each variable will be of type Float in Ada or single in C in the function prototypes. .. image:: img/QGen-Sfun-States.png In the ``Data properties`` screen, set the Input ports, output ports and data type attributes as needed. Changes to the parameters tab will not be taken into account. In the ``Libraries`` screen, add the relative path to the sources directories of your code within the ``Library/Object/Source files (one per line)`` panel. Only paths to directories are supported by the QGen-SFun block. The ``External function declarations`` only has to be filled when linking Ada code. When calling Ada you first have to make sure that you exported your Ada function(s) symbol(s) to C, as written in the example below: .. code-block:: ada package Ada_Sfun is procedure do_Compute(u0 : Long_Float; u1 : Long_Float; u2 : Long_Float; u3 : Long_Float; u4 : Long_Float; y0 : out Long_Float); pragma Export (C, do_Compute, "do_Compute"); end Ada_Sfun; .. WARNING:: You have to Export your function with a symbol equal to its name. Then you have to add in ``External function declarations`` the equivalent C prototypes for the exported symbol. Which, for the example above, would be : .. code-block:: c extern void do_Compute (const real_T u0, const real_T u1, const real_T u2, const real_T u3, const real_T u4, const real_T * y0); Be sure to realize this step for each function that you want to call directly from Simulink. In effect you will only have to do this for at most 3 functions, one for the ``Outputs`` call (mandatory) and two for ``Start`` and ``Update`` in the case where you have discrete states. Finally the last step is to call the C symbol of your function(s) in the ``Outputs`` tab. If you have states you also should call the necessary functions in the ``Start`` and ``Update`` tab. .. WARNING:: Do not type any extra C code in these panels. The only code that you should add is the call to your function. If you wish to realize additional processing of the inputs, you should do so within the called code, or call a wrapper that does the desired processing of the inputs. In our example we would write the following call, using the names of the ports defined in ``Data properties``. The states are passed as an array of single named ``xD``. .. image:: img/QGen-Sfun-Outputs.png .. WARNING:: Pass the arguments to your function in the same order they were defined in ``Data Properties``. Changing the order, omitting a parameter or combining parameters would cause the S-Function to be built correctly but the QGen code generation would not be correct. All ports and states are passed as pointers, if you are treating your input ports as scalar, make sure to dereference them, like in the example above. Once the calls are complete click ``Save`` then ``Close``. Some of your changes will appear in the ``Setup summary`` panel. .. image:: img/QGen-Sfun-complete.png In C make sure to fill the header file field next to the functions that are called. This is mandatory for code generation. .. image:: img/QGen-Sfun-cheader.png After reviewing or completing the setup, click on ``Build S-Function``. Your S-Function will be automatically built based on the provided setup. In the case of compilation errors, they will be detailed in the MATLAB command window. Fix them by going back to ``Setup S-Function`` and editing the necessary fields. You can find the generated files within ``[libname]_wrappers``. When the build is sucessful you are ready to connect your block. You can also generate code for your model and QGen will call the correct functions. .. NOTE:: If your block is already connected and your reopen the ``Setup S-Function`` panel, connecting signals will be deleted. Simply reconnect them. Debugging the QGen S-Function during simulation ----------------------------------------------- After building your QGen S-Function, you can simply debug your code during the Simulink Simulation by clicking on ``Debug S-Function in Simulation``. This will spawn a GNAT Studio or GPS instance that will automatically attach to Simulink. When the Debugger Console is available, you can start or step within the simulation and the {GNAT Studio|GPS} Debugger will automatically break when reaching your S-Function call. .. NOTE:: As soon as the S-Function code breakpoint is reached, MATLAB will hang. This is normal, just continue the code execution after debugging this step to unblock MATLAB. Once MATLAB is unblocked, you can step, continue or stop the simulation. All standard debugging capabilities are available after your stepped within the S-Function code.