Any good video game requires one very fundamental feature: video. The nanoboard includes a VGA port and seeing as I don’t know anything about the VGA protocol it seems like the perfect place to start. The nanoboard also has a built in touch-screen, however I feel like that would be easier to implement and will be covered in a later post. My goal for this section is to display the “SMPTE color bars” on a standard computer monitor.
Video Graphics Array (VGA) is a graphical standard dating back to the 1980’s. The defined VGA resolution is 640×480, meaning that each row in the display contains 640 pixels and each column contains 480 pixels. A second way to look at this is as a display with 480 horizontal rows, each row containing 640 pixels. This is a convenient viewpoint to take due to the method displays use to generate images. A simplified animation of the display process is shown below.
Enoch Hwang from has written a nice article about VGA and creating a VGA controller based on two 10-bit binary counters. The FPGA design described here is based off of the circuit and timings provided in his article. This version of the VGA controller works at a fixed resolution of 640×480 and displays a solid color across the entire screen. This VGA controller is broken up into two basic parts: a timing circuit and a color generator. The timing circuit is fed a 25MHz clock signal and generates the necessary H Sync and V Sync signals along with the state of the currently active pixel. These signals are in turn used by the color generator to provide the required data to the DAC that drives the nanoboard’s VGA port.
The 6-bit RGB color value is selected using the nanoboard’s DIP switch. The binary color commands generated from the switch configuration are displayed on the user LED’s. The code for the clock and color circuits as well as the schematic linking them to the nanoboard are all provided below. This example has been tested on an LCD monitor and is successful for displaying solid colors. There may be underlying timing problems masked by the simplicity of this example. These should be found and addressed in the more complex examples that will follow.
VGAClock.vhd
[cc lang="vhdl"] Library IEEE; USE IEEE.STD_LOGIC_1164.all; USE IEEE.STD_LOGIC_Unsigned.all; ENTITY VGAClock is Port( Clock, Enable, Reset: in STD_LOGIC; HSync, VSync, rowActive, columnActive, pixelActive : out STD_LOGIC; row,column : out STD_LOGIC_VECTOR(0 to 9) ); end VGAClock; Architecture structure of VGAClock is signal HSync_sig : STD_LOGIC := '1'; signal VSync_sig : STD_LOGIC := '1'; signal row_sig : STD_LOGIC_VECTOR(0 to 9) := "0000000000"; signal column_sig : STD_LOGIC_VECTOR(0 to 9) := "0000000000"; signal rowActive_sig : STD_LOGIC; signal columnActive_sig : STD_LOGIC; signal pixelActive_sig : STD_LOGIC; constant pixelCount : integer := 640; constant HSyncTime : integer := 95; constant HLeftBlank : integer := 23; constant HRightBlank : integer := 47; constant VSyncTime : integer := 2; constant VBlankLeft : integer := 14; constant rowCount : integer := 480; constant VBlankRight : integer := 32; begin process(Clock, Reset) begin if (Reset = '1') then HSync_sig <= '1'; VSync_sig <= '1'; column_sig <= "0000000000"; row_sig <= "0000000000"; pixelActive_sig <= '0'; rowActive_sig <= '1'; columnActive_sig <= '0'; elsif (Clock'event and Clock = '1') then --Done Pixels, enter HBlankLeft if(column_sig = (pixelCount-1)) then rowActive_sig <= '0'; column_sig <= column_sig + 1; --Done HBlankLeft, Send HSync Pulse elsif(column_sig = (pixelCount+HLeftBlank-1)) then HSync_sig <= '0'; column_sig <= column_sig + 1; --HBlankRight elsif(column_sig = (pixelCount+HLeftBlank+HSyncTime-1)) then HSync_sig <= '1'; row_sig <= row_sig + 1; column_sig <= column_sig + 1; -- Reset counter and start next line elsif(column_sig = (pixelCount+HLeftBlank+HSyncTime+HRightBlank-1)) then column_sig <= "0000000000"; rowActive_sig <= '1'; else column_sig <= column_sig + 1; end if; if(row_sig = rowCount-1) then columnActive_sig <= '0'; elsif(row_sig = (rowCount + VBlankLeft-1)) then VSync_sig <= '0'; elsif(row_sig = (rowCount + VBlankLeft + VSyncTime-1)) then VSync_sig <= '1'; elsif(row_sig = (rowCount + VBlankLeft + VSyncTime + VBlankRight-1)) then row_sig <= "0000000000"; columnActive_sig <= '1'; end if; end if; if (rowActive_sig = '1' and columnActive_sig = '1') then pixelActive_sig<='1'; else pixelActive_sig<='0'; end if; end process; HSync <= HSync_sig; VSync <= VSync_sig; column <= column_sig; row <= row_sig; pixelActive <= pixelActive_sig; rowActive <= rowActive_sig; columnActive <= columnActive_sig; end structure; [/cc]
VGAColor.vhd
[cc lang="vhdl"] Library IEEE; USE IEEE.STD_LOGIC_1164.all; USE IEEE.STD_LOGIC_Unsigned.all; ENTITY VGAColor is Port( Clock,Reset : in STD_LOGIC; pixel : in STD_LOGIC_VECTOR(7 downto 0); pixelActive : in STD_LOGIC; Red, Green, Blue : out STD_LOGIC_VECTOR(7 downto 0) ); end VGAColor; Architecture structure of VGAColor is signal Red_sig,Green_sig,Blue_sig : STD_LOGIC_VECTOR(7 downto 0); constant pixelCount : integer := 640; begin process(Clock, Reset) begin if (Reset = '1') then Blue_sig <= "00000000"; Red_sig <= "00000000"; Green_sig <= "00000000"; elsif (Clock'event and Clock = '1') then if pixelActive = '1' then if ((pixel(0) = '1') and (pixel(1) = '1')) then Blue_sig <= "11111111"; elsif pixel(0) = '1' then Blue_sig <= "01010101"; elsif pixel(1) = '1' then Blue_sig <= "10101010"; else Blue_sig <= "00000000"; end if; if ((pixel(2) = '1') and (pixel(3) = '1')) then Green_sig <= "11111111"; elsif pixel(2) = '1' then Green_sig <= "01010101"; elsif pixel(3) = '1' then Green_sig <= "10101010"; else Green_sig <= "00000000"; end if; if ((pixel(4) = '1') and (pixel(5) = '1')) then Red_sig <= "11111111"; elsif pixel(4) = '1' then Red_sig <= "01010101"; elsif pixel(5) = '1' then Red_sig <= "10101010"; else Red_sig <= "00000000"; end if; else Blue_sig <= "00000000"; Red_sig <= "00000000"; Green_sig <= "00000000"; end if; end if; end process; Red <= Red_sig; Green <= Green_sig; Blue <= Blue_sig; end structure; [/cc]
Sources:
File Downloads:
[download id=4]