CS452 - Real-Time Programming - Spring 2017

Lecture 13 - Notifier/Server, UART Interrupts

Public Service Annoucements

  1. Due date for kernel3: 5 June, 2017.
  2. Halt versus idle task.

Last gasp of the hardware context switch

I have alluded several times to the IP bug, which occurs when you fail to save IP. Very infrequently a hardware interrupt occurs between two instructions

    mov	ip,sp
    stm sp!,{fp,ip,lr}
  
in every function preamble. The result is an activated task with another task's stack. In a running train program this bug occurs every several minutes.

There is a dual bug, the LR bug, in which one task's data gets another task's instructions. Here is a sequence that produces it.

  1. A hardware interrupt occurs.
  2. The registers of the interrupted task are pushed onto its stack.
  3. The lr_IRQ overwrites the lr on the stack.
  4. When the interrupted task is next scheduled, registers r0-r14 are restored in system mode.
  5. Then, movs pc, lr restarts execution of the interrupted task in scv mode.
This almost always works, but very infrequently it crashes. Why?

AwaitEvent

The final Kernel Primitive

... except for Destroy

int AwaitEvent( int eventType )

More About AwaitEvent

Argument

  1. Somewhere there is a list of event types
  2. This is not very portable

Processing in the kernel


Clock Server, Task Structure

What does the clock server do?

How is AwaitEvent Used?

AwaitEvent is used to update the time.

Generalizing
  1. There should (almost) always be a task blocked on AwaitEvent for every interrupt type, at least when interrupts are enabled. Why?
  2. A server cannot call AwaitEvent. Why?
  3. We call the task that calls AwaitEvent a Notifier. Why?
  4. Code for a typical Notifier
      struct delivery {
          int type;
          int data;
      }
      
      void notifier( ) {
          struct delivery request;
          // Initialization, probably including device
          // Synchronization
          request.type = NOTIFIER;
          FOREVER {
              request.data = AwaitEvent( evtType );
              Send( server, &request, ... );
          }
      }
  5. Code for a typical server
      void server( ) {
          struct delivery request;
          // create notifier
          // other initialization
          // synchronization
          FOREVER {
              Receive( &requester, &request, ... );
              switch ( request.type ) {
              case NOTIFIER:
                  Reply( notifier );
                  data = request.data;
                  // use data
              case CLIENT:
                  ...
              }
          }
      }

HALT versus an Idle Task

What do you do when there are no tasks ready to run? Some tasks are probably blocked on AwaitEvent, and will run as soon as an interrupt occurs. What do you do?


Clock Server

Primitives

int Time( )
int Delay( int ticks )
int DelayUntil( int ticks )

Pseudo-implementation

void clock( ) {
    // Create Notifier and send  any initialization data
    // Initialize self
    FOREVER {
        Receive( &requester, &request, ... );
        switch ( request.type ) {
        case NOTIFIER:
            Reply( notifier, ... )
            // update time and check for terminated delays
        case TIME_REQUEST:
            Reply( requester, time,... )
        case DELAY_REQUEST: 
            // Add requester to list of suspended tasks
        }
        // Reply to suspended tasks that have timed out
    }
}

Comments:

  1. You need a common request type, or possibly a union.
  2. You should notice a typical server pattern.

It's normal to sort the list of suspended tasks. Why?


Return to: