From Hamsterworks Wiki!
Please make sure that you have completed the earlier modules of FPGA course.
Having problems with your code? Do I need to change something? Want a reference solution to the project? Email me at "email@example.com", after removing the "nospam-" bit. I'll try to get back to you in a day or so.
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 to use an quality aftermarket oscillator to correct this, but the reference manual has the wrong part number - you want to order a SGR-8002DC-PCC-N from Digikey (the only place that seems to have it!)
Test your board/monitor compatibility using the 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 an current 1080p HD LCD monitor to display a picture (although I've only tried two), but it works on plenty of CRTs.
A cheep 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 Nexys2 board or Papilio One - I've even generated signals 1440x900 (105MHz).
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.
How does the VGA interface work?
In the "good ol' days" most monitors were analogue devices, using Cathode Ray Tubes. Two signals controlled the position of the electron beam on the display.
Vertical sync (vsync)
This signal gets pulsed every 60th of a second, and took the electron beam back to the top of the screen. Once back a 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, every 16.6832ms.
Horizontal sync (hsync)
This signal is a pulsed every 1/31,468th of a second, and causes the electron beam to return 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 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 controlled the intensity of each colour, and each pixel gets 1/25,175,000th of a second.
These must only driven only for the correct portion of horizontal scan as the monitor uses the "blanking interval" to register what voltages are used for black. You have 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
The constraints for the Basys2 board are:
NET "HSYNC" LOC = "J14" | DRIVE = 2 | PULLUP ; NET "VSYNC" LOC = "K13" | DRIVE = 2 | PULLUP ; NET "Red<2>" LOC = "F13" | DRIVE = 2 | PULLUP ; NET "Red<1>" LOC = "D13" | DRIVE = 2 | PULLUP ; NET "Red<0>" LOC = "C14" | DRIVE = 2 | PULLUP ; NET "Green<2>" LOC = "G14" | DRIVE = 2 | PULLUP ; NET "Green<1>" LOC = "G13" | DRIVE = 2 | PULLUP ; NET "Green<0>" LOC = "F14" | DRIVE = 2 | PULLUP ; NET "Blue<2>" LOC = "J13" | DRIVE = 2 | PULLUP ; NET "Blue<1>" LOC = "H13" | DRIVE = 2 | PULLUP ;
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|
The horizontal blanking interval is the front porch + sync pulse + back porch = 160 pixel clocks
|Frame (vertical) timing||Duration in lines (800 pixel clocks)|
The vertical blanking interval is the front porch + sync pulse + back porch = 45 lines
The RGB signals
The Basys2 board can generate only 256 colours - four shades of red, 8 shades of green and 8 shades of blue. It does this using a passive D-to-A converter made up of a dozen of so resistors. There really isn't much more to say!
Implementation of the hsync and vsync signals should be coming clear. Here it is in pseudo-code:
every 1/25,000,000th of a second hcounter and vcounter are 10 bit counters 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 < 491 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
Special note - Without jumpers the Basys2 clock runs at 50MHz, twice as quick as needed! To get around this can either add jumpers or only do something every other cycle:
... signal divide_by_two : std_logic; ... process(clk) begin if rising_edge(clk) then if divide_by_2 = '1' then -- stuff to do at 25MHz end if; divide_by_2 = not divide_by_two; end if end processs;
Later on we will use a Digital Clock Manager, which allows you to generate clock signals of different frequencies within the FPGA.
Project 11.1 - 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 a implementation constraint file and add the definitions for 'clk' and the 10 VGA signals.
- Implement the horizontal counter (you will need a 10 bit counter). Remember to include the unsigned library ("use IEEE.STD_LOGIC_UNSIGNED.ALL;") 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 10 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 a "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 < 491 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 use 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 values 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.
Ready to carry on?
Click here to carry on to the next module.