Transistor CPU Part Fabrication

February 26th 2020, 4:25 pm

I've been working on the Transistor CPU for a few months now, which I've dubbed the MiniDragon. Since the last blog post I've changed a few minor things and nailed down a lot more of the CPU. I have a much more complete simulator, an assembler and disassembler and the beginning of a software library for an upcoming boot ROM. The CPU now has subroutine and stack instructions as well as an absolute jump, making it possible to write a reasonable library of built-in functions. Through writing a multiply subroutine I learned a lot about my chosen instruction set and have made several changes to the CPU as a result. I'm a lot more confident in the instructions since I've implemented actual software with them. That software, as well as an up-to-date description of the CPU itself, is available on GitHub.

Fleshing out the simulator allowed me to get a much better handle on the CPU itself. However, I only spent a small amount of time working on the software side of things. The majority of my time over the last few months was spent learning KiCad, digitizing schematics that I've tested on breadboards, laying boards out and sending them out for fabrication. I've been using Oshpark for fabrication since it can take pcbnew files directly and is cheap enough for small runs of boards. The boards are also my favorite color: purple! I started out with some simple logic gates that I'd breadboarded over Christmas: NOT, NAND and NOR. These aren't super interesting and they only consist of a few discrete components. However, they let me learn the ropes of KiCad before I undertook some more complicated design and routing projects like a 4-bit register.

Probably the hardest part of the last few months was being patient. As soon as I finished board layouts I wanted to throw them over the fence and get them fabricated. However, since I've never done PCB layout and fabrication before I was sure that I would make a ton of mistakes. So, I forced myself to send out small batches and observe everything that I did wrong before sending additional boards to fabrication. This paid off heavily, as I made a few mistakes that I had time to correct before it cost me unusable boards or ugly rework. It also allowed me to pace myself with board assembly, since I underestimated how much time it would take to assemble through-hole circuits.

A few specific things that I learned during this process stand out. First is to always ensure that you silkscreen the circuit title AND revision to the board. If you change component values or layout significantly, you will want to know what revision the board is in order to assemble it properly. You might be 100% sure that you will always recognize your boards, but manufacturing can take a few weeks and by the time it comes in you won't remember. I messed up my first board and forgot to silk screen it at all, so I ended up affixing labels to the circuits.

Rev 1 NOT gates with missing skilk screen.

Second is to make sure to space headers on the board apart by a multiple of the pin spacing. I chose to use 2.54mm (0.1") pitch pin headers for all of my circuits. Especially when you are dealing with 1x1 headers, it can be next to impossible to correctly insert the pin and solder it while making sure that it is plumb and square against the top of the board. However, if you have a row of multiple sets of pin headers, spacing them apart by a multiple of the pin pitch (2.54mm in my case) means that you can use a long pinsocket as a temporary holder in order to line up all the pins at once. I happened to do this on accident on my first board and was super glad that I learned this before sending out the next batch in which I had not lined up the pins at all. I use a 40-pin pinsocket as my assembly template and plug in the pin headers to it before soldering them all in one go.

Third is to use the rendered top copper and bottom copper layer photos on Oshpark. I had one board where a trace was routed far too close to a through-hole connection and another which had spurious traces. Neither of these were caught by pcbnew's constraint checker and I missed them looking at the PCB in pcbnew itself. Seeing your circuit in a new light is a super good time to check for errors. I definitely get a bit blind to errors in my layouts due to staring at them for so long while I put them together. So, a different look at the same boards has let me catch issues before sending to fabrication.

Finally, your breadboard designs might not work when laid out on an actual PCB. I arrived at values for my clock edge detection circuit when breadboarding which did not actually work when I assembled my first revision T/D flip-flop. I ended up having to do a really gross rework to verify an updated design. Luckily, I held off on sending a 4-bit register to fabrication which was based off this flip-flop. Only once I got the second revision of the flip-flop back from fabrication and verified that it worked did I send the 4-bit registers out. As a result, they worked fine! Given their size, it would have been a costly mistake had I not waited and verified. A secondary advantage to reworking the circuit was that I got to reduce the resistor part count from 4 distinct values to only 3 on clocked circuits. This makes assembly a lot easier and means that I can keep fewer parts on hand.

Nasty rework done to fix a hardware bug in the Rev. 1 T/D flip-flop.

I have a fairly decent library of logic gates and CPU components fabricated and verified on the bench at this point. I have my staples, like NOT, NAND, NOR, XOR, XNOR and a T/D flip-flop (behavior can be changed with a jumper). I am waiting for fabrication to finish on some AND and OR gates. Some of the CPU microcode logic requires them and I don't want to waste physical space and propagation delay chaining NAND/NOR circuits to NOT circuits. I have a few more useful circuits such as a 1-to-2 decoder and 2-to-4 decoder. Naturally, these can be chained to make a 3-to-8 or 4-to-16 decoder which I will be using in the instruction decoder and microcode lookup circuits. I also have a few parts that I'll use for the actual CPU designed and tested. I settled on a pull-up, open collector bus architecture due to its ease of coupling multiple driving circuits. To support that I have a few 8-bit bus backbone circuits that provide current bus value indicator LEDs and pull-up resistors as well as a ton of 8-pin headers. I have an 8-bit bus writing circuit that has an 8-pin input header and an enable control signal and an 8-bit output header designed to plug directly into a bus. Finally, I have a power on reset and clock circuit that provides automatic and manual reset as well as automatic (adjustable speed) and manual clock pulses.

Lots of completed and verified circuits!

Most of the above circuits are fairly boring in their implementation. The logic gates are similar in design to any RTL circuit you can find online. The bus circuit is just a bunch of pull-ups and a few buffered transistors driving LEDs. The bus writing circuit just converts 8 signals from 0V/5V signals to open-collector outputs. The clock circuit, however, is a bit more interesting. At its core it consists of two components: power detection and a clock generator. The clock generator is essentially an astable multivibrator buffered through some signal-shaping inverting transistors, then finally lead through a 2-bit selection switch and an output amplifier. This lets me choose either the automatic clock or manual clock pulses that are input by a switch that's debounced using a capacitor. The power detection circuit uses a zener with a reverse breakdown voltage of 4.7V, hooked up in reverse. This drives a transistor that switches on to indicate that power is good enough to use which drives an RC circuit to generate a reset pulse. The output of that is buffered, and also goes to the clock's amplifier circuit. This allows the reset logic to disable the clock while the reset line is held high or when the voltage is too low. This lets the CPU auto-reset its registers on power-up, ensuring stable boot every time power is turned on. Finally, a manual reset button is wired in as a logical OR to the reset pulse circuit. That way, if I decide to reset the CPU I can press the button and reset will be asserted while clock is disabled.

Oscilloscope showing power (purple), reset pulse (blue) and system clock (yellow).

I still have a ton of stuff to lay out and get to fabrication. I have the parts on hand to build the flags circuitry and one of the 8-bit registers (either the A register or the D register). I'm waiting on additional parts to come in to build the microcode counter circuitry. I've standardized on a 1x1 foot ABS plastic base for various logical components of the CPU but haven't permanently attached any components yet. I am also waiting on some simple bus/power/control line breakout boards that will let me run various connections to the edge of the panels for easier and more modular assembly. I need to design some jumpered 8-bit ROM boards so that I can program the microcode instructions in, and I need to start laying out the actual instructions one at a time. I also need to assemble 18 more 4-bit register boards to have enough registers for the whole CPU. Then, once all of that is brought up and verified, it will be time to start work designing, laying out and fabricating the ALU itself!