Electronic – How to multiple ICs access shared RAM

ramsharedbus

I'm trying to build a game console from scratch (as an exercise, not necessarily for practicality).

What I want to do is to have multiple "CPUs", in this case one being the Main CPU and the other one controlling the controller ports. My idea is to use shared RAM, where the "Interface Controller" CPU is polling the controller ports and then writing the results to RAM, where the "Main" CPU can read it.

What I'm trying to figure out is how I would connect the RAM to the system – I doubt I can just connect the address/data pins of both CPUs to the same pin on the RAM chip directly, but I'm not familiar with what arbitration methods exist.

What is commonly done here? Is there some kind of "intermediary" chip that serves as a "switch" to select which CPU is connected to the RAM at any given time? Or maybe some kind of "buffer" that the Interface CPU writes into, and which is then somehow flushed into the RAM? Or is there a simple "Bus" that allows multiple components to access it? (I'm thinking of SPI here, with it's chip select/SS pin)

Assume a system that's using simple CPUs (e.g., Z80, 6502) and SRAM (e.g., Cypress CY62256), so nothing that supports sophisticated protocols out of the box. And if it helps, one CPU would only ever write and one only ever read.

Best Answer

In general: There are many ways and it's not trivial. You will need to design something yourself.

If the amount of data is very small (like controller inputs) you could use a register which stores the latest data. The input side of the register connects (somehow - you'll need to design some logic) to the interface CPU's memory bus, and the output side connects (somehow) to the main CPU's memory bus. Apart from this register, the two CPUs have entirely separate memory. Done. (It seems like a waste of a CPU though)

Most CPUs don't access the memory every single cycle, so you can make them take turns. When the main CPU wants to access memory, let it access the memory bus. When the interface CPU wants to access memory and the main CPU doesn't, let it access the memory bus. When the interface CPU wants to access memory and the main CPU does, pause the interface CPU. You'll still need to design some logic.

You could give each CPU its own memory bus, but design some logic (see the pattern?) to allow the main CPU to access some of the interface CPU's memory (or vice versa). You could pause the interface CPU whenever the main CPU accesses its memory (easier) or only when the main CPU tries to access the shared memory at the same time as the interface CPU (harder, but still doable).

In any case, the specific logic circuit can't be designed without knowing all the details of how the RAM and CPUs need to connect. "Assume we have a Z80 or 6502" is not enough information to actually design the circuit. We can only make approximations and draw block diagrams.

Here are some general tips:

  • You can pause a "retro" CPU by stopping its clock signal. (This is not always true for modern CPUs)
  • In order to connect/disconnect part of a bus, you want to search for a "bidirectional bus transceiver" chip, such as a 74xx245.
  • Feel free to do the above things as often as you want. If you had shared memory, you could disconnect it and reconnect it every single cycle, and the RAM won't care. (connecting it at the correct time is where the complexity is)
  • CPUs only access a specific address at a time. They don't care what other addresses are doing. Don't feel bad about disconnecting RAM from a CPU - as long as the RAM is there when the CPU actually accesses it, it won't care.
  • Dual port RAM, which has two busses, exists. I don't know how common dual port RAM chips are. It's pretty standard for FPGAs to have some dual port RAM inside them, so if you're building your console on an FPGA, this is a non-problem.
  • (thanks to Richard the Spacecat) Many CPUs are designed to share their bus already. With a Z80 you can use the /BUSREQ signal to ask the CPU to pause itself and electrically disconnect itself from the bus (so you don't need a bus transceiver). It runs for a few cycles first, and it tells you when it's disconnected by using the /BUSACK signal.