1 00:00:03,360 --> 00:00:09,840 And let's start taking about exceptions and interrupts. 2 00:00:10,280 --> 00:00:13,503 And then we're going to start talking about superscalar two. 3 00:00:13,503 --> 00:00:16,414 So we'll start talking about out of order superscalars. 4 00:00:16,414 --> 00:00:20,209 So how do you actually start to execute instructions when they're not in 5 00:00:20,209 --> 00:00:23,380 programmatic order? And when you first hear about this, you're 6 00:00:23,380 --> 00:00:27,435 gonna say, well how is that possible? You know, you shouldn't be able to execute 7 00:00:27,435 --> 00:00:30,606 instructions out of order. But it's very, it's relatively easy. 8 00:00:30,606 --> 00:00:33,570 You can actually, just, you know, execute them out of order. 9 00:00:33,570 --> 00:00:36,845 Maybe you want to commit them in order. That may even be optional. 10 00:00:36,845 --> 00:00:39,860 But let's, let's all start off by talking about interrupts. 11 00:00:39,860 --> 00:00:45,111 So what's, what's an interrupt? So an interrupt is typically some external 12 00:00:45,111 --> 00:00:49,903 or internal event that happens. And it may be synchronous to an 13 00:00:49,903 --> 00:00:54,696 instruction or it may just be some external thing that happens. 14 00:00:54,696 --> 00:01:00,706 That is going to redirect your control flow somewhere else for a little bit of 15 00:01:00,706 --> 00:01:06,640 time and then it will come back to the instruction that you were asked before. 16 00:01:07,220 --> 00:01:11,371 So here's our program. It's executing, happily executing 17 00:01:11,371 --> 00:01:15,446 structure mean. I - one, then it's going to execute 18 00:01:15,446 --> 00:01:19,026 instruction i. Instruction i doesn't actually commit 19 00:01:19,026 --> 00:01:23,607 instead we vector over to interrupt handler which is also some sequence of 20 00:01:23,607 --> 00:01:27,761 instructions that process some problem let's say with instruction i. 21 00:01:27,761 --> 00:01:32,770 And then when it's done it'll come back, re-execute instruction one, or instruction 22 00:01:32,770 --> 00:01:35,213 i. Maybe, maybe not, we'll see why there's 23 00:01:35,213 --> 00:01:40,284 some cases that you may not actually go to re-execute this instruction, if it had a 24 00:01:40,284 --> 00:01:42,300 fatal fault. And then continue on. 25 00:01:43,180 --> 00:01:47,314 Typically, I wanted to point out that, a lot of times, this is done, sort of, for 26 00:01:47,314 --> 00:01:51,861 system level code, or operating system level code or a hypervisor level code that 27 00:01:51,861 --> 00:01:55,324 you'll jump someplace else. So, a good example of this is a timer 28 00:01:55,324 --> 00:01:57,236 interrupt on a processor. It goes off. 29 00:01:57,236 --> 00:02:01,112 It'll re-vector you to someplace else where you have to sort of update the 30 00:02:01,112 --> 00:02:05,556 internal time of the machine and then, you go back to the instruction sequence that 31 00:02:05,556 --> 00:02:10,860 you were executing before. So let's, let's look at some of these 32 00:02:10,860 --> 00:02:11,720 causes. So. 33 00:02:12,080 --> 00:02:17,832 We'll name the, first set of interrupts here asynchronous interrupts, or, some 34 00:02:17,832 --> 00:02:22,855 people call these excep, external events or external interrupts. 35 00:02:23,438 --> 00:02:28,899 And, some good examples, as I said before, are, devices cause interrupts. 36 00:02:28,899 --> 00:02:34,432 So something like the programmable interrupt timer on, X86 processor will 37 00:02:34,432 --> 00:02:39,893 cause a timer tick every once in a while, or every 100 times a second, is, is 38 00:02:39,893 --> 00:02:45,171 pretty, you probable other devices, You know, your network card gets a packet 39 00:02:45,171 --> 00:02:48,435 in on it. And the packet needs to be processed, so 40 00:02:48,435 --> 00:02:52,767 it needs to be read of the network and be put into ram somewhere. 41 00:02:52,966 --> 00:02:56,564 Hardware failures. So things like EEC memory errors. 42 00:02:56,564 --> 00:03:02,162 So its error correcting code memory errors in your main memory will sometimes cause 43 00:03:02,361 --> 00:03:05,960 interrupts to happen or asynchronous events to happen. 44 00:03:05,960 --> 00:03:09,893 Synchronous things sometimes people call these exceptions or traps. 45 00:03:09,893 --> 00:03:13,657 I'm actually calling all of these interrupts because from a naming 46 00:03:13,657 --> 00:03:17,534 perspective you read different architectural manuals they all rename 47 00:03:17,534 --> 00:03:22,142 these things slightly different and there is no sort of common utilization of the 48 00:03:22,142 --> 00:03:25,007 terms. But if you're, you're using something like 49 00:03:25,007 --> 00:03:29,278 x86 a synchronis interrupt is usually called either a trap or an exception. 50 00:03:29,446 --> 00:03:33,380 But that is not common across all other architectures are not x86. 51 00:03:33,380 --> 00:03:39,089 So some good examples of this is if you, try to execute an instruction, which is 52 00:03:39,089 --> 00:03:44,094 not in the ISA manual. It's a garbley, gobbledygook of bits on, 53 00:03:44,094 --> 00:03:47,984 on the disc. So it was an error code, effectively, or 54 00:03:47,984 --> 00:03:53,400 some non-valid instruction, you get an illegal instruction, exception. 55 00:03:53,660 --> 00:03:56,952 Or if you're trying to execute, let's say an operating system-level instruction. 56 00:03:56,952 --> 00:04:00,202 We haven't talked about this in great detail, we'll touch on this later in the 57 00:04:00,202 --> 00:04:02,036 course. But if you're trying to execute some 58 00:04:02,036 --> 00:04:05,369 instruction that only the operating system should be able to execute but you're 59 00:04:05,369 --> 00:04:09,529 executing it from your user program. Then, you're trying to execute something 60 00:04:09,529 --> 00:04:13,985 like a privilege instruction. That's a good way to have this happen. 61 00:04:14,184 --> 00:04:18,174 Earth medic overflow. Some architectures have it when the, 62 00:04:18,373 --> 00:04:23,560 precision of your numbers falls outside the scope of what you can actually 63 00:04:23,760 --> 00:04:28,415 accurately represent, you'll get an overflow exception, or an underflow 64 00:04:28,415 --> 00:04:32,656 exception. Similar sorts of things can happen in a 65 00:04:32,656 --> 00:04:36,289 floating point unit. So, great example of this is if you end up 66 00:04:36,289 --> 00:04:41,041 with what are called denormalized numbers, so in floating point numbers this means 67 00:04:41,041 --> 00:04:45,681 you basically have also lost precision in your floating point unit, so you, you fall 68 00:04:45,681 --> 00:04:50,153 out of the range of numbers that they can represent very well, and you end up in 69 00:04:50,153 --> 00:04:53,171 this other space that are called denormalized numbers. 70 00:04:53,171 --> 00:04:57,308 You get an exception usually. Sometimes other sorts of things that fall 71 00:04:57,308 --> 00:04:59,880 into this case are things like divide by zero. 72 00:04:59,880 --> 00:05:02,859 You try to divide by zero, you'll sometimes get exceptions. 73 00:05:03,013 --> 00:05:06,300 And you can do that. That's a great way, if you on your x86, if 74 00:05:06,300 --> 00:05:09,280 you guys want to write a program, Write a simple C program, 75 00:05:09,280 --> 00:05:11,077 Take some number, Divide it by zero. 76 00:05:11,077 --> 00:05:13,851 And then build a number program. You'll get a printout. 77 00:05:13,851 --> 00:05:16,420 The operating system will, you'll get an exception. 78 00:05:16,420 --> 00:05:18,628 The OS will print out divide by zero fault. 79 00:05:18,628 --> 00:05:21,300 And the program will stop. It will kill your program. 80 00:05:22,116 --> 00:05:26,301 Unaligned memory. Some architectures don't allow you to 81 00:05:26,301 --> 00:05:30,487 access memory in a unaligned manner. Some architectures do. 82 00:05:30,703 --> 00:05:35,610 So something like MIPS, the MIPS instruction set, if you go to try to 83 00:05:35,610 --> 00:05:41,022 execute a unaligned instruction sequence or unaligned load, will say, on the 84 00:05:41,022 --> 00:05:46,362 earlier versions of MIPS, you'll actually get a unaligned memory access. 85 00:05:46,362 --> 00:05:49,898 Later, actually, later MIPS have it as a option. 86 00:05:49,898 --> 00:05:54,300 You can either have a, have it take a trap or not take a trap. 87 00:05:54,900 --> 00:05:59,242 Common thing page faults. So we talk about paging later in this 88 00:05:59,242 --> 00:06:02,325 course. But, if you go try to access some piece of 89 00:06:02,325 --> 00:06:05,346 memory and the memory's not mapped in correctly. 90 00:06:05,535 --> 00:06:09,688 So you, you just can't, the machine physically can't go find memory. 91 00:06:09,688 --> 00:06:13,638 You'll take a, a trap. And then finally, the things like system 92 00:06:13,638 --> 00:06:17,732 calls or interrupts on x86. There's an instruction called int, which 93 00:06:17,732 --> 00:06:20,483 actually causes a, just an interrupt to occur. 94 00:06:20,483 --> 00:06:25,005 And then it takes as a parameter a number. So that's how system calls have 95 00:06:25,005 --> 00:06:29,344 traditionally worked on x86. They later replaced it with actually, an 96 00:06:29,344 --> 00:06:34,539 instruction called sys enter which, does a similar sort of thing with a little bit 97 00:06:34,539 --> 00:06:39,388 cleaner semantics. So I think we, oh actually there's one 98 00:06:39,388 --> 00:06:43,250 point I wanted to talk about asynchronous interrupts. 99 00:06:43,250 --> 00:06:48,860 With asynchronous interrupts, it's hard to know when to deliver the interrupt. 100 00:06:48,860 --> 00:06:51,446 Cuz it's not pegged to a specific instruction. 101 00:06:51,446 --> 00:06:55,487 If the instructions are going down the pipeline, you don't necessarily know 102 00:06:55,487 --> 00:06:58,989 whether to attack it to the first instruction that's in the pipe. 103 00:06:58,989 --> 00:07:03,084 I think it's at the sort of a fetch stage. I think it's at the execute stage. 104 00:07:03,084 --> 00:07:07,125 I think it's at the write back stage. So that's, that's a, that's a challenge. 105 00:07:07,125 --> 00:07:11,112 Another important thing to think about with, asynchronous interrupts is, 106 00:07:11,112 --> 00:07:13,860 sometimes multiple of them go off at the same time. 107 00:07:13,860 --> 00:07:17,039 So let's say your timer interrupt goes off at, at, time t0. 108 00:07:16,931 --> 00:07:21,134 = zero, your, network card gets a packet in and someone hits the keyboard exactly 109 00:07:21,134 --> 00:07:23,928 at the same time. Well, which, which should happen? 110 00:07:23,928 --> 00:07:28,797 Which should you actually go handle? Typically machines have a prioritized 111 00:07:28,797 --> 00:07:33,187 inner-request mechanism. So there would be some sort of priority 112 00:07:33,187 --> 00:07:37,447 encoder there which will determine which is the highest priority. 113 00:07:37,447 --> 00:07:42,756 Some of them, some machines will actually have re-programmable priority interrupt 114 00:07:42,953 --> 00:07:48,327 encoders effectively which will allow you, allow the system software, the operating 115 00:07:48,327 --> 00:07:53,242 system to decide what is the highest priority interrupt to go take in that 116 00:07:53,242 --> 00:07:56,480 case. Sometimes machines will just sort of make 117 00:07:56,480 --> 00:08:01,718 a decision, or the objects of the, of the machine will sort of make a decision, and 118 00:08:01,718 --> 00:08:06,762 say these classes of interrupts are not ver, or asynchronous interrupts are not 119 00:08:06,762 --> 00:08:12,129 very easy to handle so we'll sort of put those in these of low priority bucket, and 120 00:08:12,129 --> 00:08:17,496 then some small set you'll actually be alter sort of re, re-prioritize if you 121 00:08:17,496 --> 00:08:18,627 will. Oh. 122 00:08:18,627 --> 00:08:21,069 Oh, yeah. So, I wanted to talk about this. 123 00:08:21,069 --> 00:08:25,701 What actually happens, when you take a interrupt, from a hardware mechanism 124 00:08:25,701 --> 00:08:29,019 perspective. So the, this is a very idealized view, but 125 00:08:29,019 --> 00:08:33,088 from a mechanical perspective things need to happen in a machine. 126 00:08:33,088 --> 00:08:35,842 There's some state that needs to be updated. 127 00:08:35,842 --> 00:08:40,913 The first thing that needs to happen is, you should basically stop the program at 128 00:08:40,913 --> 00:08:45,855 some point. And you should try to save the program 129 00:08:45,855 --> 00:08:50,247 counter somewhere. Cuz if we want to come back to, let's say, 130 00:08:50,247 --> 00:08:55,157 the instruction that took the interrupt, We need to know where to come back to. 131 00:08:55,157 --> 00:08:59,690 But we're going to go and execute some other piece of code in the meantime. 132 00:08:59,690 --> 00:09:02,963 So our, we can't just save it in the program counter. 133 00:09:02,963 --> 00:09:06,804 We need to save it off. And that's typically called either an 134 00:09:06,804 --> 00:09:10,329 exceptional PC. So EPC that's, what its called on, on a 135 00:09:10,329 --> 00:09:14,169 MIPS, MIPS processor. On x86, I'm trying to remember what it 136 00:09:14,169 --> 00:09:17,128 does. I think it actually gets, pushed onto a 137 00:09:17,128 --> 00:09:20,339 systems stack. So it gets put into memory somewhere. 138 00:09:20,528 --> 00:09:25,795 And if you had something like MIPS, one of the tricky things here is all of your 139 00:09:25,795 --> 00:09:28,494 registers. Are still live from the previous 140 00:09:28,494 --> 00:09:32,559 instruction sequence here. So all of a sudden you, you jump into a 141 00:09:32,559 --> 00:09:37,063 new piece of code and all of your instructions are still, or all of your 142 00:09:37,063 --> 00:09:41,004 registers are still live, They have values you can't throw away. 143 00:09:41,004 --> 00:09:44,495 And you're in the interrupt exception handler. 144 00:09:44,495 --> 00:09:47,629 What do you do? Well there's, there's different, 145 00:09:47,999 --> 00:09:50,586 mechanisms to this, to, to have this happen. 146 00:09:50,770 --> 00:09:55,760 Some architectures, something like MIPS actually reserves two registers that are 147 00:09:55,760 --> 00:09:58,593 only allowed to be used by interrupt handlers. 148 00:09:58,593 --> 00:10:01,920 This is actually a pretty poor solution in my opinion. 149 00:10:02,105 --> 00:10:07,058 So they, they save off two registers. I believe it is, it's somewhere high, it's 150 00:10:07,058 --> 00:10:11,268 like, maybe like 28 and 29. Registers 28 and 29, are not allowed to be 151 00:10:11,268 --> 00:10:16,283 used by the, basically operating system, or the user code and it's only used by 152 00:10:16,283 --> 00:10:20,060 interrupt handlers, in the operating system to save off state. 153 00:10:20,060 --> 00:10:25,494 And what happens in that case is you could use those registers to basically take the 154 00:10:25,494 --> 00:10:28,947 other registers, Compute some addresses and do a store. 155 00:10:28,947 --> 00:10:33,934 Cuz if you recall on, on MIPS, you need to have an address to do the store to. 156 00:10:33,934 --> 00:10:38,985 So you compose the address into, let's say, register 28, and then use that as the 157 00:10:38,985 --> 00:10:42,374 store address. And you can store off all of the other 158 00:10:42,374 --> 00:10:46,821 registers into memory somewhere. And then you can unwind that back when 159 00:10:46,821 --> 00:10:49,910 you're, when you're going to return from the interrupt. 160 00:10:49,910 --> 00:10:54,910 So it's a, a complicated dance. Something like x86, there is some power 161 00:10:54,910 --> 00:11:00,665 mechanisms they are which actually take it and take a lot of your registers and put 162 00:11:00,665 --> 00:11:06,125 them on to a in memory stock for you. So, it's typically, well it's either a 163 00:11:06,125 --> 00:11:11,220 push A which is, push, pushes all of the register state onto the stack and, and pop 164 00:11:11,220 --> 00:11:14,333 A which pops it off. It is not actually required though. 165 00:11:14,333 --> 00:11:19,145 There's other, some operating systems don't actually do push A and pop A, better 166 00:11:19,145 --> 00:11:23,334 operating systems, or more modern operating system will actually only save 167 00:11:23,334 --> 00:11:27,080 off what is strictly needed. That's something, something to think 168 00:11:27,080 --> 00:11:29,480 about. One other important thing here. 169 00:11:29,480 --> 00:11:33,807 Typically when an interrupt occurs, you probably want to mask other interrupts 170 00:11:33,807 --> 00:11:37,401 from happening. So this is, this is a really hard problem 171 00:11:37,401 --> 00:11:40,730 to solve, is you have interrupts inside the interrupt handler. 172 00:11:40,730 --> 00:11:43,394 Ooh. Yeah, that doesn't sound like a, like a 173 00:11:43,394 --> 00:11:45,931 happy day for, for anyone. Cuz what if you're in interrupt handle and 174 00:11:46,692 --> 00:11:50,689 another external interrupt comes in? What do you, what do you do? 175 00:11:50,689 --> 00:11:55,003 You can't save the exceptional PC into the exceptional PC state now. 176 00:11:55,003 --> 00:11:59,316 Because you've already saved the, the old program counter into there. 177 00:11:59,316 --> 00:12:02,425 You can't take that second interrupt inside that. 178 00:12:02,425 --> 00:12:05,977 So you can't necessarily nest interrupts very easily. 179 00:12:05,977 --> 00:12:10,735 So what, what people do for this is, one solution, actually, is to take the 180 00:12:10,735 --> 00:12:13,780 exceptional PC, and put it into memory somewhere. 181 00:12:13,780 --> 00:12:17,287 And once you've done that, Then you can turn interrupts back on 182 00:12:17,287 --> 00:12:20,296 inside the interpender. You can take more interrupts inside the 183 00:12:20,296 --> 00:12:22,902 interpender. Alternatively, if you know that the 184 00:12:22,902 --> 00:12:27,432 interrupts going resolve itself very quickly, you can just return, you can just 185 00:12:27,432 --> 00:12:32,194 leave interrupts masks the whole time the interrupt handler is happening, and when 186 00:12:32,194 --> 00:12:37,097 it's done just the return from interrupt construction usually turns off the 187 00:12:37,097 --> 00:12:39,930 interrupt. Or, or, excuse me, turns back on the 188 00:12:39,930 --> 00:12:43,668 interrupts, if you will. So it'll, it'll re-enable interrupts. 189 00:12:43,668 --> 00:12:48,550 So it's, you got to be a little careful there, there's with interrupts inside of 190 00:12:48,550 --> 00:12:52,408 interrupts happening. So, so that's, that's a great question, so 191 00:12:52,408 --> 00:12:56,180 what do you save in the exceptional PC. On a interrupt. 192 00:12:56,180 --> 00:12:57,503 Oops. So yeah. 193 00:12:57,503 --> 00:13:03,539 That's, that's, that's there. So if you're, have instructions that are 194 00:13:03,539 --> 00:13:09,328 marching down the pipe in a two way in order superscalar we'll say. 195 00:13:09,328 --> 00:13:15,530 You're, you're gonna save the PC of the instruction that took the interrupt. 196 00:13:16,890 --> 00:13:19,897 Some architectures define this differently. 197 00:13:19,897 --> 00:13:24,792 If you go look at something like x86 typically what gets saved in the 198 00:13:24,792 --> 00:13:28,289 exceptional pc is the next instruction to execute. 199 00:13:28,289 --> 00:13:31,576 So it's, it's really dependent on architectures. 200 00:13:31,576 --> 00:13:36,822 Some architectures will save the address in the exceptional pc of the next 201 00:13:36,822 --> 00:13:42,696 instruction of program order to execute. Some things will save the address of the 202 00:13:42,696 --> 00:13:49,410 instruction that actually took the trap and as far as or took the exception or the 203 00:13:49,410 --> 00:13:53,293 interrupt. So it's a little bit of a, a, a tradeoff 204 00:13:53,293 --> 00:13:56,620 there. One of, one of the things that's actually 205 00:13:56,620 --> 00:14:00,160 hard is, you have a branch that takes an exception. 206 00:14:00,160 --> 00:14:04,827 Do you store the target of the branch location in the exceptional PC? 207 00:14:04,827 --> 00:14:08,479 Or do you, yeah. You have to sort of resolve the branch 208 00:14:08,479 --> 00:14:11,726 first. So that's one reason, actually, why lots 209 00:14:11,726 --> 00:14:17,678 of architectures favor just storing the PC of the, instruction that took the trap and 210 00:14:17,678 --> 00:14:24,577 not the, sort of next" instruction." So, I, I actually favor storing the PC of 211 00:14:24,577 --> 00:14:29,380 the instruction that took the, the trap, and not the destination of that. 212 00:14:29,760 --> 00:14:40,951 Asynchronous interrupts similiar sort of thing, Sometimes the handler wants to 213 00:14:40,951 --> 00:14:45,535 resume after sort of an instruction, where someone might have to add PC plus four if 214 00:14:45,535 --> 00:14:49,955 you have a architecture exceptional PC plus four if you need to jump over the 215 00:14:49,955 --> 00:14:55,309 instruction, for instance. Something I didn't want so here this is 216 00:14:55,309 --> 00:14:59,841 the expression that I came up was, what does this look like in a pipeline? 217 00:14:59,841 --> 00:15:03,069 And when do you note actually process the interrupt. 218 00:15:03,069 --> 00:15:07,850 So here we have a five stage pipe. So this is a little bit easier pipeline. 219 00:15:07,850 --> 00:15:14,586 And we can see, this, these bubbles here are actually different types of interrupts 220 00:15:14,586 --> 00:15:19,632 or exceptions that can come out of the pipe at different locations. 221 00:15:19,632 --> 00:15:24,745 So out in front here we can have maybe, a PC address exception. 222 00:15:24,745 --> 00:15:29,823 What do I mean by that? Well, some architectures won't allow you 223 00:15:29,823 --> 00:15:34,821 to execute, let's say, code out of, certain regions of memory. 224 00:15:34,821 --> 00:15:40,061 So an example of this is actually in the 64 bit extension of x86. 225 00:15:40,061 --> 00:15:45,301 The 64 bit extension of x86 there's what they call a memory hole. 226 00:15:45,301 --> 00:15:49,646 So between hope you'll call positive memory and negative memory, so the top 227 00:15:49,646 --> 00:15:52,564 that, being such. There's a big chunk of memory which no 228 00:15:52,564 --> 00:15:56,359 one's allowed to execute out of, or it's not math, it's not real memory. 229 00:15:56,359 --> 00:16:00,533 So they have a 64 bit address space, but they only use 42 bits of that 64 bit 230 00:16:00,533 --> 00:16:03,569 address space. So if you've any bits in the middle which 231 00:16:03,569 --> 00:16:08,177 are not, set correctly, you're basically going to be executing out of the memory 232 00:16:08,177 --> 00:16:12,406 hole, and that's an example of something that can cause a PC address exception. 233 00:16:12,406 --> 00:16:15,388 So your PC sort of falls off the end of math to memory. 234 00:16:15,388 --> 00:16:18,424 What do you do? You're in some piece of memory, you're in 235 00:16:18,424 --> 00:16:21,460 some address which by definition is not a valid address. 236 00:16:22,240 --> 00:16:24,313 Decode. Illegal, illegal op code. 237 00:16:24,313 --> 00:16:27,122 So it doesn't, it isn't in your ISA manual. 238 00:16:27,122 --> 00:16:32,807 Lots of, lots of op coding, coding space. Usually most architectures purposely leave 239 00:16:32,807 --> 00:16:37,823 some space in there just for legal instructions, or future expansion, if you 240 00:16:37,823 --> 00:16:43,374 will, and you want that to, to interrupt, overflows and underflows out of your ALU. 241 00:16:43,374 --> 00:16:48,190 There's a whole host, if you have a floating point unit here, of floating 242 00:16:48,190 --> 00:16:51,868 point exceptions. Overflows, underflows, and denormalized 243 00:16:51,868 --> 00:16:55,251 sorts of instructions. Data address exceptions. 244 00:16:55,251 --> 00:16:59,911 Well this would be if you're, let's say, you have an unmapped memory address. 245 00:17:00,090 --> 00:17:04,571 Or you do a underlined mode or store. So basically, every stage of your pipe 246 00:17:04,571 --> 00:17:07,498 here can be generating, some form of interrupt. 247 00:17:07,498 --> 00:17:11,860 And then there's also asynchronous interrupts, which we haven't drawn yet. 248 00:17:12,580 --> 00:17:18,518 Okay, so, good first question here is, how do we handle multiple simultaneous 249 00:17:18,518 --> 00:17:24,140 interrupts in different pipeline stages, happening all at the same time? 250 00:17:25,140 --> 00:17:30,644 Agree we should prioritize them. So, what is the oldest instruction in the 251 00:17:30,644 --> 00:17:35,219 pipeline here? So this is going to be the oldest 252 00:17:35,219 --> 00:17:39,862 instruction in the pipe. So, let's, let's think about that. 253 00:17:39,862 --> 00:17:43,820 So that means it's probably going to want to kill everything behind it. 254 00:17:44,360 --> 00:17:47,747 If, if there is an exception, that has happened here. 255 00:17:47,946 --> 00:17:53,060 So if we were sort of, multiple things generating exceptions here at one time. 256 00:17:53,340 --> 00:17:58,147 The oldest thing, the oldest instruction in the pipe, the instruction that's been 257 00:17:58,147 --> 00:18:02,894 in the pipe the longest, is going to want to kill all of the rest of the things. 258 00:18:03,075 --> 00:18:06,200 Going forward, though, so from a priority perspective, 259 00:18:06,820 --> 00:18:09,900 Which of these things are probably the highest priority? 260 00:18:17,380 --> 00:18:24,233 So, do you think we should be computing overflow errors if the instruction op code 261 00:18:24,233 --> 00:18:30,220 is illegal? Should be even decoding the instruction if 262 00:18:30,220 --> 00:18:35,300 the address that we try to fetch from our instruction memory doesn't make any sense. 263 00:18:35,940 --> 00:18:43,005 So the priority of sort of the, when you go to figure out where or which exception 264 00:18:43,005 --> 00:18:49,984 or which interrupt is the cause of the exception should go this way, from left to 265 00:18:49,984 --> 00:18:56,478 right and then the you're going to, want to actually kill going, going backwards. 266 00:18:56,478 --> 00:19:03,046 So let's, let's, let's skip that question for a second and look at the, at this 267 00:19:03,046 --> 00:19:09,516 drawing. So what you'll see here is we're actually 268 00:19:09,516 --> 00:19:19,555 going to just remember that we took some exception for a particular instruction and 269 00:19:19,555 --> 00:19:23,129 just pipe it forward. And then what we're going to do, is we're 270 00:19:23,129 --> 00:19:27,672 basically going to say, at the ends of the pipe, once we know everything that's 271 00:19:27,672 --> 00:19:30,580 happened. We're gonna call this the commit point. 272 00:19:32,220 --> 00:19:36,260 And that's going to feedback the other way killing everything. 273 00:19:37,440 --> 00:19:42,460 Now, Why do we put the commit point here? 274 00:19:47,320 --> 00:19:50,983 Could we put the commit point, let's say, in this stage of the pipe? 275 00:19:50,983 --> 00:19:54,313 In the execute stage. So, let's, let's define the commit point. 276 00:19:54,313 --> 00:19:58,587 The commit point is the point at which architectural state of the machine is 277 00:19:58,587 --> 00:20:04,854 committed to the is committed, and, now that may not be in the register file yet. 278 00:20:04,854 --> 00:20:08,317 Because by definition, in this point here, it's not in the register file. 279 00:20:08,317 --> 00:20:10,609 It doesn't get there till the write back stage. 280 00:20:10,609 --> 00:20:13,682 But nothing can change. We're not, we can't read, we can't take a 281 00:20:13,682 --> 00:20:16,560 branch, we can't redirect, we can't take any more exceptions. 282 00:20:17,820 --> 00:20:22,824 Well, by that definition we can't put the commit point here, because further 283 00:20:23,025 --> 00:20:28,230 exceptions can happen after the commit point, if we pull the commit point back. 284 00:20:28,424 --> 00:20:31,984 There are machines which do pull the commit point back. 285 00:20:32,179 --> 00:20:37,358 You will see, when we start talking about super, or out of order superscalars, we 286 00:20:37,358 --> 00:20:40,660 typically like to have the commit point at the end. 287 00:20:40,660 --> 00:20:44,384 Cuz, or near the ends. Because then you can sort of see if 288 00:20:44,384 --> 00:20:48,567 everything's resolved. You can handle your interrupts, exceptions 289 00:20:48,567 --> 00:20:52,618 and have exceptions going out on the pipe as long as possible. 290 00:20:52,618 --> 00:20:57,781 And if you try to pull your commit point early, that forces you to actually resolve 291 00:20:57,781 --> 00:21:03,270 whatever exception is going to happen for a particular instruction relatively early 292 00:21:03,270 --> 00:21:06,472 in the pipe. So if we want to full this forward a 293 00:21:06,472 --> 00:21:11,700 stage, we would have to basically check whether the address of a load or a store 294 00:21:11,700 --> 00:21:16,481 is valid in this pipe stage. That may be possible because we finished 295 00:21:16,481 --> 00:21:19,720 the, calculation of the address right here. 296 00:21:19,720 --> 00:21:23,333 But we haven't necessarily let's stay done the TOB lookup. 297 00:21:23,333 --> 00:21:27,196 But we can pull the TOB lookup earlier or something like that. 298 00:21:27,196 --> 00:21:31,060 That is possible. And, and we'll see that some architectures 299 00:21:31,060 --> 00:21:35,234 try to pull that early. Some architectures try to have, imprecise 300 00:21:35,234 --> 00:21:37,929 exceptions. This is a, a scary, scary place. 301 00:21:37,929 --> 00:21:40,665 We're not going to be talking about this very much. 302 00:21:40,665 --> 00:21:44,474 We're going to be talking about precise exceptions mostly in this class. 303 00:21:44,474 --> 00:21:48,604 But an imprecise exception is one where instructions are going down the pipe. 304 00:21:48,604 --> 00:21:52,896 You basically let the instruction sort of pass the commit point and you tag the 305 00:21:52,896 --> 00:21:55,417 exception to the wrong instruction effectively. 306 00:21:55,417 --> 00:21:57,885 It's not, not precise. You can't stop on a dime. 307 00:21:57,885 --> 00:22:02,230 Architecture is, there are some embedded processes that do things like that. 308 00:22:02,391 --> 00:22:06,844 And it's probably not the wisest thing to do if you want to write a real operating 309 00:22:06,844 --> 00:22:08,762 system. But like I said, yes. 310 00:22:08,762 --> 00:22:13,053 So the commit point being close to the end is probably a good place. 311 00:22:13,053 --> 00:22:16,019 So at least after all your exceptions are done. 312 00:22:16,019 --> 00:22:19,238 So by the time we get to this red dashed line here, 313 00:22:19,238 --> 00:22:24,034 So we're after the computation of the exceptional PC and everything, we know 314 00:22:24,034 --> 00:22:28,636 that we're going to be committing. Or we, or at least we have a thumbs up or 315 00:22:28,636 --> 00:22:33,141 a thumbs down. And if it's a thumbs down we kill 316 00:22:33,141 --> 00:22:40,860 everything behind here. One thing I did want to say is, cause. 317 00:22:42,220 --> 00:22:46,661 What does that register do? Well that register tells us, why did we 318 00:22:46,661 --> 00:22:50,959 take the exception. So it's a priority encoder, which 319 00:22:50,959 --> 00:22:54,860 priority, prioritizes, as we said, this direction. 320 00:22:55,120 --> 00:22:58,860 And we'll determine the cause of the, of the exception. 321 00:23:01,720 --> 00:23:05,200 Asynchronous interrupts. Hm. 322 00:23:06,200 --> 00:23:09,872 There's different places to wire this in. You just sort of have to tag it to 323 00:23:09,872 --> 00:23:12,869 something, but you have to make sure that you actually take it. 324 00:23:12,869 --> 00:23:16,831 So the simplest thing to do is just to put it into this big log at the end of the 325 00:23:16,831 --> 00:23:20,263 pipe here at the commit point. And have the asynchronous interrupt show 326 00:23:20,263 --> 00:23:22,582 up at the end of the pipe. Not all pipes do this. 327 00:23:22,582 --> 00:23:26,207 Some pipes will actually inject asynchronous interrupt at the beginning of 328 00:23:26,207 --> 00:23:30,266 the pipe, allow it to go down the end, and arbitrate like everything else here at the 329 00:23:30,266 --> 00:23:33,146 end stage of the pipe. The simplest thing to do is to have it 330 00:23:33,146 --> 00:23:36,823 come in here, and that's because otherwise you might drop this asynchronous interrupt 331 00:23:36,823 --> 00:23:39,504 on the ground, or not actually take the asynchronous interrupt. 332 00:23:39,504 --> 00:23:42,100 You want to make sure you actually have a chance to take it. 333 00:23:43,679 --> 00:23:49,707 Exceptional PC. Takes the PC and, and pipes it forward and 334 00:23:49,707 --> 00:23:54,685 saves that in a register. But this only gets loaded on a exception 335 00:23:54,685 --> 00:24:03,584 or interrupt happening. Okay, so that's, that's commit points. 336 00:24:03,928 --> 00:24:10,538 That's, that's important for out of order superscalars. 337 00:24:10,538 --> 00:24:14,312 Because we're going to have to start thinking about, where is the commit point 338 00:24:14,312 --> 00:24:16,827 of a processor? And it may not be where we want it. 339 00:24:16,827 --> 00:24:20,953 Or it's possible that we will not be able to put the commit point anywhere in the 340 00:24:20,953 --> 00:24:23,318 processor, and actually have the processor work. 341 00:24:23,318 --> 00:24:27,192 So today, we're going to actually look at a processor where it's not possible to 342 00:24:27,192 --> 00:24:30,362 have precise exceptions. So there is no line we can cut and say, 343 00:24:30,362 --> 00:24:39,196 this is the commit point. Let see, so we, we covered all this. 344 00:24:40,920 --> 00:24:47,292 Speculation. This is going back to like, our example of 345 00:24:47,292 --> 00:24:50,910 PC plus four. Do we want to assume the exception's going 346 00:24:50,522 --> 00:24:53,881 to happen? Or the interrupt is going to happen, or 347 00:24:53,881 --> 00:24:59,549 not? So we're calling it an exception for a 348 00:24:59,549 --> 00:25:01,562 reason. It is the exceptional case. 349 00:25:01,562 --> 00:25:05,412 It is not the common case. So we want to somehow predict what our 350 00:25:05,412 --> 00:25:09,498 branch vector tells us or PC4 plus four or fall through or PC, you know, the next 351 00:25:09,498 --> 00:25:12,696 instruction. We do not want to just have we don't have 352 00:25:12,696 --> 00:25:17,315 to wait till the end of the pipe to know whether an exception is taken or not 353 00:25:17,315 --> 00:25:19,980 before we try to go get the next instruction. 354 00:25:21,900 --> 00:25:29,461 One other thing When we start to go to out of order pipelines, we're going to start 355 00:25:29,461 --> 00:25:35,040 to need some, recovery mechanism here. We're going to be processing instructions 356 00:25:35,040 --> 00:25:38,740 out of order. So if they, we might be taking the 357 00:25:38,740 --> 00:25:45,118 interrupt for, in instruction after it, it's let's say, subsequent instructions 358 00:25:45,118 --> 00:25:49,920 have either committed or sort of started to go down the pipe. 359 00:25:49,920 --> 00:25:54,093 And you sort of get some out of order time questions. 360 00:25:54,093 --> 00:25:59,919 So we're going to look at a few different solutions for this, for recovery. 361 00:25:59,919 --> 00:26:04,880 In our, in our simple cases, we just basically flush the pipe, 362 00:26:06,880 --> 00:26:10,084 And kill everything behind us. In more complicated things. 363 00:26:10,084 --> 00:26:14,470 We're actually going to have extra register files that are basically going to 364 00:26:14,470 --> 00:26:18,125 be shadow register files. We're going to keep track of everything 365 00:26:18,125 --> 00:26:21,611 that's going on in the processor, of what should have happened. 366 00:26:21,611 --> 00:26:25,097 And then we're gonna dump that into our true architectural. 367 00:26:25,266 --> 00:26:28,189 Excuse me. Into our physical register file, and we'll 368 00:26:28,189 --> 00:26:34,857 look at that, maybe today, or next class. We should bypass. 369 00:26:34,857 --> 00:26:37,955 Bypassing is good. You should not have to wait to the end of 370 00:26:37,955 --> 00:26:48,730 the pipe. Okay so let's look at a time diagram here, 371 00:26:48,730 --> 00:26:56,647 of an add that takes an overflow. Instruction one here is an add and we 372 00:26:56,647 --> 00:27:00,896 speculate that it does not take a, any sort of exception. 373 00:27:00,896 --> 00:27:05,980 So we fetch the next instruction. We start sticking ads on the pipe. 374 00:27:06,940 --> 00:27:12,154 In the except in the execute stage of the pipe, we determine that there isn't 375 00:27:12,154 --> 00:27:15,569 overflow. Well as we said, as we said, we don't 376 00:27:15,569 --> 00:27:20,254 actually try to do something about this until the commit stage at the pipe. 377 00:27:20,254 --> 00:27:25,440 So in, in our simple pipe our commit stage is at the end of this memory stage. 378 00:27:25,440 --> 00:27:30,626 So we actually pipe it forward one stage. At that point, we restart the front of the 379 00:27:30,626 --> 00:27:33,375 pipe, and we start fetching, the, handler code. 380 00:27:33,375 --> 00:27:37,748 The exceptional handler code. And we're going to basically kill behind 381 00:27:37,748 --> 00:27:40,560 us, turn everything behind us into a no-op. 382 00:27:40,900 --> 00:27:50,146 So. We'll saying we're bypassing out of ex1 383 00:27:50,146 --> 00:27:57,030 here, into the Well, okay, let's say we go to this one instead. 384 00:27:57,030 --> 00:28:01,235 Or we, we can come out here, you'll see we're bypassing out of the memory stage, 385 00:28:01,235 --> 00:28:05,603 or the, the memory stage, back to the register fetch stage of instruction three. 386 00:28:05,603 --> 00:28:09,377 I think that's what you just asked about. What's going to happen is, we're going to 387 00:28:09,701 --> 00:28:14,015 let that bypass happen, we're going to let that data come around, through the bypass 388 00:28:14,015 --> 00:28:16,387 network. But what's going to happen is, we're, at 389 00:28:16,387 --> 00:28:20,270 that same time, we're sending the kill, kill signal behind us in the pipe. 390 00:28:20,270 --> 00:28:23,986 Or instruction one is going to be killing everything behind it in, in the pipe. 391 00:28:23,986 --> 00:28:27,225 So we're going to bypass the data around. It's going to get bypassed. 392 00:28:27,225 --> 00:28:30,322 It's going to end up, in, in the pipeline register in the cycle. 393 00:28:30,322 --> 00:28:33,323 But it's going to be killed basically at the end of that cycle. 394 00:28:33,323 --> 00:28:35,420 A better example is actually here. Let's say, 395 00:28:35,420 --> 00:28:42,147 It bypasses to instruction two from the execute stage to the decode stage of the 396 00:28:42,147 --> 00:28:45,146 pipe. That's actually gonna going to loaded into 397 00:28:45,146 --> 00:28:48,494 the pipeline register of the fetched, operand. 398 00:28:48,494 --> 00:28:54,283 And then it's going to go forward into the execute stage, ex2 here, for instruction 399 00:28:54,283 --> 00:28:55,817 two. So we bypassed it. 400 00:28:55,817 --> 00:29:00,281 We started executing that instruction. Everything was going fine. 401 00:29:00,281 --> 00:29:03,490 But then, we just come along, and we kill it. 402 00:29:03,490 --> 00:29:05,841 So, we're speculatively executing, it's called. 403 00:29:05,841 --> 00:29:08,297 So we're, it's a speculative execution pipeline. 404 00:29:08,297 --> 00:29:11,589 We are assuming, we're predicting that everything is going fine. 405 00:29:11,589 --> 00:29:15,300 And then we're going to be killing behind us when something goes wrong.