The morning of Thanksgiving, I woke up and decided to finally get started on a project I'd wanted to do for a while: to design a CPU and build it from discrete logic chips. I figured I had a long weekend ahead of me, and I could use it to make some good progress. I was looking down the barrel of a long day of cooking (and eating!), but for some reason I woke up really early that day, and decided to make use of the extra hours I'd involuntarily acquired. I cracked open Logisim and started working on the beating heart and soul of the CPU: the ALU.
The Arithmetic Logic Unit is the part of the CPU responsible for doing mathematic and logical operations. The CPU mostly does two things: move stuff around, and do math on that stuff. The ALU is responsible for all the "math" parts. Because of that, in many ways the ALU is the linchpin of the whole CPU.
The ALU turned out to be a nice place to start, as it can be built more or less in isolation from everything else. I had a rough idea of what the rest of the CPU would need: some registers to hold the intermediate results of all this computation, a bus structure to ship data around between registers and the ALU, and some sort of control logic to guide it. I didn't know what any of that would look like, however, so I started with a key piece that I could test in isolation.
The ALU is a combinatorial circuit, which means it doesn't have any memory or real concept of time. It simply takes two inputs at one end, and outputs the result of doing some operation on the other end. Theoretically, this happens instantaneously, and any change to the inputs is immediately reflected at the output. In reality, it takes some time for changes to propagate through all the different gates and arrive at the other end. The amount of time it takes for that chain reaction to ripple through the circuit is going to be one of the primary limitations on how fast we can eventually run our CPU, so it's our in our best interest to minimize it as much as possible. Speed isn't the only concern, however, and we have to make trade-offs between speed, flexibility, and ease of construction.
Many TTL CPU projects use off-the-shelf ALUs like the 74181, but I didn't want to go that route. It kind of felt like cheating to buy one chip that does all the hard work. Also, the ALU ICs aren't in production anymore, so they're kind of hard to find and expensive. One ALU chip can replace what is eventually going to be like a dozen individual ICs, so it's still probably cheaper in the long run, but the fact that it's hard to get your hands on one is annoying. However, the biggest reason I chose to design my own ALU is, well, it's the core of the whole thing! It'll be fun, and if I don't build that part myself, I'm not sure if I feel like the ALU is really mine.
I think of the ALU as being composed of basically three parts:
When looking at operations I wanted to support, addition and subtraction were a given. As far as logical operators, I wanted and, or, and not at a minimum, and really wanted xor as well. left and right shift would be great, as well.
Once you start supporting some of these operations, other ones start to come along for free.
So, if we have the ability to negate our inputs and outputs, then we only need Add/And/Xor/Right Shift. That's a pretty clean 4 operations, and I think that it'll be pretty practical to build.
Next time, we'll actually build an ALU in Logisim that has all the functionality we described above.