 Greetings, Risk Five friends! For this video, we are going to talk about the shifter unit, which is actually pretty easy. Let's take a look. So the shifter unit is going to implement these Risk Five instructions. There's shift-left-logical, there's shift-right-logical, and shift-right-arithmetic. So shift-left-logical and shift-right-logical is just you take the 32 bits and you shift them one way, or you shift them the other way. Bits that fall off the end disappear, and zeros get shoved into the end that's, you know, you're shifting in. Shift-right-arithmetic is a little bit different. It's the equivalent of dividing by two each time you shift right by one. However, it's on signed arithmetic, which means that if the sign bit is set, that is the most significant bit, then you have to reproduce it as you shift in order to keep the number negative. So there's also an immediate version of each of these. And with regular shift, you have the source one register, which gets shifted, say, left, by the source two register, which can be anywhere between zero and 31. And that gets put into the destination register. So this would be for shift-left, shift-right, or shift-right-arithmetic. Now for the immediate mode versions, I think it's just RS1 shifted by the immediate value in the instruction, and that goes into the destination register. And that's it. So really there are just six instructions that we're going to look at. And actually, because we're using a bus, it's only three instructions because we can put either source two or the immediate value in the instruction onto one of the buses. So in fact, it's just going to look like this. We've got our X bus, we've got our Y bus, and we've got our Z bus. And we have our shifter unit. Shifter and X goes in there, and Y goes in there, and the output goes to Z, fairly straightforward. Now the way we're going to implement our shifter is, as we did in two years ago with the original version, is we're going to try to use a logarithmic shifter. And the idea behind a logarithmic shifter is that if you want to shift by some amount, then either you're going to shift by one or by zero, and then by two or by zero, and then by four or by zero, and then by eight or by zero, and then by 16 or by zero. And all of those combinations give any shift amount between zero and 31 in basically five steps. In other words, it's log of base two of the number of shifts, of the maximum amount of shifting that you want to do. So log base two of 32 is five, because two to the fifth is 32. So that means that you really need five stages. So let's draw out those stages, shift by one, shift by two, shift by four, shift by eight, and shift by 16. And there's a shift amount. So what I'm going to do is I'm sort of going to have this five bit box over here. This is the shift amount. And in the risk five documents, you'll see this abbreviated as sham T, sham T shift amount. So the idea is that this is the least significant bit. And this is the most significant bit, bit zero, bit one, bit two, bit three, bit four. So if you want to shift by, I don't know, 12, that means that you're going to shift eight and four. So you won't be shifting by one or two, but you will be shifting by four and you will be shifting by eight. So each of the outputs feeds the input of the next layer. And of course, this would be the X bus. This would be the Y bus. And this is going to be the Z bus. Now this works great if you're only going to shift in one direction. So the interesting thing is that I found someone who was working on a risk five TTL version. Actually, I think it was CMOS, but it was basically using 7400 chips back in 2008. I think his name was Phil. And he was on hackaday.io. And I'll put a link to his project down below. And the idea was that if you wanted to do a left shift, you would just use these shifters as is because they shift left. However, if you want to shift right, what you do is you put in a reverse stage where you reverse all the bits on the input, and then you can reverse them again on the output. And I thought that was pretty clever. So if you want to shift right, you use the left shifters, except you reverse the input, then you do the left shift, then you reverse it again. And there you go. So that takes care of shift left logical and shift right logical. And then the only thing we need to take care of is that if we want to do an arithmetic shift, then we need to make sure that instead of shifting zeros, what we do is we shift the most significant bit back in. So that is just a simple gate, basically. And that's almost it. The final piece of the puzzle is, well, when do we actually output to the Z bus? Because, again, sometimes we will be doing other operations which don't involve the shifter, like, for example, adding, for example, which would be on a different card. So a different card would be activated. So of course, the answer is that you have control lines. And that's my answer to everything. Control lines. Now, the control lines, the card is going to look at the control lines. And in fact, I'm just going to go ahead and call this the ALU operation that we want to perform. Because this is in the group of ALU operations, even though it's not a mathematical operation like plus or minus or comparison. And because this will involve a lot of chips, it's going to require a separate card. In any case, this is going to look at the ALU up and determine whether it's a shift left, shift right, or shift right arithmetic. And if so, then it's going to allow its output onto the Z bus. It's always going to read the X bus, and it's always going to read the Y bus. So it's always going to be doing shifting. It's just that the result only gets placed on the Z bus if the ALU operation is for the shifter. And I think that's about it. So let's take a look at some code. So here's the code for the shifter. Let's take a look at the main class. This is just called shift card. And I've got my data buses and my control signals. So whether we want the ALU to output to the Z bus and what operation we want the ALU to perform. And again, the shifter card only responds to the ALU operations that are shifts. So here are the buses, 32 bits wide as usual, the control line, and ALU op. This is the first time we've seen this construction in this project. So the signal is a signal of an ALU op. And if I go there, we can see that it's just a Python enum. So this allows me to specify symbolic names for certain constants. And my gen will look at the lowest value and the highest value. And from that, determine the width of the signal. So of course, in this case, it's just going to be 4 bits. So that's what this signal is. It's a 4-bit signal. OK. So the logic, basically, it looks kind of like the block diagram that I drew out. We've got an input reverser, which is a conditional reverser. So it can either reverse or not. We've got shift 1, 2, 4, 8, and 16 blocks. And we've got an output reverser. And then at the end, I stuck in an output buffer. Because remember, if the ALU operation is not a shift or if we're not doing an ALU operation at all, then the output should be turned off. So those are basically the submodules. So let's take a look at the conditional reverser. So this is a nice feature of nMyGen in that it basically uses all the power of Python or a lot of it. So this is a conditional reverser. It has a data in, data out, and an enable. So if enable is 0, so in other words, it's disabled, we just pass the data in to the data out. And if it's enabled, then we reverse the data in. And this is how you reverse an array or a bit slice. This is how you reverse an array or a slice in Python. You just do colon, colon, negative 1. And that's it. So nMyGen knows how to turn this into logic. And this is a multiplexer. So if enable is high, then we're going to use the reversed version of data in. And if enable is low, then we're just going to use data in. And that's what data out is. And that's the conditional reverser. Let's take a look at the conditional shifter. It's a little more complicated. So we've got data in, data out. And we've got an enable signal, which is whether we want to shift by n or by 0, effectively copying the data in to the data out, and whether the shift is arithmetic. So this is a shift right unit. So if it's arithmetic, we're just going to copy the high bit into, we're going to shift the high bit in arithmetically. And if it's logical, we're just going to shift zeros in. So it's parameterized. So there's a width, which of course is just going to be 32. And there's an n, which is the number of bits to shift by. So of course, we're going to have five shifters, n being 1, 2, 4, 8, and 16. And the way the logic works is if it's enabled, well, let's consider the case where it's disabled. In that case, we just copy the data in to the data out. That's fairly straightforward. If we are enabled, then first of all, I'm just going to pick out the most significant bit in case we're doing an arithmetic shift and we need that. Also, w is just the width of the signal. So I just felt that having an attribute just for width and keeping it is kind of pointless when we could just take the length of the signal itself. So that's the width of the signal itself. OK, so this is a little more complicated. So the first section of this is going to take, is going to look at the high n bits of the output. So that is either going to be stuffed with zero or it's going to be stuffed with the most significant bit from data in depending on whether we're doing a logical shift or an arithmetic shift. So that's what this statement does. So there's a few constructs in here. REPL is the n my gen function, which allows you to replicate a bit or, I guess, a bunch of bits to a width. So for example, what this does is it takes the most significant bit and it replicates it by n. So now you have a signal that's n bits wide that either all ones are all zeros depending on the most significant bit. Now we have a multiplexer. So of course, if we're doing an arithmetic shift, then we want to use this special signal, otherwise we want zeros. And that's what we put in the data out for the high n bits. And that's how you would specify the high n bits using a bit select. So what this is is just the starting bit and the number of bits. So the starting bit is just w minus n and the number of bits is n. So that's that. And the rest are basically just shifted over. So again, we're going to select some number of bits from data in starting from n. And it's just going to be w minus n bits. And then we'll put that into data out. And it's just going to start from zero. And it's, again, w minus n bits. So you can sort of do this on a piece of paper to show that it works correctly. But of course, the proof is done using formal verification. You don't really have to do this with pencil and paper or pen and paper or Google Sheets. OK, so we have all those sub modules one feeding into the next. So that's what this next part does. It basically just takes the X bus feeds it into the input reverser takes the input reverser feeds it into shift one down, down, down, down, down until the output buffer gets output onto the Z bus. OK, so now we have some flags, whether we're doing an arithmetic shift and whether we're doing a left shift. We only do an arithmetic shift if the ALU operation is shift right arithmetic. So there isn't really any shift left arithmetic. Again, if you think about it, a signed number has all ones in the in the upper bits. And of course, if you shift that left, it still has all ones in the in the upper bits unless you shift it too far, in which case you've shifted it too far and you should feel bad. So we're only doing an arithmetic shift if the operation is a right arithmetic shift. Also, we're going to be shifting left only if the operation is a shift left logical, because there is no again, there's no shift left arithmetic. And then we feed those control signals into the various blocks. So the input and output reversers will reverse if we're doing a shift left, because remember the shifters that we're using are right shifters. And then we just tell the shifters whether we're doing an arithmetic shift or not. Next, there's the shift amount, which is the low five bits of whatever is on the Y bus. And we split that up and we pass those into the shifters. And then finally, the output buffer, well, you know, I just reused the transparent latch because why not? And of course, the latch is always going to be enabled. So it's always transparent. It's just whether we're enabling the output or not. So by default, we are not enabling the output only if we want the ALU output to go to the Z bus. And the ALU operation is one of the operations that the shifter can do. Then we turn the output on on the output buffer. And that's it. But so how do we formally verify this? Well, you know, obviously you probably just want to compute the various shifts in a different way to prove that that both methods yield the same result. So the first thing that we do, of course, is if we don't want the ALU output to go to Z, then the data on the Z bus should be zero. Otherwise, based on the ALU operation, if we're doing a logical left shift, then just shift left data X and buy the shift amount, lop off the low 32 bits. Because remember in nMyGen, when you do a shift, the result will have as many bits as as the original signal plus the amount you shifted by. So you want to lop off the 32 bits. If you want to do a shift right logical, well, shift right logical. And that's what you get. Now this shift right operation is actually a signed shift if the thing you're shifting is a signed signal. Now data X is not a signed signal. So that means that this is an unsigned shift. So that's important when you want to shift right arithmetic because now you want to make your unsigned signal signed. So that's what the as signed function does. It takes your signal and turns it into a signed signal. So that way, when you are shifting it right, that's actually a signed shift. Otherwise, we want to make sure that the data output to the Z bus is zero. And that's it. So if we run verification, I didn't even bother with the cover statement. So we can see that it's done. So we can see that verification passed. And that's really all we care about. We can put in a cover statement, you know, just to make sure that we're doing the right thing. So here is the cover statement. It's just ffff aaa and there are various ways to get to that. Actually, let's just make that a zero just for fun. So again, we could do a left shift of various amounts. We could do a right shift of various amounts, which would have to be an arithmetic shift because we've got a one in the most significant bit. So let's just take a look and see what it can come up with. So it decided that the ALU operation was five. And we can see that, okay, well, it decided to shift by zero, which is perfectly reasonable. Let's give it an assumption that says that maybe the shift amount is non zero. Actually, we can't give it an assumption because that assumption would hold for the entire formal verification thing. So that would basically say that when we use the shifter, we're never going to give it a zero as a shift amount, which is not true. So instead, we'll use an if statement. So that's our if statement, that should sort of, you know, force the cover statement to only work if the shift amount is greater than zero. So let's see what happens. Okay, so this time we can see that the shift amount is one zero or 16. We can see that, okay, well, this is interesting. The data input was a a a and then zeros, the shift amount was 16. And the result is ff ff a a a zero, which makes sense considering that the ALU operation was D. And what is D? Well, it's, is it an arithmetic shift? Let's take a look at some of the signals inside the shifter. So we can look at shift arith. And yes, indeed, it is an arithmetic shift. And of course, shift left should be zero because there is no left arithmetic shift. So that is the shifter taken care of. And that was fairly quick. Maybe we should cover another card. So here, what I'm showing is the cheat sheet that I use for risk five instructions. So I think probably the important part is here. So these are all the op codes that a risk five processor can execute. The ones in white are just the basic ones for RV 32 or 64 I. The ones in blue are just various extensions. The ones in red are reserved. The ones in orange are reserved for custom instructions. And the ones in green, well, those are for instructions that are greater than the usual 32 bits. And what we're going to be concentrating on are the op and op M or op immediate instructions. So these are what I've been calling the ALU instructions. So they are add and subtract. Shift left, shift left logical. Shift, no, set if less than, set if less than unsigned X or shift right logical and shift right arithmetic or an and and most of these also have an I at the end. And that is to specify the immediate version. So the instruction execution for op and op M. Well, let's first deal with op. So it's you specify a destination register and a source register one and source register two, and you just perform the operation on that. So, so for example, for add, it would just be RS one plus RS two goes to the destination register for subtract. The ordering is important. So it's always RS one minus RS two. So, you know, and this is basically the same for all of them. So these are the op instructions. Okay, so for op M, this basically replaces RS two with an immediate value and the immediate value is in the instruction format itself. And it's right over here. So you get to specify 12 bits, these are the lower 12 bits. And it's also sign extended. So you can specify like minus one by specifying all ones. So because the immediate value is 12 bits, and it's signed, you can specify anywhere from I think it would be minus 2048 all the way up to 2047, where this would be zero followed by all ones, and this would be one followed by all ones. So that's the range you get. If you want any immediate value outside that range, you're going to have to construct it yourself by constructing the high part sticking in a register or in it with a low part. And then you just using a register. So for op immediate, it would be, you know, something like RD gets RS one plus the immediate value. Now, there's one exception to this. And that is there is no sub with immediate instruction. That does not exist. The reason for this is that, well, you can just use an add, because these are all signed values. So add immediate, if you did like RD gets RS one minus one. Well, this is the same as RS one plus minus one, which you can specify in the immediate value because remember the immediate value is signed. So there is actually no reason to have a subtract with immediate value. So you just use the add with immediate value. Okay, so now let's look at the operations themselves. That's this. So what our ALU is going to have to do is I'll just list these out. So there's add, there's subtract, there's shift left logical. So I'm just going to say plus minus and shift left logical, I always have to pause because I've got left right confusion. And I have to make sure that I'm doing the correct left and you know, not the other left set if less than so what this does is it compares RS one to RS two or RS one to the signed immediate value. And it does a signed comparison. And then it sets the result to one if if it's less than or zero if it isn't, this may actually be confusing if you look at SLTU, which means set if less than unsigned. So this is less than less than you're all put a little s over here and a little u over here. So because immediate values are always sign extended, this turns out to be important when you're using set if less than unsigned because the unsigned comparison happens after you do the sign extension. So here's how it works. Let's suppose that you that you did SLTU, RS one, whatever that is, comma, and then let's just specify the immediate as bits, right 11111 and so on 12 of them. Well, when you sign extend that, that sign extends to 32 bits f f f f f f f f. Now, if you treat that as a signed number, of course, that's negative one. But if you treat that as an unsigned number, it's all f's. So what you're actually doing is you're comparing unsigned RS one. Is it less than all f's? So this can be confusing. Because, of course, you're doing sign extension and you always think, oh, well, sign extension, that means that the result is signed. But then you're doing set of less than unsigned. So you're actually treating your sign extended immediate value as an unsigned value. This can be very confusing. But let's just set that aside and keep going. Okay, so the next few operations are XOR, which is usually indicated by a carat in most languages. Shift right logical and shift right arithmetic. So these are both like this, except that this is a signed shift. So of course, again, as we discussed, the high bit gets repeated. And then there is logical or, which is this and logical and which is that. So those are all the instructions. Now, if we get rid of the instructions that we've already taken care of in the shifter, we can get rid of this one and this one and this one. So that leaves these seven operations to perform in the rest of our ALU. So remember that each card is only going to output data to the bus if that card is being called out to be active. So because we've got a shifter card next to an ALU card, the shifter card is only going to respond to the shift instructions. And the ALU card is only going to respond to the rest of the instructions. So all we really need to do in order to design this card is make it work like this. We have the X bus, we have the Y bus, and we have the Z bus. And the ALU, this is the traditional symbol for an ALU. So the ALU card is going to get an input from X, an input from Y, and then this we're going to make this a latch, except that it's always going to be transparent. And we're just going to use the output enable to determine whether the output should go to Z. And there's that. ALU. Okay, so in order to control what operation the ALU needs, well, we're going to have to have some control signals. Let's just call this ALU op. And we're also going to have another control signal to control the latch. And let's just call that ALU to Z. And this is exactly what we did in the shifter. So of course, ALU to Z, this signal right over here is also going to have to be some sort of a logic combination between ALU op and ALU to Z. ALU to Z just says whether we're going to output one of the ALU cards to the Z bus. ALU op tells us which card is actually active. So the ALU op is also going to have to feed into there. And that's all there is to it. There aren't any registers or anything. It's just, you know, combinatorial logic. So why don't we just code that up real quick. So before we look at the code, I just want to point out these branch instructions. They're branch if equal or not equal, branch if less than or greater than or equal to, and branch if less than unsigned, and branch if greater than or equal to unsigned. Now we're going to have to have something that can compare two numbers in order to find out whether they're equal or less than or greater than. And ideally, that would be the ALU, because it's already doing a subtraction. And usually what you do to compare two numbers is you subtract and you look at the results. So what I'm going to want also out of the ALU, just for subtractions are some, maybe status bits, and we will have them as an output. And I think the status bits that we're going to want are equality less than signed and less than unsigned. So that's going to go into our ALU also. Now let's look at the code. And here we are with the code. It's actually pretty straightforward. So here are all of the signals that we want to have. We have the x, y, and z buses. We have the ALU to z control signal along with the ALU operation that we want to perform. And we have those three status signals that we wanted equal less than signed and less than unsigned. So to create those, first of all, I'm going to use that transparent latch as an output buffer. And here I'm going to set it up where the output enable by default is not enabled. The latch is always enabled. So it's always in transparent mode. The input to the latch is just going to be zero by default. And we are going to send the output of the latch to the z bus. And of course, remember that when the output is not enabled, we're actually outputting zero for our code. But it will actually be in high impedance mode in the actual circuit. And finally, by default, we just set equal less than and less than unsigned to zero. Here are just two lines that I'm using for convenience, so that I don't have to type self dot data x all the time, just x will do. And here is our status. Now remember, I said that we don't actually need the status unless we're doing a subtract operation. And this is just more of a convenience right now to just compute this all the time. When we turn this into an actual circuit, we won't actually have access to this because the ALU resources are going to be used to set up these status bits. And specifically, we won't be able to compute this unless we're doing a subtract. So just keep that in mind that again, this is just the initial code, not the actual circuit. So finally, if we are going to output the ALU to the Z bus, and based on the operation, if it's an ad, we're just going to add the two together, and we are going to set up the transparent latch to output to the Z bus. Same thing with subtract x minus y. For set if less than unsigned, we're just going to take that status bit that we have computed right up here. And we're just going to output that to the data bus. And that's either going to be a zero or a one. For the less than signed, what we do is we convert x and y to a signed number and do the conversion. Now, of course, again, when we create this module in an actual circuit, we will need to select our integrated circuits and chips and, you know, decide how we're actually going to implement that. And that will come later. So that's that there is set if less than signed, there is and or and x or and that's really all there is to it. Obviously, if none of these operations are selected, like let's suppose it's a shift, then the default is that we don't output anything, which is exactly what we want. Now, in terms of formal verification, there really isn't a whole lot to formally verify. Basically, we just, you know, flip the signals up and down and make sure that the ALU does what it's supposed to. So for example, if we don't want the ALU to transfer anything to the Z bus, we make sure that the Z bus is zero. If we do, then we take a look at the operation. And if it's an ad, we make sure that the output is equal to the two inputs added together. If it's a subtract, now here, what I've done is I've only checked those status bits if we are doing a subtract, I guess I can get rid of the white board right now. So, you know, we're just checking the status bits, making sure that the status bits are correct. Same thing with and or and x or for set if less than unsigned, I'm just checking that, you know, if x is less than y, the answer is one. Not all ones, just one. Otherwise, it's zero. And the same thing with set if less than signed. And of course, if the operation is not one of the ones that this ALU is going to perform, the Z bus should be, again, zero. And that's pretty much it. And of course, when we formally verify this, and of course, when we formally verify this, it should come as no surprise that in fact, we have passed all of our tests. So that was the ALU. And I guess that is the end of this video. So we've covered the shifter and the ALU. I think that for the next video, we're going to be covering probably the most difficult parts, which are the sequencer and the memory bus slash interface. So again, all the code is down below in the GitHub. So take a look at that if you're interested in trying out the code yourself. And until next time, thanks for watching. See ya.