I talked about design last week, so this post is going to be all about implementation and debugging. During the very productive weekend before Christmas – when I had all my shopping done and didn’t have to be anywhere until Monday – I was able to add the IO, RAM, and address decoding logic to my Project:65 breadboard computer. It was a complicated and sometimes frustrating task, but in the end the results were worth it.
I’m going to try to describe the process as best I can, but a few things are working against me. I’m writing this after the fact, for one. Also, since I was kind of burnt out from work and the pre-holiday rush, and on vacation, and in a hurry to get something working before I went home for the holidays, I wasn’t as systematic or as organized as I should have been. My note keeping wasn’t very good, and sometimes I got downright careless.
There are some mistakes that I shouldn’t have to tell you to avoid – but then again, I shouldn’t have made them in the first place. For example: Do not plug your power supply into the breadboard backwards. This could lead you to asking diagnostic questions such as: “Why is this not working?”; “Why is the power light flickering like that?”; and, most alarmingly, “What is that smell?” For similar reasons, you should not ever put your EEPROM in the programmer the wrong way around. I managed to cut the power in both these circumstances before any lasting damage happened, but I was lucky. Being careful is better than being lucky so, to paraphrase the old carpenter’s adage, let’s “check the polarity twice, and let the magic blue smoke out zero times”.
Address decoding. Take one!
With the stupid mistakes (okay, some of the stupid mistakes) out of the way, let’s talk implementation. I started out by attaching another breadboard to the side of the one that had my CPU and EEPROM on it, and putting down my CY7C199 SRAM and 65c22 VIA chips. Hooking up the data and address busses was conceptually straightforward, but quickly led to a complex network of wires that covered a lot of the breadboard. I made a point of going back and double-checking that all the connections were correct before moving on.
I left a section of free space at the bottom of the second board for the address decoder. The implementation used a couple of 74xx logic chips. I haven’t talked about 7400-series logic much in this blog, aside from playing with 74HC595 shift registers a while back. Someday I may go back and build a full adder or something and get into the details a bit more, but 7400-series chips provide a lot of basic operations that can form the glue logic for a project like this.
I started with two chips: a 74LS138 decoder and a 74HC04 inverter. The ‘138 is actually designed for tasks like memory decoding. It has 3 inputs and 8 outputs, and for every combination of inputs, exactly one of the output lines will go low. The idea is that you hook 3 address lines to the inputs, and then each output can be used as the chip enable for a different block of memory. It divides my 64 kB address space into 8 chunks of 8 kB each. I used the ‘138 to handle the chip enables lines for the EEPROM and the VIA. It provides the same logic as the diagram I created last week, but without as much fuss.
My 32 kB SRAM fills the entire bottom half of memory, so instead of going through the ‘138, we can just say the chip is enabled when address line A15 is low. Following my diagram from last week, I connected A15 to the SRAM Output Enable and connected the CPU’s RWB (read/write) line to the Write Enable.
The 74hc04 contains a bunch of independent inverters. They do a NOT operation – the output is the opposite of the input. I used that to feed the SRAM’s Chip Enable line with the inverse of the CPU’s phi2 clock signal. The idea was that we should only write to the SRAM chip when the RWB signal is low and the clock signal is high. That all made sense to me at the time, although it turns out I was forgetting something important.
My first test program adapted one of my early C64 user port experiments. I wrote a simple loop that would count from 0 to 255 and output that value to one of the 65c22’s parallel ports. I hooked up LEDs to the pins of the parallel port so I’d be able to see the output. For the first try, I used the 6502’s registers to keep track of the value and to count out the delay loops. This would test the CPU, EEPROM, and the 65c22 VIA. The SRAM, while connected to the circuit, wasn’t used yet.
Surprisingly, this was an almost immediate success. You can look at the video at the top of this post to see what it looked like, although that was taken from a subsequent test.
Address decoding. Take Two!
To test the SRAM, I changed the program to store all its values in RAM instead of the CPU registers. I was feeling pretty confident, and I remember being surprised when it didn’t work. Unfortunately, my notes are vague on what “didn’t work” looked like, and I don’t remember if I got random output or nothing at all at that stage.
The hardest part of debugging this situation was that I didn’t really have a good way of figuring out what was going on. I didn’t have tools like an oscilloscope or logic analyzer to measure what was happening; all I had to go by was the LEDs and my own reasoning.
My first hunch was to look at the timing – both of the memory itself, and the propagation delay of signals going through my address decoder. I’d been running the circuit with a 4 MHz oscillator, so I replaced that with a 1 MHz clock. Unfortunately, I didn’t see any change.
The problem proved to be in the design of the control logic for the SRAM. I’d focused on making sure I was handling the CPU’s RWB and clock signals correctly, but I’d screwed up the address handling. There were actually two issues. The SRAM’s Output Enable was enabled when the CPU was trying to write to the chip (the SRAM’s datasheet is vague about what effect this would have). Worse, the SRAM’s Write Enable (/WE) and Chip Enable (/CE) were completely independent of the address decoding. Therefore, a write operation meant for the VIA would also write data to the equivalent memory locations in the SRAM.
I swapped the 74HC04 inverter for a 74HC00 NAND gate (NAND = “not and” – the output is usually high, and only goes low when both inputs are high). I connected address line A15 directly to the SRAM’s /CE input and inverted the CPU’s RWB output to feed the SRAM /OE. I generated a new signal for /WE from the clock input and the inverted RWB, as shown in the diagram.
Things were looking up. My LEDs started blinking the way they were supposed to, and things were looking up. It was at this point that I recorded the video at the top of the post. I didn’t know it, but my day’s debugging wasn’t finished.
The Great Reset Problem
A few times during testing, I’d had a problem where the CPU didn’t seem to reset correctly on startup, and I had to hit the reset button to get things going. Once I had a system that seemed to be working, it became clear that this was a significant problem. Sometimes it would start up just fine, and sometimes even hitting reset repeatedly wouldn’t do the trick. I looked for loose connections and even replaced the button, to no avail.
I’d adopted the reset circuit used in Quinn Dunkie’s Veronica design, which uses a simple resistor-capacitor setup attached to the switch. The capacitor should hold the reset line low for a few milliseconds while the system powers up, automagically resetting the CPU (and VIA). It can also help “debounce” the reset switch.
Something about that wasn’t working for me, and taking out the capacitor seems to have fixed the problem (the resistor stays in place to pull up the Reset line). Now I have to manually reset the system every time I turn it on, but the reset always works.
I’m still not sure why I’m having this problem, and I’ll probably have to go back and experiment some more now that the system is more stable. One possibility I’ve considered is a race condition: The reset signal goes to both the CPU and the 65c22 VIA. If the VIA takes longer to reset than the CPU, it might miss the first few commands the CPU sends it and never get its outputs initialized properly. Well, maybe. Another guess is that the 6522 doesn’t like the gradual voltage change it sees because of the RC circuit. But with the afternoon wearing on, I was soon to be confronted by the Two Hours of Dreadful Instability.
The Two Hours of Dreadful Instability
I’d reached a point in my construction where things worked – mostly. But not always, and not for long enough to do much real computation. My blinking lights demo worked – except that once in a while the delays would seem to get dropped and it would speed up or slow down (though I never noticed it getting the pattern wrong). Something was not quite right.
I wrote a very simple memory test routine that’d blink certain LEDs to indicate success or failure. It always indicated success – except occasionally when all the lights would go out, which was impossible to explain from the software. I was getting confused and frustrated.
The problems only happened when I used the SRAM to store data, and since I’d already messed it up once my suspicions fell on the address decoder. I went back to some of the web sites I’d been researching to see if I’d missed anything. The second iteration of my address decoder was similar to the one Garth Wilson shows in his articles, so with nothing to lose I pulled out the 74LS138, added another 74HC00, and wired it up like he demonstrated. While I was at it, I also checked that all my wires were secure and reseated all the chips.
In the course of this, I also noticed a stupid mistake that I’d made. I had a 100 μF capacitor across the power supply, which was meant to smooth out any irregularities in the supply or deal with spikes in demand from the ICs. I’d taken it off the breadboard so that I could get to the reset button more easily when I was debugging it, and never put the cap back in. I replaced it and, figuring there’d be no harm, put a couple other capacitors on the breadboard (in addition to the 0.1 μF decoupling capacitors I already had on each chip).
After a considerable amount of trial and error, I was final rewarded with a computer that seemed to be running correctly and reliably. I even swapped the 4 MHz oscillator back in, and it kept chugging along without a hitch. With the benefit of some subsequent testing, I think the problem was mainly the missing smoothing capacitor, though it may have been exacerbated by a loosely-seated SRAM chip. I’ve since gone back to my “Take Two” address decoder, which seems to work just fine, but taking out the smoothing capacitors definitely causes some subtle stability problems.
That’s a good and satisfying place to stop for now. Since then, I’ve been working on input and output – about which I’ll have a lot to say next week.