Today i’ve decided to post some information about making VGA signal generator, working in 640×480@60Hz mode. With use of such generator man can develop simple display device that works with almost any type of monitor. I wouldn’t go so far to call it a video card, but it’s definetley some part of video card project.
Every monitor (it does not matter if its LCD or an old CRT..) requires five signals to display things correctly. These are:
- R,G,B – 3 color lines
- v_sync – vertical synchonization
- h_sync – horizontal synchronization
Those signals must satisfy timing norms, which are described in details on this page. Every single pixel is read from RGB lines at about 25MHz. Entire display area is 800×525 in size, and within this area there is so called active area that represents things that we see on monitor. It has a size of 640×480 px. The rest of the area is used for front and back porch and return. I’ll give some details of timing synchronization lines.
There are four stages of horizontal syncronization
- 640px of active area (pixels are read from rgb lines and are displayed on monitor) (HD)
- 16px of horizontal front porch. (HF)
- 96px of horizontal return (HR)
- 48px of horizontal back porch (HB)
The sum of all 4 stages gives us 800 px. Horizontal sync signal should be driven to logical 0 during stage 3 and then should go high during stages 1,2 and 4.
Vertical sync is treated in simmilar manner.
- 480 lines of active area (VD)
- 10 lines of vertical front porch (VF)
- 33 lines of vertical back porch (VB)
- 2 lines of vertical return (VR)
This gives us 525 lines when added together. Just like horizontal sync signal, this one should be low during stage 3 and high during any other stage. Now we go from theory to practice.
Firstly we should define VHDL entity. We already know that we require 5 output signals to drive monitor. Vertical sync signal is called vs, and horizontal sync is hs. We will generate three color signals (R,G,B) later.
entity vga_640_gen is
port ( – common signals
clk, rst : in std_logic;
– vga signals
pix_x, pix_y : out std_logic_vector(9 downto 0);
hs, vs, blank, pix_clk : out std_logic);
end vga_640_gen;
The purpose of signals: clk and rst is obvious. Clock source has a frequency of 50MHz. It will be devided per 2, to give us pixel clock of desired frequency equal to 25MHz, as mentrioned above. Ten bit signals pix_x and pix_y contain currently displayed pixel coordinates. Pixel (0,0) is the one in upper-left corner of display area. The blank signal is high when generator is not in active area.
Let’s implement some VHDL code:
architecture Behavioral
of vga_640_gen
is
– signals for pixel clock generation
signal pix_clk_reg, pix_clk_next : std_logic;
signal blank_reg, blank_next : std_logic;
– internal signals
signal h_tick, v_tick : std_logic;
signal h_sync_reg, h_sync_next : std_logic;
signal v_sync_reg, v_sync_next : std_logic;
– counters for horizontal and vertical sync
signal v_count_reg, v_count_next : std_logic_vector(9 downto 0);
signal h_count_reg, h_count_next : std_logic_vector(9 downto 0);
– horizontal and vertical syncs generating constants
constant HD : integer := 640;
constant HF : integer := 16;
constant HB : integer := 48;
constant HR : integer := 96;
constant VD : integer := 480;
constant VF : integer := 10;
constant VB : integer := 33;
constant VR : integer := 2;
begin
– update process
update : process (clk, rst)
begin
if (rst = ‘1′) then
h_count_reg <= (others => ‘0′);
v_count_reg <= (others => ‘0′);
pix_clk_reg <= ‘0′;
h_sync_reg <= ‘0′;
v_sync_reg <= ‘0′;
blank_reg <= ‘0′;
elsif rising_edge(clk) then
pix_clk_reg <= pix_clk_next;
h_count_reg <= h_count_next;
v_count_reg <= v_count_next;
h_sync_reg <= h_sync_next;
v_sync_reg <= v_sync_next;
blank_reg <= blank_next;
end if;
end process update;
– clock generation
pix_clk_next <= not pix_clk_reg;
– blank generation
blank_next <= ‘0′ when (h_count_reg < HD) and (v_count_reg < VD) else ‘1′;
– horizontal and vertical tick signal generation
h_tick <= ‘1′ when h_count_reg = (HD+HF+HB+HR-1) else ‘0′;
v_tick <= ‘1′ when v_count_reg = (VD+VF+VB+VR-1) else ‘0′;
– sync signals generation
h_sync_next <= ‘1′ when (h_count_reg >= (HD+HF)) and
(h_count_reg <= (HD+HF+HR-1)) else ‘0′;
v_sync_next <= ‘1′ when (v_count_reg >= (VD+VF)) and
(v_count_reg <= (VD+VF+VR-1)) else ‘0′;
– horizontal counter process
h_counter : process (pix_clk_reg, h_tick, h_count_reg)
begin
if pix_clk_reg = ‘1′ then
if (h_tick = ‘1′) then
h_count_next <= (others => ‘0′);
else
h_count_next <= h_count_reg + 1;
end if;
else
h_count_next <= h_count_reg;
end if;
end process h_counter;
– vertical counter process
v_counter : process (pix_clk_reg, h_tick, v_tick, v_count_reg)
begin
if pix_clk_reg = ‘1′ and h_tick = ‘1′ then
if (v_tick = ‘1′) then
v_count_next <= (others => ‘0′);
else
v_count_next <= v_count_reg + 1;
end if;
else
v_count_next <= v_count_reg;
end if;
end process v_counter;
– pixel clock signal
pix_clk <= pix_clk_reg;
– blank signal
blank <= blank_reg;
– generate horizontal sync signal
hs <= h_sync_reg;
– generate vertical sync signal
vs <= v_sync_reg;
– output pixel counter signals
pix_x <= h_count_reg;
pix_y <= v_count_reg;
end Behavioral;
Update process is responsible for writing new waluest to all registers, which were used in this implementation. Clock division is done by one bit register negating itself at main clock frequency. h_count is responsible for horizontal syncronization, it is implemented as a simple counter, that count to 800 and then resets itself. v_count is implemented in simmilar way. Ouput signals are generated by comparers that compare counter values with constants defined (HD, VD, HF, VF and so on…).
Generator test
In order to test our generator we need to create a testbed. The simplest way to do so is to display a xor pattern on our tube. In xor pattern pixel color is derived form xor of its coordinates. Here is an implementation of such testbed.
library IEEE;
use IEEE.
STD_LOGIC_1164.
ALL;
use IEEE.
STD_LOGIC_ARITH.
ALL;
use IEEE.STD_LOGIC_UNSIGNED.
ALL;
entity test
is
port( clk
: in std_logic;
vga_hs, vga_vs : out std_logic;
vga_rgb : out std_logic_vector(7 downto 0));
end test;
architecture Behavioral of test is
signal pix_x, pix_y : std_logic_vector(9 downto 0);
signal blank : std_logic;
begin
vga_gen : entity vga_640_gen
port map(clk => clk, rst => ‘0′,
pix_x => pix_x, pix_y => pix_y,
hs => vga_hs, vs => vga_vs, blank => blank, pix_clk => open);
vga_rgb <= pix_x(7 downto 0) xor pix_y(7 downto 0) when blank = ‘0′ else x"00";
end Behavioral;
It’s worth noticing that RGB signals need to be low when we are out of acive area. If we dont take care of this some tubes are starting to freak out and emit strage noise (: I’ve heard it by myself, so beware of this. Testbed executed on NEXYS 2 board gives result shown below.
