Sunday 22 January 2017

Generating a VGA signal

Aims of module

• Generate tight tolerance signals
• Display something on a VGA monitor

Let me know if I haven’t given enough directions on how to implement this module. I think that the less hand-holding given the greater the joy when your project actually displays something for the first time.

Special note for Basys2 users

The Basys2 reference manual infers that the oscillator on the board isn’t too stable. Digilent recommends using a quality aftermarket oscillator to correct this, but the reference manual has the wrong part number - you want to order a SGR-8002DCPCC- N from DigiKey (the only place that seems to have it!). You can test your board/monitor compatibility using the board self test that is in the flash, or from the file from Digilent’s web site if you suspect that this is an issue. I have not been able to get a current 1080p HD LCD monitor to display a picture (although I’ve only tried two), but it works on plenty of CRTs. A cheap fix may be adding additional load to the power supply with a 150 Ohm resistor will help - see http://www.youtube.com/- watch?v=bVee4dDwO1k I have had no such issues with my Papilio One - I’ve even generated signals 1920 x 1080 @ 60Hz (145MHz).

VGA signal timing

For this demo we will be aiming at 640x480. As detailed on http://tinyvga.com/vga timing/640x480@60Hz this required a pixel clock of 25.175MHz . 25MHz is close enough for most monitors to sync up and display a stable image, and using a DCM we can generate that frequency from either 32MHz of the Papilo’s crystal or the 50MHz of the Basys2 clock generator.

How does the VGA interface work?

In the "good ol’ days" monitors were analogue devices, using Cathode Ray Tubes. Two signals are used control the position of the electron beam on the display.

Vertical sync (vsync)

This signal is pulsed every 60th of a second, and takes the electron beam back to the top of the screen. Once back at the top of the screen the monitor would scan the beam slowly down the screen.
In this video mode the pulse is negative pulse of 0.063555ms duration, every 16.6832ms.

Horizontal sync (hsync)

This signal is a pulsed every 1/31,468th of a second, and takes the electron beam to the left hand side of the monitor. Once there the beam scans rather more rapidly to the right hand side. In this video mode, it is a positive pulse of 3.8133068us duration every 31.777557us. When properly timed, the correct hsync and vsync timings caused the electron beam to scan the whole visible area, so all that is
needed is the colour signals.

The colour signals - red, green and blue

