CS452 - Real-Time Programming - Spring 2011

Lecture 12 - Clock Server

Pubilic Service Announcement

  1. Kernel 1 comments

The Hardware in the Trains Lab

32-bit Timer

Interrupt Control Unit (ICU)

ARM PL190.

Vectored Operation

General Idea

The standard way of programming the ICU requires the kernel to query the ICU. Sometimes (!), this is unacceptably inefficient. Then, you have another alternative, vectored interrupts.

Relevant registers:

  1. there are 16 pairs that you write
    Register Name Offset R/W Description Comments
    VICxVectAddry 0x100+4y R/W Vector address for interrupt y Entry point of ISR 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

  2. There is one pair used by the program
    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

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
        ldr   pc, #<VicVectAddr>

    (Note that this is similar to the instruction in 0x014. Could we do it all in one?)

  3. Before interrupts are re-enabled write VICxVectAddr to start priority hardware

Answer to question.

Look carefully at what's in 0x18


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 to free 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.
  3. It's normal to sort the list of suspended tasks. Why?

Return to: