Update 2015/03/08:
Added DE1 board files and a small bug fix. Fixed the repo link below to point to the right revision.
During my efforts to learn more about fpga’s I found a nice tutorial about VGA in VHDL on the DE1 board on youtube.
I was surprised how easy it is to do it when you got it explained 🙂
In short, you draw the pixel line by line on the screen and every line contains an active video phase in which the rgb pixels are sent and a blanking region which is divided into the front porch, sync pulse and back porch (see image below).

vga line
In the youtube tutorial its told that it starts with the blanking region followed by the active video phase. Probably it doesn’t matter but I did it in the same way (please comment if you know whats right and if it makes any difference).
It turned out that by starting with the visible area first the bending which was visible on the horizontal line is gone. I’ve updated the code accordingly.
The pixel clock and the pixel count of front porch, sync pulse, back porch and the active video phase is defined and can be seen here:
http://www-mtl.mit.edu/Courses/6.111/labkit/vga.shtml
http://tinyvga.com/vga-timing
I chose the resolution 640×480 with a pixel clock of 25.175 MHz. Let’s have a look at the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -- http://tinyvga.com/vga-timing/640x480@60Hz ENTITY VGASYNC IS PORT( CLK: IN STD_LOGIC; -- pixel clock 25.175 MHz HSYNC,VSYNC: OUT STD_LOGIC; R,G,B : OUT STD_LOGIC_VECTOR(5 downto 0) ); END VGASYNC; ARCHITECTURE MAIN of VGASYNC IS -- Horizontal timing (line) constant hva : integer := 640; -- Visible area constant hfp : integer := 16; -- Front porch constant hsp : integer := 96; -- Sync pulse constant hbp : integer := 48; -- Back porch -- Vertical timing (frame) constant vva : integer := 480; -- Visible area constant vfp : integer := 10; -- Front porch constant vsp : integer := 2; -- Sync pulse constant vbp : integer := 32; -- Back porch signal HPOS: integer range 0 to 800:=0; signal VPOS: integer range 0 to 525:=0; |
Our vga entity will get the pixel clock as input and outputs the horizontal and vertical sync pulse together with the rgb values (6 Bit for the MiST).
Below architecture are some constants which match the given values of our resolution. HPOS and VPOS are the horizontal and vertical counters which end positions are the sums of the above values.
|
process(CLK) begin if rising_edge(CLK) then if HPOS < (hva+hfp+hsp+hbp) then HPOS <= HPOS + 1; else HPOS <= 0; if VPOS < (vva+vfp+vsp+vbp) then VPOS <= VPOS + 1; else VPOS <= 0; end if; end if; |
With the pixel clock we change our counters. As long as HPOS is below 800 we increase it by one. If its higher we’re in the next line and set HPOS back to zero (begin of the next line). Then we check if VPOS is below 524 and if yes increase it by one and if not set it to zero (begin of the next frame).
|
if HPOS > (hva+hfp) and HPOS < (hva+hfp+hsp) then HSYNC <= '0'; else HSYNC <= '1'; end if; if VPOS > (vva+vfp) and VPOS < (vva+vfp+vsp) then VSYNC <= '0'; else VSYNC <= '1'; end if; |
From the figure above we see that HSYNC is only zero during the sync pulse. The same is valid for VSYNC. And thats exactly what the code above does.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
if (HPOS > hva) or (VPOS > vva) then R<=(OTHERS=>'0'); G<=(OTHERS=>'0'); B<=(OTHERS=>'0'); else -- Blue background R<=(OTHERS=>'0'); G<=(OTHERS=>'0'); B<=(OTHERS=>'1'); -- White cross-hair if(HPOS > 475 and HPOS < 485) or (VPOS > 280 and VPOS < 290) then R<=(OTHERS=>'1'); G<=(OTHERS=>'1'); B<=(OTHERS=>'1'); end if; end if; end if; end process; END MAIN; |
The code above sets our pixels. The first section sets the rgb values to zero when we aren’t in the active video phase. Then the background is set to blue and “overdrawn” by white when the HPOS and VPOS matches certain values which will show a horizontal (HPOS) and a vertical (VPOS) white stripe in the middle of the screen.
The full source code of this example together with the Quartus project files for the MiST can be found here:
https://github.com/wsoltys/mist-cores/tree/77fea2dc1e3986daa9138dbb2fa91fd8267c3c9e/misc/vga