CS452 - Real-Time Programming - Spring 2013
Lecture 12 - Context Switch Redux
Pubilc Service Annoucements
- Due date for kernel 2: 3 June.
Hardware Interrupts
What is a Hardware Interrupt?
In the CPU
- Test interrupt signal before fetching the next instruction
- actually AND of INT and the IRQ bit in the CPSR
- If asserted, change mode to IRQ
- Disable interrupt in CPSR
- Put CPSR into SPSR_irq
- Execute instruction at 0x18
In the Interrupt Control Unit (ICU)
- Several interrupts may be present
- There are two options for deciding which one to service
- Software
- Check ICU status registers to discover which interrupt(s)
are present
- Run an algorithm that decides which one to take
- This permits context-dependent priorities
- Hardware
- Program the ICU to a set of fixed priorities
- Entry point of highest priority interrupt appears in
ICU
- Jump to it without requiring software priority
resolution.
- Handling one interrupt sometimes exposes another one
In the Peripheral Hardware
- Several interrupt conditions may be present at one time
- ORed in peripheral hardware
- ORed in glue hardware
- Rare that there is a priority mechanism
- Handling one interrupt sometimes exposes another one
When two interrupts are present
What should happen?
- Kernel executes with interrupts disabled.
- Context switch into user task turns on interrupts.
- Before fetching the first user task instruction test IRQ input.
- If asserted, re-initiate interrupt processing.
Context Switches for Interrupts
Difference from Software Interrupts
It is impossible to predict when hardware interrupts occur ( = where the
PC is)
- Software intterrupts occur at only a few places in the code.
- You may have a kernel that takes this predictability for granted.
Assymmetry between User Task and Kernel
For the interrupted task,
- scratch registers must be saved and restored, including IP
- stack pointer must be stored
- two link registers must be kept separate
For the kernel,
- no need to save scratch registers
- stack pointer preserved in S13_svc
- link register preserved in R14_svc
Two Link Registers
- One to return from interrupt
- In the registers of the interrupt handling code
- To return to the interrupted task in the right place
- One to move to the caller's stack frame
- In the registers of the interrupted task
- To return to whatever started in interrupted task
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
Base address: 0x80810080
Three registers:
| Offset |
Function |
R/W |
Bits |
Comments |
| 0x0 |
Timer3Load |
R/W |
32: <Load/Reload Value> |
|
| 0x4 |
Timer3Value |
R |
32:<Current value> |
Set when Load is written,
even when counting |
| 0x8 |
Timer3Control |
R/W |
3:xxx<CLKSEL>xx<MODE><ENABLE> |
<CLKSEL>: 0, 2KHz clock; 1, 508KHz
<MODE>: 1, count continuously; 0, count once
<ENABLE>: Clock turned on
|
| 0xc |
Timer3Clear |
W |
32: |
Writing anything clears the interrupt |
Interrupt Control Unit (ICU)
The actual device is the ARM PL190
The logic in this design is completely asynchronous, so it functions when
the CPU clock is turned off.
- Important (= essential) for low power operation.
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 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 interrupts in device
- Enable interrupts in ICU
- Enable interrupts in CPU, usually by MOVS
Interrupt occurs
- AND of IRQ and NOT( IRQ disabled bit in CPSR ) 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
- Two Link Registers
- One to return from interrupt
- One to return from the interrupted task to whatever called
it
- 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
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
void notifier( ) {
Receive( &server, &evtType, ... );
// Initialization, probably including device
Reply( server, ... );
FOREVER {
data = AwaitEvent( evtType );
Send( server, &data, ... );
}
}
- Code for a typical server
void server( ) {
notifier = Create( HIGHEST, ... );
// other initialization
Send( notifier, &evtType, ... );
FOREVER {
Receive( &requester, &request, ... );
switch ( request.type ) {
case NOTIFIER:
Reply( notifier );
data = request.data;
// use data
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
- Acts on the data
- 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: at least % of time idle should be known.
- 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.
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
case TIME_REQUEST:
Reply( requester, time,... )
case DELAY_REQUEST:
// Add requester to list of suspended tasks
}
// Reply to any timed-out tasks
}
}
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: