Main Content

Specify S-Function Sample Times

About Sample Times

You can specify the sample-time behavior of your S-functions in mdlInitializeSampleTimes. Your S-function can inherit its rates from the blocks that drive it or define its own rates.

You can specify your S-function rates (i.e., sample times) as

  • Block-based sample times

  • Port-based sample times

  • Hybrid block-based and port-based sample times

With block-based sample times, the S-function specifies a set of operating rates for the block as a whole during the initialization phase of the simulation. With port-based sample times, the S-function specifies a sample time for each input and output port individually during initialization. During the simulation phase, with block-based sample times, the S-function processes all inputs and outputs each time a sample hit occurs for the block. By contrast, with port-based sample times, the block processes a particular port only when a sample hit occurs for that port.

For example, consider two sample rates, 0.5 and 0.25 seconds, respectively:

  • In the block-based method, selecting 0.5 and 0.25 directs the block to execute inputs and outputs at 0.25 second increments.

  • In the port-based method, setting the input port to 0.5 and the output port to 0.25 causes the block to process inputs at 2 Hz and outputs at 4 Hz.

You should use port-based sample times if your application requires unequal sample rates for input and output execution or if you do not want the overhead associated with running input and output ports at the highest sample rate of your block.

In some applications, an S-Function block might need to operate internally at one or more sample rates while inputting or outputting signals at other rates. The hybrid block- and port-based method of specifying sample rates allows you to create such blocks.

In typical applications, you specify only one block-based sample time. Advanced S-functions might require the specification of port-based or multiple block sample times.

Block-Based Sample Times

C MEX S-functions specify block-based sample time information in

The next two sections discuss how to specify block-based sample times for C MEX S-functions. A third section presents a simple example that shows how to specify sample times in mdlInitializeSampleTimes. For a detailed example, see mixedm.c.

Specifying the Number of Sample Times in mdlInitializeSizes

To configure your S-function for block-based sample times, use

ssSetNumSampleTimes(S,numSampleTimes);

where numSampleTimes > 0. This tells the Simulink® engine that your S-function has block-based sample times. the engine calls mdlInitializeSampleTimes, which in turn sets the sample times.

Setting Sample Times and Specifying Function Calls in mdlInitializeSampleTimes

mdlInitializeSampleTimes specifies two pieces of execution information:

  • Sample and offset times — In mdlInitializeSampleTimes, you must specify the sampling period and offset for each sample time using ssSetSampleTime and ssSetOffsetTime. If applicable, you can calculate the appropriate sampling period and offset prior to setting them, for example, by computing the best sample time for the block based on the S-function dialog parameters obtained using ssGetSFcnParam.

  • Function calls — In mdlInitializeSampleTimes, use ssSetCallSystemOutput to specify the output elements that are performing function calls. Seesfun_fcncall.c for an example and Implement Function-Call Subsystems with S-Functions for an explanation of this S-function.

You specify the sample times as pairs [sample_time, offset_time], using these macros

ssSetSampleTime(S, sampleTimePairIndex, sample_time)
ssSetOffsetTime(S, offsetTimePairIndex, offset_time)

where sampleTimePairIndex and offsetTimePairIndex starts at 0.

The valid sample time pairs are (uppercase values are macros defined in simstruc.h):

[CONTINUOUS_SAMPLE_TIME,  0.0                       ]
[CONTINUOUS_SAMPLE_TIME,  FIXED_IN_MINOR_STEP_OFFSET]
[discrete_sample_period,  offset                    ]
[VARIABLE_SAMPLE_TIME  ,  0.0                       ]

Alternatively, you can specify that the sample time is inherited from the driving block, in which case the S-function can have only one sample time pair,

[INHERITED_SAMPLE_TIME,  0.0                       ]

or

[INHERITED_SAMPLE_TIME,  FIXED_IN_MINOR_STEP_OFFSET]

Note

