VGA controller using FIFO memory, discrete ICs and Arduino Uno/Mega


I love the Arduino boards. They're super easy to use and give me access to fairly powerful microcontrollers for all of about $12. Unfortunately, 16MHz isn't quite fast enough to display more than 120×60, 2-bit colour VGA (which is what I have right now on my Uno, using the VGAX library). Even trying to squeeze out 3- or 4-bit colour requires a resolution drop.

I'm making an Arduino-powered games console for the fun of it (I love my Mega Drive and thought it might be fun to see what I could achieve with Arduino). My plan WAS to have an Arduino Mega2560 running the game code and storing all the sprites in its 8KB of RAM, loading and unloading as necessary to/from an SD card or other external storage. The Mega would then send the 2-bit pixel data, 4 pixels at a time, via an 8-bit parallel bus (literally 8 jumper wires with two "acknowledge" lines) to an Arduino Uno, which would act as the display unit and use 95%+ of its processing power just spewing pixels out to the monitor/TV. That was fine, and worked well… but 2-bit colour just isn't quite enough to reach Mega Drive-era games, and is pretty hard to work with. So I decided to try to invent some sort of custom VGA controller…

Ideally, what I would like to do would be have a small SRAM chip (64KB would be HEAPS, even 8KB would be enough, though 16 would be better), which the Arduino talks to. The Arduino would create an array (i.e. the front buffer) in the first section of RAM, then just update the pixels as fast as it can. Then a custom circuit on the other side of the RAM, clocked at VGA-suitable speeds (~25MHz?) would read the pixels from the RAM (using some sort of counter to move through the array?). That way, even if the Arduino couldn't keep up, the VGA controller wouldn't be left without pixel data – rather, it would just be the pixels from the last frame. So you'd get tearing but that's not a major issue.

There's probably numerous reasons that would be hard to do. Different clock speeds, for a start. I don't really know how SRAM works but as I understand it having two different clock speeds trying to write and read at the same time might be bad. So…

I came across this post. In it they mention FIFO memory. Bingo! I didn't even know this was a thing! They refer to the SN74ALVC7804, a 512×18-bit FIFO memory chip. It sounds perfect. It would appear that the chip doesn't care if the clock is consistent or not, or whether the input and output clocks differ, as long as they don't go above 40MHz. It has pins to show when it's nearly full (and when it's full). It's 18-bit, meaning I can spew 3 pixels of 6-bit colour (nice and neat – a 2-bit resistor DAC on each colour wire, giving me 64 colours to play with) into the buffer so as to keep up with the VGA controller on the other side as it reads each of the 6 bits of data… and this is where I'm stuck.

So here is my real question: What would you suggest? I could always just buy an Arduino Zero/Due and be done with it, but that's no fun (and very expensive in Australia). I want some challenge, just so long as it's not physically impossible (i.e. trying to extract 120×60 4-bit colour from an Uno). I would need some way of switching between the first, second and third set of 6 bits from the FIFO. I'd also need some way of actually timing the pixel outputs (probably the hardest part, now that I think about it – not so much the timing itself but rather counting out the sync pulses, front/back porches, etc.). I'm not looking for someone to give me a straight-up BOM, just point me in the direction.

I love electrical engineering, but I don't have much experience with it. Plus I have no clue what kind of ICs you can buy to achieve this kind of thing. And finally, even though I'm a programmer first and EE second, FPGA programming just melts my brain.

Best Answer

In effect, you're trying to recreate a color CRT controller with memory interface. This is perfectly possible, but it's much more involved than you realize. The physical implementation can be either an FPGA, as alex forencich suggests, or discrete chips. The discrete section will need something like the 74FCT series for horizontal timing, and can easily get by with 74HC for vertical timing.

First, as you realize, you'll have to generate display timing at 25 MHz - except that you won't. A 25 MHz VGA pixel clock implies that you're trying for 640 x 480 pixels, and this cannot be stored in a 64 kB RAM - it would require a 512 kB RAM. Instead, a 64 kB RAM will only support a 256 x 256 display, and this will only require a pixel clock of about 12.5 MHz. This is straightforward using a 9-bit binary synchronous counter, and can be realized with 3 74FCT161 counters. The vertical timing also uses a 9-bit counter, but 74HC161s can be used, since vertical timing is much slower than horizontal. The outputs of the two counters feed at least one static RAM, and there are at least 3 different approaches you can use for the interface.

1) FIFO - This is your first thought, but it's more complicated than you think. First, it only makes sense to transfer one byte (or 6 bits) of intensity data at a time, but you also have to store the address as well as the data. If you're going with a 64kB RAM, this means 16 bits of address along with 6 to 8 bits of intensity, and you'll need more than one FIFO. This in turn means that you'll need to ensure that the FIFOs remain synchronized. You'll also need to provide a mechanism to monitor the FIFO empty line and generate a write pulse to video RAM whenever the FIFO is not empty: that is, whenever there is data in the FIFO waiting to be written. Furthermore, you'll also need to provide a mechanism to keep memory writes from interfering with display reads. You can do this either by running the video RAM at 25 MHz, but alternating read and write cycles, or by permitting writes to RAM only during the non-display portions of the scan. This will occur during front porch, back porch, sync, or vertical blanking intervals.

2) Dual port RAM - Here's another device technology to look at. In this case you use the DPRAM as the video buffer, and feed one side from the video controller and the other from the Arduino. Be forewarned, a 64k x 8 DPRAM requires a package with a lot of pins.

3) Bank Switching - In this technique, you provide 2 video RAMs, and at any time one is being written to while the other is being read from. The state of the RAM is controlled by a flip-flop which can be triggered by the Arduino. So first (let's say) you read from bank A while writing to bank B. When you've completed writing a complete frame, you toggle the bank selector and the video is now read from B while A is being written to. This is in some ways more straightforward than the other two, but it does not permit local overwriting of areas of the image in the same way the other two approaches do.