-------------------------------------------------------------------------------
-- 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;
-------------------------------------------------------------------------------