 Greetings, processor friends. This is video 10 in the series of creating a 6800 processor on an FPGA using nMyGen. So we've only got 3 instructions left to implement, and that is the wait for interrupt, return from interrupt, and software interrupt instructions. So before I get into implementing these, I just did a little bit of housekeeping with respect to formal verification. Now, one of the things that I've done is I've reworked the formal verification framework a little bit. So I have this record, which is an nMyGen construct. It's basically a bundle of signals. So for example, it's a bundle of an address, which is an unsigned 16-bit signal, data in, data out, read, write, VMA, and BA, and you probably see where I'm going, is that I want to make sure that on a cycle by cycle basis, the read, write, VMA, and bus available signals are as they should be, as well as the address line, if VMA is showing that it's a valid address. So verification has changed so that you set up what you want to see on each cycle. In addition, you set up what registers you want to see at the end of the instruction, and then you call verify instead of check. What verify does is it actually calls check on the inside, but it also sets up data and instruction as instance variables so that we can access it without having to pass around instruction and data. And then you get something like this, which is assert cycles. So what you do is you say how many cycles the instruction was supposed to last, and what this does is it checks that the number of cycles was indeed the number of cycles that you wanted. And on every cycle, what you do is you tell it, let's see where is it, what you do is you tell it to snapshot the signals. And what that does is it stores the address data in and data outlines, read, write, VMA, and BA, as well as the cycle number. And it stores it in an array of those records. So then at the end of the instruction, you can check every single cycle to make sure that things happen the way you think they did. So we have assert cycle signals, and you pass it the cycle number, and then you pass it the VMA, the BA, and the read, write, and optionally, you can pass it an address. You don't have to if VMA is zero, because of course, the address is invalid. So it doesn't really matter what's on the address lines. And then this is just a shortcut to say the signals that we want, the signals that we actually got, and then we just compare bus available and valid memory address to that. And if valid memory address was one, then we also check the read, write line and the address line. And then we're going to return either the data in or data out, depending on whether we did a read or a write. And this is one of the keys we no longer have to say during the instructions, oh, I just did a read, let me do a snapshot of the data that I read and the address that I read it from. Because we're doing this every single cycle, if the read, write line is high, and the valid memory address line is high, then we have done a read. So we will store that information, same thing with writes. So we no longer have to explicitly say, you know, formal read or formal write, which is nice. I also have assert registers, which pretty much does the same thing. Except in this case, you can specify any of ABX or the stack pointer or the PC. And if you don't specify one of those, then it doesn't change. It's not supposed to change. But if you do specify it, then it should be equal to what you wanted it to be. In addition, I've done the truncation for you. So you actually, that should be h, shouldn't it? I've done the truncation for you. So that, for example, when you're adding one to the PC, you don't have to do the truncation yourself. It's truncated right here. So no more of that. And then assert flags is pretty much the same API, except you don't have to pass it pre-CCS or post-CCS because now data is part of the instance. So we don't have to pass a reference to it, which is kind of convenient. So what does it look like? So here's a formal branch. It hasn't changed all that much. So one of the things that changed is at the top of the check, I checked that the number of cycles is four because that's how many cycles a branch instruction is. For cycle one, I'm checking to make sure that I have read, here's RW equals one, VMA is one and bus available is zero, that I've read from this address, which is the byte that comes after the opcode. Now in the cycle by cycle breakdown, cycles two and three are unused. The valid memory address is set to zero. So presumably it's being used for some sort of calculation. So and what this actually says is that you have set VMA to zero. So I don't need to check the address lines or the data lines. The rest is pretty much exactly the same as it was with the exception of the very end, where I assert registers and all the registers should remain the same, except for the program counter, which should be equal to the target. What's the target? Well, the target is the offset plus the PC, if you are taking the branch. And what's the offset? Well, the offset is actually, let's see, where do I store it? Right here. Offset is set from data. And data is actually returned from assert cycle signals. Again, if you're doing a read, it will return the data read. If you're doing a write, it will return the data written. And finally, we just assert that the flags have not changed. And that's really it. Now the interesting thing is, is that if we look at the formal verification, we can see that it failed. So if we look at the log file, and this is from cover, if we look at the log file down here, we can see that it failed at verification dot pi line 110. So we take a look at 110. And we see that, aha, it's a VMA line. So at some in some cycle, we said we wanted this VMA, but we got a different VMA. So immediately, I would look at the implementation of the instruction and say, well, did I actually set VMA properly? Because remember, we wanted to set VMA to zero during cycles two and three. And did we? Here's BR. Here's cycle two and here's cycle three. And I don't see any setting of VMA set to zero. So that's one way of checking it. The other way of checking it is is just look at the trace. So let's look at the trace. Right. So here's the trace. And I've opened up here, let me zoom in a little bit. So I've opened up, I don't actually need this. Whether the snapshot has been taken, which of course it has. Here are the cycles. Here are the recorded cycles. And we have the instruction. And we have record record one address. So this is the address during cycle one. You don't bother to record cycle zero because that's just reading the op code. So there's nothing really special going on there. So this is the recorded address from branch at C821, which makes sense because right over here is the op code address and C821 is the address next to it, which is the offset. And you can see that record one data in is two zero. So we know what the offset is. In addition, we can see that record one read write is high because we're doing a read. And record one VMA is high because the memory address is valid. Now let's look at record two address zero branch up branch bus available zero as it should be data in zero, okay, whatever, read, write zero, fine, whatever VMA though is high. That's wrong. Same thing with record three VMA. Now we can also see what we wanted by opening up the signals and scrolling all the way down to all the wants. So we can just take this and we could just open them all up. And we can see what we wanted. So we can see that the want address for cycle one is C821, which was correct. And then we can just compare. And we can see that want two and want three VMA are zero. So that would not compare. We can see that want cycles is set to four because it's a four cycle instruction. And want PC is C822, which is the target of the branch. So so by looking at that, we can see Oh, VMA was set wrong. So we can just fix that. That's really all you wanted to do. And then we can rerun formal verification and see what happens. So my mistake, the problem was that I was setting VMA at the end of cycle two and at the end of cycle three. And that's not what was supposed to happen. It should have been at the end of cycle one and at the end of cycle two, which means that it would be zero through cycle two and cycle three. So okay, and now that that's been fixed, we can see that verification has succeeded for the branch instructions, which means now that the branch instructions are cycle for cycle accurate, at least with respect to the address lines, the data lines, read, write VMA and be a now again, when VMA is zero, we don't output any particular address or read, write signal or data signal, because it's irrelevant. If there is no valid memory address on the bus, then you should not be paying attention to that memory address. And you should also not be paying attention to read, write or anything like that. And if you really, really cared about that sort of thing, you could just move the read, write and address checks up to here so that they take place regardless of whether you have a valid memory address or not. But you know, in looking at the cycle by cycle breakdowns, I've seen that the address when VMA is zero is really just used for like intermediate calculations. So again, it's not something that the external circuitry should be paying attention to. But if you really wanted to, you could include that in your implementation and your verification. Okay, so let's talk a little bit about interrupts. So the idea behind interrupts is that your processor is booking along executing its program. And then you want it to do something, you know, maybe you want it to do something periodically, or maybe a signal comes in from the outside world, and it has to do something. So in that case, an interrupt happens. And when that happens, you push the entire processor state onto the stack. So in this case, the entire processor state is the stack pointer, the flags, the accumulators, the index register and the program counter. And then you go ahead and do your interrupt routine, wherever that happens to be. And then you pop the processor state off of the stack. And now your processor is continuing where it left off. Because of course, it's stored the PC. Now, you can't just store the processor state and start another routine in any old place. You have to wait until the instruction that you're currently executing is complete. So you can see this here in the data sheet that the IRQ and NMI interrupts are sampled during phase two. And then when you complete an instruction, then you go ahead and do the interrupt routine. So how do you know which interrupt routine to execute? There are actually three different interrupt routines. The first is the non-maskable interrupt routine, which is stored at addresses FFFC and FFFD. So that's the address of your non-maskable interrupt routine. So non-maskable basically means that there are actually two types of interrupts. There's a maskable interrupt and a non-maskable interrupt. And a non-maskable interrupt must be serviced. A maskable interrupt, there's a mask bit in the flags register called I that you can set or clear. And that tells you whether if a maskable interrupt comes in, whether you have to service it or not. So the maskable interrupt is stored at FFF8 and FFF9. And then you've got a software interrupt, which is what happens when the program itself wants to perform an interrupt. And that is stored at FFFA and FFFB. So technically speaking, I guess you could consider reset, kind of like an interrupt in that it has an interrupt vector at FFFE and FFFF. The only difference, of course, is that when you do a reset, it will immediately take place, regardless of where you are in an instruction. In other words, if you hit the reset button, it's not going to wait until the end of the instruction. Essentially, it's like a power cycle, kind of. So let's take a look at the actual instructions that make use of these interrupts. So I guess the simplest one is the software interrupt. So in this case, we can see it's a whopping 12 cycles. Of course, it's so long because the entire processor state has to be stored onto the stack. And that's seven bytes, it looks like. So there's two for the return address, two for the index, two for the accumulators, and then the flags register. So that's seven bytes in total that need to be stored on the stack. Then you have to go ahead and read the software interrupt vector at FFFA and FFFB, and then go there. So that takes 12 cycles. So when you are ready to return from the interrupt routine, basically you pop the processor state off of the stack. Again, that requires reading seven bytes of data from the stack. And then that's pretty much it. So it's kind of like an RTS in that the very last two bytes are the address that you have to return to. But it's a return from interrupt. So you've got some extra stuff on the stack that you need to deal with. That's why, of course, in an interrupt routine, you use RTI and not RTS. So here's the software description, the programming reference manual's description of software interrupt, which basically says what happens when. So we can see that we're pushing a whole bunch of stuff over here. And then we set the interrupt mask flag. And basically what that means is that you're not allowed to do a maskable interrupt request at this point in time. And then we go to, we load the vector address, and then we go there. Now, one of the interesting ways that the programming reference manual talks about the vector is this sentence over here, which is kind of a weird way of talking about addresses. It basically says memory locations n minus five and n minus four, where n is the address corresponding to a high state on all lines of the address bus, which is kind of a funny way of saying FFFA and FFFB, right? Because the address corresponding to a high state on all lines of the address bus is basically just FFFF. So why wouldn't you have just used the hexadecimal notation? I really don't know. But in any case, the implementation of software interrupt is relatively straightforward. You basically do what it says, keeping in mind that you do have to set the interrupt mask bit just before you read the interrupt vector. So that's pretty straightforward. Likewise, return from interrupt is equally, fairly trivial to implement. Wait for interrupt is a different story now. If we look at the cycle by cycle breakdown for wait for interrupt, you can see that it's nine cycles, and basically it pushes the processor state onto the stack, and then basically it just sits there doing nothing, waiting for an external interrupt signal to come in, which could be an IRQ, a maskable interrupt, or it could be a non-maskable interrupt. So basically in wait for interrupt, once we push the processor state onto the stack, we're waiting for an interrupt, and it could be an NMI or an IRQ, whichever one comes first. Now, interestingly, the description in the programming reference manual of wait for interrupt is actually incorrect. So it says down here that when an interrupt is signaled on the interrupt request line, and the iBit is clear, so in other words, you're not masking things off, execution proceeds as follows, and then it's basically the same as software interrupt, except that the interrupt vector you're using is for the not, for the maskable interrupt, and it basically says N minus 7 and N minus 6, which of course is FFFF minus 7 and FFFF minus 6. What it doesn't talk about is what happens when there's a non-maskable interrupt. So in fact, wait for interrupt waits for both cases, either a non-maskable interrupt or an interrupt. Now there's an interesting flowchart here in the data sheet which shows what happens when there's a non-maskable interrupt and a maskable interrupt. So if you get a non-maskable interrupt, then you just execute the interrupt routine that's stored at FFFC and FFFD. If you get an interrupt request, though, and provided that the iBit is not set, then you execute the interrupt routine stored at FFF8 and FFF9, so far so good. The interesting thing is that NMI and IRQ are sampled on Phase 2, which means that they're sort of, you know, latched, and then the processor takes a look at what got latched. What happens if both of them are latched at the same time? Well, NMI takes precedence according to this flowchart, so that's kind of interesting. So the implementation of SWI is not exactly straightforward because we can see that the instruction technically ends once you've pushed the state of the processor onto the stack, but it doesn't really end because you're just sitting there and not executing the next instruction. So what exactly happens? Well, I suspect that what happens is that the instruction does end, but the processor enters a special state where it just spins and does nothing. And then it handles interrupts and NMIs normally, which means that at the end of any instruction, we check the NMI and IRQ lines, and if any of them are set, we do the interrupt. So the first thing I've done here is I've added an IRQ and an NMI signal. Now, on the processor pins, these are active low signals. So in other words, if the negative IRQ goes low, then that means you're doing an interrupt. So we don't really want to do that in software because just handling negative signals is just confusing enough. So in the CPU implementation, we will just invert the signal and pass that to the core. So that's why we're just calling it IRQ and NMI and not NIRQ and NNMI. So here's the implementation for software interrupt. So basically, it just, you know, does what it needs to do. It stuffs everything onto the stack. And then if you look at the thing starting from, so here's cycle eight, where during cycle nine, we're going to set VMA to zero because that's what you do. And then during cycle nine, you can see that we're setting the interrupt flag, and we're reading the address at FFFA. And then on the next cycle, we're incrementing the address and reading that. And then on the final, at the end of the instruction, what we do is we jump to that address. With wait for interrupt though, it's not as straightforward as that. So this was my original implementation, which is not, which is not correct. So we do basically exactly the same thing as SWY. We just push things onto the stack. However, in the setup for cycle nine, what we're doing is we are setting VMA to zero and bus available to one. And that basically means that the processor is going to be disengaged and, you know, some external hardware can take control of the address and data lines. That's really all that means. And then here in cycle nine, my implementation basically just said, okay, well, if there's an IRQ and it's not masked off, then set the interrupt flag and go to FFFA. Go to the address stored at FFFA. If it's an NMI, then set the interrupt flag. I'm not sure that I had to do that, but set the interrupt flag and go to the interrupt routine stored at FFFC. But if neither of them are true, then again, just keep the signals as they are and don't increment the cycle. So in other words, we're stuck here in cycle nine, just spinning round and round and round until one of the IRQs and NMIs goes off. And then we continue the instruction. So this is the wrong thing because the instruction technically ends right over here, cycle eight. So what I'd like to do instead is I'd like to take essentially this code out and put it somewhere else and then have the processor enter some special state where it's just waiting for this, waiting for one of the interrupts to happen. As a consequence of that, we'll also be able to handle interrupts during any instruction. So we sort of get that for free. So in order to implement interrupts, what I've done is I've kind of modeled an interrupt as just another instruction. So if you look at the end of instruction handler, this is the stuff that happens at the end of an instruction. So as long as we're not in a wait for an interrupt, right? So we're just, we've just ended an instruction. So ordinarily, we take whatever the end of the instruction address was. So in the end, normally, we would just go to the instruction that you wanted to at the end of the instruction, and you would set the cycle to zero. And again, normally, you wouldn't be handling an interrupt. That is not supposed to be there. And you would also set the address lines in preparation for a read. And then cycle zero would come around and you would actually do the read. That's your fetch cycle. Now, if on the other hand, at the end of an instruction, you were hit with an interrupt. So for example, in NMI, then what we do is we set a special interrupt flag, we set a special interrupt vector register. So in this case, the vector is going to be number two. We set the address lines to the stack pointer, we get ready to do a write. And what we're going to do is we're going to write the PC that you're going to return to, which of course should not be PC, but it should actually be where you wanted to go to at the end of the instruction. So obviously, I haven't tested this thoroughly. And I'm still thinking about how to formally verify that interrupts actually work. But what I have done is I've simulated at least a non-maskable interrupt. So let's take a look at that. So here what we can see is here is the end of the instruction. And you can see that right here. There's the end of the instruction. And there should be an NMI here somewhere. Okay. And there is non-maskable interrupt. So we're setting the non-maskable interrupt. So what should really happen right here is that we start writing the state to the stack pointer. Now in this case, the stack pointer is zero. So ignore the fact that in simulation, we don't actually do any writes because this is clearly overwriting all the vectors. But anyway, so we spend one, two, three, four, five, six, seven cycles writing the state of the register, writing the state of the CPU into the stack. After that, we do nothing. You can see that the VMA is low. And I'm not quite sure if that's correct. But let's continue. And the NMI vector is stored at FFFC. So here we're doing a read of FFFC, a read of FFFD. And I've programmed the simulated memory so that it should return F0, F0. And then right after we do that read, we go into cycle zero, which is the fetch cycle. And the address is F0, F0. And at F0, F0, I set up an instruction of a knob. So that's zero, one. So there's the next cycle of executing the knob. And then we continue. In this case, we ran off the end of memory. So I'm putting FFs in here. And right now, FFs are actually valid, valid instructions. So it just continues to go. So I don't have the RTI yet. It just continues to execute FF instructions. RTI would require reading back the state of the the state of the processor. And of course, I don't have a memory that I'm writing to. So I can't actually simulate that. However, we have formally verified that that worked. Okay, so in this next simulation, I've simulated a wait for interrupt. And I've written the code if we look over here. So there's the wait for interrupt after just doing a branch to the next instruction, we do a wait for interrupt. And then after that comes a knob. And in the process, what I've done is I've basically said, okay, go for 20 cycles, clock cycles, and then set NMI to one, go for one clock cycle, set NMI back to zero, and then go for another 20 clock cycles. So in the simulation, we can see, and I've gotten kind of gotten rid of a lot of the crud here, we can see that the instruction is a three, which is a wait for interrupt. And we can see that we're going from cycle zero all the way up to cycle seven. And the read write line is low for two, three, four, five, six, seven, and eight. So that basically means that we are doing a write. And we're writing the state of the processor. So there's the address zero, because we're starting with the stack pointer of zero. So that's 1234567. Okay, and then you can see that we enter the wait for interrupt state. So we're just going to basically sit here doing nothing. VMA goes low, so we're not doing any writes anymore. And we're just basically sitting there doing nothing until NMI goes off. Now where's NMI? I don't see it. Okay, well, in any case, NMI goes off, and we immediately go and read FFFC and FFFD. And we end up with F zero F zero as the destination. So that worked as well. And the way that I made that work is that if we are waiting for an interrupt, then basically we just keep the same cycle, and we're just waiting for NMI to go off or IRQ to go off. And if none of them go off, then we just set valid memory address to zero and bus available to one. If one of these goes off, however, then again, we set interrupt to one. We do a set interrupt mask. We set the appropriate address, because what we're going to do is we're going to go into the middle of the interrupt pseudo instruction, we're going to go straight into cycle eight. So first we need to set up the address lines to do a read of the first byte of the interrupt vector, which we do. And then we take the data in and put it into temp eight. And then we increment the address and we do another read. And we go ahead and end the pseudo instruction right there. So that's how that works. I haven't fully thought about how to actually formally verify this. What I do know is that yosis is going to feel free to get rid of that. Yosis is going to feel free to modify the IRQ and the NMI signals whenever it wants. So it'll be interesting to just try a formal verification of any old instruction and see what happens. Okay, so what I've chosen to do is attempt to formally verify the pull instruction, just because it's a short instruction and it only takes four cycles and it doesn't really do much of anything. And we can see that yosis has actually changed the NMI signal. So let's take a look at some of the other signals that are happening. So we will look at the core signals and we're going to pull up the address and maybe zoom in a little bit. Okay, and we're going to take a look at the instruction that we're executing. Okay, and the pull instruction and the pull instruction is this one right here, three, two. Now it does take four cycles. So we can see that in fact the NMI line is not high by the end of the instruction. So that's just perfectly fine. However, so this is the cover statement. So we found one instance of a pull instruction that works. Now formal verification did fail. So let's take a look at y. So here's BMC failed and we can see that it pretty much immediately fails and there was an assert in the core. Wow, that's not great. What's 1577? Oh, this is interesting. Apparently, apparently NMI and reset don't play well together. So let's take a look at that and see what exactly it came up with. So first let's pull up the clock. Why not? Let's pull up the reset line. And let's pull up the NMI line. All right, clearly something's happening. So the formal verification is supposed to say, okay, if the past of reset was zero, four cycles ago, and three cycles ago, two cycles ago, and one cycle ago, I guess that would be here, here, and here, if reset was low, then we want to make sure that we actually did jump to the reset factor. So let's take a look at the address lines and the data inlines. Okay, so here's the reset factor, the reset vector that you just chose was 0001. And clearly, we did not jump there because the NMI line just went high. So we jumped to 00. Actually, we didn't jump to 00 where the stack pointer is 00. So as you can see, we're actually doing a right. So in other words, we just loaded the interrupt vector, and we just loaded the reset vector, and we got a non-maskable interrupt. So of course, we're going to start saving the state of the CPU. Now, I think this is correct behavior. So what's actually wrong is the formal verification what I need to make sure. So this is what I've done. I've just added this requirement. I'm not sure that that's going to satisfy. I'm not sure that that's actually going to do what I think it does. But I'm just going to run formal verification again on pull and just see what happens. So here's the problem. And I believe it's a problem with our formal verification framework, which is basically how we know when to start formal verification. So you can see here is a three two, that's the pull instruction, we can see that we're at cycle zero. However, notice that this was due to an NMI. So we're actually not executing the three two instruction, we're executing the interrupt pseudo instruction, because we're going from cycle zero all the way up to cycle nine. Now, when that pseudo instruction ends, formal verification thinks it has to kick off. You can see here that it took a snapshot of the instruction. And if we look at the code, we can see exactly why that's happening. It's right over here, because we've assumed that when we're at cycle zero, then whatever's in the data in is the instruction that we need to look at. And, you know, prior to interrupts, that was actually true. However, this is no longer true. So we have to gate this on whether we're in an interrupt situation or not. Now I've just added the interrupt line so that you can actually see what we could possibly do, which is that we could gate formal verification snapshotting on whether interrupt is high or low. So here I've just added that clause at the end. So we just want to make sure that interrupt is low. That means that we're not in the pseudo instruction for interrupting. So we really should be looking at data in. So let's see if that actually works. Okay, we got another failure this time during cover. So let's take a look at cover and see what it found. Okay, this time it's a slightly different problem. So here we are genuinely executing a three to pull instruction. We get to the end of the instruction right over here. And oops, we've got an NMI. So that immediately starts the next pseudo instruction. But the next pseudo instruction has interrupt sat high. And according to the code that we just wrote, we're not actually going to do formal verification until we get to the end and interrupt is low and cycle is zero. And then we start doing formal verification, which is incorrect, we need to do formal verification right here. So let's modify the code again. So this is the next iteration. Basically what I've done is I've moved this up one level. So if the cycle is zero and the reset state is three, it doesn't matter whether interrupt is set or not. But if we've taken a snapshot, then we go ahead and take a post snapshot and then do the verification. So let's see if that works. So what I've done is I've restricted taking the snapshot to only those situations where the interrupt line is low. And if the interrupt line is not low, then we're not taking a snapshot. So with that, finally, formal verification does actually work. So that's pretty good. So what I should do is I should run all of the instructions through formal verification, again, remembering that the only formal verification that I don't have yet is wait for interrupt. Oh, and I also don't have not. I really should do that. Taking all of the instructions through formal verification takes just 15 minutes with six threads. So I'm going to do that. And we're going to get back to so I'm going to do that. And we will get back to it as soon as that finishes. So with formally verifying wait for interrupt, we basically just go all the way up to the end of the instruction, you know, up until we're actually waiting for the interrupt. And at that point, we could immediately get an interrupt, or we could be waiting for a while. So, you know, the the interrupt flag could be set because we got an interrupt or, you know, maybe we're just still waiting for an interrupt and the interrupt flag could be zero or one depending on what it was before. So so the only thing that I could do in this case is just make sure that the I flag is set to whatever it is actually set to. So that's why I put in this comment saying that this is not a mistake because this may look really weird and it really look like it should be pre CCS, but it isn't. Okay. And finally, all instructions have verified including wait for interrupt. Eventually, I had to end up adding this line, because in the middle of a wait for interrupt, you're waiting for interrupt and your cycle number is not zero. So this so this part doesn't apply. So I had to add this little section over here. And all the instructions have verified. So it took about 20 minutes to do from beginning to end. So I also compiled the lattice version. And if we take a look at it, we can see that I've added a few more pins, I've added the bus available pin, negative IRQ negative NMI, I took away the reset state, I don't really need that. I did change the address lines to OE, which means that they can be tri-stated. Data of course always could be tri-stated because you could just turn them into an input. And the read write line is also now tri-statable. Because of course, when you assert bus available, the read write lines and the address lines have to be tri-stated and the data lines have to turn into inputs. They can't output anything. So I added the logic for that. Obviously, you can see here that we're getting the NIRQ and NMI pins. And we are sending them to the CPU in their negated form as we wanted. For the bus available pin, we just copy it over. But based on the state of the bus available pin, we can tri-state read write, the address lines, and the data lines turn into inputs if we are doing a read or the bus is not available. In other words, the data lines are outputs if the bus is not available and we are doing a write. So let's take a look at the number of cells we've used. It is a huge 3,895 cells, 3,500 LUTs. So that's pretty huge, which is okay. So I think the next thing that I may work on is a couple of small optimizations that may help knock that down. And now the moment that you've been waiting for. Doop! How's that, Cat?