CS452 - Real-Time Programming - Spring 2012
Lecture 12 - Hardware Interrupts
Public Service Annoucements
- Wednesday's class
- Assignment 3
Hardware Interrupts
What is a Hardware Interrupt?
Context Switches for Interrupts
Difference from Software Interrupts
It is impossible to predict where they occur
- You may have made some assumptions about when they occur
Assymmetry between User Task and Kernel
Scratch Registers must be saved
- for the user task, not for the kernel
- including the IP
Helpful Features of the ICU
- Several places where you can read state
- Several places where you can block interrupt flow
- Trigger hardware interrupt from software
- What makes interrupts hard is that you are doing two semi-hard
things at once
- Making the hardware produce the interrupt
- Responding to the interrupt
- This allows you to separate them in developing/debugging
The Hardware in the Trains Lab
32-bit Timer
Interrupt Control Unit (ICU)
The actual device is the ARM PL190
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 enabled
|
| 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.
|
Helpful Features of the ICU
- Several places where you can read state
- Several places where you can block interrupt flow
- Trigger hardware interrupt from softwareonce
- What makes interrupts hard is that you are doing two semi-hard
things at once
- Making the hardware produce the interrupt
- Responding to the interrupt
- Software interrupt generation allows you to separate them in
developing/debugging
Non-vectored Operation
Initialization
- Enable interrupt in device
- Sometimes there is a spurious interrupt that comes in, sometimes
not.
- Enable interrupt in ICU
- Enable interrupt in CPU, usually by MOVS
Interrupt occurs
- AND of IRQ and NOT( IRQ disabled ) is checked before each instruction
fetch.
- If set IRQ exception is taken in place of next instruction fetch.
- Possibly zero instructions of active task are executed.
- Make sure that this case works
- Context switch into kernel
Context switch novelties
Difference from Software Interrupts
- It is impossible to predict where they occur
- You may inadvertently have made some assumptions about when they
occur
- Scratch Registers must be saved
- r0-3
- IP -- used only very occasionally by gcc
- How do you differentiate between IRQ and SWI?
- Two Link Registers
- One to return from interrupt
- One to return from the interrupted task to whatever called
it
- Locate source of interrupt
- Collect volatile data
- Turn off interrupt in device
- Goes off automatically in the ICU
- Turn off interrupt in device
- Should turn off interrupt in ICU
- What about IRQ?
You are now ready to process the interrupt in the kernel
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:
- 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 |
- 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 |
- The first is the address (ISR entry point) of the highest priority
interrupt. Write it during interrupt processing to get the current
highest priority interrupt.
- The second would normally be 0x34, the entry point of the
kernel.
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
ldr pc, #<VicVectAddr>
(Note that this is similar to the instruction in 0x014. Could we do it
all in one?)
- 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?
- 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
Processing in the kernel
- Initialization
- Kernel initialization has IRQ masked
- Kernel initializes ICU
- For each device
- Kernel initializes hardware
- Kernel turns on interrupt(s) in the device
- Kernel starts first user task
- Eventually, Notifier is created
- Notifier
- initializes device
- turns on interrupt(s) in the device
- turns on interrupt(s) in the ICU
- calls AwaitEvent
- Procedure
- Kernel
- identifies interrupt source
- identifies the correct Notifier
- acquires volatile data
- re-enables interrupt in the device
- re-enables interrupt in the ICU
- re-enables interrupt in the CPU during task activation (eg,
movs)
- puts volatile data into AwaitEvent's return value
- Makes Notifier ready
- Notifier
- collects and packages data
- sends to server
- Eventually Server
- Replies to Notifier
- Advantage
- Clean consistent user code
- Disadvantage
- Kernel has to know a lot about the hardware.
- Hardware knowledge split between Notifier and kernel
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: