Overall Design and Build Strategy

A project log for The Spikeputor

I am building a computer featuring a 16-bit RISC CPU made of transistors for learning, fun and art. It will be pretty large.

spudfishScottspudfishScott 01/08/2019 at 03:360 Comments

As mentioned in the Details Section, the Spikeputor CPU is modeled after the "Beta" CPU from the MITx course, "Computation Structures" (see this link to the Beta diagram). The Beta is a 32-bit RISC CPU with 32 registers (31 + 1 that is hard-coded to zero). I chose it as a model because, frankly, it was the only CPU I knew from the ground up and it seemed relatively simple and elegant, yet quite powerful. Once I did a quick calculation of the number of discrete components I'd need to fully implement the Beta, however, I knew that some compromises would have to be made. The main compromise was to switch from a 32-bit to a 16-bit architecture. At that point, I did NOT look at other 16-bit CPU designs. Instead, I took on the challenge of adapting the Beta to a 16-bit design with as few changes as possible. The biggest changes centered around opcode and instruction size. The beta always executed one opcode per clock cycle. The 32-bit opcodes were wide enough to accommodate instructions featuring up to three registers (five bits to address each of the 32 register locations) or two registers and a 16-bit constant. The remainder of the bits were more than enough to describe all of the instructions defined by the Beta's ISA (Instruction Set Architecture). 

With only 16 bits to work with in a Spikeputor CPU opcode, the number of registers needed to be reduced to eight (three bits to address each of them, R0 to R7, where R7 was hard-coded to always be zero), leaving six bits for encoding the rest of the operations. Obviously, there was no available space for constants of any size within the opcode itself. Reducing the register count was acceptable to me. There'd be a code efficiency hit, but the transistor count for Register Memory was much more manageable. To be able to use constants, however, required a major change in the architecture. Instead of a simple one instruction per cycle design, a multi-phase instruction scheme was needed, necessitating several additional special-purpose registers, independent from the user registers. I envisioned three CPU phases. Phase information would be stored in a two bit CPU_PHASE register. In phase 0, the instruction would be read in to a 16 bit INSTRUCTION register. If the instruction called for a constant, the CPU phase would be updated to phase 1, the program counter incremented, and the constant read in to a 16 bit CONSTANT register. If a constant wasn't part of the instruction, the CPU phase would go directly from phase 0 to phase 2. In phase 2, the instruction would be executed, including updating the Program Counter to go to the next instruction or to branch to a new memory location.

One other difference I chose with the Spikeputor CPU was to not implement a "supervisor" address bit, mainly because I wanted the flexibility to (eventually) use the entire 16-bit address space. Instead, to handle interrupts (which I still don't know if I'll even use, but still wanted to design), there would be a one bit IRQ_FLAG register to prevent the interrupt itself from being interrupted. The whole initial design looked like this:

I'll describe each of the modules in greater detail in subsequent project logs.

Since the Register Memory and ALU were most similar to the Beta design (something I gained experience with taking the Computation Structures course), I decided to dive right in to starting to build those first. As mentioned in the Details, I'm doing the whole project with solderless breadboards. I'm doing this for purely artistic reasons: I just love how they look, the air of impermanence they invoke, and their modularity. In addition to the schematic, I needed to plan out how the actual components would be laid out. Trying to use breadboard space most efficiently is actually another major design driver. Usually this goes hand-in-hand with transistor component efficiency, but not always. Sometimes, adding a few extra components actually makes it easier to lay out the boards without having to bend transistor leads in crazy ways. And leaving extra "white space" on a breadboard has been quite helpful in lining all of the components up, and also allowing for last-minute additions when required. 

I had some 6'x4' pegboards that I decided to use to mount the breadboards. They were big enough to hold 54 breadboards in a 6 x 9 array, plus some extra space for power supplies, and a few smaller breadboards if needed. I created a pegboard template to lay everything out. Here's an example for the Register Memory, which fits perfectly onto one pegboard. Green text is an input, red text an output. Green background means I've completed the actual boards, yellow means they're designed, but not completed yet, white (not pictured here) means not yet designed. As of today, most of Pegboard 3 and all of 4 is white.

The entire Spikeputor will take up four pegboards:

The four pegboards will fit neatly on a single wall in my house, with space below for a desk, which will hold the Apple II plus that I'll be using for I/O.

Finally, I have another template for laying out components on the breadboards. Translating schematics into breadboard layouts is a Tetris-like challenge that can be maddening, but ultimately quite satisfying! Here's an example of three bits of the ripple-carry full-adder circuit of the ALU. Orange = transistors, Red = SIP-8 resistors, Other colored boxes are either LED leads, discrete resistors, or inputs and outputs:

And here's the finished product:

OK. Happy to entertain questions and comments. Next we'll get to actually building the thing!