CS452 - Real-Time Programming - Fall 2011

Lecture 13 - AwaitEvent, Clock Server

Public Service Annoucements

  1. Due date of kernel 3 (Monday, 17 October)

Hardware Interrupts

What is a Hardware Interrupt?

Context Switches for Interrupts

Your context switch must be sound for interrupts between any two instructions in user mode.

High Points worth Repeating

  1. Two link registers: make sure that both are saved and don't confuse which is which.
  2. Save all scratch registers
  3. You can only use local variables when you have the correct frame pointer in r11.
  4. You must get the handled interrupt turned off before you exit to user mode.
  5. An instruction pair like
        msr      cpsr_f, #0x50
        mov      pc, lr

    is not a satisfactory way of returning to the interrupted program, no matter how you have fiddled with lr and modes.

  6. The kernel reschedules when exiting after a hardware interrupt. Thus, the interrupted program will likely not run for a while.
  7. The kernel does not need to save or restore its scratch registers.

Turning Timer Interrupts Off

When a timer reloads it asserts its IRQ output, which remains asserted.

Kernel Provision for Interrupts

  1. Initialize the kernel with interrupts disabled
  2. Turn on interrupts by having user CPSRs with interrupts enabled
  3. Find source of interrupt
  4. Turn off source of interrupt
  5. Handle interrupt
  6. Reschedule and activate

The Hardware in the Trains Lab

Timer

Interrupt Control Unit (ICU)

All input signals are

Base addresses

Basic Operation

VIC powers up with

Procedure

Initialization

  1. leave protection off
  2. enable in VICxIntEnable when you are ready to handle the interrupt

On an interrupt

  1. Read VICxIRQStatus
  2. Choose which interrupt you wish to handle
  3. Clear the interrupt source in the device

For debugging

  1. Use VICxSoftInt and VICxSoftIntClear to turn interrupt sources off and on in software

Hardware Definitions

Registers for Basic Operation
Register Name Offset R/W Description
VICxIRQStatus 0x00 RO One bit for each interrupt source

1 if interrupt is asserted and not masked

VICxFIQStatus 0x04 RO As above for FIQ
VICxRawIntr 0x08 RO As above but not masked
VICxIntSelect 0x0c R/W 0: IRQ, 1: FIQ
VICxIntEnable 0x10 R/W 0: Masked, 1: Enabled
VICxIntEnClear 0x14 WO Clears bits in VICxIntEnable
VICxSoftInt 0x18 R/W Asserts interrupt from software
VICxSoftIntClear 0x1c WO Clears interrupt from software
VICxProtection 0x20 R/W Bit 0 enables protection from user mode access
VICxVectAddr 0x30 R/W Enables priority hardware

See documentation.

Vectored Operation

For enrichment only.

Procedure

Initialization

  1. Write kernel entry point into VICxDefVectAddr
  2. If desired write special entry point into VICxVectAddry
  3. When ready to accept interrupts write source and enable into VICxVectCntly

When an interrupt occurs

  1. Read VICxVectAddr to find address
  2. Move result to PC
  3. When service is complete write VICxVectAddr to start priority hardware
Register Name Offset R/W Description
VICxVectAddr 0x030 R/W Read: address of vector for highest priority interrupt

Write: service complete, enable priority hardware

VICxDefVectAddr 0x034 R/W Default vector address
VICxVectAddry 0x100+4y R/W Vector address for interrupt y
VICxVectCntly 0x200+4y R/W Control register for interrupt y

Bit[0-4]: interrupt source for interrupt y

Bit[5]: enable vectored interrupt y


Clock Server, Task Structure

A New Kernel Primitive: int AwaitEvent( int EventType )

How is AwaitEvent Used?

  1. There should (almost) always be a task blocked on AwaitEvent for every interrupt type. 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
    main( ) {
        Tid server;
        int evtType, data;
        Receive( &server, &evtType, ... );
        // Other initialization
        Reply( server, ... );
        FOREVER {
            data = AwaitEvent( evtType );
            Send( server, &data, ... );
        }
    }
  5. Code for a typical server
    main( ) {
        notifier = Create( HIGHEST, ... );
        // other initialization
        Send( notifier, &evtType, ... );
        FOREVER {
            Receive( &requester, &request, ... );
            switch ( request.type ) {
            case NOTIFIER:
                Reply( notifier );
                data = request.data;
                break;
            case CLIENT:
                ...
            }
        }
    }

More About AwaitEvent

Argument

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

Return value

Related tofree choices discussed in last lecture.

Strategies

Strategy 1: Kernel does it all

Strategy 2: Notifier does most of it

HALT versus an Idle Task

What do you do when there are no tasks to run?


Clock Server

Primitives

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

Implementation

main( ) {
    notifier = Create( HIGHEST, ... );
    time = 0
    Send( notifier, &evtType, ... );
    FOREVER {
        Receive( &requester, &request, ... );
        switch ( request.type ) {
        case NOTIFIER:
            Reply( notifier, ... )
            time++;
            break;
        case TIME_REQUEST:
            Reply( requester, time,... )
            break;
        case DELAY_REQUEST: 
            Add requester to list of suspended tasks
            break;
        }
        Check list of suspended tasks and reply
    }
}

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: