Project #4 Updates
Barry E. Mapen
- What are the ALU Functions?
|0000||0||0||F = A MINUS 1|
|0001||0||0||F = AB MINUS 1|
|0010||0||0||F = AB' MINUS 1|
|0011||0||0||F = MINUS 1 (2's COMP)|
|0100||0||0||F = A PLUS (A+B')|
|0101||0||0||F = AB PLUS (A+B')|
|0110||0||0||F = A MINUS B MINUS 1|
|0111||0||0||F = A + B'|
|1000||0||0||F = A PLUS (A+B)|
|1001||0||0||F = A PLUS B|
|1010||0||0||F = AB' PLUS (A+B)|
|1011||0||0||F = A+B|
|1100||0||0||F = A|
|1101||0||0||F = AB PLUS A|
|1110||0||0||F = AB' PLUS A|
|1111||0||0||F = A|
|0000||0||1||F = A|
|0001||0||1||F = AB|
|0010||0||1||F = AB'|
|0011||0||1||F = ZERO|
|0100||0||1||F = A PLUS (A+B') PLUS 1|
|0101||0||1||F = AB PLUS (A+B') PLUS 1|
|0110||0||1||F = A MINUS B|
|0111||0||1||F = (A+B') PLUS 1|
|1000||0||1||F = A PLUS (A+B) PLUS 1|
|1001||0||1||F = A PLUS B PLUS 1|
|1010||0||1||F = AB' PLUS (A+B) PLUS 1|
|1011||0||1||F = (A+B) PLUS 1|
|1100||0||1||F = A PLUS A PLUS 1|
|1101||0||1||F = AB PLUS A PLUS 1|
|1110||0||1||F = AB' PLUS A PLUS 1|
|1111||0||1||F = A PLUS 1|
|0000||1||d||F = A'|
|0001||1||d||F = (AB)'|
|0010||1||d||F = A' + B|
|0011||1||d||F = 1|
|0100||1||d||F = (A+B)'|
|0101||1||d||F = B'|
|0110||1||d||F = (A XOR B)'|
|0111||1||d||F = A + B'|
|1000||1||d||F = A'B|
|1001||1||d||F = A XOR B|
|1010||1||d||F = B|
|1011||1||d||F = A+B|
|1100||1||d||F = 0|
|1101||1||d||F = AB'|
|1110||1||d||F = AB|
|1111||1||d||F = A|
- How do we get this thing to start fetching?
We talked about the Fetch-Execute cycle that the processor must go through. Based on the fetched instruction into the IR, the processor will then perform additional steps. One "nice" feature of this processor is the lack of an instruction with an opcode of 0000-0000. This means you can treat Fetch as just another instruction. When you finish the current instruction, or when you push reset, clear the IR. If you are building your state machine using tables, this should help you since every microstep is just another line in the table. On every clock cycle, go to the next step defined in the state transition table. For each state, output the proper control signals to cause data to move around the processor.
- What do you mean sign extension? I think I understand conceptually, but how do you actually build it?
Say I have a number A7..A0 and I want to sign extend this to a 16-bit number A15..A0. The simplest approach is to take the sign bit and use it to extend the number to the desired number of bits as shown below.
A7 A7 A7 A7 A7 A7 A7 A7 A7..A0
- How do I perform a conditional jump?
My approach (one of many)...
In class I talked about a NextCtrl and a NextStep set of signals. NextStep is the value stored in microcode that you will go to on the next clock cycle provided that NextCtrl=000. However, if we want to do a conditional jump, one approach is to skip a line of microcode if the condition is true otherwise, just continue processing. You only need to skip a line once since the NextStep value can be used to unconditionally "jump" over other lines of microcode that should not be executed. How do we skip a line? Well.... one approach is to add +1 to the NextStep if the flag meets the condition of interest. Add +0 otherwise. So... you have a truth table that says something like
Using an adder, you should now have the correct value for NextStep (conditionally) based on the value of NextCtrl and Z or C.
- I still don't understand... show me a different way to handle the conditional jump
Perhaps a different way of thinking of this would be more useful. Assume that the microcode for JZ when Z is true can be found at 0x5300 and the microcode for JZ when Z is false can be found at 0x5380 - this means we have 128 microsteps for each case.
We start with Fetch in the IR (0x0000). During the fetch we get the point where we load the JZ instruction into the IR. As we are doing this, we are going to set NextStep to be 0000-0000 in the microcode (for example). However, this value is passed into a register that will not be clocked until the next edge (or falling edge). So... we need to determine what our low address byte will be 0x80 or 0x00. Notice that this corresponds to an address of Opcode7..0 Z_flag NextStep6..0. Normally we want to load Opcode7..0 NextStep7..0. But using a control signal we can decide to use Z_Flag or 0 as that 7th bit. This is yet another approach to building the circuit. I was taking a somewhat lazier approach and refused to use non-MSI logic - but this will certainly work as well. Of course, if you needed 1 step for the first case and 129 steps for the 2nd case, this approach will fail. Using the above approach you can use 1 step for the first case and 254 steps for the other if you needed. A minor detail given the requirements, but something to think about since we're on the subject :)
- Where is the TEST function in the ALU?
Again, one approach of many... The TEST function can be handled by using a decoder to select a 16-bit pattern that you will load into the ALU. Logically AND the mask with the value you are trying to test to determine if it is zero. For example, I have the value 0x1234 in R0. I want to test Bit(6) so I generate a mask 0000-0000-0100-0000. I then AND this value (0x0040) with 0x1235. This results in 0x0000 which sets the Z flag. If I want to test Bit(2) then the mask is 0000-0000-0000-0100. When I and 0x0004 with 0x01234 I get 0x0004 which is NOT zero. I provided access to the internal bus from the controller so that you can generate numbers in the controller and write them to registers directly. Similar to the source and destination control, you may want to plan ahead for multiple values that you generate within the controller to be selectively placed on the internal bus.
Said another way...
If you have a bit that you want to test start by creating a mask that only has a binary 1 in that position and a 0 in all other positions. ANDing this mask against the full number will zero out all bits except the position of interest. If the mask is called B (b15..0) and the number we are looking at is A (a15..0) and our position of interest is 2, then
B=0000 0000 0000 0100
B * A
= (b15 * a15) (b14 * a14) ... (b2 * a2) (b1 * a1) (b0 * a0)
= (0 * a15) (0 * a14) ... (1 * a2) (0 * a1) (0 * a0)
= 0000 0000 0000 0(a2)00
So... if a2 = 0, then the result of the entire number is zero. If a2 = 1 then the result of the entire number is not zero. This can be used to set your flags.
- Do we need to include the description of the instructions in our report like you did in the handout?
You do not need to include my description. Assume that the instruction set summary is provided, the internals of the processor are understood by the reader and they have a copy of the system interface schematic. Your report needs to focus on the design of the control circuit, a copy of the register transfers you are using for each instruction, a description of how you handle conditional branching, etc. Someone should be able to pick up your report and build your control circuit or at least understand what your design is doing given a copy of the schematic. Put your .BIN file if you use one in your appendix - probably printed landscape or something so it fits. Test cases are an absolute must (as always). The usual block diagram decomposition is expected with a description and testing of each internal module if you used any. Look at other microprocessor manuals for samples if you are curious to see what the industry does. One example that I'm working with now can be found here
- Do we need to use a free running clock for our circuits?
Yes. The TAs will be testing with a free running clock since testing all of the instructions using a switch will take an unusually long period of time. You can change the pulse width of your free running clock if necessary - just make sure that you do this in your circuit before you submit it so your TA will not have to guess what the correct settings are.
- I keep getting X's in my circuit, any thoughts?
One problem I have been seeing in a number of circuits is a timing hazard. If you are using the IR as part of the address (current state) into your microcode ROM, you need to ensure that this does not change when you load the instruction into the IR. In any sequential circuit, your current state must be kept stable if it is used to generate control signals. On a clock cycle, the entire computed next state must become the entire current state on a single edge. If you have the IR as 8 bits of the current state, and some registered value as the other 8 bits of the current state, BOTH must change at the same time together. If they do not, then you have a timing hazard where you may briefly think you are in an invalid state. Any line in a ROM that is not populated, will contain all X's. If you get into this state and your RAM WE line goes to an X - LogicWorks will wipe out the contents of memory. This seems to be happening in circuits where you get it to work if you use a binary switch, but not the free running clock. If you are stuck - you can always email me or your TAs and we can look over the circuits to try to help you.
- TEST Instruction Error
The goal we are looking for is something like
TEST R0, 4
Since Jump if Not Equal (JNE) is the same as Jump is Not Zero (JNZ) then we need the zero flag to be true when the bit in in R0 does not match the mask (i.e. AND produces 0x0000 which will set Z=1). Otherwise, the flag will be cleared (Z=0).
I have updated this in the project description on page 15.
- Can you provide an additional sample program?
Sure... Test2_.bin will examine the MOV instruction variants and the HALT instruction. This is one of the programs that the TAs will be running to test your processor. Similar programs will be used for the other instructions.