Adding RAM

A project log for Big Daddy 68k

Making a games console from the ground-up

SimonHSimonH 03/13/2017 at 13:340 Comments

The last log entry showed a CPU connected to an Arduino, where the Arduino would act as both RAM and I/O, all running via a memory-mapped I/O interface. The address bus was completely connected to the Arduino's inputs. The software running on the microcontroller would then decode the address bus to figure out the intent of the load/store operation. This is great because it allows us to do anything we want from an I/O perspective but directing all traditional memory load/store operations through that route is slow - each bus cycle would take hundreds of 68k cycles.

So let's add a real RAM chip to our system. Parallel static RAMs - the type used here - typically have a simple pin interface. Parallel ROMs are similar too.

RAMs are organised into words and the address bus selects which word is read out on the data bus. The data bus has the same width as the word size. As the 68008 has an 8-bit data bus I'm going to use a RAM with an 8-bit word size. The address bus has the same width as the number of words (in binary bits).

Picking a RAM

I'm going to use an Alliance AS6C4008-55PCN. This is a 5v DIP static RAM with a word size of 8 bits and has 512k this is a 512 kilobyte RAM. Remember that the 68008 has a 1 megabyte address space - it can't trivially address larger than 1 MB without resorting to funny tricks, so this means the RAM can take up to half of my address space.

This part has a ~55 ns max read cycle. This means that once the "ok, go" pin is asserted the operation will complete in ~55 ns. We're getting ahead of ourselves here a bit but this is fast enough for this CPU at the clock speeds we want to run at (at 4 MHz each clock cycle is 250 ns long and it takes four clock cycles to do a whole bus cycle...of which the RAM has roughly two clock cycles to do its thing. So ~9x more time than we require)

Connecting the RAM

This RAM has three interesting control signals: chip enable (/CE), output enable (/OE) and write enable (/WE). Here's the truth table and the waveform timing diagram.

If we only want to connect the RAM directly to the CPU then this is easy.

As we have no other device in the system - it's just the RAM and CPU, and no Arduino - and the RAM has an access time faster than what the CPU requires, we can just tie /DTACK to ground, constantly asserting the signal. This will mean that when that the CPU will never insert any wait states for a memory operation. As mentioned earlier, The CPU gives a ~2 clock cycle window for the memory to do its thing and at 4 MHz that's about 500 ns. Therefore if the RAM were ~9x slower we would need to introduce wait states. So keeping /DTACK asserted is good enough for now.

The memory map

The CPU has a 1 MB address space and memory operations present the address associated with a bus transfer onto the 20 pins of the address bus. As we have not wired the top pin to anything it is ignored by the system - reading/writing to address '1' will present the same address to the RAM as address '524289' (that's 10000000000000000001 in binary). Reading from any address 0<= addr <524288 will return the same value as 524288<= addr < 1048576.

We have mapped the 512 KB RAM block into two locations in our 1 MB address space - the low 512 KB and then the high 512 KB. To be explicit,

What next?

So our CPU has RAM, and RAM as fast as it can run. But when we power on the system the contents of that RAM is undefined. We have no program to run and there's also no I/O (so why even bother run the program?). When the CPU was connected to the Arduino we had all of those thing (but slow RAM). Next time we need to connect all three together somehow.