fpga4fun.comwhere FPGAs are fun


FPGAs can easily become video generators.

Driving a VGA monitor

A VGA monitor requires 5 signals to display a picture.

The R, G and B are analog signals, while HS and VS are digital signals.

Creating a VGA signal from FPGA pins

Here's how to drive the VGA connector:

Here's a view of the female VGA connector connected to Pluto on a breadboard.

Back view of the female VGA connector to 12-pins header assembly. The 12-pins header makes it easy to connect to a breadboard. The three 270Ω series resistors are clearly visible. We could also have used an adapter board.

Frequency generator

A monitor always displays a picture line-by-line, from top-to-bottom. Each line is drawn from left-to-right.

That's hard-coded, you cannot change that.

But you specify when the drawing starts by sending short pulses on HS and VS at fixed intervals. HS makes a new line to start drawing; while VS tells that the bottom has been reached (makes the monitor go back up to the top line).

For the standard 640x480 VGA video signal, the frequencies of the pulses should be:

Vertical Freq (VS)Horizontal Freq (HS)
60 Hz (=60 pulses per second)31.5 kHz (=31500 pulses per second)

To create a standard video signal, there is more details to take care of, like the duration of the pulses and the relationship between HS and VS. Get an idea on this page.

Our first video generator

VGA monitors are usually multisync, so can accommodate non-standard frequencies - no need to generate exactly 60Hz and 31.5KHz anymore (but if you are using an older non-multisync VGA monitor, you'll need to generate the exact frequencies).

Let's start with X and Y counter.

reg [9:0] CounterX;
reg [8:0] CounterY;
wire CounterXmaxed = (CounterX==767);

always @(posedge clk)
  CounterX <= 0;
  CounterX <= CounterX + 1;

always @(posedge clk)
    CounterY <= CounterY + 1;

CounterX counts 768 values (from 0 to 767) and CounterY counts 512 values (0 to 511).

Now, CounterX is used to generate HS, and CounterY to generate VS. Using a 25MHz clock, we get 32.5KHz for HS and 63.5Hz for VS. The pulses need to be active long enough for the monitor to detect them. Let's use a 16 clocks pulse (0.64µs) for HS and a full horizontal line length pulse for VS (768 clocks or 30µs). That's shorter than what the VGA spec calls for but works fine anyway.

We "register" the HS and VS pulses (to get glitch free outputs).

reg vga_HS, vga_VS;
always @(posedge clk)
  vga_HS <= (CounterX[9:4]==0);   // active for 16 clocks
  vga_VS <= (CounterY==0);   // active for 768 clocks

The VGA sync pulses need to be negative, so we invert the signals.

assign vga_h_sync = ~vga_HS;
assign vga_v_sync = ~vga_VS;

Finally we can drive the R, G and B signals. As a first cut, we can use some bits of the X and Y counters to get nice square color patterns...

assign R = CounterY[3] | (CounterX==256);
assign G = (CounterX[5] ^ CounterX[6]) | (CounterX==256);
assign B = CounterX[4] | (CounterX==256);

and we get a picture on the VGA monitor!