Main Content

Elementary Work Vectors

Description of Elementary Work Vector

In addition to DWork vectors, the Simulink® software provides a simplified set of work vectors. In some S-functions, these elementary work vectors can provide an easier solution than using DWork vectors:

  • IWork vectors store integer data.

  • Mode vectors model zero crossings or other features that require a single mode vector.

  • PWork vectors store pointers to data structures, such as those that interface the S-function to legacy code, another software application, or a hardware application.

  • RWork vectors store floating-point (real) data.

Relationship to DWork Vectors

The following table compares each type of work vector to a DWork vector.

Work Vector TypeComparison to DWork VectorHow to create equivalent DWork vector
IWorkIWork vectors cannot be customized in the generated code. Also, you are allowed only one IWork vector.
ssSetNumDWork(S,1);
ssSetDWorkDataType(S, 0, SS_INT8);
ModeMode vectors require more memory than DWork vectors since the mode vector is always stored with an integer data type. Also, you are allowed only one Mode vector.
ssSetNumDWork(S,1);
ssSetDWorkUsageType(S, 0, 
   SS_DWORK_USED_AS_MODE);
ssSetDWorkDataType(S, 0, SS_INT8);
PWorkUnlike DWork vectors, PWork vectors cannot be named in the generated code. Also, you are allowed only one PWork vector.
ssSetNumDWork(S,1);
ssSetDWorkDataType(S, 0, SS_POINTER);
The DWork vector then stores a pointer.
RWorkRWork vectors cannot be customized in the generated code. Also, you are allowed only one RWork vector.
ssSetNumDWork(S,1);
ssSetDWorkDataType(S, 0, SS_DOUBLE);

Using Elementary Work Vectors

The process for using elementary work vectors is similar to that for DWork vectors (see Using DWork Vectors in C MEX S-Functions.) The elementary work vectors have fewer properties, so the initialization process is simpler. However, if you need to generate code for the S-function, the S-function becomes more involved than when using DWork vectors.

The following steps show how to set up and use elementary work vectors. See Additional Work Vector Macros for a list of macros related to each step in the following process.

  1. In mdlInitializeSizes, specify the size of the work vectors using the ssSetNumXWork macro, for example:

    ssSetNumPWork(S, 2);

    This macro indicates how many elements the work vector contains, however, the Simulink engine does not allocate memory, at this time.

    An S-function can defer specifying the length of the work vectors until all information about the S-function inputs is available by passing the value DYNAMICALLY_SIZED to the ssSetNumXWork macro. If an S-function defers specifying the length of the work vectors in mdlInitializeSizes, it must provide a mdlSetWorkWidths method to set up the work vectors.

    Note

    If an S-function uses mdlSetWorkWidths, all work vectors used in the S-function must be set to DYNAMICALLY_SIZED in mdlInitializeSizes, even if the exact value is known before mdlInitializeSizes is called. The sizes to be used by the S-function are than specified in mdlSetWorkWidths.

    For an example, see sfun_dynsize.c.

  2. In mdlStart, assign values to the work vectors that are initialized only at the start of the simulation. Use the ssGetXWork macro to retrieve a pointer to each work vector and use the pointer to initialize the work vector values. Alternatively, use the ssGetXWorkValues to assign values to particular elements of the work vector.

    The Simulink engine calls the mdlStart method once at the beginning of the simulation. Before calling this method, the engine allocates memory for the work vectors. Do not use the mdlStart method for data that needs to be reinitialized over the course of the simulation, for example, data that needs to be reinitialized when an enabled subsystem containing the S-function is enabled.

  3. In mdlInitializeConditions, initialize the values of any work vectors that might need to be reinitialized at certain points in the simulation. The engine executes mdlInitializeConditions at the beginning of the simulation and any time an enabled subsystem containing the S-function is reenabled.

  4. In mdlOutputs, mdlUpdate, etc., use the ssGetXWork macro to retrieve a pointer to the work vector and use the pointer to access or update the work vector values.

  5. Write an mdlRTW method to allow the Target Language Compiler (TLC) to access the work vector. This step is not necessary if the S-function uses DWork vectors. For information on writing parameter data in an mdlRTW method, see ssWriteRTWParamSettings. For more information on generating code using an mdlRTW method, see Write Fully Inlined S-Functions with mdlRTW Routine (Simulink Coder).

Additional Work Vector Macros

MacroDescription
ssSetNumRWorkSpecify the width of the real work vector.
ssGetNumRWorkQuery the width of the real work vector.
ssSetNumIWorkSpecify the width of the integer work vector.
ssGetNumIWorkQuery the width of the integer work vector.
ssSetNumPWorkSpecify the width of the pointer work vector.
ssGetNumPWorkQuery the width of the pointer work vector.
ssSetNumModesSpecify the width of the mode work vector.
ssGetNumModesQuery the width of the mode work vector.
ssGetIWorkGet a pointer to the integer work vector.
ssGetIWorkValueGet an element of the integer work vector.
ssGetModeVectorGet a pointer to the mode work vector.
ssGetModeVectorValueGet an element of the mode work vector.
ssGetPWorkGet a pointer to the pointer work vector.
ssGetPworkValueGet one element from the pointer work vector.
ssGetRWorkGet a pointer to the floating-point work vector.
ssGetRWorkValueGet an element of the floating-point work vector.
ssSetIWorkValueSet the value of one element of the integer work vector.
ssSetModeVectorValueSet the value of one element of the mode work vector.
ssSetPWorkValueSet the value of one element of the pointer work vector.
ssSetRWorkValueSet the value of one element of the floating-point work vector.

