MATLAB Examples

Spawn and Synchronize Execution of RTOS Task

This example shows how to simulate and generate code for asynchronous events on a multitasking real-time operating system (VxWorks®). The model shows different techniques for handling asynchronous events depending on the size of the triggered subsystems.

Contents

About the Example Model

Open the example model rtwdemo_async.

The model simulates an interrupt source and includes an Async Interrupt block, a Task Sync block, function-call subsystems Count and Algorithm, and Rate Transition blocks. The Async Interrupt block creates two Versa Module Eurocard (VME) interrupt service routines (ISRs) that pass interrupt signals to subsystem Count and the Task Sync block. You can place an Async Interrupt block between a simulated interrupt source and one of the following:

  • Function call subsystem
  • Task Sync block
  • A Stateflow® chart configured for a function call input event
  • A referenced model with an Inport block that connects to one of the preceding model elements

The Async Interrupt and Task Sync blocks enable the subsystems to execute asynchronously.

Count represents a simple interrupt service routine (ISR) that executes at interrupt level. It is best to keep ISRs as simple as possible. This subsystem includes only a Discrete-Time Integrator block.

Algorithm includes more substance. It includes multiple blocks and produces two output values. Execution of larger subsystems at interrupt level can significantly impact response time for interrupts of equal and lower priority in the system. A better solution for larger subsystems is to use the Task Sync block to represent the ISR for the function-call subsystem.

The Async Interrupt block generates calls to ISRs. Place the block between a simulated interrupt source and one of the following:

  • Function call subsystem
  • Task Sync block
  • A Stateflow® chart configured for a function call input event

For each specified interrupt level, the block generates a Versa Module Eurocard (VME) ISR that executes the connected subsystem, Task Sync block, or chart.

In the example model, the Async Interrupt block is configured for VME interrupts 1 and 2, by using interrupt vector offsets 192 and 193. Interrupt 1 connects directly to subsystem Count. Interrupt 2 connects to a Task Sync block, which serves as the ISR for Algorithm. Place a Task Sync block in one of the following locations:

  • Between an Async Interrupt block and a function-call subsystem or Stateflow® chart.
  • At the output port of a Stateflow® chart that has an event, Output to Simulink, that you configure as a function call.

In the example model, the Task Sync block is between the Async Interrupt block and function-call subsystem Algorithm. The Task Sync block is configured with the task name Task(), a priority of 50, a stack size of 8192, and data transfers of the task synchronized with the caller task. The spawned task uses a semaphore to synchronize task execution. The Async Interrupt block triggers a release of the task semaphore.

Four Rate Transition blocks handle data transfers between ports that operate at different rates. In two instances, Protected Rate Transition blocks protect data transfers (prevent them from being preempted and corrupted). In the other two instances, Unprotected Rate Transition blocks introduce no special behavior. Their presence informs Simulink® of a rate transition.

The code generated for the Async Interrupt and Task Sync blocks is tailored for the example RTOS (VxWorks®). However, you can modify the blocks to generate code specific to your run-time environment.

Data Transfer Assumptions

  • Data transfers occur between one reading task and one writing task.
  • A read or write operation on a byte-size variable is atomic.
  • When two tasks interact, only one can preempt the other.
  • For periodic tasks, the task with the faster rate has higher priority than the task with the slower rate. The task with the faster rate preempts the tasks with slower rates.
  • Tasks run on a single processor. Time slicing is not allowed.
  • Processes do not stop and restart, especially while data is being transferred between tasks.

Simulate the Model

Simulate the model. By default, the model is configured to show sample times in different colors. Discrete sample times for input and output appear red and green, respectively. Constants are reddish-blue. Asynchronous interrupts and tasks are purple. The Rate Transition Blocks, which are a hybrid rate (their input and output sample times can differ), are yellow.

Generate Code and Report

Generate code and a code generation report for the model. Generated code for the Async Interrupt and Task Sync blocks is for the example RTOS (VxWorks®). However, you can modify the blocks to generate code for another run-time environment.

1. Create a temporary folder for the build and inspection process.

2. Build the model.

### Starting build procedure for model: rtwdemo_async
Warning: Simulink Coder: The tornado.tlc target will be removed in a future
release.
 
### Successful completion of code generation for model: rtwdemo_async

Review Initialization Code

Open the generated source file rtwdemo_async.c. The initialization code:

1. Creates and initializes the synchronization semaphore Task0_semaphore.

  *(SEM_ID *)rtwdemo_async_DW.SFunction_PWORK.SemID = semBCreate(SEM_Q_PRIORITY,
    SEM_EMPTY);
  if (rtwdemo_async_DW.SFunction_PWORK.SemID == NULL) {
    printf("semBCreate call failed for block Task0.\n");
  }

