Use the S-function analyzer APIs to run quality checks on C-MEX
S-functions in a model or library. These checks identify potential problems and
improvements and suggest solutions. In this tutorial, you see how to:
Find the S-functions in a model
Specify the build information for S-functions
Specify the options and run the S-function analyzer
See and interpret the results
To complete the tutorial, you need the following products:
MATLAB®
Simulink®
Polyspace® (optional)
C compiler – For most platforms, a default C compiler is supplied with
the MATLAB installation. For a list of supported compilers, see Change Default Compiler. You can also change the
default compiler using mex -setup
command.
Create a local working folder, for example
C:\sfa
.
Change to the docroot\toolbox\simulink\examples
folder.
At the MATLAB command line, enter:
cd(fullfile(docroot, 'toolbox', 'simulink',
'examples'))
Copy the listed files to your local working folder:
ex_slexSfunctionCheckExample.slx
external.c
external.h
sfcnModifyMinorStepDiscState.c
sfcnModifyMinorStepDiscState_wrapper.c
sfcnUpdateModifyContinuous.c
sfcnUpdateModifyContinuous_wrapper.c
sfcnUseExternalSrc.c
slexBadSFcn.c
slexBadSFcn_wrapper.c
Open a new script from the MATLAB Editor, and save it as
ex_slexSfunctionCheckScript.m
. To see the example model,
double-click ex_slexSfunctionCheckExample.slx
in
Current Folder or type
ex_slexSfunctionCheckExample
at the MATLAB command line.
model = 'ex_slexSfunctionCheckExample'
Use Simulink.sfunction.analyzer.findSfunctions
method to
see all the S-functions to be analyzed in the model. This method does not find
S-functions in a referenced
model.
sfunctions = Simulink.sfunction.analyzer.findSfunctions(model)
To specify the build information, such as S-function source code, external
libraries, and header files, you can use
Simulink.sfunction.analyzer.BuildInfo
. In this tutorial,
because we have four S-functions in the model, there are four different
BuildInfo
objects. For more information, see Simulink.sfunction.Analyzer
and
Simulink.sfunction.analyzer.BuildInfo
.
bdInfo1= Simulink.sfunction.analyzer.BuildInfo('sfcnUseExternalSrc.c',... 'ExtraSrcFileList',{'external.c'},... 'SrcPaths',{pwd},'IncPaths',{pwd}); bdInfo2= Simulink.sfunction.analyzer.BuildInfo('sfcnModifyMinorStepDiscState.c',... 'ExtraSrcFileList',{'sfcnModifyMinorStepDiscState_wrapper.c'},... 'SrcPaths',{pwd}); bdInfo3= Simulink.sfunction.analyzer.BuildInfo('sfcnUpdateModifyContinuous.c',... 'ExtraSrcFileList',{'sfcnUpdateModifyContinuous_wrapper.c'},... 'SrcPaths',{pwd}); bdInfo4= Simulink.sfunction.analyzer.BuildInfo('slexBadSFcn.c',... 'ExtraSrcFileList',{'slexBadSFcn_wrapper.c'},... 'SrcPaths',{pwd});
You can configure the options for executing the S-function analyzer using
Simulink.sfunction.analyzer.Options
class. You can enable
Polyspace
Code Prover™ check and parameter robustness check, set the maximum model
simulation time, and set the report path. If you do not use the class to specify
any of the options, the default options are applied to the analysis. See Simulink.sfunction.analyzer.Options
for more details.
Note
Running Polyspace Code Prover and parameter robustness checks take some time.
Performing Polyspace Code Prover checks requires a Polyspace license. For more information on using Polyspace checks in the S-function analyzer, see Enable Polyspace Checks. For this tutorial, parameter robustness checks are turned on.
opts = Simulink.sfunction.analyzer.Options(); opts.EnableRobustness = 1;
Run the S-function analyzer checks using
Simulink.sfunction.Analyzer.run
. Then use the
Simulink.sfunction.Analyzer.generateReport
to see the
issues in your model or
code.
sfunAnalyzer = Simulink.sfunction.Analyzer(model,'BuildInfo',{bdInfo1,bdInfo2,bdInfo3,bdInfo4},'Options',opts); analysisResult=sfunAnalyzer.run(); sfunAnalyzer.generateReport()
generateReport
method produces a
struct
and an HTML report of the result of S-function
analyzer checks. analysisResult = struct with fields: TimeGenerated: '13-Jul-2017 13:22:37' Platform: 'win64' Release: '(R2017b)' SimulinkVersion: '9.0' ExemptedBlocks: {} MexConfiguration: [1×1 mex.CompilerConfiguration] Data: [4×4 struct]
The example model has two warnings in Source Code Checks
and
four issues on S-function MEX-file checks
. In general, warnings
are not as significant as fails, but they are good sources to obtain further
information about your S-functions.
This issue has the description code
MinorStepModifyDiscreteStates
. This error description
indicates that block's discrete states is modified at a minor step in
mdlOutputs
. To fix this issue, discrete states should
only be modified at a major step guarded by
ssIsMajorTimeStep
.
Original Code | Modified Code |
---|---|
MinorStepDiscState_Outputs_wrapper(u0, y0, xC);
if (ssGetT(S)>0.5) {
xD[0] = xD[0]+1;
xD[1] = xD[0]+xD[1]*2.0;
} |
if (ssGetT(S)>0.5) { if (ssIsMajorTimeStep(S)) { xD[0] = xD[0]+1; xD[1] = xD[0]+xD[1]*2.0; } } |
The MEX Compile Check
description code indicates that
there is an unused variable in the line indicated on the report. This
warning is eliminated by deleting the line 208 and rerunning the
code.
Original Code | Modified Code |
---|---|
static void mdlOutputs(SimStruct *S, int_T tid) { const real_T *u0 = (const real_T*) ssGetInputPortSignal(S,0); real_T *y0 = (real_T *)ssGetOutputPortRealSignal(S,0); real_T *xC = ssGetContStates(S); *y0 = *xC; } |
static void mdlOutputs(SimStruct *S, int_T tid) { // const real_T *u0 = (const real_T*) ssGetInputPortSignal(S,0); real_T *y0 = (real_T *)ssGetOutputPortRealSignal(S,0); real_T *xC = ssGetContStates(S); *y0 = *xC; } |
The description code MdlUpdateModifyContinuousStates
indicates that in the S-function source code (in this case, in the source
code of block S-Function1
), the continuous states are
modified in its mdlUpdate
method. You can only change the
states of a block at a major time step using
ssSetSolverNeedsReset
macro.
Original Code | Modified Code |
---|---|
#define MDL_UPDATE static void mdlUpdate(SimStruct *S, int_T tid) { real_T *xC = ssGetContStates(S); *xC = *xC + 1.0; } |
#define MDL_UPDATE static void mdlUpdate(SimStruct *S, int_T tid) { real_T *xC = ssGetContStates(S); // Modify continuous states if (ssIsMajorTimeStep(S)) { *xC = *xC + 1.0; ssSetSolverNeedsReset(S); } } |
The description code MEX Compile Check
indicates that
variables outputDimsInfo
and
inputDimsInfo
are not used in the source code. You
can fix this by commenting or deleting the lines that contain these
variables.
Original Code | Modified Code |
---|---|
static void mdlInitializeSizes(SimStruct *S) { DECL_AND_INIT_DIMSINFO(inputDimsInfo); DECL_AND_INIT_DIMSINFO(outputDimsInfo); ssSetNumSFcnParams(S, NPARAMS); |
static void mdlInitializeSizes(SimStruct *S) { // DECL_AND_INIT_DIMSINFO(inputDimsInfo); // DECL_AND_INIT_DIMSINFO(outputDimsInfo); ssSetNumSFcnParams(S, NPARAMS); |
The description code
CombinedMdlOutputsMdlUpdateWithDiscreteState
indicates that the S-function has discrete states, and you need to use
MdlUpdate
and MdlOutputs
methods
in your code separately. As a solution for this description code, define a
separate mdlUpdate
to change states in your
S-function.
#define MDL_UPDATE static void mdlUpdate(SimStruct *S, int_T tid) { /* update the discrete states here! */ real_T *xD = ssGetDiscStates(S); }
The description code
DeclareCanBeConditionalExecWithState
indicates that
you have state-like data or if you are using multiple sample times in your
model, you cannot use
SS_OPTION_CAN_BE_CALLED_CONDITIONALLY
option in your
S-function source code.
To fix the issue in this particular example, delete the
ssSetOptions
function.
Original Code | Modified Code |
---|---|
static void mdlInitializeSizes(SimStruct *S) { DECL_AND_INIT_DIMSINFO(inputDimsInfo); DECL_AND_INIT_DIMSINFO(outputDimsInfo); ssSetNumSFcnParams(S, NPARAMS); if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) { return; /* Parameter mismatch will be reported by Simulink */ } ssSetNumContStates(S, NUM_CONT_STATES); ssSetNumDiscStates(S, NUM_DISC_STATES); if (!ssSetNumInputPorts(S, NUM_INPUTS)) return; ssSetInputPortWidth(S, 0, INPUT_0_WIDTH); ssSetInputPortDataType(S, 0, SS_DOUBLE); ssSetInputPortComplexSignal(S, 0, INPUT_0_COMPLEX); ssSetInputPortDirectFeedThrough(S, 0, INPUT_0_FEEDTHROUGH); ssSetInputPortRequiredContiguous(S, 0, 1); /*direct input signal access*/ if (!ssSetNumOutputPorts(S, NUM_OUTPUTS)) return; ssSetOutputPortWidth(S, 0, OUTPUT_0_WIDTH); ssSetOutputPortDataType(S, 0, SS_DOUBLE); ssSetOutputPortComplexSignal(S, 0, OUTPUT_0_COMPLEX); ssSetNumSampleTimes(S, 1); ssSetNumRWork(S, 0); ssSetNumIWork(S, 0); ssSetNumPWork(S, 0); ssSetNumModes(S, 0); ssSetNumNonsampledZCs(S, 0); ssSetOptions(S, SS_OPTION_CAN_BE_CALLED_CONDITIONALLY); } |
static void mdlInitializeSizes(SimStruct *S) { DECL_AND_INIT_DIMSINFO(inputDimsInfo); DECL_AND_INIT_DIMSINFO(outputDimsInfo); ssSetNumSFcnParams(S, NPARAMS); if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) { return; /* Parameter mismatch will be reported by Simulink */ } ssSetNumContStates(S, NUM_CONT_STATES); ssSetNumDiscStates(S, NUM_DISC_STATES); if (!ssSetNumInputPorts(S, NUM_INPUTS)) return; ssSetInputPortWidth(S, 0, INPUT_0_WIDTH); ssSetInputPortDataType(S, 0, SS_DOUBLE); ssSetInputPortComplexSignal(S, 0, INPUT_0_COMPLEX); ssSetInputPortDirectFeedThrough(S, 0, INPUT_0_FEEDTHROUGH); ssSetInputPortRequiredContiguous(S, 0, 1); /*direct input signal access*/ if (!ssSetNumOutputPorts(S, NUM_OUTPUTS)) return; ssSetOutputPortWidth(S, 0, OUTPUT_0_WIDTH); ssSetOutputPortDataType(S, 0, SS_DOUBLE); ssSetOutputPortComplexSignal(S, 0, OUTPUT_0_COMPLEX); ssSetNumSampleTimes(S, 1); ssSetNumRWork(S, 0); ssSetNumIWork(S, 0); ssSetNumPWork(S, 0); ssSetNumModes(S, 0); ssSetNumNonsampledZCs(S, 0); } |
When you fix all issues in your model, the report shows green check marks for each group of checks.
You can check for the use of undocumented APIs in your S-function source code
to avoid any incompatibilities in the future releases. To enable this check, in
the Simulink.sfunction.analyzer.Options object, set
EnableUsePublishedOnly
to 1. Alternatively, when building
your MEX files, use -DUSE_PUBLISHED_ONLY
option. For example,
try building sfcnModifyMinorStepDiscState.c
with this option
using:
mex sfcnModifyMinorStepDiscState.c -DUSE_PUBLISHED_ONLY
S-function analyzer gives you the option to run Polyspace
Code Prover checks on your code. To enable the check, in the
Simulink.sfunction.analyzer.Options object, set
EnablePolyspace
to 1. Polyspace
Code Prover divides checks into red, green, orange, and gray checks. For more
information on types of checks, see Code Prover Result and Source Code Colors (Polyspace Code Prover).
In the S-function analyzer, the most important error code is red. If the S-function source code fails execution in all paths, Polyspace Code Prover gives a red check error. For more information, see Interpret Polyspace Code Prover Results (Polyspace Code Prover). Here is an example on how to troubleshoot the red check error.
The red Polyspace Code Prover check indicates that there is a problem in your S-function source code. To investigate the issue using Polyspace, click the hyperlink in the report. This link automatically opens a Polyspace Project window. In the Results List pane, expand Red Check and select the error. The source file opens in the Source window. From this window, you can fix and save your code the same way as you do in the MATLAB Editor.
The Polyspace
Code Prover error code indicates a problem with a pointer. Pointer
*p
is out of bounds because it is used in an earlier
loop. Correct the error by replacing this pointer with a variable of your
choice, or deleting *p
from this line of code.
Original Code | Modified Code |
---|---|
if (return_val < 3)
{
tmp = *p+5; /* Out of bounds */
tmp++;
return_val = 10;
} |
if (return_val < 3)
{
tmp = 5;
tmp++;
return_val = 10;
} |
findSfunctions
| Simulink.sfunction.Analyzer
| Simulink.sfunction.analyzer.BuildInfo
| Simulink.sfunction.analyzer.Options