| Products & Services | Solutions | Academia | Support | User Community | Company |
| Download Product Updates | | | Get Pricing | | | Trial Software |
| Documentation → Real-Time Workshop |
| Contents | Index |
| Learn more about Real-Time Workshop |
Real-Time Workshop 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 appropriately, 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.
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 Creating 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
Ensure 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 Real-Time Workshop Reference. The use of the 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 Considerations.
The VxWorks library (vxlib1) is part of the Real-Time Workshop library. You can access the VxWorks library by opening the Simulink Library Browser, clicking the Real-Time Workshop entry, and clicking VxWorks. Alternatively, enter the MATLAB command vxlib1.
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 — Real-Time Embedded Coder. This target is provided with the Real-Time Workshop 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. This target is included with the Real-Time Workshop product (see Interfacing With a Real-Time Operating System ).
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.
Scheduling Considerations, discusses general multitasking and rate transition issues for periodic models.
Interfacing With a Real-Time Operating System , discusses the Tornado (VxWorks RTOS) target example.
The Real-Time Workshop 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.
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.

Requirements and Restrictions. 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
Performance Considerations. 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 Spawning 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 to ensure that no code is generated for that signal path. The Real-Time Workshop 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, ensure that the sample times of the blocks driving the simulation input are used 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 Using 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);
}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.

Initialization Code. 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);
}
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 Considerations.
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 the inserted blocks to ensure data integrity but not determinism during data transfers.
For asynchronous rate transitions, the Rate Transition block guarantees data integrity, but cannot guarantee 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 ensure data integrity and priorities are assigned to the tasks associated with the blocks, the Real-Time Workshop 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 Real-Time Workshop 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.
For rate transitions that involve asynchronous tasks, you can ensure data integrity. However, you cannot ensure 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:
Ensure 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
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 Real-Time Workshop 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 Real-Time Workshop code generator generates timer code in the function call subsystem, updating the clock tick variable from the base rate clock tick. Data integrity is ensured 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 Using Timers in Asynchronous Tasks and Controlling 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 the appropriate 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.
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 Writing S-Functions documentation describes MEX S-functions and the S-function API in general.
The Target Language Compiler documentation and Integrating External Code Using S-Functions 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).
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.
C MEX Block Implementation. 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.
Generating #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 LibGetBlockAttrribute.) 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 appropriate counter. In your implementation, you should generate either an equivalent call to the target RTOS, or generate code to read the appropriate 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 appropriate calls to your target RTOS.
Terminate Function.The Terminate function generates the call sysIntDisable to disable each ISR. You should replace this with appropriate calls to your target RTOS.
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.
C MEX Block Implementation. 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 |
}
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 |
|---|---|
LibGetBlockAttrribute | 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. |
LibBlockExecuteFcnCall | For use by inlined S-functions with function call outputs. Generates code to execute a function call subsystem. LibBlockExecuteFcnCall calls the lower-level function LibExecuteFcnCall, but has a simplified argument list. See the Target Language Compiler documentation for more information on LibExecuteFcnCall. |
LibGetCallerClockTickCounter | Provides access to an upstream asynchronous task's time counter. |
LibGetCallerClockTickCounterHighWord | Provides access to the high word of an upstream asynchronous task's time counter. |
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 |
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.
![]() | Example: Single-Tasking and Multitasking Execution of a Model | Using Timers | ![]() |

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