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


 © 1984-2008- The MathWorks, Inc.    -   Site Help   -   Patents   -   Trademarks   -   Privacy Policy   -   Preventing Piracy   -   RSS