A while back I designed and built my own 16-bit microprocessor. I wanted to be able to use my processor in my own projects. My requirements were very simple. It had to be easy to understand, have an instruction set that is simple to use, as this makes it easy to generate a simple compiler for the instruction set. My desired instructions were pretty simple. I wanted something akin to the MIPS instruction set. Instructions should take one or two source operands, and a destination register or memory address. In the case of comparative instructions, it should simply discard the results. The processor should include a hardware stack. It should contain at least 16 general purpose registers and at least four I/O ports. I would prefer instructions to take a single cycle. But If I include a multiply and divide instruction this isn’t practical. My last requirement was that I should be able to use free and open-source tools. I did a basic design in Logisim and then moved the design to VHDL and place it on an FPGA.

The project started off great but hit a few bumps in the road along the way. I eventually worked them out and got everything working. But I never really documented the project. So that’s what I’ll attempt to do here. I’ll step back and walk through the process I took; errors and all.

Designing a microprocessor isn’t a task for the faint of heart, however, it isn’t a task that should be impossible either.

This serries of blog posts will be more a record of how I proceeded with this project. I’ll include my mistakes, faux pas, and hopefully, the solutions I found, so readers may amuse themselves at my expense… If you follow along and find these posts interesting, have helpful hints or information, or if you build the MiniMips let me know in the contact form. I’d love to hear from you.

So how do we get started? Well, I’ll assume like myself, you have a grasp of digital logic basics. So let’s start by writing some requirements for the ALU. The ALU is the core of the processor. It’s the part that provides the Arithmetic and Logic functions. The ALU has at least three inputs: Operand 1, Operand 2, and the operation code. It should produce at least two outputs; the result of the operation, and a set of status indicators or flags.The ALU will perform the following operations:

- Binary Addition
- Binary Two’s Complement Subtraction
- Binary Bitwise Inversion
- Logical And of two binary values
- Logical Or of two binary values
- Logical XOR of two binary values
- Logic Shift Left
- Logical Shift Right
- Logical Rotate Left
- Logical Rotate Right

So if I count correctly that’s ten ALU operations we need circuitry for. Let’s start with the arithmetic first, Add and Subtract:

Let’s first review the laws of binary arithmetic:

Addition | 0 + 0 = 0 | 0 + 1 = 1 | 1 + 0 = 1 | 1 + 1 = 0 |

Subtraction | 0 – 0 = 0 | 1 – 0 = 1 | 1 -1 = 0 | 10 – 1 = 1 |

Now let’s look at a couple simple logic operations:

AND Operation | OR Operation | XOR Operation |
---|---|---|

1 AND 1 = 1 | 1 OR 1 = 0 | 1 XOR 1 = 0 |

0 AND 1 = 0 | 1 OR 0 = 1 | 1 XOR 0 = 1 |

1 AND 0 = 0 | 0 OR 1 = 1 | 0 XOR 1 = 1 |

0 AND 0 = 0 | 0 OR 0 = 0 | 0 XOR 0 = 0 |

You might notice that adding to binary digits produces the same results as XORing two binary digits. So to add to binary digits together we only need to XOR them. However, when we try to do arithmetic operations on multi-digit numbers we have the issue of carrys and barrows. Let’s just look at addition for the moment. When we add two two-bit binary numbers and have a one in the same column in both numbers we must carry a one to the next column. Just as we do in decimal arithmetic.

Carries | 1 | 1 | ||
---|---|---|---|---|

1 | 0 | 1 | 1 | |

+ | 0 | 0 | 0 | 1 |

Results | 1 | 1 | 0 | 0 |

If you look closely you will notice that the carry happens anytime there are two ones in the same column. This is basically the AND logic operation.

To follow along download Logisim or Logisim-Evolution and create the circuit show below. (I reccomend you open the Logisim help and locate the tutorial and complete that first if you have little or no experience with logisim.)

Above you see a simple XOR gate. Play with it and test it to see if the output is high only when A+B = 1 and low when A+B <> 1. These condition only hold true if only A * -B | B * -A is true.Remember if A and B are both 1 or high, then the addition results in the output being low and a 1 carried over to the second bit condition. However, here we have no second bit to handle the carry out. So let’s see how to add the carry out to our simple 1-bit adder circuit.

Above you can see the addition of a simple AND gate. The AND gate only generates a 1 on it’s output when both inputs are high. So in this circuit it makes the perfect carry out generator. This type of adder is known as a Half Adder because it only does half the job…

Ok, that’s fine and good you may say however, what about adding multi-bit numbers? How can we do that?

Adding multi-bit numbers requires more than a carry out signal… We also need a carry-in signal. The carry-in allows the carry-out from the previous column to be added into the result of A + B. So our new circuit needs to include the carry-in as part of the sum. To do this, we just need to add the carry-in to the result of A + B. We can do this with another Adder:

So here’s two Half-Adders cascaded together to allow for a Carry-in signal from the prevous column. There is an issue however. We now have two carry-outs. The original and the carry out from the second half adder used to add the carry-in bit. As it turns out, you can’t have both carry-outs active at the same time. To see why simply follow the logic;

- If A=1, B=1 Cin=1: Sum 1 = 0 and Carry-out 1 = 1, Sum-out 2 = 1 and Carry-out 2 = 0
- If A=1, B=1, Cin=0: Sum 1 = 0 and Carry-out 1 = 1, Sum-out 2 = 0 and Carry-out 2 = 0

Check the rest of the conditions by building a truth table. You’ll see that only one carry-out bit can be active at a time. So we simply need to detect when one or the other is active. That’s a perfect job for an OR gate:

OK, build these circuits in Logisim and play with them. Work out the a truth table for them all and prove to yourself they work as expected.

With the full adder you can create N-bit Adders by simply feeding the Carry-out of the previous column to the Carry-in of the next column. As an exercise use our full adder circuit to create an 8-Bit Adder. Next time I’ll show you my 8-Bit Adder and then we’ll look at subtraction.