Interrupt handlers can be invoked for a number of reasons. The interrupt may have occurred in the middle of a calculation. The address of this instruction was saved by the machi ne when the interr upt occurred. When the interrupt handler is due to return, the interrupt ed program needs to have its state restored.
Interrupt handlers can be invoked for a number of reasons. The interrupt may have occurred in the middle of a calculation. The address of this instruction was saved by the machi ne when the interr upt occurred. When the interrupt handler is due to return, the interrupt ed program needs to have its state restored.
Interrupt handlers can be invoked for a number of reasons. The interrupt may have occurred in the middle of a calculation. The address of this instruction was saved by the machi ne when the interr upt occurred. When the interrupt handler is due to return, the interrupt ed program needs to have its state restored.
The routi ne that processes an interrupt (called an interrupt handler )
needs to do a number of things. First, since the interr upt may have occurred anywhere in the main code, including the middle of a calculation, any of the general - pur pose registers may have impor tant, unsaved values. Since you can' t know ahead of time which registers contain important values, all general - purpose registers changed by the interrupt 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 enough to work in all cases.) The interrupt handler may have been invoked for a number of reasons. (On MIPS, the same routine is called not only for interrupt s, but also for synchronous event s such as arithmet ic overflow and unaligned loads / st or es. 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 upt needs to be serviced. For instance, if it was a receiver (keyboar d) interr upt, then the next keypress is read from the Receiver Data Register. When the interrupt handler is due to return, the interrupt ed program needs to have its state (i.e., registers) restored, and then be resumed at the point it was stopped. The address of this instruction was saved by the machi ne when the interr upt occurred, so it is simply a matter of getting this address, and jumpi ng back to it. MIPS To make interr upt s work, you'll need to know a few more features of the MIPS architect ure. Section 2.1 of the SPIM manual has a few paragraphs on Coprocessor 0 and interrupt handling, but you may find the following sections more up- to- date and informative. Coprocessor 0 MIPS comput er s contain not only the main processor (CPU), but also at least one coprocessor (see Figure 2 in the SPIM manual ). Interrupt s and exceptions are managed by Coprocessor 0, which also handles the memory subsystem. (Coprocessor 1, if present, does floating- point comput ati ons.) Normal user - level code doesn' t access Coprocessor 0, but interrupt - aware code has to use it. Coprocessor 0 has several registers which control interrupt s and exceptions. Register 12, the Status Register , is a read- write register which cont rols whether interrupt 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 interrupt or exception occurs. It specifies what kind of interr upt or exception just happened. Register 14 is Exception Program Counter (EPC). When an interrupt or exception occurs, the address of the currently running instruction is copied from the Program Counter to EPC. This is the address that your interrupt handler jumps back to when it finishes Registers 9 and 11, Timer Count and Timer Compare. In SPIM, the timer is simulat ed with two more coprocessor registers: Count ($9), whose value is continuously incremented 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 interrupt, the exception handler has to load Count , add a fixed amount called the time slice (quantum) , 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 instructions. mfc0 (Move From Coprocessor 0) moves a value from a coprocessor 0 register to a general - pur pose register: mfc0 $t5, $13 # Copy Cause register value to $t5. mtc0 (Move To Coprocessor 0) moves a value from a general - pur pose 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 upt s requires doing three things: 1. Turn on the Interrupt Enable bit at Bit 0 of the Status Register. This bit is the global on/ off contr ol for interrupt s. 2. Also in the Status Register, turn on the Interrupt Mask bits correspondi ng to the Receiver (bit 11), Transmit t er (bit 10) and Timer (bit 15). These bits per mi t the CPU to respond to interr upt s generated by the keyboard, display and timer. 3. Turn on the Interrupt Enable bit in the Receiver Control Register (0xFFFF0000). This bit tells the receiver hardware (keyboar d) that it should generate interr upt s when keypresses occur. Now an interrupt will be generated when the user presses a key, and the exception handler will automatically 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 Interrupt Enable bit and turn on the correspondi ng bit in the Transmit t er Control Register (at 0xFFFF0008). Writing an interrupt handler When an interr upt occurs, the following things are done automatically by the hardware: 1. The Exception Level bit (bit 1) in the Status Register is turned on. While this bit is 1, no further interrupt s can occur. This is essential, because we don' t want the interr upt handler to be itself interrupt ed. 2. The Cause register is set to indicate the cause of the interrupt (see Dispatching an interr upt below). 3. The EPC register is set to the current value in the Program Counter. This is the address in the main code where you will be resumi ng after handling the interrupt. 4. The Program Count er is set to 0x80000180. This address (0x80000180) is where you need to put your exception handler. 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 exception handler begins, it needs to first find out what caused the exception. 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 interrupt has occurred. But which interrupt? The Interrupt 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 transmi t ter is ready for another character and bit 15 will be on if the timer has raise an interrupt. Registers Your interrupt 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; remember that somet hi ng like sw $t0, saved_t0 expands to two machine instructions using $at). This situation is resolved by forbiddi ng user programs from using two general - purpose register s, $k0 and $k1 (The k stands for kernel, which an exception handler is part of). Your interr upt handler is allowed to use $k0 and $k1 without having to save or restore their values. This allows you just enough leeway to start saving registers, as well as making returning from the interrupt handler possible. Note that your exception handler (and main program) is probably also using the $at register, which is used silently by the assembler in the expansion of certain pseudoi nst r uct ions; for example: # 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 memory, 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 compiler 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 interrupt handler, because it is tricky to get right, and getting it wrong can lead to subtle inter mit tent 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 upt handler is ready to retur n, it must restore the interrupt ed program' 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 jump back to the user program. These are done with the eret instruction: # eret effectively does this, all at once: # mfc0 $k0, $12 # Get status register. # li $k1, 0xFFFFFFFD # and $k0, $k0, $k1 # Clear Exception Level bit (bit 1) # mtc0 $k0, $12 # to allow interrupts again. # mfc0 $k0, $14 # Get address to jump back to (EPC). # jr $k0 # Jump back to that address. eret