7. Integrating external code
7.1. 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
7.2. Using custom data types defined in external modules
Procedures for handling custom datatypes is described in Custom Data Types
7.3. Importing variables and constants defined in external modules
Variable or constant defined in an external module can be imported using Simulink Signal or Simulink Parameter 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.
7.3.1. 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.
% 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:
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
#ifndef QGEN_BASE_WORKSPACE_H
#define QGEN_BASE_WORKSPACE_H
#include "qgen_types.h"
extern GAINT16 IntVarDefault;
#endif
7.3.2. 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.
% 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.
package ext_signal_c is
IntVarC : Integer_16;
pragma Import (C, IntVarC, "IntVarC");
end ext_signal_c;
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.
#ifndef EXT_SIGNAL_ADA_H
#define EXT_SIGNAL_ADA_H
#include "qgen_types.h"
extern GAINT16 IntVarAda;
#endif
#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
#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;
}
7.3.3. Imported variables in subystem interface
QGen supports several modes of interfacing with generated code (see full list of
modes in section Calling the generated code). 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.
% 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
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.
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' */
}
7.4. 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 orGenerate a wrapper function using
qgenc
and write the actual call to the legacy function manually in this wrapper.
7.5. 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
7.5.1. 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:
#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:
% 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
% 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:
This needs to be compiled for Simulink
% create the mex file (compile the wrapper and the legacy code)
legacy_code ('compile', def)
% 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.
Drag this block to the model where the S-Function was required.
The final model:
Note
Before executing QGen on the model, the variable def
used to
generate the S-Function block should be removed from the
workspace as it may contain data in a cell format unsupported by
QGen, causing an error during the export of the model to XMI.
After exporting the model to XMI and generating code we can see that the code contains call to the myFun function:
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 */
}
7.5.2. 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:
[<return datatype> <return var>] <function name> (<arg1>, <arg2> ... <argN>)
where:
<return datatype>
is a valid Simulink data type
<return var>
and<argX>
are references to inputs outputs or paramters in form
u<no>
– input <no> where <no> in number of the input port
y<no>
– output <no> where <no> is number of the output port
p<no>
– parameter <no> where <no> is a parameter SParameter<no> defined in MaskVariablesin case block input or output is an array then
<argX>
may refer to its size:
size([u|y]<no>)
– length of the first dimension of input/output <no>
size([u|y]<no>, 1)
– length of the first dimension of input/output <no>
size([u|y]<no>, 2)
– length of the second dimension of input/output <no>
7.6. 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 <modelname>_wrappers which contains a
function for each generated block. The workflow is as follows:
Run
qgenc
on a Simulink model to generate codeqgenc
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:
<modelname>_wrappers.[h|ads]
– the prototypes of wrapper functions<modelname>_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
<S-Function_name> (<inport 1>, <inport 2>, ..., <inport N>, <outport1>, <outport 2>, ..., <outport M>)
if this was the first time to generate code for this model then rename the file “<modelname>_wrappers.[c|adb].template” to “<modelname>_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
7.6.1. Example
Let us consider a model mutlvect.mdl that calls a function mult_vect from library my_vect_ops multiplying a vector with constant.
7.6.1.1. Library my_vect_ops
my_vect_ops.h
#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
7.6.1.2. 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
/* 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
/* 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 */
7.6.1.3. Call to S-Function
no_prototype_demo.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 */
7.6.1.4. Manual code
The code written manually inside of the generated multvect_template stubs:
no_prototype_demo_wrappers.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).
7.7. 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
7.8. 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:
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.
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.
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:
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 :
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
.
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.
In C make sure to fill the header file field next to the functions that are called. This is mandatory for code generation.
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.
7.8.1. 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.