Products & Services Solutions Academia Support User Community Company

Learn more about Real-Time Workshop   

Handling Asynchronous Events

Introduction

About Asynchronous Support

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.

Overview of 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 Creating 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 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.

Accessing the VxWorks Block Library

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.

Generating 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

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

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.

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

Spawning 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

Introduction

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:

Handling Rate Transitions for Asynchronous Tasks

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:

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

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

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 Using Timers in Asynchronous Tasks and Controlling Memory Allocation for Time Counters for more information.

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

Creating a Customized Asynchronous Library

Introduction

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.

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:

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.

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.  

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

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

Asynchronous Support Limitations

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.

  


Related Products & Applications

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