The trouble with programmed I/O is that it both wastes CPU resources and it has potential for incorrect operation.
What we really want:
In order to do this we need:
The modern solution bundles the software to deal with these signals (interrupts) and other situations into an exception handler. (Effectively part of the OS.)
There are 2 categories of exceptions: interrupts and traps. All processors use the same mechanism (a combination of hardware and software) to deal with exceptions.
Note: The Java and C++ languages have overloaded the term exception. Processors have used this term since the 1950s. OO languages developed many years later. For this class, the term exception does not refer to Java or C++ exceptions.
The 2 categories:
examples:
When should the interrupt be dealt with? Answer: as soon as conveniently possible, and before the condition that caused the interrupt might cause yet-another interrupt (losing the first!)
When should the trap be dealt with? Answer: right now! The user program cannot continue until whatever caused the trap is dealt with.
The mechanism for dealing with exceptions is simple; its implementation can get complex. The implementation varies among architectures.
Situation: a user program (also called an application) is running (executing), and a device generates an interrupt request.
Mechanism to respond: the hardware temporarily suspends the user program, and instead runs code called an exception handler. After the handler is finished doing whatever it needs to, the hardware returns control to the user program. The user program continues from where it left off.
Limitations of exception handler:
Since it is being invoked (potentially) in the middle
of a user program, the handler must take extra care
not to change the state of the user program.
So, how can it do anything at all?
-- The key to this answer is that any portion of the
processor state that the handler wishes to change must be saved before
the change and restored before returning to the user program.
-- The handler often uses the system stack to temporarily
save register values.
When to handle an interrupt -- 2 possiblilities:
This is very difficult to do, because the hardware is complex. But, it has been done in simpler forms on a few machines. Example: arbitrary memory to memory copy on IBM 360.
The instruction fetch/execute cycle must be expanded to
When an exception occurs, the hardware does the following things. Note that there is no inherent ordering of #1-4. They all happen "between" instructions, and before #5.
within the Cause register -- coprocessor C0, register $13, a 32-bit register
bits 6..2 (5 bits) specify the type
of the exception, called the ExcCode
.
Here are some mappings of encodings to causes.
Examples:
00000 (0) Interrupt 00100 (4) load from an illegal address 01000 (8) syscall instruction 01100 (12) arithmetic overflow
The mode is in the Status register -- coprocessor C0, register 12, bit 1.
user mode = 1
kernel mode = 0
defined in the processor's architecture are 2 modes,
bit 0 of the Status register
the field is called IEc
(Interrupt Enable, Current)
determines whether interrupts are currently
enabled = 1
disabled = 0
If interrupts are disabled, then the hardware is not checking to see if there are further interrupts to handle. Disabling makes sure that the handling of an interrupt is not interrupted.
coprocessor C0, register 14, called the Exception Program Counter.
Gives return address within user program. Where to return to when done handling the exception.
Then, the instruction fetch and execute cycle starts up again, only now the code within the exception handler is being executed. This handler code does the following:
The handler needs to use registers too! It may not change (clobber, overwrite) the register contents of the user program. So, it saves them (on stack or in memory).
ExcCode
)
mfc0 $k0, $13 # get Cause register
andi $k0, $k0, 0x3c # Mask out all but ExcCode
ExcCode
in combination with a jump table to jump to
the correct location within the exception handler.
The EPC (Exception Program Counter).
The Status Register.
The Cause Register.
A clever mechanism for implementing a switch
statement.
A jump to one of many locations.
Keep a table of addresses (case1
, case2
, and case3
):
JumpTable: .word case0 # different syntax!
.word case1 # the address assigned to these labels
.word case2 # becomes the contents of an array element
sll $8, $8, 2 # case number shifted left 2 bits
# (need a word offset into table, not byte)
lw $9, JumpTable($8) # load address into $9
jr $9 # jump to address contained in $9
.
.
.
case0: #code for case0 here
.
.
.
case1: #code for case1 here
.
.
.
case2: #code for case2 here
Note that the cases do not have to go in any specific order.
not-yet-seen Addressing mode: label($rb)
Effective address is gotten by: label + ($rb)
label
does not fit into 16 bit displacement field of load/store
instruction.
So, the MAL->TAL synthesis of this must be something like:
la $1, label
add $1, $1, $rb
then use 0($1) as addressing mode in load/store instruction.
A problem: Multiple interrupt requests can arrive simultaneously.
Which one should get handled first?
Possible solutions:
difficulty 1) This might allow a malicious/recalcitrant device or program to gain control of the processor.
difficulty 2) There must be hardware that maintains an ordering of pending exceptions. (a queue)
Priorities for various exceptions are assigned either by the manufacturer, or by a system administrator through software. The priorities are normally defined (and fixed) when a machine is booted (the OS is started up).
difficulty 1) Exceptions with the same priority must still be handled in some order. Example of same priority exceptions might be all keyboard interrupts. Consider a machine with many terminals hooked up.
The instruction fetch/execute cycle becomes:
NOTE: This implies that there is some hardware notion of the priority for whatever code is currently running (application, keyboard interrupt handler, clock interrupt handler, etc.).
What should get given the highest priority?
clock? power failure? thermal shutdown? arithmetic overflow?
keyboard? I/O device ready?
Priorities are a matter of which is most urgent, and therefore cannot wait, and how long it takes to process the interrupt.
So, what ordering ought to be imposed?
The best solution combines priorities with an exception handler that can itself be interrupted. There are many details to get right to make this possible.
If the priority level checking is done in hardware, then all interrupts can be re-enabled.
Copyright © Karen Miller, 2006 |