2. Spawns task task0 and assigns the task priority 50.

  rtwdemo_async_DW.SFunction_IWORK.TaskID = taskSpawn("Task0",
    50.0,
    VX_FP_TASK,
    8192.0,
    (FUNCPTR)Task0,
    0, 0, 0, 0, 0, 0, 0,0, 0, 0);
  if (rtwdemo_async_DW.SFunction_IWORK.TaskID == ERROR) {
    printf("taskSpawn call failed for block Task0.\n");
  }

  /* End of Start for S-Function (vxtask1): '<S5>/S-Function' */

  /* 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);

3. Connects and enables ISR isr_num1_vec192 for interrupt 1 and ISR isr_num2_vec193 for interrupt 2.

  {
    int32_T i;

    /* InitializeConditions for RateTransition: '<Root>/Protected RT1' */
    for (i = 0; i < 60; i++) {
      rtwdemo_async_DW.ProtectedRT1_Buffer[i] = 0.0;
    }

    /* End of InitializeConditions for RateTransition: '<Root>/Protected RT1' */

    /* InitializeConditions for RateTransition: '<Root>/Protected RT2' */
    for (i = 0; i < 60; i++) {
      rtwdemo_async_DW.ProtectedRT2_Buffer[i] = 0.0;
    }

    /* End of InitializeConditions for RateTransition: '<Root>/Protected RT2' */

    /* SystemInitialize for S-Function (vxinterrupt1): '<Root>/Async Interrupt' incorporates:
     *  SubSystem: '<Root>/Count'
     */
    /* System initialize for function-call system: '<Root>/Count' */
    rtwdemo_async_DW.Count_PREV_T = rtwdemo_async_M->Timing.clockTick2;

    /* InitializeConditions for DiscreteIntegrator: '<S2>/Integrator' */
    rtwdemo_async_DW.Integrator_DSTATE_l = 0.0;

    /* SystemInitialize for Outport: '<Root>/Out1' incorporates:
     *  Outport: '<S2>/Out'
     */
    rtwdemo_async_Y.Out1 = 0.0;

    /* SystemInitialize for S-Function (vxinterrupt1): '<Root>/Async Interrupt' incorporates:
     *  SubSystem: '<S4>/Subsystem'
     */

    /* System initialize for function-call system: '<S4>/Subsystem' */

    /* SystemInitialize for S-Function (vxtask1): '<S5>/S-Function' incorporates:
     *  SubSystem: '<Root>/Algorithm'
     */

    /* System initialize for function-call system: '<Root>/Algorithm' */
    rtwdemo_async_M->Timing.clockTick4 = rtwdemo_async_M->Timing.clockTick3;
    rtwdemo_async_DW.Algorithm_PREV_T = rtwdemo_async_M->Timing.clockTick4;

    /* InitializeConditions for DiscreteIntegrator: '<S1>/Integrator' */
    rtwdemo_async_DW.Integrator_DSTATE = 0.0;

    /* SystemInitialize for Outport: '<S1>/Out1' */
    memset(&rtwdemo_async_B.Sum[0], 0, 60U * sizeof(real_T));

    /* SystemInitialize for Outport: '<Root>/Out3' incorporates:
     *  Outport: '<S1>/Out2'
     */
    rtwdemo_async_Y.Out3 = 0.0;

    /* End of SystemInitialize for S-Function (vxtask1): '<S5>/S-Function' */

    /* End of SystemInitialize for S-Function (vxinterrupt1): '<Root>/Async Interrupt' */
  }
}

/* Model terminate function */
static void rtwdemo_async_terminate(void)
{
  /* Terminate for S-Function (vxinterrupt1): '<Root>/Async Interrupt' */

  /* 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);

  /* End of Terminate for S-Function (vxinterrupt1): '<Root>/Async Interrupt' */

  /* Terminate for S-Function (vxinterrupt1): '<Root>/Async Interrupt' incorporates:
   *  SubSystem: '<S4>/Subsystem'
   */

  /* Termination for function-call system: '<S4>/Subsystem' */

  /* Terminate for S-Function (vxtask1): '<S5>/S-Function' */

  /* VxWorks Task Block: '<S5>/S-Function' (vxtask1) */
  /* Destroy task: Task0 */
  taskDelete(rtwdemo_async_DW.SFunction_IWORK.TaskID);

  /* End of Terminate for S-Function (vxtask1): '<S5>/S-Function' */

  /* End of Terminate for S-Function (vxinterrupt1): '<Root>/Async Interrupt' */
}

/*========================================================================*
 * Start of Classic call interface                                        *
 *========================================================================*/
void MdlOutputs(int_T tid)
{
  rtwdemo_async_output(tid);
}

void MdlUpdate(int_T tid)
{
  rtwdemo_async_update(tid);
}

void MdlInitializeSizes(void)
{
}

void MdlInitializeSampleTimes(void)
{
}

void MdlInitialize(void)
{
}

void MdlStart(void)
{
  rtwdemo_async_initialize();
}

void MdlTerminate(void)
{
  rtwdemo_async_terminate();
}

/* Registration function */
RT_MODEL_rtwdemo_async_T *rtwdemo_async(void)
{
  /* Registration code */

  /* initialize non-finites */
  rt_InitInfAndNaN(sizeof(real_T));

  /* initialize real-time model */
  (void) memset((void *)rtwdemo_async_M, 0,
                sizeof(RT_MODEL_rtwdemo_async_T));

  /* Initialize timing info */
  {
    int_T *mdlTsMap = rtwdemo_async_M->Timing.sampleTimeTaskIDArray;
    mdlTsMap[0] = 0;
    mdlTsMap[1] = 1;
    rtwdemo_async_M->Timing.sampleTimeTaskIDPtr = (&mdlTsMap[0]);
    rtwdemo_async_M->Timing.sampleTimes =
      (&rtwdemo_async_M->Timing.sampleTimesArray[0]);
    rtwdemo_async_M->Timing.offsetTimes =
      (&rtwdemo_async_M->Timing.offsetTimesArray[0]);

    /* task periods */
    rtwdemo_async_M->Timing.sampleTimes[0] = (0.016666666666666666);
    rtwdemo_async_M->Timing.sampleTimes[1] = (0.05);

    /* task offsets */
    rtwdemo_async_M->Timing.offsetTimes[0] = (0.0);
    rtwdemo_async_M->Timing.offsetTimes[1] = (0.0);
  }

  rtmSetTPtr(rtwdemo_async_M, &rtwdemo_async_M->Timing.tArray[0]);

  {
    int_T *mdlSampleHits = rtwdemo_async_M->Timing.sampleHitArray;
    int_T *mdlPerTaskSampleHits = rtwdemo_async_M->Timing.perTaskSampleHitsArray;
    rtwdemo_async_M->Timing.perTaskSampleHits = (&mdlPerTaskSampleHits[0]);
    mdlSampleHits[0] = 1;
    rtwdemo_async_M->Timing.sampleHits = (&mdlSampleHits[0]);
  }

  rtmSetTFinal(rtwdemo_async_M, 0.5);
  rtwdemo_async_M->Timing.stepSize0 = 0.016666666666666666;
  rtwdemo_async_M->Timing.stepSize1 = 0.05;
  rtwdemo_async_M->solverInfoPtr = (&rtwdemo_async_M->solverInfo);
  rtwdemo_async_M->Timing.stepSize = (0.016666666666666666);
  rtsiSetFixedStepSize(&rtwdemo_async_M->solverInfo, 0.016666666666666666);
  rtsiSetSolverMode(&rtwdemo_async_M->solverInfo, SOLVER_MODE_MULTITASKING);

  /* block I/O */
  rtwdemo_async_M->blockIO = ((void *) &rtwdemo_async_B);
  (void) memset(((void *) &rtwdemo_async_B), 0,
                sizeof(B_rtwdemo_async_T));

  /* states (dwork) */
  rtwdemo_async_M->dwork = ((void *) &rtwdemo_async_DW);
  (void) memset((void *)&rtwdemo_async_DW, 0,
                sizeof(DW_rtwdemo_async_T));

  /* external inputs */
  rtwdemo_async_M->inputs = (((void*)&rtwdemo_async_U));
  (void)memset((void *)&rtwdemo_async_U, 0, sizeof(ExtU_rtwdemo_async_T));

  /* external outputs */
  rtwdemo_async_M->outputs = (&rtwdemo_async_Y);
  (void) memset((void *)&rtwdemo_async_Y, 0,
                sizeof(ExtY_rtwdemo_async_T));

  /* Initialize Sizes */
  rtwdemo_async_M->Sizes.numContStates = (0);/* Number of continuous states */
  rtwdemo_async_M->Sizes.numY = (3);   /* Number of model outputs */
  rtwdemo_async_M->Sizes.numU = (60);  /* Number of model inputs */
  rtwdemo_async_M->Sizes.sysDirFeedThru = (0);/* The model is not direct feedthrough */
  rtwdemo_async_M->Sizes.numSampTimes = (2);/* Number of sample times */
  rtwdemo_async_M->Sizes.numBlocks = (17);/* Number of blocks */
  rtwdemo_async_M->Sizes.numBlockIO = (4);/* Number of block outputs */
  return rtwdemo_async_M;
}

/*========================================================================*
 * End of Classic call interface                                          *
 *========================================================================*/

The order of these operations is important. Before the code generator enables the interrupt that activates the task, it must spawn the task.

Review Task and Task Synchronization Code

In the generated source file rtwdemo_async.c, review the task and task synchronization code.

The code generator produces the code for function Task0 from the Task Sync block. That function includes a small amount of interrupt-level code and runs as an RTOS task.

The task waits in an infinite for loop until the system releases a synchronization semaphore. If the system releases the semaphore, the function updates its task timer and calls the code generated for the Algorithm subsystem.

In the example model, the Synchronize the data transfer of this task with the caller task parameter for the Task Sync block is set. This parameter setting updates the timer associated with the Task Sync block (rtM->Timing.clockTick2) with the value of the timer that the Async Interrupt block (rtM->Timing.clockTick3) maintains. As a result, code for blocks within the Algorithm subsystem use timer values that are based on the time of the most recent interrupt, rather than the most recent activation of Task0.

{
  /* Wait for semaphore to be released by system: rtwdemo_async/Task Sync */
  for (;;) {
    if (semTake(*(SEM_ID *)rtwdemo_async_DW.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_DW.SFunction_PWORK.SemID, WAIT_FOREVER);
    }

    /* Use the upstream clock tick counter for this Task. */
    rtwdemo_async_M->Timing.clockTick4 = rtwdemo_async_M->Timing.clockTick3;

    /* Call the system: '<Root>/Algorithm' */
    {
      {
        int32_T tmp;
        int32_T i;

        /* RateTransition: '<Root>/Protected RT1' */
        tmp = rtwdemo_async_DW.ProtectedRT1_ActiveBufIdx * 60;
        for (i = 0; i < 60; i++) {
          rtwdemo_async_B.ProtectedRT1[i] =
            rtwdemo_async_DW.ProtectedRT1_Buffer[i + tmp];
        }

        /* End of RateTransition: '<Root>/Protected RT1' */

        /* S-Function (vxinterrupt1): '<Root>/Async Interrupt' */

        /* S-Function (vxtask1): '<S5>/S-Function' */

        /* Output and update for function-call system: '<Root>/Algorithm' */
        {
          real_T tmp;
          int32_T i;
          uint32_T Algorithm_ELAPS_T_tmp;
          rtwdemo_async_M->Timing.clockTick4 =
            rtwdemo_async_M->Timing.clockTick3;
          Algorithm_ELAPS_T_tmp = rtwdemo_async_M->Timing.clockTick4;
          rtwdemo_async_DW.Algorithm_ELAPS_T = Algorithm_ELAPS_T_tmp -
            rtwdemo_async_DW.Algorithm_PREV_T;
          rtwdemo_async_DW.Algorithm_PREV_T = Algorithm_ELAPS_T_tmp;

          /* Outport: '<Root>/Out3' incorporates:
           *  DiscreteIntegrator: '<S1>/Integrator'
           */
          rtwdemo_async_Y.Out3 = rtwdemo_async_DW.Integrator_DSTATE;

          /* Sum: '<S1>/Sum' incorporates:
           *  Constant: '<S1>/Offset'
           */
          for (i = 0; i < 60; i++) {
            rtwdemo_async_B.Sum[i] = rtwdemo_async_B.ProtectedRT1[i] + 1.25;
          }

          /* End of Sum: '<S1>/Sum' */

          /* Sum: '<S1>/Sum1' */
          tmp = rtwdemo_async_B.Sum[0];
          for (i = 0; i < 59; i++) {
            tmp += rtwdemo_async_B.Sum[i + 1];
          }

          /* Update for DiscreteIntegrator: '<S1>/Integrator' incorporates:
           *  Sum: '<S1>/Sum1'
           */
          rtwdemo_async_DW.Integrator_DSTATE += 0.016666666666666666 * (real_T)
            rtwdemo_async_DW.Algorithm_ELAPS_T * tmp;
        }

        /* End of Outputs for S-Function (vxtask1): '<S5>/S-Function' */

        /* End of Outputs for S-Function (vxinterrupt1): '<Root>/Async Interrupt' */
      }

      {
        int32_T i;

        /* Update for RateTransition: '<Root>/Protected RT2' */
        for (i = 0; i < 60; i++) {
          rtwdemo_async_DW.ProtectedRT2_Buffer[i +
            (rtwdemo_async_DW.ProtectedRT2_ActiveBufIdx == 0) * 60] =
            rtwdemo_async_B.Sum[i];
        }

        rtwdemo_async_DW.ProtectedRT2_ActiveBufIdx = (int8_T)
          (rtwdemo_async_DW.ProtectedRT2_ActiveBufIdx == 0);

        /* End of Update for RateTransition: '<Root>/Protected RT2' */
      }
    }
  }
}

The code generator produces code for ISRs isr_num1_vec192 and isr_num2_vec293. ISR isr_num2_vec192:

  • Disables interrupts.
  • Saves floating-point context.
  • Calls the code generated for the subsystem that connects to the referenced model Inport block, which receives the interrupt.
  • Restores floating-point context.
  • Reenables interrupts.
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 */
  rtwdemo_async_M->Timing.clockTick2 = tickGet();

  /* disable interrupts (system is configured as non-preemptive) */
  lock = intLock();

  /* save floating point context */
  fppSave(&context);

  /* Call the system: '<Root>/Count' */
  {
    /* S-Function (vxinterrupt1): '<Root>/Async Interrupt' */

    /* Output and update for function-call system: '<Root>/Count' */
    {
      uint32_T Count_ELAPS_T_tmp;
      Count_ELAPS_T_tmp = rtwdemo_async_M->Timing.clockTick2;
      rtwdemo_async_DW.Count_ELAPS_T = Count_ELAPS_T_tmp -
        rtwdemo_async_DW.Count_PREV_T;
      rtwdemo_async_DW.Count_PREV_T = Count_ELAPS_T_tmp;

      /* Outport: '<Root>/Out1' incorporates:
       *  DiscreteIntegrator: '<S2>/Integrator'
       */
      rtwdemo_async_Y.Out1 = rtwdemo_async_DW.Integrator_DSTATE_l;

      /* Update for DiscreteIntegrator: '<S2>/Integrator' */
      rtwdemo_async_DW.Integrator_DSTATE_l += 0.016666666666666666 * (real_T)
        rtwdemo_async_DW.Count_ELAPS_T;
    }

    /* End of Outputs for S-Function (vxinterrupt1): '<Root>/Async Interrupt' */
  }

  /* restore floating point context */
  fppRestore(&context);

  /* re-enable interrupts */
  intUnlock(lock);
}

/* VxWorks Interrupt Block: '<Root>/Async Interrupt' */

ISR isr_num2_vec293 maintains a timer that stores the tick count at the time that the interrupt occurs. After updating the timer, the ISR releases the semaphore that activates Task0.

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' */
  {
    /* S-Function (vxinterrupt1): '<Root>/Async Interrupt' */

    /* Output and update for function-call system: '<S4>/Subsystem' */

    /* S-Function (vxtask1): '<S5>/S-Function' */

    /* VxWorks Task Block: '<S5>/S-Function' (vxtask1) */
    /* Release semaphore for system task: Task0 */
    semGive(*(SEM_ID *)rtwdemo_async_DW.SFunction_PWORK.SemID);

    /* End of Outputs for S-Function (vxtask1): '<S5>/S-Function' */

    /* End of Outputs for S-Function (vxinterrupt1): '<Root>/Async Interrupt' */
  }
}

/* VxWorks Task Block: '<S5>/S-Function' (vxtask1) */

Review Task Termination Code

The Task Sync block generates the following termination code.

static void rtwdemo_async_terminate(void)
{
  /* Terminate for S-Function (vxinterrupt1): '<Root>/Async Interrupt' */

  /* 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);

  /* End of Terminate for S-Function (vxinterrupt1): '<Root>/Async Interrupt' */

  /* Terminate for S-Function (vxinterrupt1): '<Root>/Async Interrupt' incorporates:
   *  SubSystem: '<S4>/Subsystem'
   */

  /* Termination for function-call system: '<S4>/Subsystem' */

  /* Terminate for S-Function (vxtask1): '<S5>/S-Function' */

  /* VxWorks Task Block: '<S5>/S-Function' (vxtask1) */
  /* Destroy task: Task0 */
  taskDelete(rtwdemo_async_DW.SFunction_IWORK.TaskID);

  /* End of Terminate for S-Function (vxtask1): '<S5>/S-Function' */

  /* End of Terminate for S-Function (vxinterrupt1): '<Root>/Async Interrupt' */
}

Related Information