Category: Veronica

Veronica – CPU Board

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):

In the upper left, you can see the upgraded single-step clock. Below that is the fast clock. Center left is the connectors for the debugging controls. Lower left is the reset circuit. At bottom is the decoupling capacitors for all the ICs. Above those are some buffers for control signals that will be going long distances down the backplane. I'm using double-inverters for this just because I had extras on the board. In the center is the 65C02, with all the unused control signals pulled high just to be safe. On the right are the tri-state buffers to control the CPU's access to the backplane. There's an error here, which I'll get to later. That lone inverter is the "Bus Master" signal, which is how the backplane tells the CPU it can use the system bus. It sends that signal into the tri-state buffers to enable them.

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):

I definitely got bold here with my trace sizes and complexity. This was going to be by far the most intricate board I've attempted to etch myself. However, the alternative was dozens of jumpers, so I was determined to make this work. Over the past few projects, I've started to pick up the art of hand-layout of PCBs. It's a lot of fun, and a lot of puzzle solving. You can really outperform the autorouter if you put your head in the game. One headache I couldn't solve was that the address lines on the top pins of the 6502 are in reverse order from how the tri-state buffer wants them. I ended up with a mess of twisted jumpers here. If anyone knows a better solution, let me know!

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:

The traces are thin (0.01"/0.254mm), but the gaps between them are larger (typically 0.024"/0.6mm minimum), and that has proven within my realm of home-etching. If I get closer than 0.024" between elements, I start seeing occasional little bridges which have to be cleaned up with a knife.

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:

Extreme Closeup! The white fringe you see around the holes is a bit of fiberglass cruft left over from the drilling. There's good copper under it, which means plenty of solder surface despite the smaller pads. Hooray! You can also see here why the "more-clearance-smaller-traces" method works. The traces themselves are sort of "blurry" (that's not just the photo). What I think is happening is that I'm approaching the resolution limits of my acetate printing method, and my contact method of masking for photo-developing the resist layer. Increasing clearances allows for this "blurriness", and the traces can afford to be thinner because they end up etching a bit chunkier than they appeared in the mask.

So let’s get to some assembly!

I ended up with rather more jumpers than I'd like. I'm seriously considering trying my hand at double-sided PCB etching. Also note the clearance notches in the PCB for the ends of the ISA connector.

All ready for ICs! Well, mostly. It turns out there are three errors on this board- one minor, one moderate, and one major. Keep reading to find out more!

Looking good, and ready to start testing! Three errors; do you seem them?

Just for giggles, I marked out the major subsections here. You can see the debug control connections, the buffers for the control signals and system bus, the reset circuit, and the clock (fast, and single-step)

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.

Yup.. still got 5 volts and no apparent shorts or other problems. So far so good.

 

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:

For those of you who don't speak Veronica, this translates to a bus arbitration scheme of OMG EVERYBODY GO RIGHT NOW. That should do nicely until I have, you know, more than one thing 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.

A chip puller and eight little yellow jumpers later, and Veronica could talk to her data bus again. Woot!

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.

Here's the debugged version of our girl, installed and ready to run some NOPs. I hacked up a little panel for the Run/Stop, Single Step, and Reset controls. I haven't decided what to do for a case yet, so I'm leaving this kind of stuff temporary for now.

Looking good back here, too. If you look closely, you can see the bus arbitration connector (entirely shorted to Vcc) below the power connectors. All your slots are belong to us!

Check out that user friendly interface! Take THAT, Xerox PARC! I see your GUI, and raise you two tactile buttons and a switch salvaged from an old boom box! The controls are even labelled. Truly, this is the Computer for the Rest of Us.

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!

Veronica