Main Content

Run Quality Checks on S-Functions Using S-Function Analyzer

This topic shows how to use the S-function analyzer programmatic interfaces 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 topic, 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

Prerequisites

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.

Setup the Working Environment to Check S-Function Using S-Function Analyzer

This example opens up a directory containing the files required for this topic.

Create a local working folder, for example C:\sfa. Open this example to access the following files.

  1. ex_slexSfunctionCheckExample.slx

  2. external.c

  3. external.h

  4. sfcnModifyMinorStepDiscState.c

  5. sfcnModifyMinorStepDiscState_wrapper.c

  6. sfcnUpdateModifyContinuous.c

  7. sfcnUpdateModifyContinuous_wrapper.c

  8. sfcnUseExternalSrc.c

  9. slexBadSFcn.c

  10. slexBadSFcn_wrapper.c

Run the S-Function Checks

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.

Specify the Model

model = 'ex_slexSfunctionCheckExample'

Find S-Functions in the Model (Optional)

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)

Specify Build Information (Optional)

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});       

Specify Options (Optional)

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 Checks and See Results

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()
When applied, the 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]

Explore and Fix Problems

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.

  1. 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 CodeModified 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;
           } 
        }

  2. 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 CodeModified 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;
    }

  3. 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 CodeModified 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);
        }
    }

  4. 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 CodeModified 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);

  5. 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);
    }

  6. 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 CodeModified 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.

Enable Undocumented API 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

Enable Polyspace Checks

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 Code Prover Results in Polyspace Desktop User Interface (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 CodeModified 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;
    }

See Also

| | |