JoeProcessor3000
Article
Appeared in
Make, vol 3, summer 2005
I didn't set out to build a microprocessor. It just sort of happened. We were building logic circuits in my Logic Machines course at Bennington College. Nothing complicated, just adders and flip-flops made from 70s era TTL chips. We even wired eight big flip-flops on breadboards to make one whole byte (we estimated sixteen square miles to make the memory in my PowerBook).
Over mid-term weekend I brought home parts and breadboards and spread them out on the coffee table. I'd been thinking about how a microprocessor is just a bunch of simple circuits tied together by a common interface (address and data buses) and controlled by some kind of clever sequencer. I started by building a register: a single byte of memory with eight data lines in/out and two wires to control whether it's recording or playing back. I wired up LEDs and switches to the data lines. By setting a value on the switches and pressing the record button I could set the byte, and by pressing the play button I could see the value on the LEDs. Amazing.
Emboldened, I built a math unit with two registers as inputs and an 8-bit adder and latch to put the sum back on the data lines. I built a program counter out of binary counters -- tricky because it needs to be incremented or set to any value on the data bus (to implement branches), and its value goes out on the address bus. The address bus needed somewhere to go so I added a RAM chip. I made another register just because they’re easy.
I sequenced the whole thing with more RAM, an instruction register and a 4-bit counter. The RAM bits go to the different components' controls. By sequencing the bit patterns I can for example tell the math unit to place its sum on the data bus and tell a register to record it. Another sequence can increment the program counter and load the next RAM value into the instruction register. My big insight is that a machine language instruction is just a reference to a sequence of controls, like a band leader reading a score and calling out chords.
The whole project came together over the weekend. By Monday I had a wirey bunch of breadboards that was busy figuring square roots. I think I'll add a serial interface, make a gcc back-end and port Zork.
Article Notes
[These are random jottings I wrote in preparation for the article.]
Avie Tevanian, then-VP of Software Engineering at Apple, addressed a gathering of Apple engineers a few years ago and wondered out loud, "How in the hell can OS X be so sluggish when the CPU is executing a hundred million instructions per second?!" I write assembly language and I knew what he was talking about. I learned to program counting cycles on my Apple II, Z80, 68K and PowerPC. Those poor programmers of high-level languages with their overloaded operators and their virtual machines. They don't know what's really happening down there. They write slow, wasteful code because they don't know any better.
The surprising thing about the design of a computer is its simplicity. At the lowest level, a computer works by collecting a handful of relatively simple circuits, defining a common interface between them, and then sequencing their interactions. Addressable memory (RAM), registers (scratch memory), math and logic units -- each of these is treated as a black box that does its thing when directed. "Math unit, shout out your result. Register, listen and remember the value. Register, shout out your value. Math unit, listen and add 1, then shout out the result. RAM, listen and remember the value." And so on. Components read or write values and possibly do something trivial in between. Memory components simply remember values and give them back when asked. Math units take multiple values and generate a result. Values are moved from register to register, register to RAM, RAM to math unit, math unit back to register. Different combinations, operating millions of times per second.
I built my microprocessor by prototyping each of the components. I used 70s era LS TTL chips, still available on the cheap from places like Jameco and Digi-Key. (The most expensive parts were the breadboards and wire.) I never really had a schematic since each component consisted of just a few chips. I knew every component would need to read and write something, and have some kind of enable, so I just worked from data sheets and tested with LEDs and DIP switches as I went along. It was very ad hoc.
Let's take the math unit for example. It adds two 8-bit numbers and gives an 8-bit sum. It has two 74LSxx registers (X and Y) which remember the input numbers. They feed into two 74LSxx 4-bit adders, and the output is controlled by a 74LSxx. Both the inputs and the output share the same eight data wires in and out. To test, I put a number on the wires (using the DIP switches) and loaded it into X by bringing pin 5 low. I then did the same for Y. The sum appears on the wires when I enabled the 74LSxx by taking pin 3 high. Add LEDs here and there to see.
I ended up with two registers labeled A and M, a program counter, a status register, and the math unit.
Everything's eight bits wide. The common interface is the white and blue wires (white for data, blue for address). The control lines are black (red is power and yellow is debugging). Even though every component is attached to the white and blue wires, only a few of the components are active at a time.
Assembly language does the directing. High level languages get translated to equivalent sequences of assembly. (iTunes on OS X is a million and a half PowerPC instructions.) If the computer design is Turing Complete, it can do anything that's computable. Given enough RAM and time, my microprocessor could run Photoshop.
Handwritten Notes
The attached images named "notes_..." are handwritten explanations that I set out with the running microprocessor while it was on display in the science building. I dated and versioned each page and rewrote each page several times in response to questions and comments.
- Page 1, It's a microprocessor!
- Page 2, This is the program that's running...
- Page 3, The Map
- Page 4, 5, 6, The microcode
- Page 7, 8, 9, Initializing the microcode RAM
- a schematic of sorts, more like a rough block diagram
- a program that calculates square roots