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.
The following table compares each type of work vector to a DWork vector.
Work Vector Type | Comparison to DWork Vector | How to create equivalent DWork vector |
---|---|---|
IWork | IWork vectors cannot be customized in the generated code. Also, you are allowed only one IWork vector. | ssSetNumDWork(S,1); ssSetDWorkDataType(S, 0, SS_INT8); |
Mode | Mode 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); |
PWork | Unlike 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); |
RWork | RWork vectors cannot be customized in the generated code. Also, you are allowed only one RWork vector. | ssSetNumDWork(S,1); ssSetDWorkDataType(S, 0, SS_DOUBLE); |
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.
In mdlInitializeSizes
, specify
the size of the work vectors using the ssSetNum
macro,
for example:X
Work
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 ssSetNum
macro.
If an S-function defers specifying the length of the work vectors
in X
WorkmdlInitializeSizes
, 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
.
In mdlStart
, assign values to the
work vectors that are initialized only at the start of the simulation.
Use the ssGet
macro
to retrieve a pointer to each work vector and use the pointer to initialize
the work vector values. Alternatively, use the X
WorkssGet
to
assign values to particular elements of the work vector.X
WorkValues
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.
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.
In mdlOutputs
, mdlUpdate
,
etc., use the ssGet
macro
to retrieve a pointer to the work vector and use the pointer to access
or update the work vector values.X
Work
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).
Macro | Description |
---|---|
ssSetNumRWork | Specify the width of the real work vector. |
ssGetNumRWork | Query the width of the real work vector. |
ssSetNumIWork | Specify the width of the integer work vector. |
ssGetNumIWork | Query the width of the integer work vector. |
ssSetNumPWork | Specify the width of the pointer work vector. |
ssGetNumPWork | Query the width of the pointer work vector. |
ssSetNumModes | Specify the width of the mode work vector. |
ssGetNumModes | Query the width of the mode work vector. |
ssGetIWork | Get a pointer to the integer work vector. |
ssGetIWorkValue | Get an element of the integer work vector. |
ssGetModeVector | Get a pointer to the mode work vector. |
ssGetModeVectorValue | Get an element of the mode work vector. |
ssGetPWork | Get a pointer to the pointer work vector. |
ssGetPworkValue | Get one element from the pointer work vector. |
ssGetRWork | Get a pointer to the floating-point work vector. |
ssGetRWorkValue | Get an element of the floating-point work vector. |
ssSetIWorkValue | Set the value of one element of the integer work vector. |
ssSetModeVectorValue | Set the value of one element of the mode work vector. |
ssSetPWorkValue | Set the value of one element of the pointer work vector. |
ssSetRWorkValue | Set the value of one element of the floating-point work vector. |
The following sections provide examples of the four types of elementary work vectors.
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.
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.
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]); } }