If your S-function inherits its sample time, you should specify whether it is safe to use the S-function in a referenced model, i.e., a model referenced by another model. See Specifying Model Reference Sample Time Inheritance for more information.

The following guidelines might help in specifying sample times:

  • A continuous function that changes during minor integration steps should register the [CONTINUOUS_SAMPLE_TIME, 0.0] sample time.

  • A continuous function that does not change during minor integration steps should register the [CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET] sample time.

  • A discrete function that changes at a specified rate should register the discrete sample time pair

    [discrete_sample_period, offset]
    

    where

    discrete_sample_period > 0.0
    

    and

    0.0 <= offset < discrete_sample_period
    
  • A discrete function that changes at a variable rate should register the variable-step discrete [VARIABLE_SAMPLE_TIME, 0.0] sample time. In C MEX S-functions, the mdlGetTimeOfNextVarHit function is called to get the time of the next sample hit for the variable-step discrete task. The VARIABLE_SAMPLE_TIME can be used with variable-step solvers only.

If your function has no intrinsic sample time, you must indicate that it is inherited according to the following guidelines:

  • A function that changes as its input changes, even during minor integration steps, should register the [INHERITED_SAMPLE_TIME, 0.0] sample time.

  • A function that changes as its input changes, but doesn't change during minor integration steps (meaning, is held during minor steps), should register the [INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET] sample time.

To check for a sample hit during execution (in mdlOutputs or mdlUpdate), use the ssIsSampleHit or ssIsContinuousTask macro. For example, use the following code fragment to check for a continuous sample hit:

if (ssIsContinuousTask(S,tid)) {
}

To determine whether the third (discrete) task has a hit, use the following code fragment:

