DWork Vector Examples
General DWork Vector
The S-function sfun_rtwdwork.c
shows
how to configure a DWork vector for use with the Simulink®
Coder™ product.
The Simulink model sfcndemo_sfun_rtwdwork
uses
this S-function to implement a simple accumulator.
The following portion of the mdlInitializeSizes
method
initializes the DWork vector and all code generation properties associated
with it.
ssSetNumDWork(S, 1); ssSetDWorkWidth(S, 0, 1); ssSetDWorkDataType(S, 0, SS_DOUBLE); /* Identifier; free any old setting and update */ id = ssGetDWorkRTWIdentifier(S, 0); if (id != NULL) { free(id); } id = malloc(80); mxGetString(ID_PARAM(S), id, 80); ssSetDWorkRTWIdentifier(S, 0, id); /* Type Qualifier; free any old setting and update */ tq = ssGetDWorkRTWTypeQualifier(S, 0); if (tq != NULL) { free(tq); } tq = malloc(80); mxGetString(TQ_PARAM(S), tq, 80); ssSetDWorkRTWTypeQualifier(S, 0, tq); /* Storage class */ sc = ((int_T) *((real_T*) mxGetPr(SC_PARAM(S)))) - 1; ssSetDWorkRTWStorageClass(S, 0, sc);
The S-function initializes the DWork vector in mdlInitializeConditions
.
#define MDL_INITIALIZE_CONDITIONS /* Function: mdlInitializeConditions ============================ * Abstract: * Initialize both continuous states to zero */ static void mdlInitializeConditions(SimStruct *S) { real_T *x = (real_T*) ssGetDWork(S,0); /* Initialize the dwork to 0 */ x[0] = 0.0; }
The mdlOutputs
method assigns the DWork vector
value to the S-function output.
/* Function: mdlOutputs ======================================== * Abstract: * y = x */ static void mdlOutputs(SimStruct *S, int_T tid) { real_T *y = ssGetOutputPortRealSignal(S,0); real_T *x = (real_T*) ssGetDWork(S,0); /* Return the current state as the output */ y[0] = x[0]; }
The mdlUpdate
method increments the DWork
value by the input.
#define MDL_UPDATE /* Function: mdlUpdate ============================================ * Abstract: * This function is called once for every major integration * time step. Discrete states are typically updated here, but * this function is useful for performing any tasks that should * only take place once per integration step. */ static void mdlUpdate(SimStruct *S, int_T tid) { real_T *x = (real_T*) ssGetDWork(S,0); InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); /* * Increment the state by the input * U is defined as U(element) (*uPtrs[element]) */ x[0] += U(0); }
DWork Scratch Vector
The following example uses a scratch DWork vector to store a
static variable value. The mdlInitializeSizes
method
configures the width and data type of the DWork vector. The ssSetDWorkUsageType
macro
then specifies the DWork vector is a scratch vector.
ssSetNumDWork(S, 1); ssSetDWorkWidth(S, 0, 1); ssSetDWorkDataType(S, 0, SS_DOUBLE); ssSetDWorkUsageType(S,0, SS_DWORK_USED_AS_SCRATCH);
The remainder of the S-function uses the scratch DWork vector
exactly as it would any other type of DWork vector. The InitializeConditions
method
sets the initial value and the mdlOutputs
method
uses the value stored in the DWork vector.
#define MDL_INITIALIZE_CONDITIONS /* Function: mdlInitializeConditions ================================ */ static void mdlInitializeConditions(SimStruct *S) { real_T *x = (real_T*) ssGetDWork(S,0); /* Initialize the dwork to 0 */ x[0] = 0.0; } /* Function: mdlOutputs ============================================= */ static void mdlOutputs(SimStruct *S, int_T tid) { real_T *y = ssGetOutputPortRealSignal(S,0); real_T *x1 = (real_T*) ssGetDWork(S,1); x[0] = 2000; y[0] = x[0] * 2; }
If you have Simulink
Coder, the Simulink
Coder software
handles scratch DWork differently from other DWork vectors when generating
code for inlined S-function. To inline the S-function, create the
following Target Language Compiler (TLC) file to describe the mdlOutputs
method.
%implements sfun_dscratch "C" %% Function: Outputs ========================================================== %% /* dscratch Block: %<Name> */ %<LibBlockDWork(DWork[0], "", "", 0)> = 2000.0; %<LibBlockOutputSignal(0,"","",0)> = %<LibBlockDWork(DWork[0],"","", 0)> * 2;
When the Simulink Coder software generates code for the model, it inlines the S-function and declares the second DWork vector as a local scratch vector. For example, the model outputs function contains the following lines:
/* local scratch DWork variables */ real_T SFunction_DWORK1; SFunction_DWORK1 = 2000.0;
If the S-function used a general DWork vector instead of a scratch DWork vector, generating code with the same TLC file would have resulted in the DWork vector being included in the data structure, as follows:
sfcndemo_dscratch_DWork.SFunction_DWORK1 = 2000.0;
DState Work Vector
This example rewrites the S-function example dsfunc.c
to
use a DState vector instead of an explicit discrete state vector.
The mdlInitializeSizes
macro initializes the number
of discrete states as zero and, instead, initializes one DWork vector.
The mdlInitializeSizes
method then configures
the DWork vector as a DState vector using a call to ssSetDWorkUsedAsDState
. This is equivalent
to calling the ssSetDWorkUsageType
macro with the
value SS_DWORK_USED_AS_DSTATE
. The mdlInitializeSizes
method
sets the width and data type of the DState vector and gives the state
a name using ssSetDWorkName
.
Note
DWork vectors configured as DState vectors must be assigned
a name for the Simulink engine to register the vector as discrete
states. The function Simulink.BlockDiagram.getInitialStates(
returns
the assigned name in the mdl
)label
field for the initial
states.
static void mdlInitializeSizes(SimStruct *S) { ssSetNumSFcnParams(S, 0); /* Number of expected parameters */ if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) { return; /* Parameter mismatch reported by the Simulink engine */ } ssSetNumContStates(S, 0); ssSetNumDiscStates(S, 0); if (!ssSetNumInputPorts(S, 1)) return; ssSetInputPortWidth(S, 0, 2); ssSetInputPortDirectFeedThrough(S, 0, 1); if (!ssSetNumOutputPorts(S, 1)) return; ssSetOutputPortWidth(S, 0, 2); ssSetNumSampleTimes(S, 1); ssSetNumRWork(S, 0); ssSetNumIWork(S, 0); ssSetNumPWork(S, 0); ssSetNumModes(S, 0); ssSetNumNonsampledZCs(S, 0); ssSetNumDWork(S, 1); ssSetDWorkUsedAsDState(S, 0, SS_DWORK_USED_AS_DSTATE); ssSetDWorkWidth(S, 0, 2); ssSetDWorkDataType(S, 0, SS_DOUBLE); ssSetDWorkName(S, 0, "SfunStates"); ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE); }
The mdlInitializeConditions
method initializes
the DState vector values using the pointer returned by ssGetDWork
.
#define MDL_INITIALIZE_CONDITIONS /* Function: mdlInitializeConditions =============================== * Abstract: * Initialize both discrete states to one. */ static void mdlInitializeConditions(SimStruct *S) { real_T *x0 = (real_T*) ssGetDWork(S, 0); int_T lp; for (lp=0;lp<2;lp++) { *x0++=1.0; } }
The mdlOutputs
method then uses the values
stored in the DState vector to compute the output of the discrete
state-space equation.
/* Function: mdlOutputs ======================================== * Abstract: * y = Cx + Du */ static void mdlOutputs(SimStruct *S, int_T tid) { real_T *y = ssGetOutputPortRealSignal(S,0); real_T *x = (real_T*) ssGetDWork(S, 0); InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); UNUSED_ARG(tid); /* not used in single tasking mode */ /* y=Cx+Du */ y[0]=C[0][0]*x[0]+C[0][1]*x[1]+D[0][0]*U(0)+D[0][1]*U(1); y[1]=C[1][0]*x[0]+C[1][1]*x[1]+D[1][0]*U(0)+D[1][1]*U(1); }
Finally, the mdlUpdate
method updates the
DState vector with new values for the discrete states.
#define MDL_UPDATE /* Function: mdlUpdate ============================================ * Abstract: * xdot = Ax + Bu */ static void mdlUpdate(SimStruct *S, int_T tid) { real_T tempX[2] = {0.0, 0.0}; real_T *x = (real_T*) ssGetDWork(S, 0); InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); UNUSED_ARG(tid); /* not used in single tasking mode */ /* xdot=Ax+Bu */ tempX[0]=A[0][0]*x[0]+A[0][1]*x[1]+B[0][0]*U(0)+B[0][1]*U(1); tempX[1]=A[1][0]*x[0]+A[1][1]*x[1]+B[1][0]*U(0)+B[1][1]*U(1); x[0]=tempX[0]; x[1]=tempX[1]; }
DWork Mode Vector
This example rewrites the S-function sfun_zc.c
to
use a DWork mode vector instead of an explicit mode work vector (see Elementary Work Vectors for more information
on mode work vectors). This S-function implements an absolute value
block.
The mdlInitializeSizes
method sets the number
of DWork vectors and zero-crossing vectors (see Zero Crossings) to DYNAMICALLY_SIZED
.
The DYNAMICALLY_SIZED
setting allows the Simulink engine
to defer specifying the work vector sizes until it knows the dimensions
of the input, allowing the S-function to support an input with an
arbitrary width.
static void mdlInitializeSizes(SimStruct *S) { ssSetNumSFcnParams(S, 0); if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) { return; /* Parameter mismatch reported by the Simulink engine */ } ssSetNumContStates( S, 0); ssSetNumDiscStates( S, 0); if (!ssSetNumInputPorts(S, 1)) return; ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED); ssSetInputPortDirectFeedThrough(S, 0, 1); if (!ssSetNumOutputPorts(S,1)) return; ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED); ssSetNumSampleTimes(S, 1); ssSetNumRWork(S, 0); ssSetNumIWork(S, 0); ssSetNumPWork(S, 0); ssSetNumDWork(S, 1); ssSetNumModes(S, 0); /* Initializes the zero-crossing and DWork vectors */ ssSetDWorkWidth(S,0,DYNAMICALLY_SIZED); ssSetNumNonsampledZCs(S, DYNAMICALLY_SIZED); /* Take care when specifying exception free code - see sfuntmpl_doc.c */ ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE); }
The Simulink engine initializes the number of zero-crossing
vectors and DWork vectors to the number of elements in the signal
coming into the first S-function input port. The engine then calls
the mdlSetWorkWidths
method, which uses ssGetNumDWork
to
determine how many DWork vectors were initialized and then sets the
properties for each DWork vector.
#define MDL_SET_WORK_WIDTHS static void mdlSetWorkWidths(SimStruct *S) { int_T numdw = ssGetNumDWork(S); int_T i; for (i = 0; i < numdw; i++) { ssSetDWorkUsageType(S, i, SS_DWORK_USED_AS_MODE); ssSetDWorkDataType(S, i, SS_BOOLEAN); ssSetDWorkComplexSignal(S, i, COMPLEX_NO); } }
The mdlOutputs
method uses the value stored
in the DWork mode vector to determine if the output signal should
be equal to the input signal or the absolute value of the input signal.
static void mdlOutputs(SimStruct *S, int_T tid) { int_T i; InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); real_T *y = ssGetOutputPortRealSignal(S,0); int_T width = ssGetOutputPortWidth(S,0); boolean_T *mode = ssGetDWork(S,0); UNUSED_ARG(tid); /* not used in single tasking mode */ if (ssIsMajorTimeStep(S)) { for (i = 0; i < width; i++) { mode[i] = (boolean_T)(*uPtrs[i] >= 0.0); } } for (i = 0; i < width; i++) { y[i] = mode[i]? (*uPtrs[i]): -(*uPtrs[i]); } }