An 8 bit breadboard computer based on the Malvino SAP-2.
Today I hooked up the control logic for the A and B registers. The first step was to work out the wiring schematic on paper. Each register consists of two chips a 74HC245 bidirectional bus transceiver and a 74HC574 8 bit flip-flop. I want to be able to have three states for the register:
The register has four inputs to manage:
On the A and B registers, the control logic to drive these input lines are a Lx (latch) and Ex (enable) signal. We can break this into a state table that describes what the input lines should be to satisfy the signal:
Once we have a mapping of our logic to our chip input lines, we can create a truth table to determine the logic we want to use to actually wire up the signals. In my case I didn't want to use more than 2 gates per chip. I use quad gate array chips, so 1 chip can support 2 registers. I like to write the logic out as a phrase that matches the state table above.
We combine the system clock with L to create the input clock, so that we are only clocking the flip-flop chip when we want to load something. We can also save some wires by using a nor gate to create the 574 output enable signal, since it lets us avoid having to not E.
I did all of this on paper:
I just enjoy working out the logic on paper to be absolutely sure the schematic makes sense. The above doesn't show the nor gates which I added afterwards when I realized it simplified things a bit. There are probably other simplifications that I didn't find or didn't look for. I ended up using 1 NOT, 2 NOR, 1 OR, and 1 AND gate per register.
Another aspect of this approach is that the on state for a signal is high. This just keeps it simpler in my head, instead of having to worry about remembering something like "bit X of the CON word is not-L" because I wanted to save a NOT gate.
Here is the resulting CON wiring for the A and B registers:
The CLK and is done on the breadboard below, where I moved the clock circuit. I'll probably neaten up the lines by cutting them to the same height off the board before I implement another register's CON.
And here is the logic working. I previously latched a value into the B register and then I enabled that register to drive the bus:
Another bank of of these chips will be placed below for the X and Y index registers and then each should also have enough gates free to drive the registers on the left side of the computer. The X and Y index registers will also have INC and DEC control lines.
So next up is a clean-up pass on the wiring, then wiring up all the other registers (as well as the ALU enable line) this same way.
The idiot lights really don't need to be there, but make debugging a lot quicker. I may pull them?
Preparing for running a 2E AD&D campaign took me away from this project for a few weeks, but I've managed several hours of good work lately.
I started by hooking up the 16 bit program counter using a chained series of 74HC163 synchronous presettable 4-bit binary counter, with synchronous clear. Using settable counters is important for wiring up jump instructions later and the clear helps with implementing a reset function and clean state after power-on. This module is a little messy, as I haven't consolidated the control logic yet. As a result, colored wires that perform various functions like the reset are just hard wired with various colored jumpers and the module looks more chaotic than it will after a clean-up pass.
Below that are the two memory address registers, MARL and MARH, the low and high portion of a 13-bit address bus, wired in yellow to the RAM chip. The output of the RAM chip is then wired to the flip-flop of the memory data register. All of these registers are wired to the bus in green. Green wires are used exclusively for the 8-bit data bus.
The RAM is an Alliance Memory AS6C6264-55PCN 64K Parallel SRAM, which I haven't tested out yet.
On the right side of the board I've added a X and Y register which will be used to implement 6502-style indexed addressing modes (as well as acting as extra scratch registers when needed). No control logic is hooked up to these registers yet, but they are wired to the bus.
On the bottom left side of the board the stack pointer remains to be wired to the bus and the instruction register has to be hooked up. The IR will be wired to the instruction decoder which will reside in the middle of the board with the rest of the control logic. You can also see that I've added some extra breadboard strips at the top to span the data bus lines across board halves.
The control logic is where the real cognitive effort lies, in a future post I'll enumerate the expected control lines.
I also started wiring up a clock divider board so I could drive the computer from a 14.31818 mhz crystal at speeds stepping down to 1 hz.
Today I had a large chunk of uninterrupted time to work on this project and a bunch of progress resulted.
First, I wired up a pair of 74HC181s, 4-bit arithmetic logic unit and function generators. I didn't run through an exhaustive set of tests, but I did ensure that several operations worked correctly. I'm generally confident the wiring is good. Here you can see the ALU with various control lines labeled.
I also spent some time hooking up the control logic for the A and B registers. Each of these registers has an E and L control for enable-output and load, respectively. When Ex is high, the register should export its value to the bus. When Lx is high, the register should clock in a new value at the next rising clock edge. If both are low, the register should be in a high-impedance state.
My first pass at this logic was inefficient, using a chip array for each register. This is the right half the board (yellow wiring) in the image below.
After getting this working, I sat back to think about the geometry of the computer. I didn't like the inefficient use of the chips, I didn't like how the lines ran the entire length of the board to the bus transceiver on the left, and I also wanted to change how I was doing the central data bus.
Originally, I was thinking that the data bus would run down the middle of the machine, like in the Ben Eater computer. Here's my finished version of what that kind of design looks like:
I don't like how the control bus is a crow's nest of yellow wires. So, for this computer, I think I'll try a wider design that puts the CON logic in the center of the computer.
After an hour of re-organizational wiring and listening to Conversations with Tyler, I ended up with a much cleaner set of control logic for the registers and the ALU, although I haven't relocated the S, Cn, and M leads yet:
You can see how the green data bus lines are now daisy chained along the transceivers. The clock stepping switch that I use for testing is now also located in this central area. The white clock leads are temporary. In fact, that entire CON block will probably get refactored a couple times as more pieces come into place. I haven't worked out the layout on paper and I may do that soon.
Here is a look at the complete computer so far:
The left side of the board has the program counter, the incomplete subroutine counter, and a 64K ram chip roughly where the memory will be. The center is the new CON block and the right are the registers A and B and the ALU.
I think at this point the left side needs some attention. I need to make some progress on the address bus, although I also need to complete the subroutine counter and the index register. Once all of that is done, I'll have to work on the instruction register and the instruction decoder. I figure the control logic will develop stepwise with the various modules.
I've also been thinking about input, output, and how to program the computer. I'd rather not program this machine with DIP switches. I'm leaning towards hooking up a bootstrap loader that can dump code into the RAM on startup. That would be removable, allowing me to code the computer with an assembler on the PC. For output, maybe some kind of LED array? The problem with a lot of those devices is that they require a very high refresh rate, some > 500 Mhz, which this device won't be able to deliver.
Yesterday I finished wiring up and debugging the 74181. The wiring of the second half is fairly disorganized, mostly because I wanted to try to fit everything on two more boards. It was also difficult to organize the logic by function, so the ICs are unlabeled. It's mostly AND gates, with a few OR, XOR, and inverters thrown in.
Here it is pictured next to an actual 74HC181:
Since this is a 4-bit ALU, if I wanted to use this logic in the computer project I'd need to build 2! That would make the ALU nearly as complicated as my first 8-bit computer in its entirety.
Now that I've built this model of the 74181, it'll probably go on a shelf and I'll use the actual 74HC181 IC in the computer project. I'm considering this sub-project a success and I'll assemble the ALU for the new computer next.
A quick update to note that I've wired up the generator G outputs on the model 74181. I re-organized the board a bit and co-located them with the propagation bit logic.
The board is a bit less clean now, but I think the trade-off is acceptable. Running wires to the P & G intermediate output LEDs creates a bit of a stacked bus in the middle of the board. If I didn't have those toy LEDs the overall layout would be pretty clean. It also doesn't help that b and s0 is as far away from where it's used as it can possibly be, resulting in the long white wires on the bottom of the circuit.
I've run through several test cases and everything is working, although I won't be able to show this well until I get new DIP switches. The current ones like to pop out of the board so I have to hold them down while testing.
Now I just need to add circuitry for calculating the sums and carries and then I'll have a completed ALU!
Two days ago I completed the propagator P output for the model 74181. It looked like a complete mess.
The circuit was functional, but didn't really serve the purpose of making that functionality transparent. I mean, there's a laundry list of bad things going on here.
This didn't meet my standard. I ripped everything out and reworked the circuit.
Here is the current state of the model:
The input switches are more clearly separated. These DIPS do not want to stay in the breadboard, though, so I ordered a different kind to see if I can get better results.
The organization of the logic is as follows:
I'm pretty happy with the progress so far and I've now hooked up this logic enough times that I understand it well.
The next task will be to wire up the generate bits!
I'm building a CMOS computer out of 70s technology to learn about the path we took to get from then to now, but also to develop a deep understanding of how each part of the computer works. I could read about it and it would feel like I understood the material while I was reading, but I would quickly forget. True understanding of a technology is demonstrated in the ability to use that knowledge to effect a change in the world.
I like to think of my hobbies and my work in the frame of working through a personal tech tree.
On this project, I'm applying that concept narrowly. I want to use a 74hc181 4-bit arithmetic logic unit and function generator as the ALU in my computer. I've build arithmetic units before, but not ones that also had a logic component. While looking at options for the ALU in this computer, I discovered the 74181 and it introduced me to a host of new ideas.
I knew that I could chain multiple counters or logic units together to go from 4 bits to 8 and so on and that this was done at the expense of an increasing propagation delay. The outputs of the first IC have to have stabilized for the outputs of the second IC to begin to stabilize and so forth. The more chips in the chain, the longer it takes for the final set of outputs to be stable. Clock the result too early and you get garbage bits.
The 74181 solves the propagation delay problem by using a method called carry lookahead to calculate whether an operation is likely to generate a carry. If you can do this, then you can do all your arithmetic operations simultaneously and greatly reduce the delay. I spent a bunch of time two days ago working out the boolean algebra and truth tables for P and G generations, once I heard about the idea.
The 74181 also has a clever internal implementation where it's 16 logic functions are added to the A input to create 16 arithmetic functions. This took me a while to wrap my head around. A bunch of it seems super obvious in hindsight, but the schematic and the design stumped me for quite a while.
The four S inputs select a function to apply to A and B...f(A, B). This boggled my mind when it clicked: the S inputs represent the answers to a 2-bit, 4-row truth table. S0 and S1 are inverted. So, when you set S == ( 1, 1, 0, 0 ) you are asserting "there is some boolean operation where for any A (1, 0) and any B (1, 0) the answer is 0, apply that function to my A and B". For any S you provide, you'll get back an answer that conforms to the truth table you've provided. If you set S == ( 1, 1, 0, 1 ) then you've given it the truth table for AND and it'll perform an AND on the inputs (remember the first two S are inverted). This just seems like a completely backwards way of thinking about the implementation of boolean logic and it's awesome.
Describing a function in terms of the set of its outputs, given a known and constrained range of inputs. My modern programmer brain wants to think of the options I could pick from like an enumerated menu or like some kind of library of operations. I thought of a function as a process, not as a mapping of input to output. I had lost sight of what a function IS.
The first half of the circuit above implements the carry lookahead logic and the second half implements the truth table. The Cn signal is your carry-in so you can chain ICs. The M signal selects between logic or arithmetic modes.
LET'S BUILD A 74181
I think I understand this chip pretty well, but I'd like to build an interactive model of one. Working things out on paper and pondering the ideas in the logical schematic has helped a lot, but I think wiring up each of the gates by hand will crystallize things further.
Once I have my model built, I can sub in a real 74hc181 in the new computer. The IC will be much faster, but I can feel like I've mastered the large chunk of functionality that the chip aggregates....Read more »
Tonight I built the program counter (PC) register for the computer. I also blinded myself with some FREAKISHLY bright green LEDs that I hooked up for debugging. Holy crap, I can still see the after images even now. Yeah, so, I'll be putting bigger resistors on those suckers tomorrow.
The program counter is composed of two banks of 4-bits, giving it an 8-bit capacity. My previous breadboard computer could only count to F, this one can count to FF... progress!
There are three ICs that compose this module, configured similarly to the registers A and B. There are two 74hc163 presettable synchronous 4-bit binary counters; synchronous reset and one of the 74hc245 octal bus transceiver; 3-state. Since the binary counters don't have 3-state output, we can't wire them directly to the bus. The input and output pins of the counters are wired to the A side of the transceiver. The B side will be connected to the bus. The transceiver can be set to inload from or exload to the bus or be in the high impedance (invisible) state which effectively disconnects the module from the bus.
The wiring of the counters is straightforward. I've labeled the various control leads so I don't forget what does what. It's easy to read a datasheet, get everything working, and then five minutes later have no idea which pin does what operation. I've labeled these using the terms that Malvino uses in his discussion of the SAP-2.
The 74hc163 has a carry out bit called the "terminal count output" which is wired to the enable of the second set of 4 bits. This will cause the high order bits to count when the low bits have a carry. In the implementation below, the left chip handles the low 4 bits and the right chip is the high 4 bits.
BLINDED BY SCIENCE
I don't really want to have blinkenlights all over this computer. I got over that with my first breadboard computer and my hope is that this one operates at a high enough speed that the lights wouldn't offer any useful information or entertainment. I did wire up an array of tiny LEDs to the counter to test it out.
This isn't very smart on my part because i have light sensitive eyes and a nasty case of visual snow. Acute retinal damage aside, when I hooked up the CLK signal to a mono-stable switch I was able to click through all 256 states. The LEDs are wired up backwards, but they work.
After that test, I plugged a 14.31818 Mhz clock crystal into the CLK signal and let her rip. That's why all the lights are on in the picture above. It's blinking really, really fast.
The most significant bit is ticking away at a cool 55.5 kHz, which I captured on the oscilloscope.
Is there anything better than getting a package from Digikey? Tomorrow I should receive a bunch of basic 74HC gates (NAND, AND, OR, XOR) which I'll use when working on the ALU. So, it's tempting to shift right away over onto that part of the project.
However, I also need to build a second 8-bit counter to act as the subroutine counter register and it will have the same design as the module above. That'll just be busy work, since I know the program counter works, but it needs to get done.
Another work item for the near term is to go ahead and produce a few other utility registers, like the index register, that the machine will use and which don't involve much complexity. I figure the more of the simple modules I have finished and on hand, the more I have to work with when I begin to piece together the control and memory logic.
I also need to figure out what I want to call this computer project.
Last night I built and tested registers A & B. This is an easy place to start as these registers are only two chips each.
One of my goals for this project is to avoid using Ben Eater's schematics. When I built my first computer, I followed his videos and made sure I understood each step - but knowledge can fade without regular use. By forcing myself to make my own schematics and do my own planning, I can find the places where my understanding is weak. I'll make more mistakes and that will force me to improve.
The brain of each register is a 74hc574. The 574 is the mil-spec version of the 374 octal d-type flip-flop; positive edge-trigger; 3-state. I ordered the 574's from Digikey on accident. The 374 would have been just fine.
Since this is a 3-state IC, it can provide outputs as high, low, or high-impedance. I think of that third state as invisible. When it's in that state, it's like those output pins aren't there. This would let me tie the inputs and outputs of the 574 directly to the bus, but I can't in this case because I also need these registers to be tied to the ALU. The ALU wants the A and B registers to always be on, but the bus only wants to see them when the correct control flags are set.
To solve this, I'm using a 74hc245 octal bus transceiver; 3-state. This chip gives us a directional control to determine if we're reading data from the bus or writing data to the bus. It also gives us a high-impedance setting so we can effectively disconnect the register from the bus when it's not active.
The d-flip-flop's output will always be on and it will drive the 245 and the ALU. The control logic for the register will set the appropriate state on the 245.
We can wire both the output and input of the d-flip-flop to one side of the transceiver (blue wires). The other side of the transceiver connects to the bus (green wires).
The yellow wires are control bits. From left to right: transceiver direction, transceiver enable, and register enable. The white wire is the clock. The yellow and white jumpers are temporary. Those lines will be connected to the control bus later.
When I built my first breadboard computer I duplicated Ben Eater's wiring style and layout. All of the lines were cut short and flat to the board. This time I'm favoring lines that arc above the board in more direct paths. My hope is this ends up cleaner, with connections that are easier to trace and fewer kinks because of less need to crimp right angles. I got this idea looking at the Vulcan-74 (https://www.atomiczombie.com/vulcan-74/). We'll see if this scales.
I ended the night by wiring the registers to a temporary bus (green wires) and testing the read and write functions.