------------------------------------------------------------------------------- -- SIMPLE VGA CONTROLLER -- -- Author : Deshanand Singh -- -- Date : Nov 2, 1998 -- -- Version : 1.0 -- -- -- -- This file is the VHDL source for a SIMPLE VGA CONTROLLER. The controller -- -- currently contains enough memory to maintain a 64x64 resolution and two -- -- colours for each pixel. The controller enables external devices to write -- -- pixels into the memory, while creating the signals necessary for display -- -- on a standard VGA/SVGA monitor. Since only a 64x64 resolution is supported-- -- by the memory while the monitor supports 640x480 pixels, each one of the -- -- pixels in memory is mapped to a 10x8 block of real pixels called a -- -- superpixel. -- -- -- -- Comments/Suggestions/Problems/Improvements -- -- -> email singhd@eecg.toronto.edu -- ------------------------------------------------------------------------------- library ALTERA; use ALTERA.maxplus2.all; library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; use IEEE.std_logic_unsigned.all; library LPM; use LPM.lpm_components.ALL; entity vgacon is generic ( ramfile : string := "UNUSED" ); port( clock : in std_logic; -- 25.175 MHz board clock resetn : in std_logic; -- Active low reset line -- The row, column and colour for the pixel to be written. -- row, col : in std_logic_vector( 5 downto 0 ); colour : in std_logic_vector( 2 downto 0 ); do_write : in std_logic; -- Write the colour to (row,col) coords done_write : out std_logic; -- The write will completed on next cyc hsync, vsync : out std_logic; -- Horz, Vert vga sync lines data_r : out std_logic; -- Red data line data_g : out std_logic; -- Green data line data_b : out std_logic; -- Blue data line done_screen : out std_logic -- Drawn One Screen ); end vgacon; architecture ComponentLevel of vgacon is signal Reset : std_logic; signal EnableVert_din, EnableVert : std_logic; signal ResetVert_din, ResetVert : std_logic; signal CounterHoriz, CounterVert : std_logic_vector( 9 downto 0 ); signal CounterTen : std_logic_vector( 3 downto 0 ); signal EnableCol_din, EnableCol : std_logic; signal CounterCol, CounterRow : std_logic_vector( 5 downto 0 ); signal RAMreadAddress , RAMwriteAddress_din, RAMwriteAddress, RAMaddress_din, RAMaddress : std_logic_vector(11 downto 0 ); signal WriteStart : std_logic; signal WriteControl : std_logic_vector( 1 downto 0 ); signal Reading_din, Reading : std_logic; signal HorizValid_pipe1, VertValid_pipe1, ViewValid_pipe2, ViewValid_pipe3 : std_logic; signal PixelDataIn, PixelDataOut_pipe3 : std_logic_vector( 0 downto 0 ); signal DataR_din, hsync_din, vsync_din : std_logic; signal High : std_logic; signal Low2 : std_logic_vector( 1 downto 0 ); signal Low4 : std_logic_vector( 3 downto 0 ); -- -- Constants that define the timing for the VGA sync signals. -- Don't change these unless you are absolutely sure of how the -- VGA timing works!. The monitor could be damaged when the -- timing is incorrect. --- constant C_VERT_NUM_PIXELS : integer := 480; constant C_VERT_SYNC_START : integer := 493; constant C_VERT_SYNC_END : integer := 494; constant C_VERT_TOTAL_COUNT : integer := 525; constant C_HORZ_NUM_PIXELS : integer := 640; constant C_HORZ_SYNC_START : integer := 659; constant C_HORZ_SYNC_END : integer := 755; constant C_HORZ_TOTAL_COUNT : integer := 800; begin -- Various Constants High <= '1'; Low4 <= "0000"; Low2 <= "00"; Reset <= not Resetn; -- Counter Enables and Resets EnableVert_din <= '1' when CounterHoriz = (C_HORZ_TOTAL_COUNT-2) else '0'; dff1: dff port map ( d => EnableVert_din, q => EnableVert, clk => Clock, clrn => Resetn, prn => High ); ResetVert_din <= '1' when ( EnableVert_din = '1' and CounterVert= (C_VERT_TOTAL_COUNT-1) ) else '0'; dff2: DFF port map ( d => ResetVert_din , q => ResetVert , clk => Clock, clrn => Resetn, prn => High ); -- Horizontal and Vertical counters, which keep allow us to send out the pixel -- data and sync signals at the correct time. Horizontal: lpm_counter generic map ( lpm_width => 10 ) port map ( clock => Clock, aclr => Reset, sclr => EnableVert, -- Reset Horiz on next line q => CounterHoriz ); Vertical: lpm_counter generic map ( lpm_width => 10 ) port map ( clock => Clock, aclr => Reset, sclr => ResetVert, q => CounterVert, cnt_en => EnableVert ); -- Every SuperPixel that is shown on the screen is made up of 10 horizontal pixels -- so we must keep track of the column number in addition to the pixel number. -- The following increments the column counter for every 10 pixels encountered. Count10: lpm_counter generic map ( lpm_width => 4 ) port map ( clock => Clock, aclr => Reset, sclr => EnableCol, -- Move to new column, so reset q => CounterTen ); -- ten pixel counter. EnableCol_din <= '1' when CounterTen="1000" else '0'; dff3: DFF port map ( d => EnableCol_din, q => EnableCol, clk => Clock, clrn => Resetn, prn => High ); CountCol: lpm_counter generic map ( lpm_width => 6 ) port map ( clock => Clock, aclr => Reset, sclr => EnableVert, -- Next line, reset col counter q => CounterCol, cnt_en => EnableCol ); -- Every SuperPixel shown is made up of 8 vertical pixels. No counter circuitry is req -- since dividing by 8 is equivalent to shifting right by 3 bits. Thus only the following -- line is used to keep track of the row number. CounterRow <= CounterVert(8 downto 3); -- Are we in the the Signal Range where we should send out pixels ? -- The follwing calculates the if the we should actually send out pixels -- or if the r,g,b lines should be set to '0'. Three cycles of latency -- are added to the output ViewValid_pipe3. The FFs are placed between the -- the combinational units rather than in a SR configuration so that the -- path delays are reduced as much as possible. -- process (Clock, Reset) begin if Reset = '1' then HorizValid_pipe1 <= '0'; VertValid_pipe1 <= '0'; ViewValid_pipe2 <= '0'; ViewValid_pipe3 <= '0'; elsif Clock'Event and Clock='1' then if CounterHoriz >= 0 and CounterHoriz < C_HORZ_NUM_PIXELS then HorizValid_pipe1 <= '1'; else HorizValid_pipe1 <= '0'; end if; if CounterVert >= 0 and CounterVert < C_VERT_NUM_PIXELS then VertValid_pipe1 <= '1'; else VertValid_pipe1 <= '0'; end if; ViewValid_pipe2 <= HorizValid_pipe1 and VertValid_pipe1; ViewValid_pipe3 <= ViewValid_pipe2; end if; end process; -- When are we Reading ? Reading_din <= '1' when ( CounterHoriz <= (C_HORZ_NUM_PIXELS +20) or CounterHoriz >= (C_HORZ_TOTAL_COUNT-20) ) else '0'; dff5: dff port map ( d => Reading_din, q => Reading, clk => Clock, prn => Resetn, clrn => High ); -- RAM read address. The address in the RAM which contains the current SuperPixel -- being displayed. RAMreadAddress( 11 downto 6 ) <= CounterRow; -- upper six bits contain the row RAMreadAddress( 5 downto 0 ) <= CounterCol; -- lower six bits contain the column -- RAM write address. The address where ther user wishes to place a SuperPixel. RAMwriteAddress_din(11 downto 6 ) <= row; RAMwriteAddress_din( 5 downto 0 ) <= col; reg1: lpm_ff generic map ( lpm_width => 12 ) port map ( data => RAMwriteAddress_din, q => RAMwriteAddress, clock => Clock, aclr => Reset ); -- The WRITE Memory controller. -- Write to memory when we get a request and we are not reading the -- memory to display pixels. WriteStart <= '1' when ( do_write='1' and Reading='0' ) else '0'; -- Currently a write request is processed in one cycle when we are in -- writing mode. Thus as soon as the request is made the done_write ack -- is sent back. done_write <= not Reading; -- Register the WriteStart Signal with 2 cycles of latency. srg1: lpm_shiftreg generic map ( lpm_width => 2 ) port map ( clock => Clock, data => Low2, aclr => Reset, shiftin => WriteStart, q => WriteControl ); -- Register the Colour input with 2 cycles of latency. This is necessary since the -- { row, col } address experiences two cycles of latency before getting to the -- synchronous RAM. srg2: lpm_shiftreg generic map ( lpm_width => 2 ) port map ( clock => Clock, data => Low2, aclr => Reset, shiftin => colour(0), shiftout => PixelDataIn(0) ); -- Multiplexer that selects between the reading and writing address. The signal -- is also registered before it is sent to the RAM. RAMaddress_din <= RAMreadAddress when WriteControl(0)='0' else RAMwriteAddress; reg2: lpm_ff generic map ( lpm_width => 12 ) port map ( data => RAMaddress_din, q => RAMaddress, clock => Clock, aclr => Reset ); -- The VIDEO RAM :-). Two of the EABs in the FLEX10K20 will be used to implement -- the 4096 bits of ram used for creating a two color display with 64x64 resolution. -- VideoRam: lpm_ram_dq generic map ( lpm_widthad => 12, -- 64x64 SuperPixel grid lpm_outdata => "REGISTERED", -- register the output lpm_indata => "REGISTERED", -- and input as well lpm_address_control => "REGISTERED", -- as add/cont lines. lpm_file => ramfile, lpm_width => 1 ) port map ( data => PixelDataIn , address => RAMaddress, we => WriteControl(1), q => PixelDataOut_pipe3, inclock => Clock , outclock => Clock ); -- Send out the Data. The data is ANDed with the valid signal. This ensures -- that data is only sent out when displaying a pixel. The r,g,b lines should -- be '0' at all other times. DataR_din <= ViewValid_pipe3 and PixelDataOut_pipe3( 0 ); dff8: dff port map ( d => DataR_din, q => data_g, clk => Clock, clrn => Resetn, prn => High ); -- data_g -> 4 cycles of latency -- Currently, we can only support two colours so that we dont consume all -- of the EABs in the FLEX10K20. Thus two of the bit planes must be left -- inactive. Each bit plane consumes 2 EABs for a 64x64 resolution. data_r <= '0'; data_b <= '0'; -- Send out the Sync Signals -- Note: A four bit shift register is a placed between these signals -- and the output. It is needed to match the latency of the r,g,b -- signals. The shift reg FFs can propbably be used for retiming, -- but for simplicity, it is just left as a 4 bit SR. -- hsync_din <= '0' when ( CounterHoriz >= C_HORZ_SYNC_START and CounterHoriz <= C_HORZ_SYNC_END ) else '1'; srg3: lpm_shiftreg generic map ( lpm_width => 4 ) port map ( clock => Clock, data => Low4, aset => Reset, shiftin => hsync_din, shiftout => hsync ); vsync_din <= '0' when ( CounterVert >= C_VERT_SYNC_START and CounterVert <= C_VERT_SYNC_END ) else '1'; sr4: lpm_shiftreg generic map ( lpm_width => 4 ) port map ( clock => Clock, data => Low4, aset => Reset, shiftin => vsync_din, shiftout => vsync ); -- The done screen signal done_screen <= ResetVert; end ComponentLevel; -------------------------------------------------------------------------------