 Greetings, Risk Five friends. Yes, Risk Five. The last time we touched upon Risk Five was two years ago, and there are a variety of reasons for that. So the first thing is that I made a lot of mistakes when I was working on that project. One of the worst mistakes that I made was to start building circuitry before I had actually designed the entire system. So what happened was I had a great idea for what the bus should look like, and then I started designing a card for it, and I built the card, and that seemed to work, but then I realized that other cards needed to do other things, so the bus was not good for that. So I had to re-implement the bus, which meant re-implementing all the other cards, and then I would try to design yet another card and find out that something was missing. So yeah, that didn't turn out too well. I also had some misunderstandings in some of the instructions, so that didn't work. My idea for memory-based registers also didn't work. And also, in the meantime, I had a lot of junk in this junk room, and I really purchased these to work on projects, and with all that junk just sitting around in the junk room, actually not being involved in projects, sort of weighed down on me psychologically. So every time I sat down to work on a project and maybe make a video, I would think about all that stuff that was in the junk room, and then I'd get kind of depressed because I wasn't actually doing anything with it, so I would figure what was the point in starting a new project. So yeah, that sort of explains a lot of my absence. Also, I got these neat shades that go up and down automatically, which is kind of neat. This happened, and I got a pet bug. So anyway, I decided that I would reboot the RISC-5 project. So hopefully everybody who has been waiting and waiting and waiting for the RISC-5 project to recommence, well it's not only recommencing, but it's rebooting, so we're starting from the beginning. And this time, since I've done a lot of projects using NMyGen as an HDL, and I've got a lot of experience now simulating and formally verifying circuits using NMyGen, I will be using NMyGen to design the entire system with all the chips that I plan to use. Now, the thing is that NMyGen doesn't support things like propagation delays. So there is going to be some simulation or formal verification aspects that just can't be captured, but hopefully I can minimize that by clever choices of circuitry and code, hopefully. So with that, let's get started. So we'll start with kind of a basic bus design. Just like before, I'm going to have three buses. Instead of calling them RS1, RS2, and RD for the two source registers and the destination register, I'm just going to call them X, Y, and Z, or Z. And the idea is that there's also going to be some huge amount of control signals. And I'm not going to specify what they are right now because I haven't finished the design. Obviously, as I continue with the design, I will fill in what control signals need to be present. Now, we're also going to have a backplane, and there are going to be some cards. So one of the cards is going to be an ALU card, and we know that the ALU operates on the X and Y, or source register 1 and source register 2, so the X and Y buses, and it outputs something optionally to the Z bus. So that later on, so here we can have our register card. So that later on, register card can output source register 1 to the X bus, source register 2 to the Y bus, and it can receive something on the Z bus to store in the destination register. And the control lines would tell us which registers to source from or to write to. Same thing with the ALU, there would be some operational bits, so ALU op would be here. So we have which source registers and which destination registers for here. The ALU is also capable of doing the set if less than operation, so there are a few conditions. The branch instructions will also need to determine whether something is equal, or whether something is less than. So there are some conditional bits that come off of the ALU and go into the control bus. Now alongside the ALU, and I know that I'm going to have to do this, is a shifter. So if you recall from the last time, the shifter is really a logarithmic shifter, which is a shifter that's capable of shifting anywhere between zero places and 31 places, either to the left or to the right, either logically or arithmetically. So the idea is that it would take input from, well, one of these buses, I'm not really sure which one, an output to the Z bus, presumably, and presumably there would also be some control lines for the shifter. That may very well turn out to be the ALU op. So, for example, instead of plus or minus, it would be shift right arithmetic, or shift left logical, or something like that. And the shifter would look at that and say, oh, this is the operation that I have to do, so I'm going to read X, shift it by Y, and put the answer on Z, or something like that. So that's the ALU, the shifter, and the register card. So the other major component that we're going to have is what I'm calling the sequencer. And what that is, is it's the thing that controls the rest of the machine. So obviously, the sequencer is going to be connected to the control lines in order to control exactly what all these other cards are doing. And the sequencer is probably going to be able to maybe read from X or write to X. I'm not really sure. I'll just label these as bi-directional. I'm not sure how that's going to work, but we'll just leave that until we actually start coding. Now, finally, there is the memory itself. And I'm just going to call this memory. Now, memory is not going to have access to the X, or the Y, or the Z bus. And it's probably going to have some different set of control lines. So let's actually make kind of a separate bus over here. And one of those buses is going to be an address bus. And one of those buses is going to be a data bus. So the memory is definitely going to be connected to the data, to the address, and of course, to the control lines. So now the question is, well, you have to read from memory in order to execute instructions and to read and write memory. So how exactly does the rest of the computer, which I'm going to call the CPU, which I think may actually be what RISC-5 calls a heart, and how does it communicate with the memory? Well, I think that what's going to happen is that there is going to be some sort of a, I don't like that, but some sort of a, I don't know what I'm going to call it, but it's something that allows the sequencer, actually, why don't I just do this? It's going to be something that allows the sequencer to also talk to the memory type bus. So the sequencer is kind of like a double card. So if I actually had a backplane, the ALU, the shifter, the register, and the sequencer would all fit into this XYZ backplane. I'm just checking on what I'm recording. Yeah, I guess you can't see my mouse, huh? Well, like that kind of sucks. But you can see my eraser. Pretty funny. So this is sort of the CPU backplane over here. And this is kind of the memory and what the hell I'll just say it's the IO backplane. And the idea is that you could, you know, stick a bunch of memory cards here, like, you know, maybe a memory card isn't that big. So you can have multiple memory cards. You could also have an IO card because IO, in my system anyway, is going to be memory mapped. So that's how that would work. And of course, an IO card could be, you know, say a UART or a serial port or, you know, maybe a USB port or something. I don't know. And I think that's probably how it's going to be. Now, notice that I didn't specify any of the control lines, you know, except for what I know is going to have to be there. I didn't specify any of the memory control lines. Because again, I don't really know what's going to be there. So I'm not going to go off and run and, you know, break out Kaikad and start, you know, making a backplane. What I'm going to do is I'm going to write the software first and my gen, and then I'm going to add to the bus as I need to. So what I'm going to do is, you know, make an ALU card in software and make a shifter card in software or a register card in software at the sequencer card in software. And then I'm going to make sure that they can communicate and do the right thing, you know, same thing with memory cards. So I think probably the thing that I want to start with right now is the register card right here. It seems to me to be the simplest thing to do, considering that again, I am working with the RV 32e version of the ISA. This means that there are going to be 32 32 bit registers. And you can access you can read to registers at a time while writing a single register all in one clock cycle. And this is actually required by some of the instructions. So what do I think the register card is going to look like? Well, we know that there are such things called octal flip flops. So let's suppose this is an octal D flip flop. That just means it's got eight bits. And, you know, possibly there are 16 bits, 16 bit version. So let's make this a 16 bit D flip flop. So the idea is that you've got 16 bits going in, that's data in, and you've got 16 bits coming out, that's data out, you've got a clock. And we'll just say for the sake of argument that it's a positive edge triggered. So on the positive edge of the clock, the flip flop store data in, and we've also got an output enable signal. And usually that is a negative output enable signal, which means that when the negative output enable signal is low, so this is an active low signal, it means that whatever is stored in the flip flop is output on data out. And if the output and if the negative output enable is high, it means that the output just goes high impedance, which means that it can be connected to a bus and other things can drive the bus. So, you know, if we assume that this is sort of a 16 bit item, then of course, we would need two of them for a single register, right? And since we've got 32 registers, we would need 32 of them. And since we need to be able to access two registers at the same time, we will need to have two of these. So the way this would probably work is, you know, let's suppose you had bank one bank one, which is composed of 32 of these of these things. So 32 by 32 bits. And then we've also got over here a bank two, which is also 32 by 32 bits. And let's suppose that we can select which one we want to write to, because remember that there are going to actually be 32 clocks coming off of these, right? Because each one of these registers has a clock. So there are going to be 32 clocks. And in order to select one of those 32, well, we need five selection lines. So this is a five to 32 decoder, say. And then, of course, we're going to have the same thing over here, five to 32. Okay, and that's just to write. Well, what about to read? So the thing is that to read, well, we need to determine which of these registers is going to output. So of course, again, we're going to need another five to 32 decoder over here, and another five to 32 decoder over here. The idea is that this would be your destination register, and they would both be connected up. So you would be writing both banks, you would be writing the same location in both banks when you write to the destination register. But you can individually select which bank you want to output, which register of each bank you want to output. And that, of course, would be RS1 and RS2. So the idea is that your data in would be your Z bus. And your data out would be your X and your Y bus. And that would be 32. And that would be 32. And that would be great, except for this problem right over here, because now all of a sudden I need 128 of these chips. And there's no way in hell that I'm going to do that. So there is an alternate way of doing this. And that way is to use memories, because memories already have an address decoder in them. So we can, first of all, save a whole lot of stuff. And second of all, memories have all the storage in them. So we can have bank one, be a memory of at least 32 by 32. And bank two, be a memory of at least 32 by 32. Now, typically memories today don't come that small. I mean, they're not as wide as 32 typically, you can get memories that are as wide as say 16 bits. But, you know, they're going to be something like 32 K or, you know, 64 K or something like that. And we only really need 32 of those addresses, which is okay. Now, the problem is that once you've got a memory like this, you need to be able to read and write to different addresses or even the same address. So for example, consider an instruction that's just like RS1 plus RS2 goes to RD, where RS1 is, you know, register seven, RS2 could be register six, and the destination register could be register six. So you would have to be able to read from register six and then write to register six on a clock pulse. Or, you know, for example, you can write to a different register. So you would have to be able to read from registers seven and six and then write to register eight. So how do you do that? With a typical memory, you've only got one set of address lines, right? So here's your set of address lines. And because we've only got 32 addresses that we care about, we only need five address lines for each of these. And you typically have data lines. So we've got 32 data lines. And these are bi-directional, depending on whether you're reading or writing, because you can only do one at a time. So there is typically a read, write line. So how would we read and write at the same time? Well, you might have heard of these things called dual port memory. And the idea behind dual port memory is that it's really a memory that has two sets of address lines and two sets of data lines and two read, write lines. So you can read from two different or two of the same registers at the time, at a time. The problem with dual port memories typically is that you cannot access two of, you can't access the same address if you're both reading and writing it. So that's not going to work, because you saw that when we want to add a register, say, to itself, you're both reading and writing that same register. So we can't use dual port memories either. So what are we going to do? Well, I'm going to take a page from the old processors of the 70s. And typically what they did, first of all, were the old systems, they interleaved read and write cycles. So let's suppose this is your system clock. The idea is that you only read on the positive side of the clock and you write on the negative side of the clock. So all of your calculations are done and latched by the time you get to the next part of the cycle. And then on the positive edge of the clock, you write your data. So here, for example, you would read your RS1 and RS2. And then you would latch that in. So you would have this, this could be either a flip flop or a latch. Let's just, let's just call it a latch for now. So that would be G. And the reason that I want to make it a latch, specifically a transparent latch is that we want to be able to start reading the memory at the beginning of the clock cycle and do, you know, whatever calculation needs to be done, which could take a little bit of time. And then say somewhere here in the cycle, the calculation is complete, which means that by the time we want to write that result to some other register, it's available. So that's what these latches are for. And these are transparent latches. So the idea is that during the read cycle, these latches are transparent. But during the write cycle, these latches latch their values so that the result is actually held. And then we can put the result onto these data lines. So this would be the result result, which of course is probably going to be on the Z bus. And this would go to the X bus and this would go to the Y bus. So, you know, for example, you can consider, you know, maybe the X and the Y bus get added and the result goes to Z. So the moment we select the RS1 and RS2, we output the data to the X and Y bus, we add it, and the result goes into Z. Because those are latched, Z is held. And that means that by the time we get to the right side of the cycle, we can change the address lines to whatever they need to be to whatever the destination register is and write Z into the memory at that location without worrying that X and Y are going to change just because we changed the address lines. That's because the values were latched. So I think that is pretty much what I'm going to do. Now, the only other thing about this is the output enable or, you know, the read, maybe. So sometimes memories just have a write line and a separate output enable line. And the idea is that you enable the output in order to read as long as write is not is not activated. But if you were to write, then typically it doesn't matter what you do in output enable the data lines here will just be read. So you still need to put something on the data lines. So in effect, there's kind of like a tri-state buffer here kind of like this output enable. But it looks kind of like that where this is right. And output enable is actually going to be inhibited by the right signal. So it's almost like it's something like this. So here is output enable but write is negated. So if you enable write, then the output buffer is going to be disabled. So the write buffer will be enabled, which means that the direction of the data bus is going to be into the memory rather than out of the memory. So this is basically what's inside the memory chip. Now, the other thing, of course, is how do you inject Z into that data bus? And that just means that there's another buffer that let's see, where would we do this? I don't know, I'll just call it Z enable. So the other unfortunate thing about memories is that there is some setup time and hold time that you have to maintain. So let's suppose this is your write cycle. And let's suppose that your data on Z is available here. This is the data that you want to write. So the problem is that if you were to say shut off the output of this memory, it actually takes a little bit of time to do that. And if you were to immediately enable input to the memory, well, they may actually collide. So you have to sort of leave some space, which is why we're going to have a separate signal that looks kind of like this. And that's the actual write signal. So this is, I'm just going to call this the right half. And this is the read half of the global clock over here. And this is just like, you know, the right clock. And if you want, you can call this, you know, phase phase one and phase two. Many of the older processors had two phase clocks for this very reason. So the idea is that you turn off the buffers once you're done reading. So this is the memory data bus right here. And the data is being output because you're reading it. But then you turn off the buffers. And the data actually stays for a little while, and then it finally turns off. And then you can open up the Z bus to the memory. So this would be, for example, X is being output, and this is Z being input. And you could probably, you know, just let it go all the way up until the right half. And then there was a question, well, you know, don't we have the opposite problem when we go to the right half? So yeah, maybe we do. So let's just maybe turn off the buffer here. And then you can turn it back on on the right on the read half of the cycle. I hope this is making sense. So the other thing is what about the address? Well, we can, here's the address. So let's suppose, you know, the address is whatever RS1 is, and that is going to be presented to the memory during the read half of the cycle. And during the right half of the cycle, you simply present the destination register, right? So the idea is that even if you, you sort of have a different register over here on the next cycle. And, you know, the output data, you know, might be, you know, at the very beginning of the cycle, it might just be the destination register. Well, well, it doesn't matter because these output latches are transparent, which means that they're just going to reflect whatever is being output by the memory. And, you know, at the very beginning, it might be the destination register, but then it's going to very rapidly change to source one. So again, it's this amount of time that you can take to actually do the calculation. And you've got pretty much the entire read cycle to do that calculation. Okay, so that's the address. Now what about the right pulse? Well, the right pulse, you're supposed to maintain the data for a certain amount of time around the positive edge of the right pulse. And that is the address and the data. And in fact, it basically says that the whole time for the data, that is the amount of time that you have to hold the data on the data lines after the right pulse is zero nanoseconds. So that means that you can drop, you can sort of drop the data right when the right pulse goes high. And you can do that simply because it will take a little bit of time for this buffer to deactivate. So let's suppose this is now your right signal. So it takes a little bit of time for the buffer to activate. And then it takes a little bit of time for the buffer to deactivate. And of course, it's going to take certainly more than zero nanoseconds for the buffer to deactivate, which means that you've satisfied the whole time for the data. And of course, with the address, there is no particular problem because the address is stable all throughout the entire right cycle, which is a very good thing. You don't want to be changing the address during the right cycle because then unspecified things can happen. So that's the idea. So let me get rid of all of this and maybe clean up the diagram so that you can see what I am thinking of. So again, we have a memory and we have another memory. This is bank one and this is bank two. We have some address lines and there are only five of each. And what we're going to do is we're going to multiplex some address lines. So this would be source one, which is five address lines. And this would be the destination. And this is going to be RS2. And this is the destination. So you can see that during the read part of the cycle, we're going to be reading from RS1 and RS2. So this is during the read part of the cycle. And during the right part of the cycle, we're going to be writing to the destination address in both memories to keep them in sync. So really, that means that what this multiplex signal is is it's just the clock or the system clock. So that's this thing read and write. Now the memories also have an output enable signal. And what we're going to do, I guess, is put it out that way. And they also have a write signal. And of course, because we're going to be writing the memories at the same time, we're just going to connect them together. And now remember that we said that there was this other clock, sort of like this, and you know, we'll call that phase two. So this write signal is just tied directly to phase two, not necessarily because sometimes we don't want to write anything. So in fact, what I'm going to do is I'm going to put a little AND gate here with phase two and, I don't know, write enable. That could be, you know, one of our control signals, whether we are actually going to write the destination register or write nothing, because sometimes we just want to read registers. We know that we are going to have a latch, a transparent latch for both of these memories. So there's going to be 32 bits going into that. And 32 bits, 32 bits, this is going to go into the X register, the X bus, this is going to go into the Y bus. And the latch, let's see, the latch is probably going to be something like the same thing that we did with the write signal. So we're going to put the clock in here with an AND gate. And sometimes we want to read RS1 onto the X bus and sometimes we don't. So this is going to be, let's just call this X enable and let's call this Y enable. So now, you know, I could say, you know, read RS1 onto the X bus and do nothing with the Y bus and also do nothing with the destination register, the Z bus. Okay. And finally, the final piece of the puzzle are these 32 bit buffers that go in here. Let's see, gonna have another buffer here. Here, let me just run this line all the way over here. And that's going to be connected to the Z bus. And again, because we're going to always write both memories at the same time, we're going to tie those buffers together. And that is also going to be in the write signal. So I think that means that this line basically goes all the way down to here. So okay, so it's not that cleaned up, but you get the basic idea. So now, again, I'm not going to assume that this is all we need, right? I can see that we need an X enable, a Y enable, and I don't know, let's just call this a Z enable, or something like that. So we've got those control lines, we've got the two clocks, we've got RS1 and RS2, and we've got RD. And of course, we've got the buses themselves, the Z, the X and the Y bus. Oh, and I apparently forgot to have the output enable. That kind of seems like it's going to be the opposite of write. So I don't know, maybe I'm going to do this, something like that. I don't know. So now that I've sort of sketched out what I want to do, again, I'm not going to immediately break out Kaikad and start wiring up chips and, you know, doing things like that. No, I'm going to write this in software. And I'm going to simulate a little bit of it to make sure that, you know, what I think should happen is actually happening. Then I'm going to formally verify it to make sure that I didn't make any mistakes. Then I can say, okay, I've got in software, the register card, then I'm going to write the software for other cards, and I'm going to tie them together, simulate the system, formally verify the system. And only once every single card and every piece of the architecture is simulated and formally verified, then I can say, okay, I can be reasonably certain that my circuit is going to work. And then I can actually start developing cards, because creating the card, sending it off to China, getting it back in two weeks, testing it, and discovering that I forgot something is just a waste of time and a waste of money. So where we're going to start is with the latch, because we know that we're going to need these probably scattered throughout the design. And it would be great to have an nMyGen module that represents a latch. And what we're going to do is we're going to simulate it and formally verify it so that we can be certain that when we add a whole bunch of these to the design, they're going to be correct. Okay, so we're going to take a look at the nMyGen code for a transparent latch. But before I do, before everybody starts writing in in the comments and screaming at their screens, I am aware that register zero, when it's written to has no effect. And when you read it, it should always be zero. So of course, the register card that I showed wouldn't actually work with register zero because there was no, you know, special circuitry for, for yielding a zero whenever you wanted to read register zero. And that can be changed. So when we actually write the code for the register card, we'll make sure that we have that in there. And then when we translate that into actual chips, it will carry over. So so let's talk about the transparent latch. Now, hopefully, you can see the cursor. Yes, that's perfect. Okay, so this is written in nMyGen, which is basically Python. If you are interested in knowing more about nMyGen, there is a link to a tutorial that I wrote down below. You can check it out. It's a little bit out of date because there have been improvements to nMyGen since I wrote that many, many, many, many months ago. But for the most part, it's pretty much accurate. So a transparent latch. So first of all, it's a class and it's going to be derived from elaboratable, which is nMyGen class that says this is a block of logic. And in the init, basically what I'm going to do is I'm going to say, well, you know, I can parameterize the size. I can make it however large I want. Obviously, for something like the initial simulation, I'm going to make that 32 bits. However, when I implement this in chips, I'm going to want to move to a more chip oriented simulation. So I'm going to end, as far as I know, there aren't any 32 bit latches, but there are 16 bit latches that we can use. So instead of just having one transparent latch with a size of 32, I'm going to use a specific chip with a size of 16. And that's why I parameterized it in the code so that, you know, when I use 16 bit chips, I can simply say, okay, now these are 16 bit transparent latches. So that's nice. So these are the publicly available interface signals, basically your pins. So we have a data in, which is a bus of however many bits the size is data out, we have a latch enable, and we have a negative output enable. And I chose to make this negative output enable instead of output enable, because all of the latch chips have negative output enables. So I figured that would, you know, make it a little bit easier to translate it to chips. So the way it works is that when the le signal is high, that means that the latch is completely transparent. So the data in gets reflected into the data out. However, when the latch enable goes low, that latches the last seen data input, and then data input can change and the data output will not change. And the output enable just tells us whether the output is high impedance or whether it, you know, outputs the state of the internal register. So in the elaborate function, this is where we actually build up the logic of the module. So we start with just a blank module. And what we're going to do is we're going to use an internal register to store the state. So that's why it's a signal of, you know, whatever size it is. And then this reset stuff that basically just says, well, you know, the internal register is going to be controlled by some kind of a clock that in and my Gent clocks have both a clock signal and a reset signal. And if you pass this through formal verification, the system will feel free to, you know, change the reset line. However, there is no reset for a transparent latch. It's just, you know, you power it on and it's basically a random state, and you never get to zero it or reset it or anything. So that's why I said reset list is true. I also set the reset value to zero. And that's basically if there is no reset signal for if there is no reset setting for a signal, then it it is the reset is the value on sort of power up or, you know, when when the system first comes up. And I set it to zero, you know, even though, honestly, it's going to be a random value, but, you know, it's a simulation. So okay, so that's our internal register. So next, what we're going to do is we're going to set up a clock domain. So this is a local clock domain. So in other words, nothing above this module is ever going to be able to see this clock. And it's a and it's a clock because in and my gen, you only have two ways that you can change signals. The first way is combinatorial, where the signal is always going to be changed, based on its inputs. And the second way is essentially synchronous. So it's based on a clock domain. So it's only on a particular edge of that clock that signals will change. Now in this case, I set up the internal clock. This is our sort of latch enable to be on the negative edge, right, because when the latch enable signal goes from high to low, that's when the data actually gets clocked in. So that's why I specified a negative edge. So now I just add it to my internal clock domains. And I specify what the clock signal is because in and my gen, when you declare a clock domain, it automatically creates a clock signal and a reset signal for you. So what I'm doing here now is I'm saying, well, the clock signal for this clock domain is actually the le signal, the latch enable signal. Okay, so the next thing that we're going to do is we're going to say that whenever there is a negative edge on the latch enable signal, the internal register is going to get loaded with whatever is in data in. Okay, so that makes pretty much sense. That's the way the chip works. And then in the combinatorial domain, so this is every time the data out is equal to if the negative output enable signal is one, in other words, the output is disabled, then we're going to output a zero. Now, in n my gen, you don't have high impedance signals, you don't have Z or Z. So instead, we're going to call it zero for the reason that if you've got, say, a bus and you've got a whole bunch of outputs connected to it, and only one output is enabled at a time, if all the other disabled outputs are outputting zero, then in your simulation, you can actually just order the outputs of all of those, and the bus will get the output of the one enabled thing that's outputting to the bus, if that makes any sense. So that's why I'm going to be outputting zero rather than some other value like, you know, all ones. So anyway, if the negative enable signal is one, then just output zero, otherwise output the state of the internal register, whatever that happens to be. And then finally, what we get is the transparency mode. So if latch enable is high, and the output is enabled, then the data out is actually equal to the data in, not the internal register. So in this way, this is basically just the logic of the latch. Now I have a simulation method here, which we're not really going to look at because, you know, this was just a test to make sure that things were working okay. What I'm really interested in is the formal verification side of things. So with formal verification, what you want to do is say, well, these are the properties of the circuit, and they should always hold, or, you know, they should hold under certain circumstances. The other thing that you can do with formal verification is, you can say, what series of signals have to change in order for this condition to be true? And that's called a cover statement. So in this particular case, what I'm going to do is I'm going to set up my top module, and I'm going to give it a sub module, which is a transparent latch of 32 bits. So the first thing that I'm going to do is I'm going to say, well, I want to cover the case where the data out at a particular point in time is all a's, and the latch enable is zero. In other words, this is a value that is latched. And two cycles ago, the data out was all b's, and two cycles ago, latch enable was also zero. So formal verification, tell me what you have to do with all the input signals in order to make that true. So that's what the cover statement does. The rest of these are asserts, which basically means that these are properties that must hold all the time. So for example, when the negative output enable is one, that is, we're disabled, obviously you want to assert that the data out is always zero. So let's just make sure that I wrote the code properly. Also, if the output is enabled, and also the latch is transparent, then of course, we want to assert that the data out is always equal to the data in. That makes sense. Finally, this is this is probably the more complicated one. If the output is enabled, and latch enable fell, that is, before it was one, and now it's zero, then we want to make sure that the data out is equal to the past value of data in. In other words, the value of data in that it was when latch enable was one. So the next thing is this sby file, and this basically just controls formal verification. So I've actually got two tasks cover and bounded model checking. And what cover will do is it will try to make all the cover statements true. What bounded model checking will do is it will try to make sure that all the asserts are true. That is, it will actually try to falsify the assert statements. And if it can't, then it's successful. But if it can, then it will show you a trace showing you exactly what sequence of signals was required in order to make the assumption not in order to make the assertion not true. And then you can debug that way. Multi clock on is basically required when you've got anything more than a single clock. And we've got two clocks. One is the global clock, which was represented by sync. And the other, of course, is our latch enable clock. So because of that, I'm forced to use multi clock on. Now there is this section over here, which kind of looks like a magic incantation. And it kind of sort of is my thanks go out to white cork, who is a huge maintainer of the end my gen code. And white cork helped me work around an issue that is currently being worked on in and my gen where with multi clock on, it doesn't quite work the way it's expected. So they're working on a solution. But in the meantime, this is the only way to get it to work. So you can ignore this because eventually this is going to go away. This is a workaround for a for a bug. So what we do now is we run the Python code in generate mode. So what this is going to do is it's going to generate all the formal verification code that it needs to great, no issues. And then we run SBY on it. And if if you're wondering where SBY comes from, again, look at the end my gen tutorial, and it will explain, you know, how to download SBY, which stands for symbiosis. Okay, so I'm going to run it. And it's going to, you know, just spit out a bunch of stuff. So I'm going to scroll up and look specifically for the cover task that's right over here. So you can see that it passed, which means that we reached a cover statement. So we can look at that trace. And in addition, bounded model checking also passed, which means that all our assertions were correct and they worked. We're going to look at the cover statement now. So here is the top module with the clock, which is, which is the global clock. And there's data in, there's data out. There's the latch enable, and there's the output enable. So remember that our code, which unfortunately you can't see right now, was looking for the output to be all a's and for the latch enable to be zero, and for two clock cycles ago for the output to be all b's and the latch enable to be zero. So the way that formal verification found that it could do that is by making the data in all b's and then lowering the latch enable signal that latches it so that the output is now all b's, then raising the latch enable signal and putting all a's on data in and then lowering the latch enable signal to latch that as an output. So that's how two cycles ago the output could be b, and in this cycle the output could be all a's. So that's great. Now there isn't any trace for bounded model checking simply because bounded model checking passed, all the assertions passed, so there is nothing to show. What I'm going to do though is I'm going to break the one of the assertions and I'm going to say let's suppose that I forgot that when you when you latch the data then the data out is should be the same as it was one cycle ago and not as it is right now because of course when you when you disable the latch in other words the latch is no longer transparent you've registered the data so that no matter what data in does the data out is always going to remain the same. So let's suppose I forgot that and I you know put a bug in the assertion. So in that case what we're going to do is we're going to recompile the code and then we're going to run it and we're going to see that there we go bounded model checking failed. So what we can now do is we can bring up the trace of that failure which I'm going to do right now okay so here's top let's take a look at data in latch data out the le signal and the negative output enabled signal okay so my buggy assertion was that when output enable was low uh that data out should be uh equal to data in and when latch enable fell so right over here we can see that the data in is four zero zero while the data out is four zero zero zero zero so obviously that's a mismatch and then I would have to go in and figure out why now in this case it's because my assertion was incorrect so let me put back my assertion and let's put a bug in the actual implementation code now so let's suppose that um uh oh I don't know uh let's see what sort of mistake can I make okay well let's let's put a bug in here and say uh well no I mean that's that's kind of a silly bug let's suppose let's suppose I forgot that you know the latch enable signal is actually high if it's transparent and low if it's registered and and I just got this this one signal inverted that happens all the time to people so let's go ahead and recompile and see what happens okay and we've got a failure and now let's take a look at that failure data in data out latch enable output enable okay so basically what we're seeing is that the latch enable remained high the data in was two the data out was zero um and we can look at exactly what line fell um it's basically saying that the assertion failed in line 108 so now we can look at that assertion and if we look at line 108 we can see that the assertion that failed was that data out should be equal to data in when latch enable is one and of course if we look at the trace we can see that data out is not equal to data in even though latch enable signal is high so obviously there was something wrong with our code and we would debug it and we would find oh yeah I inverted this signal it's probably not as simple as that you know debugging is never as simple as that but still you know the first thing that you would do is you know check that you know maybe you you inverted a signal so that's how you would basically check your code so that's really all I wanted to do for this video you know just get us started you know take a look at where we are with the risk five project and write a little bit of code and actually get started here and not actually create any printed circuit boards or any actual circuitry because that was a huge mistake last time it was a very expensive mistake and it cost a lot of time and a lot of money so in the next video I think that we're going to continue with the register card we're going to look at the individual pieces of the register card and write modules for those formally verify those modules so we can be assured that those modules relatively assured that they have no bugs and then we're going to actually put together the register card and formally verify it and of course again I want to stress that that's not the end that doesn't mean that I can go off running to kycat and creating the circuit and getting it made because the entire system has to be written and formally verified before I can actually say that yes all the pieces now work properly together now if you want to get the code I have a github for the rebooted risk five project link down below and I guess that's it for this video hope to see you on the next video please remember to hit the like button because that tells youtube that this is a good video and hit the subscribe button because it makes me feel better bye