Elementary Work Vector Examples

The following sections provide examples of the four types of elementary work vectors.

Pointer Work Vector

This example opens a file and stores the FILE pointer in the pointer work vector.

The following statement, included in the mdlInitializeSizes function, indicates that the pointer work vector is to contain one element.

ssSetNumPWork(S, 1)   /* pointer-work vector */

The following code uses the pointer work vector to store a FILE pointer, returned from the standard I/O function fopen.

#define MDL_START  /* Change to #undef to remove function. */
#if defined(MDL_START)
static void mdlStart(real_T *x0, SimStruct *S)
{
  FILE *fPtr;
  void **PWork = ssGetPWork(S);
  fPtr = fopen("file.data", "r");
  PWork[0] = fPtr;
}
#endif /*  MDL_START */

The following code retrieves the FILE pointer from the pointer work vector and passes it to fclose in order to close the file.

static void mdlTerminate(SimStruct *S)
{
  if (ssGetPWork(S) != NULL) {
    FILE *fPtr;
    fPtr = (FILE *) ssGetPWorkValue(S,0);
    if (fPtr != NULL) {
      fclose(fPtr);
    }
    ssSetPWorkValue(S,0,NULL);
  }
}

Note

Although the Simulink engine handles deallocating the PWork vector, the mdlTerminate method must always free the memory stored in the PWork vector.

Real and Integer Work Vectors

The S-function stvctf.c uses RWork and IWork vectors to model a time-varying continuous transfer function. For a description of this S-function, see the example Discontinuities in Continuous States.

Mode Vector

The following example implements a switch block using a mode work vector. The mdlInitializeSizes method configures two input ports with direct feedthrough and one output port. The mode vector element indicates if the signal from the first or second input port is propagated to the output. The S-function uses one S-function parameter and a corresponding run-time parameter to store the mode value and allow the switch to be toggled during simulation.

static void mdlInitializeSizes(SimStruct *S)
{
    /* Initialize one S-function parameter to toggle the mode value */
    ssSetNumSFcnParams(S, 1);
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
        /* Return if number of expected != number of actual parameters */
        return;
    }

    {
        int iParam = 0;
        int nParam = ssGetNumSFcnParams(S);

        for ( iParam = 0; iParam < nParam; iParam++ )
        {
            ssSetSFcnParamTunable( S, iParam, SS_PRM_TUNABLE );
        }
    }
    /* Initialize two input ports with direct feedthrough */
    if (!ssSetNumInputPorts(S, 2)) return;
    ssSetInputPortWidth(S, 0, 1);
    ssSetInputPortWidth(S, 1, 1);
    ssSetInputPortDataType(  S, 0, SS_DOUBLE);
    ssSetInputPortDataType(  S, 1, SS_DOUBLE);
    ssSetInputPortDirectFeedThrough(  S, 0, 1);
    ssSetInputPortDirectFeedThrough(  S, 1, 1);

    /* Initialize one output port */
    if (!ssSetNumOutputPorts(S, 1)) return;
    ssSetOutputPortWidth(S, 0, 1);
    ssSetOutputPortDataType(  S, 0, SS_DOUBLE);

    /* Initialize one element in the mode vector */
    ssSetNumSampleTimes(S, 1);
    ssSetNumModes(S,1);
    
    ssSetOptions(S,
                 SS_OPTION_WORKS_WITH_CODE_REUSE |
                 SS_OPTION_USE_TLC_WITH_ACCELERATOR |
                 SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME |
                 SS_OPTION_NONVOLATILE);
}

The mdlInitializeConditions method initializes the mode vector value using the current value of the S-function dialog parameter.

#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions =============================
 * Abstract:
 *    Initialize the mode vector value.
 */
static void mdlInitializeConditions(SimStruct *S)
{
    int_T *mv = ssGetModeVector(S);
    real_T param = mxGetScalar(ssGetSFcnParam(S,0));
    mv[0] = (int_T)param;
}

The mdlProcessParameters and mdlSetWorkWidths methods initialize and update the run-time parameter. As the simulation runs, changes to the S-function dialog parameter are mapped to the run-time parameter.

/* Function: mdlSetWorkWidths =============================================
 * Abstract:
 *    Sets the number of runtime parameters.
 */
#define MDL_SET_WORK_WIDTHS
static void mdlSetWorkWidths(SimStruct *S) {
    ssSetNumRunTimeParams(S,1);
    ssRegDlgParamAsRunTimeParam(S,0,0,"P1",SS_INT16);
}

/* Function: mdlProcessParameters ===========================================
 * Abstract:
 *      Update run-time parameters.
 */
#define MDL_PROCESS_PARAMETERS
static void mdlProcessParameters(SimStruct *S)
{
    ssUpdateDlgParamAsRunTimeParam(S,0);
}

The mdlOutputs method updates the mode vector value with the new run-time parameter value at every major time step. It then uses the mode vector value to determine which input signal to pass through to the output.

static void mdlOutputs(SimStruct *S, int_T tid)
{
    InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
    InputRealPtrsType u2Ptrs = ssGetInputPortRealSignalPtrs(S,1);
    real_T    *y = ssGetOutputPortSignal(S,0);
    int_T  *mode = ssGetModeVector(S);
    real_T param = mxGetScalar(ssGetSFcnParam(S,0));
    
    if (ssIsMajorTimeStep(S)) {
        mode[0] =  (int_T)param;
    }

    if (!mode[0]) {
        /* first input */
        y[0] = (*uPtrs[0]);
    }
    if (mode[0]) {
        /* second input */
        y[0] = (*u2Ptrs[0]);
    }
}