Interrupt handler s

advertisement
Interrupt handlers
The routi ne that processes an interru p t (called an interrupt handler )
needs to do a num be r of thing s.
•
First, since the interr up t may have occurred anywhere in the main
code, including the mid dle of a calculation, any of the general pur pos e registers may have impor ta n t, unsaved values. Since you
can't know ahead of time which registers contain importa nt values,
all general - purp ose registers changed by the interrup t handler
must be saved on entry and restored on exit. (Don't try to use
registers that the main program isn't using: the main program
might be using all of them, and your routine has to be general
enoug h to work in all cases.)
•
The interru p t handler may have been invoked for a number of
reason s. (On MIPS, the sam e routine is called not only for
interru p t s, but also for synchrono us event s such as arithm et ic
overflow and unaligned loads / s t or e s. Collectively, these are called
exceptions , and so a better term for the handler would be
"exception handler".) The exception handler thus needs to
deter mi ne the cause of the exception, and jump to the relevant
subrouti ne that will handle it.
•
Now, the interr up t needs to be serviced. For instance, if it was a
receiver (keyboar d) interr up t, then the next keypress is read from
the Receiver Data Register.
•
When the interru pt handler is due to return, the interru pt e d
program needs to have its state (i.e., registers) restore d, and then
be resu m e d at the point it was stoppe d. The addres s of this
instruction was saved by the machi ne when the interr up t occurre d,
so it is sim ply a matter of getting this address, and jumpi ng back to
it.
MIPS
To make interr up t s work, you'll need to know a few more features of the
MIPS architect u re. Section 2.1 of the SPIM manual has a few paragrap h s
on Coprocess or 0 and interrup t handling, but you may find the following
sections more up - to- date and inform ative.
Coprocess or 0
MIPS com put er s contain not only the main process or (CPU), but also at
least one coprocessor (see Figure 2 in the SPIM man ual ). Interrup t s and
exceptions are managed by Coprocessor 0 , which also handles the
mem ory subsyste m. (Coprocess or 1, if present, does floating - point
com put ati ons.) Normal user - level code doesn't access Coprocessor 0, but
interru pt - aware code has to use it.
Coprocessor 0 has several registers which control interrup t s and
exceptions.
•
Register 12, the Status Register , is a read - write register which
cont rols whether interrup t s are allowed to happen, and if so, which
ones.
•
Register 13, the Cause Register , is a mostly read - only register
whose value is set by the system when an interru pt or exception
occurs. It specifies what kind of interr upt or exceptio n just
happene d.
•
Register 14 is Exception Progra m Counter (EPC). When an interru p t
or exception occurs, the address of the curren tly running
instruction is copied from the Program Counter to EPC. This is the
addres s that your interru pt handler jumps back to when it finishes
•
Registers 9 and 11, Timer Count and Timer Compare. In SPIM, the
timer is sim ulat ed with two more coprocess or registers: Count ($9),
whose value is continuo u sly incremen te d by the hardwar e, and
Compare ($11), whose value can be set. When Count and Compare
are equal, an interr upt is raised, at Cause register bit 15. To
schedul e a timer interru p t, the exception handler has to load
Count , add a fixed amoun t called the time slice (quantu m) , and
store this value into Compare . The smaller the time slice, the
greater the frequency of timer interr upt s.
To access these registers, you'll need to know two new MIPS
instruction s.
•
mfc0 (Move From Coprocessor 0) moves a value from a coprocessor
0 register to a general - pur pos e register:
mfc0 $t5, $13 # Copy Cause register value to $t5.
•
mtc0 (Move To Coprocessor 0) moves a value from a general -
pur pos e register to a coprocessor 0 register:
mtc0 $v0, $12 # Copy $v0's value to Status register.
If you want to modify a value in a coprocessor 0 register, you need to
move the register's value to a general - purpose register with mfc0, modify
the value there, and move the changed value back with mtc0.
Enabling interrupts
Enabling interr u p t s requires doing three things:
1. Turn on the Interru pt Enable bit at Bit 0 of the Status Register. This
bit is the global on/ off contr ol for interru pt s.
2. Also in the Status Register, turn on the Interru pt Mask bits
corres po n di ng to the Receiver (bit 11), Trans mit t er (bit 10) and
Timer (bit 15). These bits per mi t the CPU to respon d to interr upt s
generate d by the keyboard, display and timer.
3. Turn on the Interru pt Enable bit in the Receiver Control Register
(0xFFFF0000). This bit tells the receiver hardware (keyboar d) that it
should generate interr up t s when keypresses occur.
Now an interru p t will be generated when the user presses a key, and the
exception han dler will autom atically be jumped to.
Later, when your program is ready to write to the termi nal, you'll want to
turn off the Receiver Control Register Interru pt Enable bit and turn on the
corres po n di ng bit in the Trans mit t er Control Register (at 0xFFFF0008).
Writing an interrupt handler
When an interr up t occurs, the following things are done autom atically by
the hardware:
1. The Exception Level bit (bit 1) in the Status Register is turned on.
While this bit is 1, no further interru pt s can occur. This is essential,
because we don't want the interr up t handler to be itself
interru p t e d.
2. The Cause register is set to indicate the cause of the interru pt (see
Dispatching an interr u p t below).
3. The EPC register is set to the current value in the Program Counter.
This is the add res s in the main code where you will be resumi ng
after han dling the interrup t.
4. The Program Count er is set to 0x8000018 0.
This addres s (0x80000 1 80) is where you need to put your exception
han dler. Do this with the .ktext (kernel text) and .kdata (kernel data)
directives.
.kdata
# Put any data structures required by the interrupt handler
here.
.ktext 0x80000180
# Exception handler begins here.
Dispatching an interrupt
When the excep tion handler begins, it needs to first find out what caused
the exceptio n. This can be achieved by examining the Cause Register from
coprocessor 0. The Exception Code (bits 6- 2) describes what caused the
trap; if the Exception Code value is zero, then an interrup t has occurred.
But which interru p t? The Interrup t Pending bits in the Cause Register
indicate which devices need servicing; bit 11 will be on if the receiver has
data to be read, bit 10 will be on if the trans mi t te r is ready for another
character and bit 15 will be on if the timer has raise an interrup t.
Registers
Your interru p t handler must save any general - purpose registers that it is
going to use (to be restored at return). But to do so requires you to
modify at least one register first (try it and see; reme m ber that somet hi ng
like sw $t0, saved_t0 expands to two machin e instructions using $at).
This situation is resolved by forbiddi ng user program s from using two
general - purpo se register s, $k0 and $k1 (The k stands for kernel, which an
exception han dler is part of). Your interr upt han dler is allowed to use $k0
and $k1 withou t having to save or restore their values. This allows you
just enough leeway to start saving registers, as well as making returning
from the interru pt han dler possible.
Note that your exception handler (and main program) is probably also
using the $at register, which is used silently by the assem bler in the
expansio n of certain pseudoi ns t r uct io ns; for exam ple:
# Expansion of "blt $t3, $t4, foo".
slt $at, $t3, $t4
bne $at, $zero, foo
To save $at, you will need to stage its value to a tempor ary location
before saving it to mem ory, since the act of executing a sw instruction
destr oys $at as a side - effect. Any mention of $at must be bracketed by .
set noat and .set at or the com piler will complain:
# Allow direct references to $at
# (and forbid its use in pseudoinstructions).
.set noat
# Copy $at to a temporarily safe place.
move $k1, $at
# Reserve $at for pseudoinstruction expansions again.
.set at
# Save value to memory (side effect: this changes $at!).
sw $k1, saved_at
# Now save the other registers used by the interrupt handler.
Simply do this in reverse to restore $at at the end of the interr upt
handler.
Spend a bit of time on your register - saving code when you write your
interru pt handler, because it is tricky to get right, and getting it wrong
can lead to subtle inter mit te n t failures. For instance, since $at is so
delicate, it makes sense to save it first and restore it last.
Returning from an interrupt
When your interr u p t handler is ready to retur n, it must restore the
interru pt e d progra m' s state first. Most of this is done when your handler
restores saved general - purpose registers. The final steps are to restore
the Status register and to jum p back to the user program. These are done
with the eret instruction:
#
#
#
#
#
#
#
eret effectively does
mfc0 $k0, $12
li $k1, 0xFFFFFFFD
and $k0, $k0, $k1
mtc0 $k0, $12
mfc0 $k0, $14
jr $k0
this, all at once:
# Get status register.
#
#
#
#
Clear Exception Level bit (bit 1)
to allow interrupts again.
Get address to jump back to (EPC).
Jump back to that address.
eret
Download