These are analogue signals which control the intensity of each colour, and each pixel lasts 1/25,175,000th of a second. These signals should only be driven for the correct portion of the horizontal scan, as the monitor uses the "blanking interval" to register what voltages are used for black. There is two blanking intervals - the horizontal blanking interval (either side of the hsync pulse) and the vertical blacking interval (either side of the vsync pulse.

Pins used to drive the VGA connector

Ten pins are used to drive the VGA connecter - the Red, Green and Blue signals use a passive D2A convertor made out of resisters

The constraints for the Papilio board are:

NET "HSYNC" LOC = "J14" | DRIVE = 2;
NET "VSYNC" LOC = "K13" | DRIVE = 2;
NET "Red<2>" LOC = "F12" | DRIVE = 2;
NET "Red<1>" LOC = "D13" | DRIVE = 2;
NET "Red<0>" LOC = "C14" | DRIVE = 2;
NET "Green<2>" LOC = "G14" | DRIVE = 2;
NET "Green<1>" LOC = "G13" | DRIVE = 2;
NET "Green<0>" LOC = "F14" | DRIVE = 2;
NET "Blue<2>" LOC = "J13" | DRIVE = 2;
NET "Blue<1>" LOC = "H13" | DRIVE = 2;
The constraints for the Basys2 board are:
NET "HSYNC" LOC = "J14" | DRIVE = 2;
NET "VSYNC" LOC = "K13" | DRIVE = 2;
NET "Red<2>" LOC = "F13" | DRIVE = 2;
NET "Red<1>" LOC = "D13" | DRIVE = 2;
NET "Red<0>" LOC = "C14" | DRIVE = 2;
NET "Green<2>" LOC = "G14" | DRIVE = 2;
NET "Green<1>" LOC = "G13" | DRIVE = 2;
NET "Green<0>" LOC = "F14" | DRIVE = 2;
NET "Blue<2>" LOC = "J13" | DRIVE = 2;
NET "Blue<1>" LOC = "H13" | DRIVE = 2;

Making the timings easy implement

If you multiply the hsync and vsync timings by the pixel clock you will get something close to the following numbers:

Scanline (Horizontal) timing                      Duration in pixel clocks
Visible area                                                            640
Front porch                                                            16
Sync pulse                                                             96
Back porch                                                            48
Whole line                                                             800

The horizontal blanking interval is the front porch + sync pulse + back porch = 160 pixel clocks

Frame (vertical) timing                 Duration in lines (800 pixel clocks)
Visible area                                                    480
Front porch                                                     10
Sync pulse                                                       2
Back porch                                                      33
Whole frame                                                   525

The vertical blanking interval is the front porch + sync pulse + back porch = 45 lines

The RGB signal

Both baords can generate only 256 colours - eight shades of red, eight shades of green and four shades of blue. It does this using a passive D2A converter made up of a dozen or so resistors. There really isn’t much more to say!

Pseudo-code implementation

Implementation of the hsync and vsync signals should be coming clear. Here it is in pseudo-code:

hcounter and vcounter are 10 bit counters
every 1/25,000,000th of a second
if hcount == 799 then
hcount = 0
if vcount == 524 then
vcount = 0
else
vcount = vcount + 1
end if
else
hcount = hcount + 1
end if
if vcount >= 490 and vcount < 492 then
vsync = ’0’
else
vsync = ’1’
end if
if hcount >= 656 and hcount < 752 then
hsync = 0
else
hsync = 1
end if
if hcount < 640 and vcount < 480 then
display a colour on the RGB signals
else
display black colour on the RGB signals
end if

Project - Displaying something on a VGA monitor

• Create a new project to drive the VGA device. It needs to accept a clk signal and generate hsync, vsync, red(2 downto 0), green(2 downto 0) and blue(2 downto 1) outputs. ”Note that it is Blue(2 downto 1) not Blue(2 downto 0)”
• Add an implementation constraint file and add the definitions for clk and the 10 VGA signals.
• Implement the horizontal counter (you will need a ten-bit counter). Remember to include the unsigned library so you will be able to do numeric operations on STD_LOGIC_VECTOR signals.
• Run it in the simulator, and verify the pulse widths and direction.
• Implement the vertical counter (once again you will need a ten-bit counter). You can also verify this in the simulator, but as you need to simulate 16,667us to see the whole frame it can take a while!
• To generate a white image, assign ’1’s to all the RGB signals during the active time. Test this too in the simulator. You only want to see ’1’s for the first 640 pixel clocks of the first 480 lines.
• If all looks correct, plug a VGA monitor into your board. It should detect the signal and display an image.
• Rather than assigning ’1’s to the RGB values, experiment with assigning different bits out of hcounter and vcounter - you can make colour bars and check-board patterns.
• Look really closely at the simulation. Do the RGB values go to 1 when hcounter transitions from 799 back to 0? If not, why not?

 A common cause of problems

It looks as though this code doesn’t need to go into an "if rising_edge(clk) then. . . " block:

if hcount >= 656 and hcount < 752 then
hsync = ’0’
else
hsync = ’1’
end if
if vcount >= 490 and vcount < 492 then
vsync = ’0’
else
vsync = ’1’
end if

For maximum reliably, it does. As the counters ripple between two values (remember, at about 0.1ns per bit) the binary value of the counters will be in transition. If the signals are not buffered in a flip-flop, the hsync and vsync can contain unpredictable pulses of around 1ns wide. You won’t see these in simulation, and not many of us have a 1GHz Logic Analyser or ’scope, but it is really there.

I’ve generated a 1440x900 signal (105MHz clock rate) and used logic to display objects on the screen. If I didn’t buffer the RGB outputs, the objects wouldn’t show correctly or had fuzzy edges. Registering all the VGA signals made these problems go away, as the signals were solidly high or low for the entire clock duration.

This is only an annoyance while generating VGA signals, but if you are interfacing into other devices (e.g., SRAM) this can cause you no end of heartache. A few implementation time tool options are available that can alter this too, by forcing all the I/O flip-flops to be put as close to the pin as possible, instead of being buried away in the middle of the FPGA fabric. It is also possible to add an "IOB=TRUE" constraint to your UCF file to enable this behaviour on a pin by pin basis

No comments:

Post a Comment