System/1 Build Log
An ongoing chronology of System/1's construction, and any other related musings.
16th November, 2014 - Prototyping the memory interface
This week, I have been mostly tinkering with System/1's memory arrangements. The plan is to have a 32-bit wide data bus, with RAM and ROM arranged as sets of four 8-bit wide chips, and then arrange the memory data register (MDR) such that any of the four bytes can optionally be read out independently. In addition, providing four write strobes instead of one should allow bytes to be written individually too; to simplify the address decoding logic for devices on the memory bus, the four strobes will be generated at the CPU end instead of providing a 'word/byte' signal on the bus.
First things first, though; I wanted to make sure I understand the timing requirements of the RAM chips I'm using, in case I've made a mistake that has consequences for the clock generator design. Wiring up a single RAM chip to a 74HC299 shift register (the MDR obviously needs to be both parallel-in-serial-out and serial-in-parallel-out, depending on data direction) and a 74HC164 to act as memory address register (MAR — this can be a plain serial-in-parallel-out device, since we never need to read from the address bus), and then using a little glue logic to control the '299's mode pins and the CE, OE and WE signals for the RAM, I can perform simple reads and writes via test code on the mbed quite happily.
However, I want to try running the glue logic at a more realistic speed, so I reconstruct a few salient sections of the clock generator circuit — the oscillator, clock gating and a cut-down clock phase counter — and then tweak my test code to perform operations on the MAR and MDR based on the mbed's (slow) clock but run bus transactions based on the 4MHz oscillator instead. Everything still works properly, so it doesn't look like I have any problem with the timing requirements (at least, not at a 4MHz system clock.)
The next step was to extend the prototype to accommodate a 16-bit data bus, with support for reading and writing full words or individual bytes. Unfortunately I ran out of breadboarding supplies part-way through — I prefer the ready-made flexible jumper wires for long-distance signals, rather than cutting solid-core wire to fit — so there was a slight delay whilst I ordered a bundle of additional wires from eBay, along with a set formed from ribbon cable to keep the buses together neatly. Then there was another slight delay whilst I ordered a second set of the latter type, having ordered female-to-male versions by accident initially. Once the correct cables arrived, extending the logic to support the extra features seemed to be quite straightforward...
... until I started getting garbage results when reading bytes. Writing bytes was fine, and reading or writing words worked perfectly, but reading bytes was just plain broken. Checking the bus signals with the logic analyser didn't show anything amiss during the memory transaction, but watching them as the data was read out of the MDR showed the obvious issue — when reading a single byte, a one-of-four multiplexer is used to select the lowest bit of each '299 as the output. This multiplexer is driven from the bottom two bits of the MAR, to select the appropriate bit based on address, but of course the MAR (being composed of '164s and wired to the system's bit clock) was also shifting its contents out at the same time! This meant the byte being selected from the MDR would change during the serial transfer, based on the bit pattern of the address. Oops.
One way to fix this would be to gate the clock to the MAR or add a latch to hold the bottom two bits steady during the readout cycle, but it seemed like that might be inviting trouble; the slightest glitch in the logic that determines load/hold could result in an accidental change of address. However, since I'm already using '299s for the MDR, and I have some spares that were intended for the front panel, I figured I might as well use those instead — they can be switched between 'shift in' and 'hold' with one pin, which conveniently maps directly to the LOAD_MAR signal that will already be required to load the address in the first place. Handy.
Once that change was made, everything looked good — byte and word reads and writes across a 16-bit bus all worked, and adding the optional sign extension on byte reads proved straightforward too. Hopefully I'll find time during the coming week to draw up a proper schematic including the various little tweaks I had to make to the original plans (the MDR is actually the oldest set of pages in my current notebook!), then I can look at building the full 32-bit version on a proper board.