Skip to Main Content Skip to Search
Product Documentation

Scheduling

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.

About Scheduling

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.

Single-Tasking and Multitasking Execution Modes

About Tasking Modes

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.

Execute Multitasking Models

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:

Multitasking and Pseudomultitasking Modes

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.

Build a Program for Multitasking Execution

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.

Single-Tasking Mode

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.

Build a Program for Single-Tasking Execution

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:

Model Execution and Rate Transitions

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.

Simulate Models with the Simulink Product

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).

Execute Models in Real Time

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 Versus Multitasking Operation

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.

Handle Rate Transitions

About Rate Transitions

Two periodic sample rate transitions can exist within a model:

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.

Data Transfer Problems

Rate Transition blocks deal with issues of data integrity and determinism associated with data transfer between blocks running at different rates.

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.

Data Transfer Assumptions

When processing data transfers between tasks, the Simulink Coder software assumes the following:

Rate Transition Block Options

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.

Thus the Rate Transition block offers three modes of operation with respect to data transfer. In order of level of safety:

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,

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.

Faster to Slower Transitions in a Simulink Model

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.

Faster to Slower Transitions in Real Time

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).

Slower to Faster Transitions in a Simulink Model

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.

Slower to Faster Transitions 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:

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):

  1. 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.

  2. The Rate Transition update uses the output of the 2 second task to update its internal state.

  3. 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.

Example: Single-Tasking and Multitasking Execution of a Model

Introduction

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.

Single-Tasking Execution

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
(in Execution Order)

Sample Time
(in Seconds)

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.

Multitasking Execution

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 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
(in Execution Order)

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.
Output runs at the output port sample time under 0.1 second task.
Update runs at input port sample time under 1 second task.
For more information on port-based sample times, see Inheriting Sample Times in the Simulink documentation.

Y

Y

A

0.1 second task

Y

N

B

The Rate Transition block uses port-based sample times.
Output runs at the output port sample time under 0.1 second task.
For more information on port-based sample times, see Inheriting Sample Times in the Simulink documentation.

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.

Handle Asynchronous Events

About Asynchronous Events

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

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:

Demos and Additional Information.  Additional information relevant to the topics in this chapter can be found in

Handling Interrupts

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

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:

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 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,

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.

Initialization Code.  

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:

Model Termination Code.  

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:

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

Rate Transitions and Asynchronous Blocks

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:

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:

Alternatively, you can use target-specific rate transition blocks. The following blocks are available for the VxWorks RTOS:

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

  1. 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.

  2. 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:

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:

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.

Use Timers in Asynchronous Tasks

An ISR can set a source for absolute time. This is done with the function ssSetTimeSource, which has the following three options:

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.

Create a Customized Asynchronous Library

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

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 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:

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:

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

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

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:

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

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 |
}
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

Import Asynchronous Event Data for Simulation

Capabilities.  You can inport asynchronous event data into a function-call subsystem via an Inport block. For standalone fixed-step simulations, you can specify:

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.

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.

  1. To input data via the Configuration Parameters dialog box,

    1. Select Simulation > Configuration Parameters > Data Import/Export.

    2. Select the Input parameter.

    3. 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.

  2. By default, the Time and Output options are selected and the output variables are named tout and yout.

  3. Simulate the model.

  4. 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    18

    Here 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 Support Limitations

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.

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

  2. 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.

  3. Convert the subsystem to an atomic subsystem. Select Edit > Subsystem Parameters > Treat as atomic unit.

  4. 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.

  5. Replace the subsystem in the top model with the new model reference block.

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

  7. 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.)

  8. 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.

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

Timers

Absolute and Elapsed Time Computation

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

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:

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.

APIs for Accessing Timers

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

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.

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.

Elapsed Timer Code Generation Example

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.

Limitations on the Use of Absolute Time

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:

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.

Configure Scheduling

For details about solver options, see Solver Pane in the Simulink reference documentation.

Configure Start and Stop Times

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.

Configure the Solver Type

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.

Configure the Tasking Mode

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.

  


Related Products & Applications

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