FPGA what’s that? An FPGA (Field Programmable Gate Array) is an amazing little chip! Unlike a your average microchip, this little fellow does not execute code sequentially, but replicates hardware. In very simplistic terms, one can configure gates inside an FPGA to behave like any hardware you like. It can even be configured to act like a processor!That makes them incredibly powerful and amazing fun. Now the downside is that one requires hardware to make hardware, i.e. logic elements (LE) to make a circuit and let me tell you, an FPGA with many elements is very pricy. The two big cheeses Altera and Xilinx managed to cram many 10,000s of LEs into their chips but they may as well cost around $1000 a pop. So having found a company called Terasic that retails small Altera FPGA based developing boards was ideal for me.
Throughout this tutorial I will explain how I built a simply VGA signal generator on my DE0 Nano Developing ($60 for students) board. I used Verilog (a hardware description language) to configure the device with the free Altera developing tools.
VGA but how
The VGA signal produced a 800×600@72Hz output, which was due to the clock speed of the board being “only” 50MHz. Older screens may require you to adjust the pixel clock to meet the lower 60Hz or 50Hz standards, but more modern equipment should automatically adjust this to suit whatever frequency you apply.
So after this little intro, let’s start with the VGA signal itself. With VGA, one essentially requires five data lines, red, green, blue, horizontal synchronization and vertical synchronization. Each pixel’s color is therefore transmitted by a combination of the RGB (analogue) values, which individually capture the brightness of each color component. Depending upon the resolution of analogue values, one can increase the number of actually displayed colors very quickly (i.e. using a resistor ladder), but for this simple system I am using basic 3bit per pixel i.e. 8 colors.
Each RGB value is sent after the other in one line signal. The line signal can be imagined as the signal the screen lays over one horizontal row of pixels from left to right. The screen knows (well, it doesn’t know but assumes) when to move to the next pixel since each line should be identical in timing, so that they can easily be read and displayed. After each line, there is a phase of no data transmission over which the H-Sync signal will spike. This tells the screen “one line sent” and it moves on to the beginning of the next line (like return, \n\r, endl, or whatever you may have come across…). Having sent all lines with data requires to send some extra, empty lines without data, so that the V-Sync signal can spike letting the screen know to reset and prepare to receive another frame (i.e. collection of lines). This will make the screen jump back to the top left and wait for the next bit of data to be received. Then you start over with the line data for the next frame and so on.
For a full (and maybe better) explanation of VGA, check out this page.
Well, since each pixel will have 1/50MHz or 20ns to display, ensuring that the one line is transmitted correctly, I choose the following timing values:
- Data part: 800 pixels
- Empty data part: 240 pixels
- Total pixels per line: 1040 pixels [~21µs]
The vertical signal was the split up into this:
- Data part: 600 lines
- Empty data part: 66 lines
- Total lines per frame: 666 lines [~14ms]
The H-sync and V-sync signals must then be sent during the empty data parts for a line and a frame respectively. I choose the following properties:
- H-Sync length: 120 pixels
- V-Sync length: 6 lines
- H-Sync delay: 56 pixels
- V-Sync delay: 37 lines
This should be close enough to meet the specifications.
This should be close enough to meet the specifications. Now comes the coding part. Having been taught (well, teaching is overrated at by some lecturers…) some Verilog at university, I decided to give it a go and coded a basic outline. The code is as follows:
/* Internal registers for horizontal signal timing */
reg [10:0] hor_reg;
wire hor_max = (hor_reg == 1039);
/* Internal registers for vertical signal timing */
reg [9:0] ver_reg;
wire ver_max = (ver_reg == 665);
I have not as of yet put in the main functionality (that will go in line 27. // Code). But the backbone, i.e. the time keeper of the VGA signal is already there. Using the minimum amount of resources, I choose a 11bit register for the horizontal counter (to count 1040 different values up to 1039) and a 10bit register for the vertical counter (to count 666 different values up to 665). The horizontal and vertical maximum values are there to tell when a line is full. Coding this here avoids the need to make a comparison later on. You may think this is not resource efficient but HDL makes a physical connections but not execute code.
So, now the actual signal generation. This is to make a frame:
always @ (posedge CLOCK_50) begin
if (hor_max) begin
hor_reg <= 0;
/* Running through frame */
ver_reg <= 0;
ver_reg <= ver_reg + 1;
hor_reg <= hor_reg + 1;
What happens here is; the horizontal register increments whenever the pixel clock ticks (i.e. when a positive edge occurs). Whenever the maximum value is reached (indicated by hor_max) the register is reset to zero and the vertical register is incremented (i.e. jumping to the next line). When the vertical maximum is reached (indicated by ver_max) then the vertical register is reset to zero and a frame has been displayed. Nothing big happens here, but the correct incrementation of the two registers.
So now comes the part to make the synchronization signals:
/* Generating the horizontal sync signal */
if (hor_reg == 856)
hor_sync <= 1;
else if (hor_reg == 976)
hor_sync <= 0;
/* Generating the vertical sync signal */
if (ver_reg == 637)
ver_sync <= 1;
else if (ver_reg == 643)
ver_sync <= 0;
assign VGA_HS = ~hor_sync;
assign VGA_VS = ~ver_sync;
Here, upon each clock tick, the horizontal and vertical registers are being checked to see whether the synchronization signals need to be changed. When reaching the 857th pixel (i.e. when hor_reg is 856) the hor_sync is set and when the 977th pixel is reached (i.e. when hor_reg is 976) the synchronization signal is reset. The same happens to the vertical signal, where the ver_reg is being checked. In the end, both signals need to be assigned to the signal outputs (declared in the verilog module in the beginning of the code). Here, they are also inverted, because that’s what the specifications declared them to be.
Now we have frames being displayed, but no data being transmitted. Let’s do a simple binary pattern to see if it works:
assign VGA_GREEN = (!hor_reg && !ver_reg && ver_reg < 600 && hor_reg < 800);
assign VGA_BLUE = (!hor_reg && !ver_reg && ver_reg < 600 && hor_reg < 800);
All I did here was to assign a different pattern for each color. Red will turn on and off for every other pixel, green will be on for two and off for two pixels and blue will be on for four and off for four pixels. A nice pattern ensuring correct functionality should therefore appear on the screen. Of course, making sure that we do not output anything during the synchronization periods is important, too. This is why I added the less than 800×600 statement to each line.
Hardware and DE0-Nano
Nearly done, but the important part is missing; hardware. As previously stated, one just needs five signals to generate a valid VGA signal. I made a little PCB that forwarded the signal from my DE0-Nano’s GPIO pins to a 15-Pin VGA socket. If you follow the link I posted further up (and here it is again) you will see which signal goes to which pin of the VGA socket. Making sure that the signal has both source and ground connected properly, 10 pins (that is 5xsignal and 5xground) should be needed.
Once all cables are connected correctly, the DE0-Nano needs to be configured properly, too. If you want to replicate the system to how I have done it the follow the GPIO layout as in the picture to the left. I used Altera’s Quartus II developing environment to program the DE0-Nano board and what you see here is the Pin Planner. All pin entries are listed in the table at the bottom. Make sure the verilog input and output lines match the location I specified to replicate my results. When connecting the VGA to the GPIO pins as shown in the next image you ought to get a beautiful test patter on your screen. (R is red, G is green, B is blue, V is vertical sync., H is horizontal sync. and Gnd is ground)
So 800×600 may seem all good and well, but why not display a higher resolution you may wonder. Nevertheless there is PLL which would increase the clock even further. But the hardware itself is limiting us there. The DE0-Nano with its FPGA may be very powerful, but it can just about switch fast enough for a 50MHz pixel clock.
The fastest switching signal we have is for the color red, which changes on every clock edge. Therefore one expects to see a 25MHz square wave, but in fact it looks much more like a sinusoid. This slow switching time suggests that we have hit the limit of the poor board’s switching speed. But hey, it’s 800×600@72Hz! Only a decade ago (around 2000) many PC users were still using on 640×480!
Furthermore, we get noise, lots of noise. When looking at the least changing signals, that is the H-Sync and V-Sync on the oscilloscope, one can clearly see the line interference due to crosstalk. I believe this is partially due to my quick PCB layout and me bundling the wires, but it is interesting to see. Over my academia at university I never experienced this sort of behavior due to low signal frequencies, mostly simulated or theoretical systems and due to the fact that one can often neglect crosstalk currents on insensitive systems.