; ; 5-May-98 ; ; RCS: $Id$ ; Lecture notes for Monday, 4-May-98 At this point, we've seen the computational guts of a processor using the single-bus approach. We've also seen how to split up the system into a "processor" with an internal bus and an external memory bus. The memory bus can also be used for I/O. The goal of this lecture is to introduce exceptions/traps/interrupts. 1. idea & definitions a. idea is an "unsolicited" subroutine call. Such subroutines are conventionally called exception/trap/interrupt "handlers". b. definitions vary. Here's what we'll use (what the book uses): i. "exception" is the generic term. ii. a "trap" is associated with a particular instruction. For instance, an arithmetic instruction that fails due to overflow, or a load/store that addresses nonexistant memory. A trap is "synchronous" in the sense that when it happens, you (x) really want to know which instruction caused the trap and (y) you really want to stop right away, usually because the instruction did something bad and you don't want the bad results to propagate further through the program. iii. an "interrupt" is an event that is unrelated to a specific instruction. For instance, a network interface I/O device can raise an interrupt when a message comes in over the network. An interrupt is asynchronous; you don't care exactly when the interrupt handler is invoked (within reason). 2. Strawman implementation (programmer-visible spec) a. add a set of "coprocessor" (pompous MIPS terminology) registers. We add three: i. EPC: the 32-bit PC of the instruction at the time of the exception ii. cause: an n-bit register, where each one of the n bits represents an exception. At the time an exception handler is run, each pending exception has its bit set in cause [[[better read up on how this actually works in MIPS]]] iii. EI: a 1-bit "enable interrupt" register, used for syncrhonization with interrupts. b. add the exception mechanism. In response to an exception condition, do this: EPC <- PC cause = cause | PC = 0x8000 EI = 0 c. add three new instructions: i. MFC0 CRA RB RB <- CRA (CRA is a coprocessor register) ii. MTC0 RA CRB CRB <- RA (CRB is a coprocessor register) iii. RTI PC <- EPC; EI <- 1 3. Uses for exceptions. An exception is just like a test-and-branch, except that the hardware does the testing automatically. Exceptions are appropriate when two things are true: a. The test is frequent b. The condition being tested for is infrequent. -- For instance, exceptions are appropriate for arithmetic overflow since (if you use it) you want to test every addition instruction, but the additions will almost never actually overflow. -- On the other hand, designing an exception for a condition that happens all the time would be inappropriate because traps, when taken, are pretty expensive (many cycles). You'd be better off testing the condition explicitly and branching explicitly in the code. -- Also, designing an exception for a condition that you test infrequently, like whether a file open succeeded, would also be wasteful -- the test is rare enough that hardawre support is not justified. Here are the conventional uses for exceptions: a. (traps) reporting error conditions in instructions, like arithmetic overflow or divide-by-zero. Used a lot more often in floating-point arithmetic than integer arithmetic. b. (interrupts) reporting events associated with I/O devices, for instance, the disk controller raises an interrupt when a requested block has been read into memory, or the network interface raises an interrupt when a message arrives. Here are some more subtle uses for exceptions: c. (traps) use exceptions to extend the instruction set. E.g., add a multiply instruction by defining an illegal opcode to be "mul" and then writing multiply in terms of adds & shifts in the exception handler. More subtle: use exceptions as a signal to change representation (if the language/runtime system supports it). E.g. use arithmetic overflow to cause a switch to 64-bit (or bignum) arithmetic. lisps do this. d. (interrupts/traps) extend the capabilities of I/O components or memory components. We'll use this idea to implement virtual memory (which we'll discuss at length). Another example is "semi-autonomous" I/O devices, for instance a network controller that can receive messages automatically into a queue in memory -- until the allocated queue space fills up. Define an interrupt to be raised when the queue is nearly full so the processor can allocate more space. e. (interrupts/traps) implement protection for multiprogramming. A timeslice interrupt prevents one process from running for more than a fixed amount of time. Second, traps are a means of providing "protected access" subroutines (i.e. system calls). 4. Hardware requirements. Not too much... Essentially just more inputs and outputs to the control state machine. Since many conditional inputs to the control FSM leads to an explosion in the size of the nextstate ROM, I showed a trick using a mux to select one condition to test, since typically one cares about only one condition at a time. 5. Microcode changes. This starts getting messy... First, the instruction fetch code needs to check for interrupts once per instruction. This isn't too bad, actually; we can tuck the check in with the multi-way branch on opcodes. MAR <- PC ! standard instruction fetch IR <- RAM, csel = INT -- ! 17-way branch on OP and COND bits Next, to implement arithmetic overflow detection (for instance), the arithmetic states need to be augmented with extra states to check for overflow: A <- REGS[RA] B <- REGS[RB] overflow = overflow(A + B), csel = overflow -- ! branch to overflow exception on overflow REGS[RD] = A + B ! otherwise redo addition Now you can see that this arithmetic overflow check is a bit annoying: it adds extra states to the addition code. The extra states are required because we can't write the destination register until we know that overflow has not occurred. The exception code (either for the interrupt or the overflow) looks the same: cause <- overflowcode ! or intcode as appropriate (constant) EPC <- PC EI <- 0 PC <- 0x4000 ! constant; go to instruction fetch