A real copper home for our girl.
Now that we have Veronica’s backplane mostly sorted, we have everything we need to move the 6502 itself off the breadboard and into “the real world”. No more training wheels! *sniff* I just hope she holds her balance when I let go of the seat…
First things first, my old clock circuit had some flaws. For the single step clock, it was a simple 555 timer configured monostable, and triggered on the rising edge of a pushbutton. The problem is, after reading the 65C02 datasheet more carefully, I learned that the clock input must be held high between pulses, or it may lose internal register state. We can’t have that! The simplest solution I could think of was simply to invert the single-step pulse with a single transistor. The CPU will still get the rising edge that it cares about, and the clock line will be held high at all other times. I tested this change on my breadboard, and it seems to work, so into the design it goes.
There’s one catch with this. My old clock circuit shared a ground between the crystal oscillator and the 555 timer, and used a SPDT switch to control Vcc and thus select the desired clock. However, when using a transistor to invert the clock, I now get a conflict between the single step clock (when activated) and the TTL oscillator (when deactivated). There’s a messy interaction between the two circuits via the shared ground path that causes the crystal to get sufficient potential to generate pulses, even with its Vcc disconnected. The simplest way I found to solve this was to go to a DPDT switch, so I could switch both Vcc and Ground on the two clocks. Seems to be working! I suspect that, moving forward, I won’t be using the single-step clock much anyway. It’s just neat to play with.
So, with all that in mind, here’s what I ended up with on the CPU Board (and here’s the Eagle file):
This seems like a good time to explain my bus design. Let’s call it VeroniBus (thanks for the idea, Jac Goudsmit 🙂 ). It has 31 pins, because that’s what I can get out of surplus ISA connectors using single-sided PCBs. The signal lines are:
0-15: Address 16-23: Data 24: System Clock 25: Power (+5V) 26: Memory Read/Write 27: Interrupt Request 28: No Connection (future expansion) 29: Bus Master 30: Ground (0V)
Obviously, there’s a lot of signals I would liked to include on there, but with only 31 pins, I had to make some tough choices. I opted to leave out things like non-maskable interrupts and DMA-control signals. Also, with only a single bus control pin, my bus arbitration scheme will need to be very primitive. Later on, I may try my hand at double-sided PCB etching, which would double my signal lines. This would require me to rebuild the backplane board, though. As mentioned earlier, I didn’t have good luck etching traces precisely enough to enable use of both sides of the ISA connectors. With such limited control signals, Veronica will interact with peripherals using a lot of the same tricks that 1980s computers used. Things like bank-switching, soft-switch registers, and memory-mapping will all come into play.
Okay, enough gum flapping and chalkboard scratching. Let’s get our hands dirty! Here’s the PCB that I generated (and the Eagle file):
That looks nice onscreen, but do I have any real hope of etching it? Well, I learned some lessons with the backplane, and I’ve found that the most important variable is the distance between traces and pads, rather than the dimensions of pads and traces themselves. You can home-etch very small traces, but copper in small spaces tends to get left behind. So by shrinking my pads and traces to increase their clearances, I was able to make this:
There is a price to pay for this “increased clearance” approach, however. It means shrinking my pads to make room between them for traces. I normally use a pad radii restring of 50% in Eagle, which makes the pads juicy and plump. However, to get 0.024″ clearance on traces, I needed to go down to 25%. That means drilling the holes gets trickier. To deal with this, I used a combination of smaller drill bits, and taking more care. Using smaller bits only works to a point, because on large groups of pins (such as the 40-pin IC socket for the CPU), precision gets quite important if you want the device to actually fit. Accumulated error over a long run of evenly-spaced pins can really bite you if they aren’t all drilled in exactly the right place. The easiest way to compensate is to drill larger holes, which gives you more margin for error getting all the pins to line up. I’ve found that, if I take my time with the drilling, I can use a #71 bit for the large groups (IC sockets, long headers, etc), and a #69 bit for everything else. With a bit of practice, I had no trouble drilling out the smaller pads without destroying them. I also started using a head-mounted magnifier with a light on it. That did wonders for my accuracy on drilling. Here’s a sample, zoomed in way past the abilities of my camera:
So let’s get to some assembly!
Now it’s time to start testing. First of all, since it’s been a while since I built the backplane, I figured I’d better double-check the power supply. After spending so many hours on this CPU board, it would be a shame to fry it.
The next thing I’ll need is some kind of bus arbitration. As you may recall, each slot on the VeroniBus has a Bus Master pin which gives permission to that slot to drive the system bus. These signals are collected at the left end in a connector that will someday be connected to a bus-arbitration circuit of some sort. In the meantime, I made this, which connects the bus arbitration header to the Vcc pin on the bus:
Now that Veronica can access the bus, it was time to start testing! I connected HexOut to the address lines on the bus using the breakout pins I added to each end. Then I used my hex input panel to put $EA on the data bus. This would perform the same Free Run that I did previously on the breadboard.
Then I turned on the power to see what happens! Well, sort of nothing, as it turned out. Using the fast clock, the CPU was clearly running, but not doing a proper free-run. The single-step clock didn’t work at all. I busted out the meters, scopes, and probes (oh my) and started tracing the single-step clock back from the CPU. There were pulses coming out of the 555, but they weren’t reaching the CPU. That made me suspect the inverting transistor, and sure enough, I had managed to solder it in backwards. Oops!
Now the single step clock was working, but the CPU was getting gibberish on the data bus, instead of the expected $EA. Why? Well, after much tracing of signals and mucking about, I found I had made a real rookie digital design mistake. See, the CPU’s data bus pins are tri-state buffered internally. It knows not to try to drive the data bus unless it is writing data to memory. In the case of a free run, it will be trying to read data, and thus its pins will be in a high-impedence input state. I tried to be too clever in my design, and I put my own tri-state buffers on all the address lines and the data lines. I thought, “why not cut the CPU off from the bus entirely when it isn’t its turn?”. Well, because the data pins are bi-directional. Guess what happens when you put a one-way buffer on a bi-directional pin, then reverse the direction of that pin? You get random garbage on it, that’s what. Oops.
Since the data buffer was pretty much superfluous anyway, the quickest fix was to remove it.
The real lesson learned here isn’t that I suck at digital design (though that’s also true). The root mistake I made was altering the design on the PCB from what I had implemented on the breadboard. While developing this on the breadboard, I simply turned off power to the CPU when I didn’t want it using the bus (such as when programming the ROM emulator). As I was building the real CPU board, I thought, “oh, I should toss buffers on all these”. Had I made this change on the breadboard first, I’d have found this problem immediately and could have saved a lot of time and trouble. I probably lost a few hours to this problem, if you count time spent debugging the board, and time spent implementing the schematic and laying out the PCB incorrectly. Furthermore, without that extra buffer on the board, I would have had more room for layout, and could have probably done a cleaner job. So, in short:
Test what you’ll build, and build what you tested.
You can quote me on that.
While fumbling around with the PCB, I did notice one other little problem. The hex-inverter chip that supplies buffering and inversions for all the control signals was missing its decoupling cap. The board was set up for it, I just neglected to solder it in place. So, one tiny problem, one moderate one, and one big one. Not bad for a board that is easily the most complex I’ve ever built.
Alright, enough faffing about. Can it run code, already? You bet! Once again, I’ve put HexOut on the address lines, and my hex input panel on the data lines, all using the breakout headers at the ends of the system bus. The hex panel is driving the data bus with $EA, so this will be the same free run test I’ve been using all along. It’s much cooler on real hardware, though! At least, I think so. Maybe that’s because my hand still hurts from the Dremel.
That’s all for now. Next we need to get some ROM in there, and for that I need to redesign my ROM Emulator. My little deadstart-panel method for entering code is going to get old really fast, but EEPROM burners are stupidly expensive. However, I have a plan, and I have 2/3rds of my breadboards back, so…. stay tuned!