if (ssIsSampleHit(S,2,tid) {
}

The Simulink engine always assigns an index of 0 to the continuous sample rate, if it exists, however you get incorrect results if you use ssIsSampleHit(S,0,tid).

Example: mdlInitializeSampleTimes

This example specifies that there are two discrete sample times with periods of 0.01 and 0.5 seconds.

static void mdlInitializeSampleTimes(SimStruct *S)
{
  ssSetSampleTime(S, 0, 0.01);
  ssSetOffsetTime(S, 0, 0.0);
  ssSetSampleTime(S, 1, 0.5);
  ssSetOffsetTime(S, 1, 0.0);
} /* End of mdlInitializeSampleTimes. */

Specifying Port-Based Sample Times

You cannot use port-based sample times with S-functions that have neither input ports nor output ports. If an S-function uses port-based sample times and has no ports, the S-function produces errors when the Simulink model is updated or run. If the number of input or output ports on an S-function is variable, extra protection should be added into the S-function to ensure the total number of ports does not go to zero.

To use port-based sample times in your C MEX S-function, you must specify the number of sample times as port-based in the S-function mdlInitializeSizes method:

ssSetNumSampleTimes(S, PORT_BASED_SAMPLE_TIMES)

You must also specify the sample time of each input and output port in the S-function mdlInitializeSizes method, using the following macros

ssSetInputPortSampleTime(S, idx, period)
ssSetInputPortOffsetTime(S, idx, offset)
ssSetOutputPortSampleTime(S, idx, period)
ssSetOutputPortOffsetTime(S, idx, offset)

The call to ssSetNumSampleTimes can be placed before or after the port-based sample times are actually specified in mdlInitializeSizes. However, if ssSetNumSampleTimes does not configure the S-function to use port-based sample times, any sample times set on the ports will be ignored.

For any given port, you can specify

Specifying Inherited Sample Time for a Port

To specify that a port's sample time is inherited in a C MEX S-function, the mdlInitializeSizes method should set its period to -1 and its offset to 0. For example, the following code specifies inherited sample time for a C MEX S-function first input port:

ssSetInputPortSampleTime(S, 0, -1);
ssSetInputPortOffsetTime(S, 0, 0);

When you specify port-based sample times, the Simulink engine calls mdlSetInputPortSampleTime and mdlSetOutputPortSampleTime to determine the rates of inherited signals.

Once all rates have been determined, the engine calls mdlInitializeSampleTimes. Even though there is no need to initialize port-based sample times at this point, the engine invokes this method to give your S-function an opportunity to configure function-call connections. Your S-function must thus provide an implementation for this method regardless of whether it uses port-based sample times or function-call connections. Although you can provide an empty implementation, you might want to use it to check the appropriateness of the sample times that the block inherited during sample time propagation. Use ssGetInputPortSampleTime and ssGetOutputPortSampleTime in mdlInitializeSampleTimes to obtain the values of the inherited sample times. For example, the following code in mdlInitializeSampleTimes checks if the S-function first input inherited a continuous sample time.

if (!ssGetInputPortSampleTime(S,0)) {
    ssSetErrorStatus(S,"Cannot inherit a continuous sample time.")
};

Note

If you specify that your S-function ports inherit their sample time, you should also specify whether it is safe to use the S-function in a referenced model, i.e., a model referenced by another model. See Specifying Model Reference Sample Time Inheritance for more information.

If you write TLC code to generate inlined code from an S-function, and if the TLC code contains an Outputs function, you must modify the TLC code if these conditions are true:

  • The output port has a constant value. It uses or inherits a sample time of Inf.

  • The S-function is a multirate S-function or uses port-based sample times.

In this case, the TLC code must generate code for the constant-valued output port by using the function OutputsForTID instead of the function Outputs. For more information, see Specifying Constant Sample Time (Inf) for a Port.

To prevent ports from inheriting a sample time of Inf, set the option SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME in the S-function code. In this case, you can use the TLC function Outputs to generate code for constant-valued output ports.

Specifying Constant Sample Time (Inf) for a Port

If your S-function uses port-based sample times, it can set a sample time of Inf on any of its ports. A port-based sample time of Inf means that the signal entering or leaving the port stays constant.

To make a port output a constant value, the S-function must:

  • Use ssSetOptions in its mdlInitializeSizes method to add support for a sample time of Inf:

    ssSetOptions(S,SS_OPTION_ALLOW_CONSTANT_PORT_SAMPLE_TIME);
    

    Note

    This option causes the S-function’s ports to support a sample time of Inf, including ports that inherit their sample times from other blocks. If any S-function ports that inherit sample time cannot have a sample time of Inf, an error occurs. Set sample times for these ports using the mdlSetInputPortSampleTime and mdlSetOutputPortSampleTime methods.

  • Set the port's sample time to Inf and its offset to 0, e.g.,

    ssSetInputPortSampleTime(S,0,mxGetInf());
    ssSetInputPortOffsetTime(S,0,0);
    
  • Check in mdlOutputs whether the method's tid argument equals CONSTANT_TID and if so, set the value of the port's output if it is an output port.

To see an example of how to create ports which output a constant value, see sfun_port_constant.c, the source file for the sfcndemo_port_constant example.

If you write TLC code to generate inlined code from an S-function, and if the TLC code contains an Outputs function, modify the TLC code if all these conditions are true:

  • The output port has a constant value. It uses or inherits a sample time of Inf.

  • The S-function is a multirate S-function or uses port-based sample times.

In this case, the TLC code must generate code for the constant-valued output port by using the function OutputsForTID instead of the function Outputs. The function OutputsForTID generates output code for the constant-valued component of the S-function. If you configure a model to generate multitasking code, OutputsForTID also generates output code for the periodic components of the S-function.

For example, view the TLC file sfun_port_constant.tlc for the C S-function sfun_port_constant.c in the model sfcndemo_port_constant. In the model, the input of the block S-Function2 has a constant value throughout the simulation. In the S-function code, the first output port inherits the sample time of the input port, so the output port also has a constant value. The S-function code directly specifies a constant value for the second output port.

In the TLC code, if the port has a constant value, the function Outputs does not generate code for the first output port. The function does not generate code for the second output port under any circumstances because the port always has a constant value.

For this S-function, OutputsForTID generates code for output ports that have a constant value. The code generator invokes the function OutputsForTID, and sets the argument tid to the task identifier that corresponds to constant values. Only if the task identifier of an output port corresponds to constant values does ,OutputsForTID then generate code for the port.

Configuring Port-Based Sample Times for Use in Triggered Subsystems

To use a C MEX S-function in a triggered subsystem, your port-based sample time S-function must perform the following tasks.

  • Use ssSetOptions in the mdlInitializeSizes method to indicate the S-function can run in a triggered subsystem:

    ssSetOptions(S, 
    SS_OPTION_ALLOW_PORT_SAMPLE_TIME_IN_TRIGSS);
    
  • Set all of its ports to have either inherited (-1) or constant sample time (Inf) in its mdlInitializeSizes method.

  • Handle inheritance of a triggered sample time in mdlSetInputPortSampleTime and mdlSetOutputPortSampleTime methods as follows.

    Since the S-function ports inherit their sample times, the Simulink engine invokes either mdlSetInputPortSampleTime or mdlSetOutputPortSampleTime during sample time propagation. The macro ssSampleAndOffsetAreTriggered can be used in these methods to determine if the S-function resides in a triggered subsystem. If the S-function does reside in a triggered subsystem, whichever method is called must set the sample time and offset of the port for which it is called to INHERITED_SAMPLE_TIME (-1).

    Setting a port's sample time and offset both to INHERITED_SAMPLE_TIME indicates that the sample time of the port is triggered, i.e., it produces an output or accepts an input only when the subsystem in which it resides is triggered. The method must then also set the sample times and offsets of all of the other S-function input and output ports to have either triggered or constant sample time (Inf), whichever is appropriate, e.g.,

    static void mdlSetInputPortSampleTime(SimStruct *S,
                                          int_T portIdx,
                                          real_T sampleTime
                                          real_T offsetTime)
    {
        /* If the S-function resides in a triggered subsystem,
           the sample time and offset passed to this method
           are both equal to INHERITED_SAMPLE_TIME. Therefore,
           if triggered, the following lines set the sample time
           and offset of the input port to INHERITED_SAMPLE_TIME.*/
    
        ssSetInputPortSampleTime(S, portIdx, sampleTime);
        ssSetInputPortOffsetTime(S, portIdx, offsetTime);
        
        /* If triggered, set the output port to inherited, as well */
    
        if (ssSampleAndOffsetAreTriggered(sampleTime,offsetTime)) {
            ssSetOutputPortSampleTime(S, 0, INHERITED_SAMPLE_TIME);
            ssSetOutputPortOffsetTime(S, 0, INHERITED_SAMPLE_TIME);
    
            /* Note, if there are additional input and output ports
               on this S-function, they should be set to either
               inherited or constant at this point, as well. */
        }
    }

    There is no way for an S-function residing in a triggered subsystem to predict whether the Simulink engine will call mdlSetInputPortSampleTime or mdlSetOutputPortSampleTime to set its port sample times. For this reason, both methods must be able to set the sample times of all ports correctly so the engine has to call only one of the methods a single time.

  • In mdlUpdate and mdlOutputs, use ssGetPortBasedSampleTimeBlockIsTriggered to check whether the S-function resides in a triggered subsystem and if so, use appropriate algorithms for computing its states and outputs.

See sfun_port_triggered.c, the source file for the sfcndemo_port_triggered example model, for an example of how to create an S-function that can be used in a triggered subsystem.

Hybrid Block-Based and Port-Based Sample Times

The hybrid method of assigning sample times combines the block-based and port-based methods. You first specify, in mdlInitializeSizes, the total number of rates at which your block operates, including both block and input and output rates, using ssSetNumSampleTimes.

You then set the SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED option, using ssSetOptions, to tell the simulation engine that you are going to use the port-based method to specify the rates of the input and output ports individually. Next, as in the block-based method, you specify the periods and offsets of all of the block's rates, both internal and external, using

ssSetSampleTime
ssSetOffsetTime

Finally, as in the port-based method, you specify the rates for each port, using

ssSetInputPortSampleTime(S, idx, period)
ssSetInputPortOffsetTime(S, idx, offset)
ssSetOutputPortSampleTime(S, idx, period)
ssSetOutputPortOffsetTime(S, idx, offset)

Note that each of the assigned port rates must be the same as one of the previously declared block rates. For an example S-function, see mixedm.c.

Note

If you use the SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED option, your S-function cannot inherit sample times. Instead, you must specify the rate at which each input and output port runs.

Multirate S-Function Blocks

In a multirate S-Function block, you can encapsulate the code that defines each behavior in the mdlOutputs and mdlUpdate functions with a statement that determines whether a sample hit has occurred. In a C MEX S-function, the ssIsSampleHit macro determines whether the current time is a sample hit for a specified sample time. The macro has this syntax:

ssIsSampleHit(S, st_index, tid)

where S is the SimStruct, st_index identifies a specific sample time index, and tid is the task ID (tid is an argument to the mdlOutputs and mdlUpdate functions).

For example, these statements in a C MEX S-function specify three sample times: one for continuous behavior and two for discrete behavior.

ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetSampleTime(S, 1, 0.75);
ssSetSampleTime(S, 2, 1.0);

In the mdlUpdate function, the following statement encapsulates the code that defines the behavior for the sample time of 0.75 second.

if (ssIsSampleHit(S, 1, tid)) {
}

The second argument, 1, corresponds to the second sample time, 0.75 second.

Use the following lines to encapsulate the code that defines the behavior for the continuous sample hit:

if (ssIsContinuousTask(S,tid)) {
}

Example of Defining a Sample Time for a Continuous Block

This example defines a sample time for a block that is continuous.

/* Initialize the sample time and offset. */
static void mdlInitializeSampleTimes(SimStruct *S)
{
  ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
  ssSetOffsetTime(S, 0, 0.0);
}

You must add this statement to the mdlInitializeSizes function.

ssSetNumSampleTimes(S, 1);

Example of Defining a Sample Time for a Hybrid Block

This example defines sample times for a hybrid S-Function block.

/* Initialize the sample time and offset. */
static void mdlInitializeSampleTimes(SimStruct *S)
{
  /* Continuous state sample time and offset. */
  ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
  ssSetOffsetTime(S, 0, 0.0);

  /* Discrete state sample time and offset. */
  ssSetSampleTime(S, 1, 0.1);
  ssSetOffsetTime(S, 1, 0.025);
}

In the second sample time, the offset causes the Simulink engine to call the mdlUpdate function at these times: 0.025 second, 0.125 second, 0.225 second, and so on, in increments of 0.1 second.

The following statement, which indicates how many sample times are defined, also appears in the mdlInitializeSizes function.

ssSetNumSampleTimes(S, 2);

Multirate S-Functions and Sample Time Hit Calculations

For fixed-step solvers, Simulink uses integer arithmetic, rather than floating-point arithmetic, to calculate the sample time hits. Consequently, task times are integer multiples of their corresponding sample time periods.

This calculation method becomes important if you consider performing Boolean logic based upon task times in multirate S-functions. For example, consider an S-function that has two sample times. The fact that (ssIsSampleHit(S, idx1) == true && ssIsSampleHit(S,idx2) == true, does not guarantee that ssGetTaskTime(S, idx1) == ssGetTaskTime(S, idx2).

Synchronizing Multirate S-Function Blocks

If tasks running at different rates need to share data, you must ensure that data generated by one task is valid when accessed by another task running at a different rate. You can use the ssIsSpecialSampleHit macro in the mdlUpdate or mdlOutputs routine of a multirate S-function to ensure that the shared data is valid. This macro returns true if a sample hit has occurred at one rate and a sample hit has also occurred at another rate in the same time step. It thus permits a higher rate task to provide data needed by a slower rate task at a rate the slower task can accommodate. When using the ssIsSpecialSampleHit macro, the slower sample time must be an integer multiple of the faster sample time.

Suppose, for example, that your model has an input port operating at one rate (with a sample time index of 0) and an output port operating at a slower rate (with a sample time index of 1). Further, suppose that you want the output port to output the value currently on the input. The following example illustrates usage of this macro.

if (ssIsSampleHit(S, 0, tid) {
  if (ssIsSpecialSampleHit(S, 0, 1, tid) {
     /* Transfer input to output memory. */
     ...
  }
}

if (ssIsSampleHit(S, 1, tid) {
   /* Emit output. */
   ...
}

In this example, the first block runs when a sample hit occurs at the input rate. If the hit also occurs at the output rate, the block transfers the input to the output memory. The second block runs when a sample hit occurs at the output rate. It transfers the output in its memory area to the block's output.

Note that higher-rate tasks always run before slower-rate tasks. Thus, the input task in the preceding example always runs before the output task, ensuring that valid data is always present at the output port.

Specifying Model Reference Sample Time Inheritance

If your C MEX S-function inherits its sample times from the blocks that drive it, your S-function should specify whether referenced models containing your S-function can inherit sample times from their parent model. If the S-function output does not depend on its inherited sample time, use the ssSetModelReferenceSampleTimeInheritanceRule macro to set the S-function sample time inheritance rule to USE_DEFAULT_FOR_DISCRETE_INHERITANCE. Otherwise, set the rule to DISALLOW_SAMPLE_TIME_INHERITANCE to disallow sample-time inheritance for referenced models that include S-functions whose outputs depend on their inherited sample time and thereby avoid inadvertent simulation errors.

Note

If your S-function does not set this flag, the Simulink engine assumes that it does not preclude a referenced model containing it from inheriting a sample time. However, the engine optionally warns you that the referenced model contains S-functions that do not specify a sample-time inheritance rule (see Blocks Whose Outputs Depend on Inherited Sample Time).

If you are uncertain whether an existing S-function output depends on its inherited sample time, check whether it invokes any of the following C macros:

  • ssGetSampleTime

  • ssGetInputPortSampleTime

  • ssGetOutputPortSampleTime

  • ssGetInputPortOffsetTime

  • ssGetOutputPortOffsetTime

  • ssGetInputPortSampleTimeIndex

  • ssGetOutputPortSampleTimeIndex

or TLC functions:

  • LibBlockSampleTime

  • CompiledModel.SampleTime

  • LibBlockInputSignalSampleTime

  • LibBlockInputSignalOffsetTime

  • LibBlockOutputSignalSampleTime

  • LibBlockOutputSignalOffsetTime

If your S-function does not invoke any of these macros or functions, its output does not depend on its inherited sample time and hence it is safe to use in referenced models that inherit their sample time.

Sample-Time Inheritance Rule Example

As an example of an S-function that precludes a referenced model from inheriting its sample time, consider an S-function that has the following mdlOutputs method:

static void mdlOutputs(SimStruct *S, int_T tid) {
    const real_T *u = (const real_T*) 
    ssGetInputPortSignal(S,0);
    real_T       *y = ssGetOutputPortSignal(S,0);
    y[0] = ssGetSampleTime(S,tid) * u[0];
}

The output of this S-function is its inherited sample time, hence its output depends on its inherited sample time, and hence it is unsafe to use in a referenced model. For this reason, this S-function should specify its model reference inheritance rule as follows:

ssSetModelReferenceSampleTimeInheritanceRule
(S, DISALLOW_SAMPLE_TIME_INHERITANCE);

See Also

| |

Related Topics