Structure of
an Interrupt Handler
I/O
devices and their controllers fall into three major classes.
1. Program
Controlled
2. Interrupt
Driven
3. Direct
Memory Access
Of
these classes, only the latter two can generate interrupts. Here we focus on how the CPU processes the
interrupts associated with such devices.
This lecture focuses on what I
call the “Interrupt Controller Hub”.
This
hub processes interrupts from multiple devices and sends a single INT signal to
the
CPU when an interrupt is recognized.
The
CPU sends a single ACK signal back to the hub, which sends it to the
appropriate device.
Handling I/O
Interrupts
An
interrupt is a signal external to
the CPU program that causes the normal program
execution to be interrupted and another program (the interrupt handler) to be run.
There
are two basic types of interrupts: I/O interrupts and page fault
interrupts. The
latter type are used in virtual memory
systems and can be quite complex to handle.
Interrupts
from I/O devices are more easily handled in that they can be easily fit into
the standard fetch/execute cycle of
the CPU.
In
this cycle, each instruction is fetched from the Instruction Memory and then
executed. The control unit selects the
instruction to be executed next.
For
I/O interrupts, the design change to accommodate interrupts is minor.
Before each instruction in a program is executed, the
control unit checks the status
of the INT signal. There are two
possibilities:
1. INT = 0. In this case the next instruction is
executed.
2. INT = 1. In this case, the address of this next
instruction is saved for later use,
and a high–level interrupt handler
is run to identify the source of the interrupt.
Interrupt
Priority and CPU Priority
We
follow the design of the PDP–11, developed by the Digital Equipment Corporation
(now defunct) in discussing an interrupt structure.
We
begin with the idea of a CPU execution priority. This is specified by a 3–bit number
in the program status register (PSR).
Here is the structure that we shall use.
Bits |
15 – 8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Use |
Other Uses |
N |
Z |
V |
C |
I |
CPU Priority |
We postulate a 16–bit
program status register with the following bits:
N, Z, V, & C Status of the previous arithmetic
operation
(Always
included in the PSR, so we use these too)
I Interrupts
enabled.
When
I = 0, the CPU ignores any interrupt.
Priority A 3–bit unsigned integer representing the CPU
execution
priority.
The I Bit
and CPU Execution Priority
These
four bits are used in processing interrupts.
Disabling Interrupts (I = 0)
This
should be done very seldom. Only the
Operating System can set the I bit.
There
are certain times in processing an interrupt during which another interrupt
cannot be processed. During these short
times, the CPU sets I = 0.
CPU Priority
Normal
programming practice allows for multiple interrupt priorities and nested
interrupts. A high priority device, such
as a disk, can take precedence over the
processing of an interrupt for a low priority device, such as a keyboard.
To
manage devices at various priorities, each interrupt is processed as follows.
1. A
device interrupts with priority K.
2. The
CPU sets I = 0 and saves various registers.
3. The
CPU sets its priority to K (the same number), sets I = 1,
and then begins execution
of the interrupt handler.
Devices
with higher priorities can now interrupt and have their interrupts handled.
More on CPU
Priority
We
follow the PDP–11 convention.
Priority
= 0 All user programs
execute at this level.
Priority
= 1, 2, 3 Various Operating System
Utilities operate at these levels.
We
generally ignore these levels.
Priority
= 4, 5, 6, 7 Interrupt handlers
operate at these levels.
The
CPU will acknowledge and process an interrupt only if the priority of the
interrupting device is higher than the CPU execution priority.
For
this reason, almost all interrupt handlers are written to execute with a CPU
priority exactly equal to the device priority.
The
convention is that all hardware devices are assigned one of four interrupt
levels:
4, 5, 6, and 7.
Priority 4 is the lowest hardware priority, reserved for the keyboard,
etc.
Priority 7 is the highest hardware priority, reserved for disks, etc.
Vectored
Interrupts
How
does the CPU identify the device that asserted the interrupt and begin
execution of its interrupt handler? More
on this later, but for now:
1. The device sends its “vector”, which is an
address of a data structure.
This is most often an address in
low memory, say addresses 0 – 1023.
2. The data structure at the specified address
contains the following.
a) The
address of the interrupt handler associated with the device.
b) The
CPU execution priority for the interrupt handler.
The interrupt handling sequence
can be elaborated.
1. Clear the Interrupt Enabled bit (set I = 0)
to block other interrupts.
2. Store the essential registers, so that the
user program can be restarted later.
3. Load the PSR with the execution priority
and load the PC with the address.
4. Set I = 1 to allow nested interrupts and start
execution of the handler.
More on
Vectors as Used in I/O
In
this context a vector is just an address into the computer main memory.
It is most commonly a low memory address.
Here
is the scenario in which an I/O vector is appropriately used.
1. The CPU has
received the INT (Interrupt) signal
from the I/O hub,
indicating that there is a valid
interrupt pending.
2. The CPU has
sent the ACK (Acknowledge) signal to
the I/O hub,
commanding the I/O device to
identify itself so that the data can be
transferred properly.
In vectored I/O
handling, the device that raised the interrupt will identify itself
by indicating the program to manage the data transfer.
How is this program to be identified? The best way is to indicate its address in
memory,
so that the handler program can be called quickly.
This must be based on an address that is part of a
published standard, so that it is
known ahead of time and can be used by all manufacturers of the device.
What About
Fixed Addresses for the Device Driver?
The software to manage I/O for an I/O device is often
called the “device driver”.
One way to manage this is to place the device driver
at a fixed address in the computer
memory. The driver for the NIC (Network
Interface Card, used to connect the computer
to the Internet) might be placed at address 0x01F0 8000.
This solution would have been sound in the early
1970’s, but would present severe
problems for the design of a modern operating system.
Modern operating systems, based on sharing the
computer among several programs,
assume that no program is placed at a fixed address.
Each time the computer is booted up, the operating
system selects an address for each
program to be run, and places that program in its slot.
The memory slot for a given program is selected by the
operating system in response to
a number of factors. The operating
system does this to optimize overall performance.
The Use of a
Vector
The idea of a vector is a location in which to store
the address of the device driver.
As an example, suppose the following.
1. The vector to be associated with the NIC is 0x100.
2. At boot–up time, the operating system selects
the address 0x01F0 8000 as the
start address for the device
handler for the NIC.
After loading the device driver into the address 0x01F0 8000, the operating system loads
that address into location 0x100. What we have is as follows:
Processing
the NIC Interrupt
Here, I assume that the NIC vector is 0x100.
At the start of the scenario, the CPU must first
identify the device raising the interrupt.
1. The INT is asserted and the CPU begins
processing the interrupt.
As a first step, the CPU saves
the current value of the PC (Program Counter)
so that the executing program
can be restarted.
2. The CPU asserts the ACK signal.
3. The NIC places the vector 0x100 onto the I/O
data lines.
4. The CPU recognizes 0x100 as a vector.
It goes to address 0x100 and
retrieves the contents: 0x01F0 8000.
5. The CPU loads the address 0x01F0 8000 into the PC (Program
Counter) and
begins execution of the program
at that address.
At some time later, the program that was interrupted
is resumed by restoring the
saved value of the PC.
Interrupt Lines
and Assertion Levels
The
structure of the interrupt lines on our computer is as follows:
We
have four interrupt lines, one for each of the four priority levels.
Each is paired with an acknowledge line for the same priority.
Interrupts
are asserted low; that is, the
signal goes to 0 when the device interrupts.
Acknowledgements
are asserted high; that is, the
signal goes to logic 1 to acknowledge.
In
order to understand the process of low assertions, we must explain the idea of
a pull–down resistor. We begin with some basic electronics.
Voltage
Across Resistors
We
begin by studying an electronic circuit in which a voltage is placed across two
resistors in series. Standard algebra
gives the current through these two resistors
as a function of the voltage applied and the individual resistors.
It
is a basic result that the current through the two resistors is the same.
The
goal of this analysis is to find the voltage at the point between the
resistors.
I call it V2.
The Voltage
V2
Here
we apply some basic electronics to get the voltage at this point between
the two resistors. Here is the figure
with the basic equation.
Apply
a few algebra tricks to get this into a form that is useable.
We
assume that R1 > 0 (that it is never zero) and look at two cases.
1. R2
= 0, and
2. R2
= 1000·R1.
Suppose R2
= 0
Suppose
that R2 = 0. Then, we use the
first simple equation to note that V2 = 0.
This
can be derived with the first form of the second equation.
Suppose R2
= 1000·R1
If
R2 = 1000·R1, then R1/R2 = 0.001. We use the second variant of the equation.
If
the source voltage is 5.00 volts, this gives V2 = 4.995 volts, which
cannot be
distinguished from 5.00 volts.
If
R2 = 1000·R1, then for all practical purposes V2 = V.
Mechanism
for Asserting an Interrupt
Each
interrupt line is attached to a “pull down” resistor.
When
the device asserts an interrupt, it sets its Interrupt Flip–Flop. Thus Q = 1.
This enables the tri–state, which becomes a closed switch with very low
resistance.
With
the tri–state enabled, all the voltage drop is across the resistor so that the
voltage
on the Interrupt Line becomes 0. The
interrupt is asserted.
With
the tri–state disabled, it becomes an open switch, a line with very high
resistance.
All the voltage drop is across the tri–state, so the Interrupt Line stays at
voltage.
Multiple
Devices on One Line
By
design, Interrupts are active low in order to facilitate attaching multiple
interrupting devices on a single interrupt line.
Here
we see four devices attached to a single line.
If
no device is interrupting, we have Int = Logic 1
If
any one device is interrupting, we have Int = Logic 0. The interrupt is asserted.
If
more than one device is interrupting, we still have Int
= Logic 0.
The devices cannot interfere with each other.
The Big
Picture
Here
is the entire circuit for the controller hub.
Daisy
Chaining
In
daisy chaining, the ACK is passed from device to device until it hits a device
that has asserted an interrupt.
If
the I/O device has not asserted an interrupt, it passes the ACK to the
next “downstream” I/O device.
Priority
rank is by physical proximity to the CPU.
Daisy Chaining
(Part 2)
The
ACK is passed down the line to the first I/O device attached.
If
that device has not raised an interrupt, it passes the ACK to the next device.
When a device that asserted an interrupt gets the ACK, it captures it and does
not pass it.