| Contents | Index |
The following sections explain and illustrate how the Simulink and Simulink Coder products handle multirate (mixed-rate) models, depending on whether code is being generated for single-tasking or multitasking environments.
Simulink models run at one or more sample times. The Simulink product provides considerable flexibility in building multirate systems, that is, systems with more than one sample time. However, this same flexibility also allows you to construct models for which the code generator cannot generate real-time code for execution in a multitasking environment. To make multirate models operate as expected in real time (that is, to give the right answers), you sometimes must modify your model or instruct the Simulink engine to modify the model for you. In general, the modifications involve placing Rate Transition blocks between blocks that have unequal sample times. The following sections discuss issues you must address to use a multirate model in a multitasking environment. For a comprehensive discussion of sample times, including rate transitions, see Working with Sample Times in the Simulink User's Guide.
There are two execution modes for a fixed-step Simulink model: single-tasking and multitasking. These modes are available only for fixed-step solvers. To select an execution mode, use the Tasking mode for periodic sample times menu on the Solver pane of the Configuration Parameters dialog box. Auto mode (the default) applies multitasking execution for a multirate model, and otherwise selects single-tasking execution. You can also select SingleTasking or MultiTasking execution explicitly.
Execution of models in a real-time system can be done with the aid of a real-time operating system, or it can be done on a bare-board target, where the model runs in the context of an interrupt service routine (ISR).
The fact that a system (such as The Open Group UNIX or Microsoft Windows systems) is multitasking does not imply that your program can execute in real time. This is because the program might not preempt other processes when required.
In operating systems (such as PC-DOS) where only one process can exist at any given time, an interrupt service routine (ISR) must perform the steps of saving the processor context, executing the model code, collecting data, and restoring the processor context.
Other operating systems, such as POSIX-compliant ones, provide automatic context switching and task scheduling. This simplifies the operations performed by the ISR. In this case, the ISR simply enables the model execution task, which is normally blocked. The next figure illustrates this difference.

In cases where the continuous part of a model executes at a rate that is different from the discrete part, or a model has blocks with different sample rates, the Simulink engine assigns each block a task identifier (tid) to associate the block with the task that executes at the block's sample rate.
You set sample rates and their constraints on the Solver pane of the Configuration Parameters dialog box. To generate code with the Simulink Coder software, you must select Fixed-step for the solver type. Certain restrictions apply to the sample rates that you can use:
The sample rate of any block must be an integer multiple of the base (that is, the fastest) sample period.
When Periodic sample time constraint is unconstrained, the base sample period is determined by the Fixed step size specified on the Solvers pane of the Configuration parameters dialog box.
When Periodic sample time constraint is Specified, the base rate fixed-step size is the first element of the sample time matrix that you specify in the companion option Sample time properties. The Solver pane from the demo model rtwdemo_mrmtbb shows an example.

Continuous blocks always execute by using an integration algorithm that runs at the base sample rate. The base sample period is the greatest common denominator of all rates in the model only when Periodic sample time constraint is set to Unconstrained and Fixed step size is Auto.
The continuous and discrete parts of the model can execute at different rates only if the discrete part is executed at the same or a slower rate than the continuous part and is an integer multiple of the base sample rate.
When periodic tasks execute in a multitasking mode, by default the blocks with the fastest sample rates are executed by the task with the highest priority, the next fastest blocks are executed by a task with the next higher priority, and so on. Time available in between the processing of high-priority tasks is used for processing lower priority tasks. This results in efficient program execution.
Where tasks are asynchronous rather than periodic, there may not necessarily be a relationship between sample rates and task priorities; the task with the highest priority need not have the fastest sample rate. You specify asynchronous task priorities using Async Interrupt and Task Sync blocks. You can switch the sense of what priority numbers mean by selecting or deselecting the Solver option Higher priority value indicates higher task priority.
In multitasking environments (that is, under a real-time operating system), you can define separate tasks and assign them priorities. In a bare-board target (that is, no real-time operating system present), you cannot create separate tasks. However, Simulink Coder application modules implement what is effectively a multitasking execution scheme using overlapped interrupts, accompanied by programmatic context switching.
This means an interrupt can occur while another interrupt is currently in progress. When this happens, the current interrupt is preempted, the floating-point unit (FPU) context is saved, and the higher priority interrupt executes its higher priority (that is, faster sample rate) code. Once complete, control is returned to the preempted ISR.
The next figures illustrate how timing of tasks in multirate systems are handled by the Simulink Coder software in multitasking, pseudomultitasking, and single-tasking environments.

The next figure shows how overlapped interrupts are used to implement pseudomultitasking. In this case, Interrupt 0 does not return until after Interrupts 1, 2, and 3.

To use multitasking execution, select Auto (the default) or MultiTasking from the Tasking mode for periodic sample times menu on the Solver pane of the Configuration Parameters dialog box. This menu is active only if you select Fixed-step as the solver type. Auto mode results in a multitasking environment if your model has two or more different sample times. A model with a continuous and a discrete sample time runs in single-tasking mode if the fixed-step size is equal to the discrete sample time.
You can execute model code in a strictly single-tasking manner. While this mode is less efficient with regard to execution speed, in certain situations, it can simplify your model.
In single-tasking mode, the base sample rate must define a time interval that is long enough to allow the execution of all blocks within that interval.
The next figure illustrates the inefficiency inherent in single-tasking execution.

Single-tasking system execution requires a base sample rate that is long enough to execute one step through the entire model.
To use single-tasking execution, select SingleTasking from the Tasking mode for periodic sample times menu on the Solver pane of the Configuration Parameters dialog box. If you select Auto, single-tasking is used in the following cases:
If your model contains one sample time
If your model contains a continuous and a discrete sample time and the fixed step size is equal to the discrete sample time
To generate code that executes as expected in real time, you (or the Simulink engine) might need to identify and handle sample rate transitions within the model. In multitasking mode, by default the Simulink engine flags errors during simulation if the model contains invalid rate transitions, although you can use the Multitask rate transition diagnostic to alter this behavior. A similar diagnostic, called Single task rate transition, exists for single-tasking mode.
To avoid raising rate transition errors, insert Rate Transition blocks between tasks. You can request that the Simulink engine handle rate transitions automatically by inserting hidden Rate Transition blocks. See Automatic Rate Transition for an explanation of this option.
To understand such problems, first consider how Simulink simulations differ from real-time programs.
Before the Simulink engine simulates a model, it orders all the blocks based upon their topological dependencies. This includes expanding virtual subsystems into the individual blocks they contain and flattening the entire model into a single list. Once this step is complete, each block is executed in order.
The key to this process is the ordering of blocks. Any block whose output is directly dependent on its input (that is, any block with direct feedthrough) cannot execute until the block driving its input executes.
Some blocks set their outputs based on values acquired in a previous time step or from initial conditions specified as a block parameter. The output of such a block is determined by a value stored in memory, which can be updated independently of its input. During simulation, computations are performed prior to advancing the variable corresponding to time. In essence, this results in all computations occurring instantaneously (that is, no computational delay).
A real-time program differs from a Simulink simulation in that the program must execute the model code synchronously with real time. Every calculation results in some computational delay. This means the sample intervals cannot be shortened or lengthened (as they can be in a Simulink simulation), which leads to less efficient execution.
Consider the following timing figure.

Note the processing inefficiency in the sample interval t1. That interval cannot be compressed to increase execution speed because, by definition, sample times are clocked in real time.
You can circumvent this potential inefficiency by using the multitasking mode. The multitasking mode defines tasks with different priorities to execute parts of the model code that have different sample rates.
See Multitasking and Pseudomultitasking Modes for a description of how this works. It is important to understand that section before proceeding here.
Single-tasking programs require longer sample intervals, because all computations must be executed within each clock period. This can result in inefficient use of available CPU time, as shown in the previous figure.
Multitasking mode can improve the efficiency of your program if the model is large and has many blocks executing at each rate.
However, if your model is dominated by a single rate, and only a few blocks execute at a slower rate, multitasking can actually degrade performance. In such a model, the overhead incurred in task switching can be greater than the time required to execute the slower blocks. In this case, it is more efficient to execute all blocks at the dominant rate.
If you have a model that can benefit from multitasking execution, you might need to modify your model by adding Rate Transition blocks (or instruct the Simulink engine to do so) to generate expected results. The next section, Handle Rate Transitions, discusses issues related to rate transition blocks.
Two periodic sample rate transitions can exist within a model:
A faster block driving a slower block
A slower block driving a faster block
The following sections concern models with periodic sample times with zero offset only. Other considerations apply to multirate models that involve asynchronous tasks. For details on how to generate code for asynchronous multitasking, see Handle Asynchronous Events.
In single-tasking systems, there are no issues involving multiple sample rates. In multitasking and pseudomultitasking systems, however, differing sample rates can cause problems by causing blocks to be executed in the wrong order. To prevent possible errors in calculated data, you must control model execution at these transitions. When connecting faster and slower blocks, you or the Simulink engine must add Rate Transition blocks between them. Fast-to-slow transitions are illustrated in the next figure.

Slow-to-fast transitions are illustrated in the next figure.

Note Although the Rate Transition block offers a superset of the capabilities of the Unit Delay block (for slow-to-fast transitions) and the Zero-Order Hold block (for fast-to-slow transitions), you should use the Rate Transition block instead of these blocks. |
Rate Transition blocks deal with issues of data integrity and determinism associated with data transfer between blocks running at different rates.
Data integrity: A problem of data integrity exists when the input to a block changes during the execution of that block. Data integrity problems can be caused by preemption.
Consider the following scenario:
A faster block supplies the input to a slower block.
The slower block reads an input value V1 from the faster block and begins computations using that value.
The computations are preempted by another execution of the faster block, which computes a new output value V2.
A data integrity problem now arises: when the slower block resumes execution, it continues its computations, now using the "new" input value V2.
Such a data transfer is called unprotected. Faster to Slower Transitions in Real Time shows an unprotected data transfer.
In a protected data transfer, the output V1 of the faster block is held until the slower block finishes executing.
Deterministic versus nondeterministic data transfer: In a deterministic data transfer, the timing of the data transfer is completely predictable, as determined by the sample rates of the blocks.
The timing of a nondeterministic data transfer depends on the availability of data, the sample rates of the blocks, and the time at which the receiving block begins to execute relative to the driving block.
You can use the Rate Transition block to protect data transfers in your application and make them deterministic. These characteristics are considered desirable in most applications. However, the Rate Transition block supports flexible options that allow you to compromise data integrity and determinism in favor of lower latency. The next section summarizes these options.
When processing data transfers between tasks, the Simulink Coder software assumes the following:
Data transitions occur between a single reading task and a single writing task.
A read or write of a byte-sized variable is atomic.
When two tasks interact through a data transition, only one of them can preempt the other.
For periodic tasks, the faster rate task has higher priority than the slower rate task; the faster rate task always preempts the slower rate task.
All tasks run on a single processor. Time slicing is not allowed.
Processes do not crash or restart (especially while data is transferred between tasks).
Several parameters of the Rate Transition block are relevant to its use in code generation for real-time execution, as discussed below. For a complete block description, see Rate Transition in the Simulink documentation.
The Rate Transition block handles periodic (fast to slow and slow to fast) and asynchronous transitions. When inserted between two blocks of differing sample rates, the Rate Transition block automatically configures its input and output sample rates for the type of transition; you do not need to specify whether a transition is slow-to-fast or fast-to-slow (low-to-high or high-to-low priorities for asynchronous tasks).
The critical decision you must make in configuring a Rate Transition block is the choice of data transfer mechanism to be used between the two rates. Your choice is dictated by considerations of safety, memory usage, and performance. As the Rate Transition block parameter dialog box in the next figure shows, the data transfer mechanism is controlled by two options.

Ensure data integrity during data transfer: When this option is on, data transferred between rates maintains its integrity (the data transfer is protected). When this option is off, the data might not maintain its integrity (the data transfer is unprotected). By default, Ensure data integrity during data transfer is on.
Ensure deterministic data transfer (maximum delay): This option is supported for periodic tasks with an offset of zero and fast and slow rates that are multiples of each other. Enable this option for protected data transfers (when Ensure data integrity during data transfer is on). When this option is on, the Rate Transition block behaves like a Zero-Order Hold block (for fast to slow transitions) or a Unit Delay block (for slow to fast transitions). The Rate Transition block controls the timing of data transfer in a completely predictable way. When this option is off, the data transfer is nondeterministic. By default, Ensure deterministic data transfer (maximum delay) is on for transitions between periodic rates with an offset of zero; for asynchronous transitions, it cannot be selected.
Thus the Rate Transition block offers three modes of operation with respect to data transfer. In order of level of safety:
Protected/Deterministic (default): This is the safest mode. The drawback of this mode is that it introduces deterministic latency into the system for the case of slow-to-fast periodic rate transitions. For that case, the latency introduced by the Rate Transition block is one sample period of the slower task. For the case of fast-to-slow periodic rate transitions, the Rate Transition block introduces no additional latency.
Protected/NonDeterministic: In this mode, for slow-to-fast periodic rate transitions, data integrity is protected by double-buffering data transferred between rates. For fast-to-slow periodic rate transitions, a semaphore flag is used. The blocks downstream from the Rate Transition block always use the latest available data from the block that drives the Rate Transition block. Maximum latency is less than or equal to one sample period of the faster task.
The drawbacks of this mode are its nondeterministic timing. The advantage of this mode is its low latency.
Unprotected/NonDeterministic: This mode is not recommended for mission-critical applications. The latency of this mode is the same as for Protected/NonDeterministic mode, but memory requirements are reduced since neither double-buffering nor semaphores are required. That is, the Rate Transition block does nothing in this mode other than to pass signals through; it simply exists to notify you that a rate transition exists (and can cause generated code to compute incorrect answers). Selecting this mode, however, generates the least amount of code.
Automatic Rate Transition. The Simulink engine can detect mismatched rate transitions in a multitasking model and automatically insert Rate Transition blocks to handle them. To instruct the engine to do this, select Automatically handle rate transition for data transfer on the Solver pane of the Configuration Parameters dialog box.
The Automatically handle rate transition for data transfer option is off by default. When you select it,
The Simulink engine handles all transitions between periodic sample times and asynchronous tasks.
The Simulink engine inserts "hidden" Rate Transition blocks that are not visible on the block diagram.
The Simulink Coder software generates code for the automatically inserted Rate Transition blocks that is identical to that generated for manually inserted Rate Transition blocks.
Automatically inserted Rate Transition blocks operate in protected mode for periodic tasks and asynchronous tasks, which you cannot alter. For periodic tasks, automatically inserted Rate Transition blocks operate with the level of determinism specified by the Solver pane parameter Deterministic data transfer. (The default setting is Whenever possible, which enables determinism for data transfers between periodic sample-times that are related by an integer multiple; for more information, see Deterministic data transfer in the Simulink reference documentation.) To use other modes, you must insert Rate Transition blocks and set their modes manually.
For example, in the following model SineWave2 has a Sample time of 2, and SineWave3 has a Sample time of 3.

If Automatically handle rate transition for data transfer is on, the Simulink engine inserts an invisible Rate Transition block between each Sine Wave block and the Product block. The inserted blocks have the parameter values required to reconcile the Sine Wave block sample times.
Inserted Rate Transition Block HTML Report.When the Simulink engine has automatically inserted Rate Transition blocks into a model, after code generation the optional HTML code generation report includes a List of inserted blocks that describes the blocks. For example, the following report describes the two Rate Transition blocks that the engine automatically inserts into the previous model.

Only automatically inserted Rate Transition blocks appear in a List of inserted blocks. If no such blocks exist in a model, the HTML code generation report does not include a List of inserted blocks.
Rate Transition Blocks and Continuous Time. The sample time at the output port of a Rate Transition block can only be discrete or fixed in minor time step. This means that when a Rate Transition block inherits continuous sample time from its destination block, it treats the inherited sample time as Fixed in Minor Time Step. Therefore, the output function of the Rate Transition block runs only at major time steps. If the destination block sample time is continuous, Rate Transition block output sample time is the base rate sample time (if solver is fixed-step), or zero-order-hold-continuous sample time (if solver is variable-step).
The next four sections describe cases in which Rate Transition blocks are required for periodic sample rate transitions. The discussion and timing diagrams in these sections are based on the assumption that the Rate Transition block is used in its default (protected/deterministic) mode; that is, the Ensure data integrity during data transfer and Ensure deterministic data transfer (maximum delay) options are both on. These are the settings used for automatically inserted Rate Transition blocks.
In a model where a faster block drives a slower block having direct feedthrough, the outputs of the faster block are always computed first. In simulation intervals where the slower block does not execute, the simulation progresses more rapidly because there are fewer blocks to execute. The next figure illustrates this situation.

A Simulink simulation does not execute in real time, which means that it is not bound by real-time constraints. The simulation waits for, or moves ahead to, whatever tasks are required to complete simulation flow. The actual time interval between sample time steps can vary.
In models where a faster block drives a slower block, you must compensate for the fact that execution of the slower block might span more than one execution period of the faster block. This means that the outputs of the faster block can change before the slower block has finished computing its outputs. The next figure shows a situation in which this problem arises (T = sample time). Note that lower priority tasks are preempted by higher priority tasks before completion.

In the above figure, the faster block executes a second time before the slower block has completed execution. This can cause unpredictable results because the input data to the slow task is changing. Data might not maintain its integrity in this situation.
To avoid this situation, the Simulink engine must hold the outputs of the 1 second (faster) block until the 2 second (slower) block finishes executing. The way to accomplish this is by inserting a Rate Transition block between the 1 second and 2 second blocks. The input to the slower block does not change during its execution, maintaining data integrity.

It is assumed that the Rate Transition block is used in its default (protected/deterministic) mode.
The Rate Transition block executes at the sample rate of the slower block, but with the priority of the faster block.

When you add a Rate Transition block, the block executes before the 2 second block (its priority is higher) and its output value is held constant while the 2 second block executes (it executes at the slower sample rate).
In a model where a slower block drives a faster block, the Simulink engine again computes the output of the driving block first. During sample intervals where only the faster block executes, the simulation progresses more rapidly.
The next figure shows the execution sequence.

As you can see from the preceding figures, the Simulink engine can simulate models with multiple sample rates in an efficient manner. However, a Simulink simulation does not operate in real time.
In models where a slower block drives a faster block, the generated code assigns the faster block a higher priority than the slower block. This means the faster block is executed before the slower block, which requires special care to avoid incorrect results.

This timing diagram illustrates two problems:
Execution of the slower block is split over more than one faster block interval. In this case the faster task executes a second time before the slower task has completed execution. This means the inputs to the faster task can have incorrect values some of the time.
The faster block executes before the slower block (which is backward from the way a Simulink simulation operates). In this case, the 1 second block executes first; but the inputs to the faster task have not been computed. This can cause unpredictable results.
To eliminate these problems, you must insert a Rate Transition block between the slower and faster blocks.

It is assumed that the Rate Transition block is used in its default (protected/deterministic) mode.
The next figure shows the timing sequence that results with the added Rate Transition block.

Three key points about transitions in this diagram (refer to circled numbers):
The Rate Transition block output runs in the 1 second task, but at a slower rate (2 seconds). The output of the Rate Transition block feeds the 1 second task blocks.
The Rate Transition update uses the output of the 2 second task to update its internal state.
The Rate Transition output in the 1 second task uses the state of the Rate Transition that was updated in the 2 second task.
The first problem is alleviated because the Rate Transition block is updating at a slower rate and at the priority of the slower block. The input to the Rate Transition block (which is the output of the slower block) is read after the slower block completes executing.
The second problem is alleviated because the Rate Transition block executes at a slower rate and its output does not change during the computation of the faster block it is driving. The output portion of a Rate Transition block is executed at the sample rate of the slower block, but with the priority of the faster block. Since the Rate Transition block drives the faster block and has effectively the same priority, it is executed before the faster block.
Note This use of the Rate Transition block changes the model. The output of the slower block is now delayed by one time step compared to the output without a Rate Transition block. |
This section examines how a simple multirate model executes in both real time and simulation, using a fixed-step solver. It considers the operation of both SingleTasking and MultiTasking Solver pane tasking modes.
The example model is shown in the next figure. The discussion refers to the six blocks of the model as A through F, as labeled in the block diagram.
The execution order of the blocks (indicated in the upper right of each block) has been forced into the order shown by assigning higher priorities to blocks F, E, and D. The ordering shown is one possible valid execution ordering for this model. (See Simulating Dynamic Systems in the Simulink documentation.)
The execution order is determined by data dependencies between blocks. In a real-time system, the execution order determines the order in which blocks execute within a given time interval or task. This discussion treats the model's execution order as a given, because it is concerned with the allocation of block computations to tasks, and the scheduling of task execution.

Note The discussion and timing diagrams in this section are based on the assumption that the Rate Transition blocks are used in the default (protected/deterministic) mode, with the Ensure data integrity during data transfer and Ensure deterministic data transfer (maximum delay) options on. |
This section considers the execution of the above model when the solver Tasking mode is SingleTasking.
In a single-tasking system, if the Block reduction option on the Optimization pane is on, fast-to-slow Rate Transition blocks are optimized out of the model. The default case is shown (Block reduction on), so block B does not appear in the timing diagrams in this section. For more information, see Block reduction.
The following table shows, for each block in the model, the execution order, sample time, and whether the block has an output or update computation. Block A does not have discrete states, and accordingly does not have an update computation.
Execution Order and Sample Times (Single-Tasking)
Blocks | Sample Time | Output | Update |
|---|---|---|---|
F | 0.1 | Y | Y |
E | 0.1 | Y | Y |
D | 1 | Y | Y |
A | 0.1 | Y | N |
C | 1 | Y | Y |
Real-Time Single-Tasking Execution. The next figure shows the scheduling of computations when the generated code is deployed in a real-time system. The generated program is shown running in real time, under control of interrupts from a 10 Hz timer.

At time 0.0, 1.0, and every second thereafter, both the slow and fast blocks execute their output computations; this is followed by update computations for blocks that have states. Within a given time interval, output and update computations are sequenced in block execution order.
The fast blocks execute on every tick, at intervals of 0.1 second. Output computations are followed by update computations.
The system spends some portion of each time interval (labeled "wait") idling. During the intervals when only the fast blocks execute, a larger portion of the interval is spent idling. This illustrates an inherent inefficiency of single-tasking mode.
Simulated Single-Tasking Execution. The next figure shows the execution of the model during the Simulink simulation loop.

Because time is simulated, the placement of ticks represents the iterations of the simulation loop. Blocks execute in exactly the same order as in the previous figure, but without the constraint of a real-time clock. Therefore there is no idle time between simulated sample periods.
This section considers the execution of the above model when the solver Tasking mode is MultiTasking. Block computations are executed under two tasks, prioritized by rate:
The slower task, which gets the lower priority, is scheduled to run every second. This is called the 1 second task.
The faster task, which gets higher priority, is scheduled to run 10 times per second. This is called the 0.1 second task. The 0.1 second task can preempt the 1 second task.
The following table shows, for each block in the model, the execution order, the task under which the block runs, and whether the block has an output or update computation. Blocks A and B do not have discrete states, and accordingly do not have an update computation.
Task Allocation of Blocks in Multitasking Execution
Blocks | Task | Output | Update |
|---|---|---|---|
F | 0.1 second task | Y | Y |
E | 0.1 second task | Y | Y |
D | The Rate Transition block uses port-based sample times. | Y | Y |
A | 0.1 second task | Y | N |
B | The Rate Transition block uses port-based sample times. | Y | N |
C | 1 second task | Y | Y |
Real-Time Multitasking Execution. The next figure shows the scheduling of computations in MultiTasking solver mode when the generated code is deployed in a real-time system. The generated program is shown running in real time, as two tasks under control of interrupts from a 10 Hz timer.

Simulated Multitasking Execution. The next figure shows the Simulink execution of the same model, in MultiTasking solver mode. In this case, the Simulink engine runs all blocks in one thread of execution, simulating multitasking. No preemption occurs.

Asynchronous Support. Simulink Coder models are normally timed from a periodic interrupt source (for example, a hardware timer). Blocks in a periodically clocked single-rate model run at a timer interrupt rate (the base rate of the model). Blocks in a periodically clocked multirate model run at the base rate or at submultiples of that rate.
Many systems must also support execution of blocks in response to events that are asynchronous with respect to the periodic timing source of the system. For example, a peripheral device might signal completion of an input operation by generating an interrupt. The system must service such interrupts, for example, by acquiring data from the interrupting device.
This chapter explains how to use blocks to model and generate code for asynchronous event handling, including servicing of hardware-generated interrupts, maintenance of timers, asynchronous read and write operations, and spawning of asynchronous tasks under a real-time operating system (RTOS). Although the blocks target the Wind River Systems VxWorks Tornado® RTOS, this chapter provides source code analysis and other information you can use to develop blocks that support asynchronous event handling for an alternative target RTOS.
Block Library for Wind River Systems VxWorks Real-Time Operating System. The next figure shows the blocks in the VxWorks block library (vxlib1).

The key blocks in the library are the Async Interrupt and Task Sync blocks. These blocks are targeted for the VxWorks Tornado operating system. You can use them, without modification, to support VxWorks applications.
If you want to implement asynchronous support for an RTOS other than VxWorks RTOS, guidelines and example code are provided that will help you to adapt the VxWorks library blocks to target your RTOS. This topic is discussed in Create a Customized Asynchronous Library.
The VxWorks library includes blocks you can use to
Generate interrupt-level code — Async Interrupt block
Spawn a VxWorks task that calls a function call subsystem — Task Sync block
Enable data integrity when transferring data between blocks running as different tasks — Protected RT block
Use an unprotected/nondeterministic mode when transferring data between blocks running as different tasks — Unprotected RT block
For detailed descriptions of the blocks in the VxWorks library, see the Simulink Coder Reference. The use of protected and unprotected Rate Transition blocks in asynchronous contexts is discussed in Rate Transitions and Asynchronous Blocks. For general information on rate transitions, see Scheduling.
Access the VxWorks Block Library. To access the VxWorks library, enter the MATLAB command vxlib1.
Generate Code with the VxWorks Library Blocks. To generate a VxWorks compatible application from a model containing VxWorks library blocks, select one of the following targets from the System Target File Browser associated with the model:
ert.tlc — Embedded Coder. This target is provided with the Embedded Coder product.
When using the ERT target with VxWorks library blocks, you must select the Generate an example main program option, and select VxWorksExample from the Target operating system menu.
tornado.tlc — Tornado (VxWorks) Real-Time Target.
Demos and Additional Information. Additional information relevant to the topics in this chapter can be found in
The rtwdemo_async model. To open this demo, type rtwdemo_async at the MATLAB command prompt.
The rtwdemo_async_mdlreftop model. To open this demo, type rtwdemo_async_mdlreftop at the MATLAB command prompt.
Scheduling, discusses general multitasking and rate transition issues for periodic models.
The Embedded Coder documentation discusses the Embedded Real-Time (ERT) target, including task execution and scheduling.
See your VxWorks system documentation for detailed information about the VxWorks system calls mentioned in this chapter.
Generate Interrupt Service Routines. To generate an interrupt service routine (ISR) associated with a specific Wind River Systems VxWorks VME interrupt level, use the Async Interrupt block. The Async Interrupt block enables the specified interrupt level and installs an ISR that calls a connected function call subsystem.
You can also use the Async Interrupt block in a simulation. It provides an input port that can be enabled and connected to a simulated interrupt source.
Connecting the Async Interrupt Block.To generate an ISR, connect an output of the Async Interrupt block to the control input of
A function call subsystem
The input of a Task Sync block
The input to a Stateflow chart configured for a function call input event
The next figure shows an Async Interrupt block configured to service two interrupt sources. The outputs (signal width 2) are connected to two function call subsystems.

Note the following requirements and restrictions:
The Async Interrupt block supports VME interrupts 1 through 7.
The Async Interrupt block requires a VxWorks Board Support Package (BSP) that supports the following VxWorks system calls:
sysIntEnable
sysIntDisable
intConnect
intLock
intUnlock
tickGet
Execution of large subsystems at interrupt level can have a significant impact on interrupt response time for interrupts of equal and lower priority in the system. As a general rule, it is best to keep ISRs as short as possible. Connect only function call subsystems that contain a small number of blocks to an Async Interrupt block.
A better solution for large subsystems is to use the Task Sync block to synchronize the execution of the function call subsystem to a VxWorks task. The Task Sync block is placed between the Async Interrupt block and the function call subsystem. The Async Interrupt block then installs the Task Sync block as the ISR. The ISR releases a synchronization semaphore (performs a semGive) to the task, and returns immediately from interrupt level. The task is then scheduled and run by the VxWorks RTOS. See Spawn a Wind River Systems VxWorks Task for more information.
Using the Async Interrupt Block in Simulation and Code Generation.This section describes a dual-model approach to the development and implementation of real-time systems that include ISRs. In this approach, you develop one model that includes a plant and a controller for simulation, and another model that only includes the controller for code generation. Using a Simulink library, you can implement changes to both models simultaneously. The next figure shows how changes made to the plant or controller, both of which are in a library, are propagated to the models.
Dual-Model Use of Async Interrupt Block for Simulation and Code Generation

A single-model approach is also possible. In this approach, the Plant component of the model is active only in simulation. During code generation, the Plant components are effectively switched out of the system and code is generated only for the interrupt block and controller parts of the model. For an example of this approach, see the rtwdemo_async model.
Dual-Model Approach: Simulation.The following block diagram shows a simple model that illustrates the dual-model approach to modeling. During simulation, the Pulse Generator blocks provide simulated interrupt signals.

The simulated interrupt signals are routed through the Async Interrupt block's input port. Upon receiving a simulated interrupt, the block calls the connected subsystem.
During simulation, subsystems connected to Async Interrupt block outputs are executed in order of their VxWorks priority. In the event that two or more interrupt signals occur simultaneously, the Async Interrupt block executes the downstream systems in the order specified by their interrupt levels (level 7 gets the highest priority). The first input element maps to the first output element.
You can also use the Async Interrupt block in a simulation without enabling the simulation input. In such a case, the Async Interrupt block inherits the base rate of the model and calls the connected subsystems in order of their VxWorks priorities. (In effect, in this case the Async Interrupt block behaves as if all inputs received a 1 simultaneously.)
Dual-Model Approach: Code Generation.In the generated code for the sample model,
Ground blocks provide input signals to the Environment Controller block
The Async Interrupt block does not use its simulation input
The Ground blocks drive control input of the Environment Controller block so no code is generated for that signal path. The Simulink Coder code generator does not generate code for blocks that drive the simulation control input to the Environment Controller block because that path is not selected during code generation. However, the sample times of driving blocks for the simulation input to the Environment Controller block contribute to the sample times supported in the generated code. To avoid including unnecessary sample times in the generated code, use the sample times of the blocks driving the simulation input in the model where generated code is intended.

Standalone functions are installed as ISRs and the interrupt vector table is as follows:
| Offset | |
|---|---|
| 192 | &isr_num1_vec192() |
| 193 | &isr_num2_vec193() |
Consider the code generated from this model, assuming that the Async Interrupt block parameters are configured as shown in the next figure.

In the generated code, the Async Interrupt block installs the code in the Subsystem blocks as interrupt service routines. The interrupt vectors for IRQ1 and IRQ2 are stored at locations 192 and 193 relative to the base of the interrupt vector table, as specified by the VME interrupt vector offset(s) parameter.
Installing an ISR requires two VxWorks calls, int_connect and sysInt_Enable. The Async Interrupt block inserts these calls in the model_initialize function, as shown in the following code excerpt.
/* VxWorks Interrupt Block: '<Root>/Async Interrupt' */
/* Connect and enable ISR function: isr_num1_vec192 */
if( intConnect(INUM_TO_IVEC(192), isr_num1_vec192, 0) != OK) {
printf("intConnect failed for ISR 1.\n");
}
sysIntEnable(1);
/* VxWorks Interrupt Block: '<Root>/Async Interrupt' */
/* Connect and enable ISR function: isr_num2_vec193 */
if( intConnect(INUM_TO_IVEC(193), isr_num2_vec193, 0) != OK)
{
printf("intConnect failed for ISR 2.\n");
}
sysIntEnable(2);The hardware that generates the interrupt is not configured by the Async Interrupt block. Typically, the interrupt source is a VME I/O board, which generates interrupts for specific events (for example, end of A/D conversion). The VME interrupt level and vector are set up in registers or by using jumpers on the board. You can use the mdlStart routine of a user-written device driver (S-function) to set up the registers and enable interrupt generation on the board. You must match the interrupt level and vector specified in the Async Interrupt block dialog to the level and vector set up on the I/O board.
Generated ISR Code.The actual ISR generated for IRQ1 is listed below.
/* VxWorks Interrupt Block: '<Root>/Async Interrupt' */
void isr_num1_vec192(void)
{
int_T lock;
FP_CONTEXT context;
/* Use tickGet() as a portable tick counter example.
A much higher resolution can be achieved with a
hardware counter */
Async_Code_M->Timing.clockTick2 = tickGet();
/* disable interrupts (system is configured as non-ive) */
lock = intLock();
/* save floating point context */
fppSave(&context);
/* Call the system: <Root>/Subsystem A */
Count(0, 0);
/* restore floating point context */
fppRestore(&context);
/* re-enable interrupts */
intUnlock(lock);
}There are several features of the ISR that should be noted:
Because of the setting of the Preemption Flag(s) parameter, this ISR is locked; that is, it cannot be preempted by a higher priority interrupt. The ISR is locked and unlocked by the VxWorks int_lock and int_unlock functions.
The connected subsystem, Count, is called from within the ISR.
The Count function executes algorithmic (model) code. Therefore, the floating-point context is saved and restored across the call to Count.
The ISR maintains its own absolute time counter, which is distinct from other periodic base rate or subrate counters in the system. Timing data is maintained for the use of any blocks executed within the ISR that require absolute or elapsed time.
See Use Timers in Asynchronous Tasks for details.
The model's termination function disables the interrupts:
/* Model terminate function */
void Async_Code_terminate(void)
{
/* VxWorks Interrupt Block: '<Root>/Async Interrupt' */
/* Disable interrupt for ISR system: isr_num1_vec192 */
sysIntDisable(1);
/* VxWorks Interrupt Block: '<Root>/Async Interrupt' */
/* Disable interrupt for ISR system: isr_num2_vec193 */
sysIntDisable(2);
}Spawn a Wind River Systems VxWorks Task. To spawn an independent VxWorks task, use the Task Sync block. The Task Sync block is a function call subsystem that spawns an independent VxWorks task. The task calls the function call subsystem connected to the output of the Task Sync block.
Typically, the Task Sync block is placed between an Async Interrupt block and a function call subsystem block or a Stateflow chart. Another example would be to place the Task Sync block at the output of a Stateflow chart that has an event, Output to Simulink, configured as a function call.
The Task Sync block performs the following functions:
An independent task is spawned, using the VxWorks system call taskSpawn. When the task is activated, it calls the downstream function call subsystem code. The task is deleted using taskDelete during model termination.
A semaphore is created to synchronize the connected subsystem to the execution of the Task Sync block.
The spawned task is wrapped in an infinite for loop. In the loop, the spawned task listens for the semaphore, using semTake. When semTake is first called, NO_WAIT is specified. This allows the task to determine whether a second semGive has occurred prior to the completion of the function call subsystem. This would indicate that the interrupt rate is too fast or the task priority is too low.
The Task Sync block generates synchronization code (for example, semGive()). This code allows the spawned task to run; the task in turn calls the connected function call subsystem code. The synchronization code can run at interrupt level. This is accomplished by connecting the Task Sync block to the output of an Async Interrupt block, which triggers execution of the Task Sync block within an ISR.
If blocks in the downstream algorithmic code require absolute time, it can be supplied either by the timer maintained by the Async Interrupt block, or by an independent timer maintained by the task associated with the Task Sync block.
For an example of how to use the Task Sync block, see the rtwdemo_async demo. The block diagram for the model appears in the next figure. Before reading the following discussion, open the demo model and double-click the Generate Code button. You can then examine the generated code in the HTML code generation report produced by the demo.

In this model, the Async Interrupt block is configured for VME interrupts 1 and 2, using interrupt vector offsets 192 and 193. Interrupt 2 is connected to the Task Sync block, which in turn drives the Algorithm subsystem. Consider the code generated from this model, assuming that the Task Sync block parameters are configured as shown in the next figure.

The Task Sync block generates initialization code for initialization by MdlStart, which itself creates and initializes the synchronization semaphore. It also spawns an independent task (task0).
/* VxWorks Task Block: <S5>/S-Function (vxtask1) */
/* Spawn task: Task0 with priority 50 */
if ((*(SEM_ID *)rtwdemo_async_DWork.SFunction_PWORK.SemID =
semBCreate(SEM_Q_PRIORITY, SEM_EMPTY)) == NULL) {
printf("semBCreate call failed for block Task0.\n");
}
if ((rtwdemo_async_DWork.SFunction_IWORK.TaskID = taskSpawn("Task0",
50.0, VX_FP_TASK, 8192.0, (FUNCPTR)Task0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0)) == ERROR) {
printf("taskSpawn call failed for block Task0.\n");
}After spawning Task0, MdlStart connects and enables the ISR (isr_num2_vec193) for interrupt 2:
/* VxWorks Interrupt Block: '<Root>/Async Interrupt' */
/* Connect and enable ISR function: isr_num1_vec192 */
if( intConnect(INUM_TO_IVEC(192), isr_num1_vec192, 0) != OK) {
printf("intConnect failed for ISR 1.\n");
}
sysIntEnable(1);The ordering of these operations is significant. The task must be spawned before the interrupt that activates it can be enabled.
Task and Task Synchronization Code.The function Task0, generated by the Task Sync block, runs as a VxWorks task. The task waits for a synchronization semaphore in an infinite for loop. If it obtains the semaphore, it updates its task timer and calls the Algorithm subsystem.
For this demo, the Synchronize the data transfer of this task with the caller task option of the Task Sync block is selected. Therefore, the timer associated with the Task Sync block (rtM->Timing.clockTick3) is updated with the value of the timer that is maintained by the Async Interrupt block (rtM->Timing.clockTick4). Therefore, blocks within the Algorithm subsystem use timer values based on the time of the most recent interrupt (not the most recent activation of Task0).
/* VxWorks Task Block: <S5>/S-Function (vxtask1) */
/* Spawned with priority: 50 */
void Task0(void)
{
/* Wait for semaphore to be released by system:
rtwdemo_async/Task Sync */
for(;;) {
if (semTake(*(SEM_ID
*)rtwdemo_async_DWork.SFunction_PWORK.SemID,NO_WAIT) !=
ERROR) {
logMsg("Rate for Task Task0() too fast.\n",0,0,0,0,0,0);
#if STOPONOVERRUN
logMsg("Aborting real-time simulation.\n",0,0,0,0,0,0);
semGive(stopSem);
return(ERROR);
#endif
} else {
semTake(*(SEM_ID
*)rtwdemo_async_DWork.SFunction_PWORK.SemID,
WAIT_FOREVER);
}
/* Use the upstream clock tick counter for this Task. */
rtwdemo_async_M->Timing.clockTick2 =
rtwdemo_async_M->Timing.clockTick3;
/* Call the system: <Root>/Algorithm */
{
/* Output and update for function-call system: '<Root>/Algorithm' */
{
uint32_T rt_currentTime = ((uint32_T)rtwdemo_async_M->Timing.clockTick2);
uint32_T rt_elapseTime = rt_currentTime -
rtwdemo_async_DWork.Algorithm_PREV_T;
rtwdemo_async_DWork.Algorithm_PREV_T = rt_currentTime;
{
int32_T i;
/* DiscreteIntegrator: '<S1>/Integrator' */
rtwdemo_async_B.Integrator = rtwdemo_async_DWork.Integrator_DSTATE;
for(i = 0; i < 60; i++) {
/* Sum: '<S1>/Sum' */
rtwdemo_async_B.Sum[i] = rtwdemo_async_B.ProtectedRT1[i] + 1.25;
}
}
/* Sum: '<S1>/Sum1' */
rtwdemo_async_B.Sum1 = rtwdemo_async_B.Sum[0];
{
int_T i1;
const real_T *u0 = &rtwdemo_async_B.Sum;[1];
for (i1=0; i1 < 59; i1++) {
rtwdemo_async_B.Sum1 += u0[i1];
}
}
{
int32_T i;
if(rtwdemo_async_DWork.ProtectedRT2_ActiveBufIdx) {
for(i = 0; i < 60; i++) {
rtwdemo_async_DWork.ProtectedRT2_Buffer0[i] =
rtwdemo_async_B.Sum[i];
}
rtwdemo_async_DWork.ProtectedRT2_ActiveBufIdx = (boolean_T)0U;
} else {
for(i = 0; i < 60; i++) {
rtwdemo_async_DWork.ProtectedRT2_Buffer1[i] =
rtwdemo_async_B.Sum[i];
}
rtwdemo_async_DWork.ProtectedRT2_ActiveBufIdx = (boolean_T)1U;
}
}
/* Update for DiscreteIntegrator: '<S1>/Integrator' */
rtwdemo_async_DWork.Integrator_DSTATE = (real_T)rt_elapseTime *
1.6666666666666666E-002 * rtwdemo_async_B.Sum1 +
rtwdemo_async_DWork.Integrator_DSTATE;
}The semaphore is granted by the function isr_num2_vec193, which is called from interrupt level:
/* VxWorks Interrupt Block: '<Root>/Async Interrupt' */
void isr_num2_vec193(void)
{
/* Use tickGet() as a portable tick counter example. A much
higher resolution can be achieved with a hardware counter */
rtwdemo_async_M->Timing.clockTick3 = tickGet();
/* Call the system: <S4>/Subsystem */
/* Output and update for function-call system:
'<S4>/Subsystem' */
{
{
int32_T i;
for(i = 0; i < 60; i++) {
if(rtwdemo_async_DWork.ProtectedRT1_ActiveBufIdx) {
rtwdemo_async_B.ProtectedRT1[i] =
rtwdemo_async_DWork.ProtectedRT1_Buffer1[i];
} else {
rtwdemo_async_B.ProtectedRT1[i] =
rtwdemo_async_DWork.ProtectedRT1_Buffer0[i];
}
}
}
/* VxWorks Task Block: <S5>/S-Function (vxtask1) */
/* Release semaphore for system task: Task0 */
semGive(*(SEM_ID *)rtwdemo_async_DWork.SFunction_PWORK.SemID);
}
}The ISR maintains a timer that stores the tick count at the time of interrupt. This timer is updated before releasing the semaphore that activates Task0.
As this example shows, the Task Sync block generates only a small amount of interrupt-level code.
Task Termination Code.The Task Sync block also generates the following termination code.
/* Model terminate function */
void rtwdemo_async_terminate(void)
{
/* VxWorks Interrupt Block: '<Root>/Async Interrupt' */
/* Disable interrupt for ISR system: isr_num1_vec192 */
sysIntDisable(1);
/* VxWorks Interrupt Block: '<Root>/Async Interrupt' */
/* Disable interrupt for ISR system: isr_num2_vec193 */
sysIntDisable(2);
/* Terminate for function-call system: '<S4>/Subsystem' */
/* VxWorks Task Block: <S5>/S-Function (vxtask1) */
/* Destroy task: Task0 */
taskDelete(rtwdemo_async_DWork.SFunction_IWORK.TaskID);
}About Rate Transitions and Asynchronous Blocks. Because an asynchronous function call subsystem can preempt or be preempted by other model code, an inconsistency arises when more than one signal element is connected to an asynchronous block. The issue is that signals passed to and from the function call subsystem can be in the process of being written to or read from when the preemption occurs. Thus, some old and some new data is used. This situation can also occur with scalar signals in some cases. For example, if a signal is a double (8 bytes), the read or write operation might require two machine instructions.
The Simulink Rate Transition block is designed to deal with preemption problems that occur in data transfer between blocks running at different rates. These issues are discussed in Scheduling.
You can handle rate transition issues automatically by selecting the Automatically handle data transfers between tasks option on the Solver pane of the Configuration Parameters dialog box. This saves you from having to manually insert Rate Transition blocks to avoid invalid rate transitions, including invalid asynchronous-to-periodic and asynchronous-to-asynchronous rate transitions, in multirate models. For asynchronous tasks, the Simulink engine configures inserted blocks for data integrity but not determinism during data transfers.
For asynchronous rate transitions, the Rate Transition block provides data integrity, but cannot provide determinism. Therefore, when you insert Rate Transition blocks explicitly, you must clear the Ensure data determinism check box in the Block Parameters dialog box.
When you insert a Rate Transition block between two blocks to maintain data integrity and priorities are assigned to the tasks associated with the blocks, the Simulink Coder software assumes that the higher priority task can preempt the lower priority task and the lower priority task cannot preempt the higher priority task. If the priority associated with task for either block is not assigned or the priorities of the tasks for both blocks are the same, the Simulink Coder software assumes that either task can preempt the other task.
Priorities of periodic tasks are assigned by the Simulink engine, in accordance with the options specified in the Solver options section of the Solver pane of the Configuration Parameters dialog box. When the Periodic sample time constraint option field of Solver options is set to Unconstrained, the model base rate priority is set to 40. Priorities for subrates then increment or decrement by 1 from the base rate priority, depending on the setting of the Higher priority value indicates higher task priority option.
You can assign priorities manually by using the Periodic sample time properties field. The Simulink engine does not assign a priority to asynchronous blocks. For example, the priority of a function call subsystem that connects back to an Async Interrupt block is assigned by the Async Interrupt block.
The Simulink task priority field of the Async Interrupt block specifies a priority level (required) for every interrupt number entered in the VME interrupt number(s) field. The priority array sets the priorities of the subsystems connected to each interrupt.
For the Task Sync block, if the Wind River Systems VxWorks RTOS is the target, the Higher priority value indicates higher task priority option should be deselected. The Simulink task priority field specifies the block priority relative to connected blocks (in addition to assigning a VxWorks priority to the generated task code).
The VxWorks library provides two types of rate transition blocks as a convenience. These are simply preconfigured instances of the built-in Simulink Rate Transition block:
Protected Rate Transition block: Rate Transition block that is configured with the Ensure data integrity during data transfers on and Ensure deterministic data transfer off.
Unprotected Rate Transition block: Rate Transition block that is configured with the Ensure data integrity during data transfers option off.
Handle Rate Transitions for Asynchronous Tasks. For rate transitions that involve asynchronous tasks, you can maintain data integrity. However, you cannot achieve determinism. You have the option of using the Rate Transition block or target-specific rate transition blocks.
Consider the following model, which includes a Rate Transition block.

You can use the Rate Transition block in either of the following modes:
Maintain data integrity, no determinism
Unprotected
Alternatively, you can use target-specific rate transition blocks. The following blocks are available for the VxWorks RTOS:
Protected Rate Transition block (reader)
Protected Rate Transition block (writer)
Unprotected Rate Transition block
Handle Multiple Asynchronous Interrupts. Consider the following model, in which two functions trigger the same subsystem.

The two tasks must have equal priorities. When priorities are the same, the outcome depends on whether they are firing periodically or asynchronously, and also on a diagnostic setting. The following table and notes describe these outcomes:
Supported Sample Time and Priority for Function Call Subsystem with Multiple Triggers
| Async Priority = 1 | Async Priority = 2 | Async Priority Unspecified | Periodic Priority = 1 | Periodic Priority = 2 |
|---|---|---|---|---|---|
Async Priority = 1 | Supported (1) |
|
|
|
|
Async Priority = 2 |
| Supported (1) |
|
|
|
Async Priority Unspecified |
|
| Supported (2) |
|
|
Periodic Priority = 1 |
|
|
| Supported |
|
Periodic Priority = 2 |
|
|
|
| Supported |
Control these outcomes using the Tasks with equal priority option in the Diagnostics pane of the Configuration Parameters dialog box; set this diagnostic to none if tasks of equal priority cannot preempt each other in the target system.
For this case, the following warning message is issued unconditionally:
The function call subsystem <name> has multiple asynchronous triggers that do not specify priority. Data integrity will not be maintained if these triggers can preempt one another.
Empty cells in the above table represent multiple triggers with differing priorities, which are unsupported.
The Simulink Coder product provides absolute time management for a function call subsystem connected to multiple interrupts in the case where timer settings for TriggerA and TriggerB (time source, resolution) are the same.
Assume that all the following conditions are true for the model shown above:
A function call subsystem is triggered by two asynchronous triggers (TriggerA and TriggerB) having identical priority settings.
Each trigger sets the source of time and timer attributes by calling the functions ssSetTimeSource and ssSetAsyncTimerAttributes.
The triggered subsystem contains a block that needs elapsed or absolute time (for example, a Discrete Time Integrator).
The asynchronous function call subsystem has one global variable, clockTick# (where # is the task ID associated with the subsystem). This variable stores absolute time for the asynchronous task. There are two ways timing can be handled:
If the time source is set to SS_TIMESOURCE_BASERATE, the Simulink Coder code generator generates timer code in the function call subsystem, updating the clock tick variable from the base rate clock tick. Data integrity is maintained if the same priority is assigned to TriggerA and TriggerB.
If the time source is SS_TIMESOURCE_SELF, generated code for both TriggerA and TriggerB updates the same clock tick variable from the hardware clock.
The word size of the clock tick variable can be set directly or be established according to the Application lifespan (days) setting and the timer resolution set by the TriggerA and TriggerB S-functions (which must be the same). See Use Timers in Asynchronous Tasks and Control Memory Allocation for Time Counters for more information.
An ISR can set a source for absolute time. This is done with the function ssSetTimeSource, which has the following three options:
SS_TIMESOURCE_SELF: Each generated ISR maintains its own absolute time counter, which is distinct from any periodic base rate or subrate counters in the system. The counter value and the timer resolution value (specified in the Timer resolution (seconds) parameter of the Async Interrupt block) are used by downstream blocks to determine absolute time values required by block computations.
SS_TIMESOURCE_CALLER: The ISR reads time from a counter maintained by its caller. Time resolution is thus the same as its caller's resolution.
SS_TIMESOURCE_BASERATE: The ISR can read absolute time from the model's periodic base rate. Time resolution is thus the same as its base rate resolution.
Note The function ssSetTimeSource cannot be called before ssSetOutputPortWidth is called. If this occurs, the program will come to a halt and generate an error message. |
By default, the counter is implemented as a 32-bit unsigned integer member of the Timing substructure of the real-time model structure. For any target that supports the rtModel data structure, when the time data type is not set by using ssSetAsyncTimeDataType, the counter word size is determined by the Application lifespan (days) model parameter. As an example (from ERT target code),
/* Real-time Model Data Structure */
struct _RT_MODEL_elapseTime_exp_Tag {
const char *errorStatus;
/*
* Timing:
* The following substructure contains information regarding
* the timing information for the model.
*/
struct {
uint32_T clockTick1;
uint32_T clockTick2;
} Timing;
};The example omits unused fields in the Timing data structure (a feature of ERT target code not found in GRT). For any target that supports the rtModel data structure, the counter word size is determined by the Application lifespan (days) model parameter.
By default, the library blocks for the Wind River Systems VxWorks RTOS set the timer source to SS_TIMESOURCE_SELF and update their counters by using the system call tickGet. tickGet returns a timer value maintained by the VxWorks kernel. The maximum word size for the timer is UINT32. The following VxWorks example for the shows a generated call to tickGet.
/* VxWorks Interrupt Block: '<Root>/Async Interrupt' */
void isr_num2_vec193(void)
{
/* Use tickGet() as a portable tick counter example. A much
higher resolution can be achieved with a hardware counter */
rtM->Timing.clockTick2 = tickGet();
. . .The tickGet call is supplied only as an example. It can (and in many instances should) be replaced by a timing source that has better resolution. If you are targeting the VxWorks RTOS, you can obtain better timer resolution by replacing the tickGet call and accessing a hardware timer by using your BSP instead.
If you are implementing a custom asynchronous block for an RTOS other than the VxWorks RTOS, you should either generate an equivalent call to the target RTOS, or generate code to read a timer register on the target hardware.
The default Timer resolution (seconds) parameter of your Async Interrupt block implementation should be changed to match the resolution of your target's timing source.
The counter is updated at interrupt level. Its value represents the tick value of the timing source at the most recent execution of the ISR. The rate of this timing source is unrelated to sample rates in the model. In fact, typically it is faster than the model's base rate. Select the timer source and set its rate and resolution based on the expected rate of interrupts to be serviced by the Async Interrupt block.
For an example of timer code generation, see Async Interrupt Block Implementation.
About Implementing Asynchronous Blocks. This section describes how to implement asynchronous blocks for use with your target RTOS, using the Async Interrupt and Task Sync blocks as a starting point. (Rate Transition blocks are target-independent, so you do not need to develop customized rate transition blocks.)
You can customize the asynchronous library blocks by modifying the block implementation. These files are
The block's underlying S-function MEX-file
The TLC files that control code generation of the block
In addition, you need to modify the block masks to remove references specific to the Wind River Systems VxWorks RTOS and to incorporate parameters required by your target RTOS.
Custom block implementation is an advanced topic, requiring familiarity with the Simulink MEX S-function format and API, and with the Target Language Compiler (TLC). These topics are covered in the following documents:
The Overview of S-Functions in the Simulink documentation describes MEX S-functions and the S-function API in general.
The Target Language Compiler documentation and ??? describe how to create a TLC block implementation for use in code generation.
The following sections discuss the C/C++ and TLC implementations of the asynchronous library blocks, including required SimStruct macros and functions in the TLC asynchronous support library (asynclib.tlc).
Async Interrupt Block Implementation. The source files for the Async Interrupt block are located in matlabroot/rtw/c/tornado/devices:
vxinterrupt1.c: C MEX-file source code, for use in configuration and simulation
vxinterrupt1.tlc: TLC implementation, for use in code generation
asynclib.tlc: library of TLC support functions, called by the TLC implementation of the block. The library calls are summarized in asynclib.tlc Support Library.
Most of the code in vxinterrupt1.c performs ordinary functions that are not related to asynchronous support (for example, obtaining and validating parameters from the block mask, marking parameters nontunable, and passing parameter data to the model.rtw file).
The mdlInitializeSizes function uses special SimStruct macros and SS_OPTIONS settings that are required for asynchronous blocks, as described below.
Note that the following macros cannot be called before ssSetOutputPortWidth is called:
ssSetTimeSource
ssSetAsyncTimerAttributes
ssSetAsyncTimerResolutionEl
ssSetAsyncTimerDataType
ssSetAsyncTimerDataTypeEl
ssSetAsyncTaskPriorities
ssSetAsyncTaskPrioritiesEl
If any one of the above macros is called before ssSetOutputPortWidth, the following error message will appear:
SL_SfcnMustSpecifyPortWidthBfCallSomeMacro {
S-function '%s' in '%<BLOCKFULLPATH>'
must set output port %d width using
ssSetOutputPortWidth before calling macro %s
}ssSetAsyncTimerAttributes. ssSetAsyncTimerAttributes declares that the block requires a timer, and sets the resolution of the timer as specified in the Timer resolution (seconds) parameter.
The function prototype is
ssSetAsyncTimerAttributes(SimStruct *S, double res)
where
S is a Simstruct pointer.
res is the Timer resolution (seconds) parameter value.
The following code excerpt shows the call to ssSetAsyncTimerAttributes.
/* Setup Async Timer attributes */ ssSetAsyncTimerAttributes(S,mxGetPr(TICK_RES)[0]);ssSetAsyncTaskPriorities.
ssSetAsyncTaskPriorities sets the Simulink task priority for blocks executing at each interrupt level, as specified in the block's Simulink task priority field.
The function prototype is
ssSetAsyncTaskPriorities(SimStruct *S, int numISRs,
int *priorityArray)where
S is a SimStruct pointer.
numISRs is the number of interrupts specified in the VME interrupt number(s) parameter.
priorityarray is an integer array containing the interrupt numbers specified in the VME interrupt number(s) parameter.
The following code excerpt shows the call to ssSetAsyncTaskPriorities:
/* Setup Async Task Priorities */
priorityArray = malloc(numISRs*sizeof(int_T));
for (i=0; i<numISRs; i++) {
priorityArray[i] = (int_T)(mxGetPr(ISR_PRIORITIES)[i]);
}
ssSetAsyncTaskPriorities(S, numISRs, priorityArray);
free(priorityArray);
priorityArray = NULL;
}SS_OPTION Settings. The code excerpt below shows the SS_OPTION settings for vxinterrupt1.c. SS_OPTION_ASYNCHRONOUS_INTERRUPT should be used when a function call subsystem is attached to an interrupt. For more information, see the documentation for SS_OPTION and SS_OPTION_ASYNCHRONOUS in matlabroot/simulink/include/simstruc.h.
ssSetOptions( S, (SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME |
SS_OPTION_ASYNCHRONOUS_INTERRUPT |TLC Implementation. This section discusses each function of vxinterrupt1.tlc, with an emphasis on target-specific features that you will need to change to generate code for your target RTOS.
Generate #include Directives.vxinterrupt1.tlc begins with the statement
%include "vxlib.tlc"
vxlib.tlc is a target-specific file that generates directives to include VxWorks header files. You should replace this with a file that generates includes for your target RTOS.
BlockInstanceSetup Function.For each connected output of the Async Interrupt block, BlockInstanceSetup defines a function name for the corresponding ISR in the generated code. The functions names are of the form
isr_num_vec_offset
where num is the ISR number defined in the VME interrupt number(s) block parameter, and offset is an interrupt table offset defined in the VME interrupt vector offset(s) block parameter.
In a custom implementation, there is no requirement to use this naming convention.
The function names are cached for use by the Outputs function, which generates the actual ISR code.
Outputs Function.Outputs iterates over all connected outputs of the Async Interrupt block. An ISR is generated for each such output.
The ISR code is cached in the "Functions" section of the generated code. Before generating the ISR, Outputs does the following:
Generates a call to the downstream block (cached in a temporary buffer).
Determines whether the ISR should be locked or not (as specified in the Preemption Flag(s) block parameter).
Determines whether the block connected to the Async Interrupt block is a Task Sync block. (This information is obtained by using the asynclib calls LibGetFcnCallBlock and LibGetBlockAttribute.) If so,
The preemption flag for the ISR must be set to 1. An error results otherwise.
VxWorks calls to save and restore floating-point context are generated, unless the user has configured the model for integer-only code generation.
When generating the ISR code, Outputs calls the asynclib function LibNeedAsyncCounter to determine whether a timer is required by the connected subsystem. If so, and if the time source is set to be SS_TIMESOURCE_SELF by ssSetTimeSource, LibSetAsyncCounter is called to generate a VxWorks tickGet function call and update the counter. In your implementation, you should generate either an equivalent call to the target RTOS, or generate code to read the a timer register on the target hardware.
If you are targeting the VxWorks RTOS, you can obtain better timer resolution by replacing the tickGet call and accessing a hardware timer by using your BSP instead. tickGet supports only a 1/60 second resolution.
Start Function.The Start function generates the required VxWorks calls (int_connect and sysInt_Enable) to connect and enable each ISR. You should replace this with calls to your target RTOS.
Terminate Function.The Terminate function generates the call sysIntDisable to disable each ISR. You should replace this with calls to your target RTOS.
Task Sync Block Implementation. The source files for the Task Sync block are located in matlabroot/rtw/c/tornado/devices. They are
vxtask1.c: MEX-file source code, for use in configuration and simulation.
vxtask1.tlc: TLC implementation, for use in code generation.
asynclib.tlc: library of TLC support functions, called by the TLC implementation of the block. The library calls are summarized in asynclib.tlc Support Library.
Like the Async Interrupt block, the Task Sync block sets up a timer, in this case with a fixed resolution. The priority of the task associated with the block is obtained from the Simulink task priority parameter. The SS_OPTION settings are the same as those used for the Async Interrupt block.
ssSetAsyncTimerAttributes(S, 0.01);
priority = (int_T) (*(mxGetPr(PRIORITY)));
ssSetAsyncTaskPriorities(S,1,&priority);
ssSetOptions(S, (SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_ASYNCHRONOUS |
SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME |
}TLC Implementation. Generate #include Directives. vxtask1.tlc begins with the statement
%include "vxlib.tlc"
vxlib.tlc is a target-specific file that generates directives to include VxWorks header files. You should replace this with a file that generates includes for your target RTOS.
BlockInstanceSetup Function.The BlockInstanceSetup function derives the task name, block name, and other identifier strings used later in code generation. It also checks for and warns about unconnected block conditions, and generates a storage declaration for a semaphore (stopSem) that is used in case of interrupt overflow conditions.
Start Function.The Start function generates the required VxWorks calls to define storage for the semaphore that is used in management of the task spawned by the Task Sync block. Depending on the code format of the target, either a static storage declaration or a dynamic memory allocation call is generated. This function also creates a semaphore (semBCreate) and spawns a VxWorks task (taskSpawn). You should replace these with calls to your target RTOS.
Outputs Function.The Outputs function generates a VxWorks task that waits for a semaphore. When it obtains the semaphore, it updates the block's tick timer and calls the downstream subsystem code, as described in Spawn a Wind River Systems VxWorks Task. Outputs also generates code (called from interrupt level) that grants the semaphore.
Terminate Function.The Terminate function generates the VxWorks call taskDelete to end execution of the task spawned by the block. You should replace this with calls to your target RTOS.
Note also that if the target RTOS has dynamically allocated any memory associated with the task , the Terminate function should deallocate the memory.
asynclib.tlc Support Library. asynclib.tlc is a library of TLC functions that support the implementation of asynchronous blocks. Some functions are specifically designed for use in asynchronous blocks. For example, LibSetAsyncCounter generates a call to update a timer for an asynchronous block. Other functions are utilities that return information required by asynchronous blocks (for example, information about connected function call subsystems).
The following table summarizes the public calls in the library. For details, see the library source code and the vxinterrupt1.tlc and vxtask1.tlc files, which call the library functions.
Summary of asynclib.tlc Library Functions
Function | Description |
|---|---|
LibBlockExecuteFcnCall | For use by inlined S-functions with function call outputs. Generates code to execute a function call subsystem. |
LibGetBlockAttribute | Returns a field value from a block record. |
LibGetFcnCallBlock | Given an S-Function block and call index, returns the block record for the downstream function call subsystem block. |
LibGetCallerClockTickCounter | Provides access to the time counter of an upstream asynchronous task. |
LibGetCallerClockTickCounterHighWord | Provides access to the high word of the time counter of an upstream asynchronous task. |
LibManageAsyncCounter | Determines whether an asynchronous task needs a counter and manages its own timer. |
LibNeedAsyncCounter | If the calling block requires an asynchronous counter, returns TLC_TRUE, otherwise returns TLC_FALSE. |
LibSetAsyncClockTicks | Returns code that sets clockTick counters that are to be maintained by the asynchronous task. |
LibSetAsyncCounter | Generates code to set the tick value of the block's asynchronous counter. |
LibSetAsyncCounterHighWord | Generates code to set the tick value of the high word of the block's asynchronous counter |
Capabilities. You can inport asynchronous event data into a function-call subsystem via an Inport block. For standalone fixed-step simulations, you can specify:
The time points at which each asynchronous event occurs
The number of asynchronous events at each time point
Input Data Format. You can enter your asynchronous data at the MATLAB command line or on the Data Inport/Export pane of the Configuration Parameters dialog box. In either case, a number of restrictions apply to the data format.
The expression for the parameter Configuration Parameters > Data Import/Export > Input must be a comma-separated list of tables as described in Enabling Data Import.
The table corresponding to the input port outputting asynchronous events must be a column vector containing time values for the asynchronous events.
The time vector of the asynchronous events must be of double data type and monotonically increasing.
All time data must be integer multiples of the model step size.
To specify multiple function calls at a given time step, you must repeat the time value accordingly. In other words, if you wish to specify three asynchronous events at t = 1 and two events at t = 9, then you must list 1 three times and 9 twice in your time vector. ( t = [1 1 1 9 9])
The table corresponding to normal data input port can be of any other supported format as described in Enabling Data Import.
Example. In this model, a function-call subsystem is used to track the total number of asynchronous events and to multiply a set of inputs by 2.

To input data via the Configuration Parameters dialog box,
Select Simulation > Configuration Parameters > Data Import/Export.
Select the Input parameter.
For this example, enter the following command in the MATLAB window:
>> t = [1 1 5 9 9 9]', u = [[0:10]' [0:10]']
Alternatively, you can enter the data as t, tu in the Data Import/Export:

Here, t is a column vector containing the times of asynchronous events for Inport block In1 while tu is a table of input values versus time for Inport block In2.
By default, the Time and Output options are selected and the output variables are named tout and yout.
Simulate the model.
Display the output by entering [tout yout] at the MATLAB command line and obtain:
ans =
0 0 -1
1 2 2
2 2 2
3 2 2
4 2 2
5 3 10
6 3 10
7 3 10
8 3 10
9 6 18
10 6 18Here the first column contains the simulation times.
The second column represents the output of Out1 — the total number of asynchronous events. Since the function-call subsystem is triggered twice at t = 1, the output is 2. It is not called again until t = 5, and so does not increase to 3 until then. Finally, it is called three times at 9, so it increases to 6.
The third column contains the output of Out2 obtained by multiplying the input value at each asynchronous event time by 2. At any other time, the output is held at its previous value
Asynchronous Task Priority. The Simulink product does not simulate asynchronous task behavior. Although you can specify a task priority for an asynchronous task represented in a model with the Task Sync block, the priority setting is for code generation purposes only and is not honored during simulation.
Convert an Asynchronous Subsystem into a Model Reference. You can use the Asynchronous Task Specification block to specify an asynchronous function-call input to a model reference. However, you must convert the Async Interrupt and Function-Call blocks into a subsystem and then convert the subsystem into a model reference.
Following is an example with step-by-step instructions for conversion.

Convert the Async Interrupt and Count blocks into a subsystem. Select both blocks and right-click Count. From the menu, select Create Subsystem.

To prepare for converting the new subsystem to a Model block, set the following configuration parameters in the top model. To access the Configuration Parameters dialog box, select Simulation > Configuration Parameters
If you are simulating in Normal mode, then you must make the following change. From the Optimization node, navigate to the Signals and Parameters pane. Under Simulation and code generation, selsect the Inline parameters option.
From the Diagnostics node, navigate to the Sample Time pane. Then set Multitask rate transition to error and Multitask conditionally executed subsystem to error.
Under Diagnostics, navigate to the Data Validity pane and set the Multitask data store option to error and set the Underspecified initialization detection to Simplified. If your model is large or complex, in the Model Advisor, run the Check consistency of initialization parameters for Outport and Merge blocks check and make suggested changes.
Under Diagnostics, navigate to the Connectivity pane. Set Mux blocks used to create bus signals, Bus signal treated as vector, and Invalid function-call connection to error. Also set Context-dependent inputs to Enable All.
Convert the subsystem to an atomic subsystem. Select Edit > Subsystem Parameters > Treat as atomic unit.

Convert the subsystem to a Model block. Right-click the subsystem and select Convert to Model Block. A window opens with a model reference block inside of it.
Replace the subsystem in the top model with the new model reference block.

Move the Async Interrupt block from the model reference to the top model, before the model reference block.

Insert an Asynchronous Task Specification block in the model reference. Set the priority of the Asynchronous Task Specification block. (For more information on setting the priority, see Asynchronous Task Specification.)

In the model reference, double-click the input port to open its Source Block Parameters dialog box. Click theSignal Attributes tab and select the Output function call option. Click OK.

Save your model and then perform an Update Diagram to verify your settings.

About Timers. Certain blocks require the value of either absolute time (that is, the time from the start of program execution to the present time) or elapsed time (for example, the time elapsed between two trigger events). All targets that support the real-time model (rtModel) data structure provide efficient time computation services to blocks that request absolute or elapsed time. Absolute and elapsed timer features include
Timers are implemented as unsigned integers in generated code.
In multirate models, at most one timer is allocated per rate. If no blocks executing at a given rate require a timer, no timer is allocated to that rate. This minimizes memory allocated for timers and significantly reduces overhead involved in maintaining timers.
Allocation of elapsed time counters for use of blocks within triggered subsystems is minimized, further reducing memory usage and overhead.
The Simulink Coder product provides S-function and TLC APIs that let your S-functions access timers, in both simulation and code generation.
For ERT and ERT-derived targets, the word size of the timers is determined by a user-specified maximum counter value. If you specify this value, timers will not overflow. See the description of the parameter Control Memory Allocation for Time Counters. See also the Embedded Coder documentation for information on restrictions on its use.
See Limitations on the Use of Absolute Time and Blocks that Depend on Absolute Time for more information about absolute time and the restrictions that it imposes.
Timers for Periodic and Asynchronous Tasks. This chapter discusses timing services provided for blocks executing within periodic tasks (that is, tasks running at the model's base rate or subrates).
The Simulink Coder product also provides timer support for blocks whose execution is asynchronous with respect to the periodic timing source of the model. See the following sections of the Asynchronous Support chapter:
Allocation of Timers. If you create or maintain an S-Function block that requires absolute or elapsed time data, it must register the requirement (see APIs for Accessing Timers). In multirate models, timers are allocated on a per-rate basis. For example, consider a model structured as follows:
There are three rates, A, B, and C, in the model.
No blocks running at rate B require absolute or elapsed time.
Two blocks running at rate C register a requirement for absolute time.
One block running at rate A registers a requirement for absolute time.
In this case, two timers are generated, running at rates A and C respectively. The timing engine updates the timers as the tasks associated with rates A and C execute. Blocks executing at rates A and C obtain time data from the timers associated with rates A and C.
Integer Timers in Generated Code. In the generated code, timers for absolute and elapsed time are implemented as unsigned integers. The default size is 64 bits. This is the amount of memory allocated for a timer if you specify a value of inf for the Application lifespan (days) parameter. For an application with a sample rate of 1000 MHz, a 64-bit counter will not overflow for more than 500 years. See Use Timers in Asynchronous Tasks and Control Memory Allocation for Time Counters for more information.
Elapsed Time Counters in Triggered Subsystems. Some blocks, such as the Discrete-Time Integrator block, perform computations requiring the elapsed time (delta T) since the previous block execution. Blocks requiring elapsed time data must register the requirement (see APIs for Accessing Timers). A triggered subsystem then allocates and maintains a single elapsed time counter if required. This timer functions at the subsystem level, not at the individual block level. The timer is generated if the triggered subsystem (or any unconditionally executed subsystem within the triggered subsystem) contains one or more blocks requiring elapsed time data.
Note If you are using simplified initialization mode, elapsed time is always reset on first execution after becoming enabled, whether or not the subsystem is configured to reset on enable. For more information, see Underspecified initialization detection in the Simulink documentation. |
About Timer APIs. This section describes APIs that let your S-functions take advantage of the efficiencies offered by the absolute and elapsed timers. SimStruct macros are provided for use in simulation, and TLC functions are provided for inlined code generation. Note that
To generate and use the new timers as described above, your S-functions must register the need to use an absolute or elapsed timer by calling ssSetNeedAbsoluteTime or ssSetNeedElapseTime in mdlInitializeSampleTime.
Existing S-functions that read absolute time but do not register by using these macros will continue to operate as expected, but will generate old-style, less efficient code.
C API for S-Functions. The SimStruct macros described in this section provide access to absolute and elapsed timers for S-functions during simulation.
In the functions below, the SimStruct *S argument is a pointer to the simstruct of the calling S-function.
void ssSetNeedAbsoluteTime(SimStruct *S, boolean b): if b is TRUE, registers that the calling S-function requires absolute time data, and allocates an absolute time counter for the rate at which the S-function executes (if such a counter has not already been allocated).
int ssGetNeedAbsoluteTime(SimStruct *S): returns 1 if the S-function has registered that it requires absolute time.
double ssGetTaskTime(SimStruct *S, tid): read absolute time for a given task with task identifier tid. ssGetTaskTime operates transparently, regardless of whether or not you use the new timer features. ssGetTaskTime is documented in the SimStruct Functions chapter of the Simulink documentation.
void ssSetNeedElapseTime(SimStruct *S, boolean b): if b is TRUE, registers that the calling S-function requires elapsed time data, and allocates an elapsed time counter for the triggered subsystem in which the S-function executes (if such a counter has not already been allocated). See also Elapsed Time Counters in Triggered Subsystems.
int ssGetNeedElapseTime(SimStruct *S): returns 1 if the S-function has registered that it requires elapsed time.
void ssGetElapseTime(SimStruct *S, (double *)elapseTime): returns, to the location pointed to by elapseTime, the value (as a double) of the elapsed time counter associated with the S-function.
void ssGetElapseTimeCounterDtype(SimStruct *S, (int *)dtype): returns the data type of the elapsed time counter associated with the S-function to the location pointed to by dtype. This function is intended for use with the ssGetElapseTimeCounter function (see below).
void ssGetElapseResolution(SimStruct *S, (double *)resolution): returns the resolution (that is, the sample time) of the elapsed time counter associated with the S-function to the location pointed to by resolution. This function is intended for use with the ssGetElapseTimeCounter function (see below).
void ssGetElapseTimeCounter(SimStruct *S, (void *)elapseTime): This function is provided for the use of blocks that require the elapsed time values for fixed-point computations. ssGetElapseTimeCounter returns, to the location pointed to by elapseTime, the integer value of the elapsed time counter associated with the S-function. If the counter size is 64 bits, the value is returned as an array of two 32-bit words, with the low-order word stored at the lower address.
To determine how to access the returned counter value, obtain the data type of the counter by calling ssGetElapseTimeCounterDtype, as in the following code:
int *y_dtype;
ssGetElapseTimeCounterDtype(S, y_dtype);
switch(*y_dtype) {
case SS_DOUBLE_UINT32:
{
uint32_T dataPtr[2];
ssGetElapseTimeCounter(S, dataPtr);
}
break;
case SS_UINT32:
{
uint32_T dataPtr[1];
ssGetElapseTimeCounter(S, dataPtr);
}
break;
case SS_UINT16:
{
uint16_T dataPtr[1];
ssGetElapseTimeCounter(S, dataPtr);
}
break;
case SS_UINT8:
{
uint8_T dataPtr[1];
ssGetElapseTimeCounter(S, dataPtr);
}
break;
case SS_DOUBLE:
{
real_T dataPtr[1];
ssGetElapseTimeCounter(S, dataPtr);
}
break;
default:
ssSetErrorStatus(S, "Invalid data type for elaspe time
counter");
break;
}If you want to use the actual elapsed time, issue a call to the ssGetElapseTime function to access the elapsed time directly. You do not need to get the counter value and then calculate the elapsed time.
double *y_elapseTime; . . . ssGetElapseTime(S, elapseTime)
TLC API for Code Generation. The following TLC functions support elapsed time counters in generated code when you inline S-functions by writing TLC scripts for them.
LibGetTaskTimeFromTID(block): Generates code to read the absolute time for the task in which block executes.
LibGetTaskTimeFromTID is documented with other sample time functions in the TLC Function Library Reference pages of the Target Language Compiler documentation.
LibGetElapseTime(system): Generates code to read the elapsed time counter for system. (system is the parent system of the calling block.) See Elapsed Timer Code Generation Example for an example of code generated by this function.
LibGetElapseTimeCounter(system): Generates code to read the integer value of the elapsed time counter for system. (system is the parent system of the calling block.) This function should be used in conjunction with LibGetElapseTimeCounterDtypeId and LibGetElapseTimeResolution. (See the discussion of ssGetElapseTimeCounter above.)
LibGetElapseTimeCounterDtypeId(system): Generates code that returns the data type of the elapsed time counter for system. (system is the parent system of the calling block.)
LibGetElapseTimeResolution(system): Generates code that returns the resolution of the elapsed time counter for system. (system is the parent system of the calling block.)
This section shows a simple model illustrating how an elapsed time counter is generated and used by a Discrete-Time Integrator block within a triggered subsystem. The following block diagrams show the model elapseTime_exp, which contains subsystem Amplifier, which includes a Discrete-Time Integrator block.
elapseTime_exp Model

Amplifier Subsystem

A 32-bit timer for the base rate (the only rate in this model) is defined within the rtModel structure, as follows, in model.h.
/*
* Timing:
* The following substructure contains information regarding
* the timing information for the model.
*/
struct {
time_T stepSize;
uint32_T clockTick0;
uint32_T clockTickH0;
time_T stepSize0;
time_T tStart;
time_T tFinal;
time_T timeOfLastOutput;
void *timingData;
real_T *varNextHitTimesList;
SimTimeStep simTimeStep;
boolean_T stopRequestedFlag;
time_T *sampleTimes;
time_T *offsetTimes;
int_T *sampleTimeTaskIDPtr;
int_T *sampleHits;
int_T *perTaskSampleHits;
time_T *t;
time_T sampleTimesArray[1];
time_T offsetTimesArray[1];
int_T sampleTimeTaskIDArray[1];
int_T sampleHitArray[1];
int_T perTaskSampleHitsArray[1];
time_T tArray[1];
} Timing;Had the target been ERT instead of GRT, the Timing structure would have been pruned to contain only the data required by the model, as follows:
/* Real-time Model Data Structure */ (for ERT!)
struct _RT_MODEL_elapseTime_exp_Tag {
/*
* Timing:
* The following substructure contains information regarding
* the timing information for the model.
*/
struct {
uint32_T clockTick0;
} Timing;
};Storage for the previous-time value of the Amplifier subsystem (Amplifier_PREV_T) is allocated in the D_Work(states) structure in model.h.
typedef struct D_Work_elapseTime_exp_tag {
real_T DiscreteTimeIntegrator_DSTATE; /* '<S1>/Discrete-Time
Integrator' */
int32_T clockTickCounter; /* '<Root>/Pulse Generator' */
uint32_T Amplifier_PREV_T; /* '<Root>/Amplifier' */
} D_Work_elapseTime_exp;These structures are declared in model.c:
/* Block states (auto storage) */ D_Work_elapseTime_exp elapseTime_exp_DWork; . . . /* Real-time model */ rtModel_elapseTime_exp elapseTime_exp_M_; rtModel_elapseTime_exp *elapseTime_exp_M = &elapseTime_exp_M_;
The elapsed time computation is performed as follows within the model_step function:
/* Output and update for trigger system: '<Root>/Amplifier' */
uint32_T rt_currentTime =
((uint32_T)elapseTime_exp_M->Timing.clockTick0);
uint32_T rt_elapseTime = rt_currentTime -
elapseTime_exp_DWork.Amplifier_PREV_T;
elapseTime_exp_DWork.Amplifier_PREV_T = rt_currentTime;As shown above, the elapsed time is maintained as a state of the triggered subsystem. The Discrete-Time Integrator block finally performs its output and update computations using the elapsed time.
/* DiscreteIntegrator: '<S1>/Discrete-Time Integrator' */ OUTPUT = elapseTime_exp_DWork.DiscreteTimeIntegrator_DSTATE; /* Update for DiscreteIntegrator: '<S1>/Discrete-Time Integrator'*/ elapseTime_exp_DWork.DiscreteTimeIntegrator_DSTATE += 0.3 * (real_T)rt_elapseTime * 1.5 ;
Because the triggered subsystem maintains the elapsed time, the TLC implementation of the Discrete-Time Integrator block needs only a single call to LibGetElapseTime to access the elapsed time value.
About Absolute Time Limitations. Absolute time is the time that has elapsed from the beginning of program execution to the present time, as distinct from elapsed time, the interval between two events. See Absolute and Elapsed Time Computation for more information.
When you design an application that is intended to run indefinitely, you must take care when logging time values, or using charts or blocks that depend on absolute time. If the value of time reaches the largest value that can be represented by the data type used by the timer to store time, the timer overflows and the logged time or block output is incorrect.
If your target uses rtModel, you can avoid timer overflow by specifying a value for the Application life span parameter. See Integer Timers in Generated Code for more information.
Logging Absolute Time. If you log time values by enabling Configuration Parameters > Data Import/Export > Save to workspace > Time your model uses absolute time.
Absolute Time in Stateflow Charts. Every Stateflow chart that uses time is dependent on absolute time. The only way to eliminate the dependency is to change the Stateflow chart to not use time.
Blocks that Depend on Absolute Time. The following Simulink blocks depend on absolute time:
Discrete-Time Integrator (only when used in triggered subsystems)
Sine Wave (only when the Sine type parameter is set to Time-based)
To Workspace (only when logging to StructureWithTime format)
In addition to the Simulink blocks above, blocks in other blocksets may depend on absolute time. See the documentation for the blocksets that you use.
For details about solver options, see Solver Pane in the Simulink reference documentation.
The stop time must be greater than or equal to the start time. If the stop time is zero, or if the total simulation time (Stop minus Start) is less than zero, the generated program runs for one step. If the stop time is set to inf, the generated program runs indefinitely.
When using the GRT or Wind River Systems Tornado targets, you can override the stop time when running a generated program from the Microsoft Windows command prompt or UNIX[1] command line. To override the stop time that was set during code generation, use the -tf switch.
model -tf n
The program runs for n seconds. If n = inf, the program runs indefinitely. See Getting Started in the Simulink Coder documentation for an example of the use of this option.
Certain blocks have a dependency on absolute time. If you are designing a program that is intended to run indefinitely (Stop time = inf), and your generated code does not use the rtModel data structure (that is, it uses simstructs instead), you must not use these blocks. See Limitations on the Use of Absolute Time for a list of blocks that can potentially overflow timers.
If you know how long an application that depends on absolute time needs to run, you can prevent the timers from overflowing and force the use of optimal word sizes by specifying the Application lifespan (days) parameter on the Optimization pane. See Control Memory Allocation for Time Counters for details.
For code generation, you must configure a model to use a fixed-step solver for all targets except the S-function and RSim targets. You can configure the S-function and RSim targets with a fixed-step or variable-step solver.
The Simulink Coder product supports both single-tasking and multitasking modes for periodic sample times. See Scheduling for details.
[1] UNIX is a registered trademark of The Open Group in the United States and other countries.
![]() | Component-Based Modeling | Supported Products and Block Usage | ![]() |

Learn more about Simulink through this collection of videos, articles, technical literature and the Getting Started with Simulink Guide.
| © 1984-2012- The MathWorks, Inc. - Site Help - Patents - Trademarks - Privacy Policy - Preventing Piracy - RSS |