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