Following my previous column, Using an FPGA to Drive a VGA Display, someone commented as follows: "I'd like to know what comes next in the process of taking an image in RAM and spooling this to the RGB outputs. Some information on interfacing circuits would also be useful. I have looked at examples before, but as yet I've not seen anyone spend the time to explain it. For myself, and other people getting into FPGAs, I think this could help a lot."
I think this is a great idea. In my original article, we discussed how we could use two counters -- the pixel counter and the line counter -- to generate the "H_Sync" (horizontal sync) and "V_Sync" (vertical sync) signals that are used to synchronize the VGA display. Now, in this article, we will consider how to also generate some RGB (red, green, and blue) signals to create an image on the display.
My Spartan 3A development board.
The first step was for me to retrieve my trusty Spartan 3A development board, which I had loaned to a friend at work. Once I had this board back in my hands, I started to ponder my implementation. Originally, I had planned to generate a full-color image as described in Max's post Using FPGAs to Generate 24-Bit Color Images. Sadly, however, my development board does not contain proper digital-to-analog converters (DACs) that can be driven by 8-bit wide red, green, and blue signals generated by the FPGA. Instead, it uses only four bits to represent each color, and it employs a simple resistor network to convert these digital outputs into corresponding analog voltages.
This means the color palette of my Spartan board is limited to four bits for the red channel, four bits for the blue channel, and four bits for the green channel, which equates to 2^4 x 2^4 x 2^4 = 4,069 colors. Although this 12-bit color scheme is admittedly somewhat limited, as we shall see it can still provide excellent results.
The next problem is the amount of memory required to hold the image. Once again, I had originally planned on storing an 800 x 600 pixel image in a frame buffer on the FPGA as described in Max's article. Even with my limited color palette, however, just one frame would require 800 x 600 x 12-bits, which equals 5.76 megabits of RAM. This is more memory than is available in the FPGA on my development board.
As a "cheap-and-cheerful" alternative, I decided to generate a series of simple test patterns algorithmically. A high-level block diagram of my VGA test pattern generator is illustrated below:
High-level block diagram of my VGA test pattern generator.
First we have a "System Clock," which is used to synchronize all of the activities inside the FPGA. The "VGA Timing" module comprises the pixel and line counters we discussed in my original article. In addition to generating the "H_Sync" and "V_Sync" signals that are used to synchronize the VGA display itself, this module also generates a number of other signals that are used to control the "VGA Video" module.
The "Algorithmic Test Pattern Generator" module is used to generate a series of simple test patterns. The "VGA Video" module takes these test patterns and presents them to the outside world in the form of the three 4-bit RGB signals that are presented to the DACs (or resistor networks, in the case of my development board).
Actually, I should note that in my real-world implementation, the "Algorithmic Test Pattern Generator" and "VGA Video" modules are one and the same thing, but it's easier to think of them as being separate entities for the purposes of these discussions.
My implementation of this test pattern generator consumes only a small portion of the resources available on my Spartan FPGA. In fact, it requires just 96 slices out of the 5,888 slices that are available, which means it utilizes less than 2 percent of the chip's total resources.
To be honest, I'm glad that the limitations of my development board forced me to take this intermediate step -- that is, to create a test pattern generator. This is because a test pattern provides the simplest way to output images to prove that the backend display drivers are working correctly. Generating a test pattern (or a series of test patterns, in this case) is a good idea for a variety of reasons:
It allows the RGB color outputs to be verified to prove that they are functioning correctly. This can be achieved by displaying incremental bars where the color is gradually increased from 0 to its maximum value.
It allows the timing to be checked. Is the frame updating correctly? Are the borders correct? And so forth.
More advanced test patterns can be used to align the image with a camera viewfinder on systems that are used to capture real-world images.
As an aside, a famous television test pattern many people will recognize is the Indian Head Test Card. This was common in America until the early 1970s, at which time it was replaced by the SMTPE Color Bars.
If you wish to probe deeper into my design, click here to download a ZIP (compressed) version of my project file. As you will see, this design consists of one structural unit tying together two modules: the "VGA Timing" module and the "VGA Video" module (which includes the algorithmic test pattern generation code as noted above).
The "VGA Video" module outputs the RGB video signals during the active periods of the video display period, as can be seen in the results of the simulation shown in the following screenshot:
The results from my initial simulations.
Again, the values in the line and pixel counters in the "VGA Timing" module are used by the "VGA Video" module to determine positions on the screen and to decide when the RGB outputs need to be manipulated to achieve the desired result.
Once everything was up and running, I created a video that I posted on YouTube. This shows the output of my algorithmic test pattern generator as it cycles from pattern to pattern:
Now, as I mentioned earlier, the on-chip RAM in the FPGA on my development board is not large enough to hold even a single image. Thus, the next step will be to store one or more images in an external memory device on the development board as illustrated below:
Block diagram for a more sophisticated VGA implementation.
The external memory might be RAM or it might be Flash... I haven’t decided yet. The main thing is that we will have to design a "Memory Interface" module to read the image(s) out of the external memory. We will also have to design a FIFO (first-in, first-out) module to buffer our image data before it's passed to the "VGA Video" module. The main thing is that, if we do this, it is going to require some time and effort on my part -- so are you interested in my taking this to the next stage?
Very useful tutorial! I really look forward to reading the memory controller guide.
I have a Nexys 3 with a 100Mhz clock - here is some working VGA code along with clock division down to 25Mhz for 640x480: http://pastebin.com/TQ2nwref. I welcome patches related to improving the clock division code.
It is very true that compromise results in a win win situation as most project start off with a lot of nice to have along with we definately need and it is a fine balancing act to ensure that the nice to have's do not overly complicate the design or introduce to much risk
Indeed I often say that engineering is the art of compromise we the different disciplines involved in a project Systems, FPGA, Hardware, Software and even management all compromise until we are all equally unhappy....
Adam -- please do carry on with this series. I agree with the comment to your previous column that it's really hard to find in-depth descriptions as to how all of this (driving a VGA display) is actually implemented. Most articles I've seen explain things at a high-level of abstraction, but they don't explain what goes on "under the hood" -- it's the nitty-gritty details that set your articles apart from the crowd.
Adam -- this is another corker. I particularly like the way in which you explain what you were originally planning to do, and then how your plans evolved to address limitations with the kit you have at your disposal. This reflects real-life engineering challenges and solutions.
Here we discover how to use the XADC (Xilinx Analog-to-Digital Convertor) in the Zynq All Programmable SoC to read the chip's internal temperature and voltage parameters and output them over an RS-232 link.