CS452 - Real-Time Programming - Fall 2011
Lecture 13 - AwaitEvent, Clock Server
Public Service Annoucements
- 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
- Two link registers: make sure that both are saved and don't confuse
which is which.
- Link register of interrupted task in IRQ mode
- Save all scratch registers
- You can only use local variables when you have the correct frame
pointer in r11.
- You must get the handled interrupt turned off before you exit to user
mode.
- 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.
- The kernel reschedules when exiting after a hardware interrupt. Thus,
the interrupted program will likely not run for a while.
- 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.
- You have to turn off this interrupt in the kernel,
- which you do by writing to a register such as 0x8081008c.
- What happens if you don't turn it off?
- Do you have to turn off anything else?
Kernel Provision for Interrupts
- Initialize the kernel with interrupts disabled
- Turn on interrupts by having user CPSRs with interrupts enabled
- First instruction of user task might not execute immediately
- Find source of interrupt
- Turn off source of interrupt
- In device
- In ICU
- PSR remains with IRQ disabled
- Handle interrupt
- Find waiting task
- Make waiting task READY
- Reschedule and activate
The Hardware in the Trains Lab
Timer
Interrupt Control Unit (ICU)
All input signals are
- active high
- level sensitive
Base addresses
- VIC1:
0x800B0000
- VIC2:
0x800C0000
Basic Operation
VIC powers up with
- all vectored interrupts disabled.
- all interrupts masked
- all interrupts giving IRQ
Procedure
Initialization
- leave protection off
- enable in VICxIntEnable when you are ready to handle the interrupt
On an interrupt
- Read VICxIRQStatus
- Choose which interrupt you wish to handle
- Clear the interrupt source in the device
For debugging
- 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
- Write kernel entry point into VICxDefVectAddr
- If desired write special entry point into VICxVectAddry
- When ready to accept interrupts write source and enable into
VICxVectCntly
When an interrupt occurs
- Read VICxVectAddr to find address
- Move result to PC
- 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?
- There should (almost) always be a task blocked on AwaitEvent for every
interrupt type. Why?
- A server cannot call AwaitEvent. Why?
- We call the task that calls AwaitEvent a Notifier. Why?
- 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, ... );
}
}
- 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
- Somewhere there is a list of event types
- Application programmer knows the list
- Kernel can respond to each event type on the list
- This is not very portable
- The list would normally be the union of all types occurring on all
hardware
- This is the Windows problem
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?
- Idle task
- lowest priority
- diagnose system
- search for ETI
- HALT
- turns off CPU clock
- save power (battery)
- provided two ways
- through System Controller Co-processor
- through the TS-7200 clock controller
- IRQ path is asynchronous, so it works when the clock is off
Clock Server
Primitives
int Time( )
- Clock server starts at zero when it initializes
- Unit of time is tick
int Delay( int ticks )
- Note error returns
- You might want to add an error for negative arguments
- ticks is usually calculated, and a negative value is an early
warning of falling behind.
int DelayUntil( int ticks )
- Can be constructed from the above two primitives.
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:
- You need a common request type, or possibly a union.
- You should notice a typical server pattern.
- Notifier updates data
- Client who can be serviced now is serviced
- Client who needs service in the future is suspended
- List of suspended tasks is checked regularly
It's normal to sort the list of suspended tasks. Why?
Return to: