-------------------------------------------------------------------------------
--
-- VGA timing and address generator
--
-- Fixed-resolution address generator.  Generates h-sync, v-sync, and blanking
-- signals along with a 20-bit RAM address.  H-sync and v-sync signals are
-- delayed two cycles to compensate for the DAC pipeline.
--
-- Cristian Soviani, Dennis Lim, and Stephen A. Edwards
--
-------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity vga_timing is
  port (
    pixel_clock  : in std_logic;
    reset        : in std_logic;
    h_sync_delay : out std_logic;
    v_sync_delay : out std_logic;
    blank        : out std_logic;
    vga_ram_read_address : out std_logic_vector(19 downto 0));
end vga_timing;

architecture Behavioral of vga_timing is

  constant SRAM_DELAY : integer := 3;

-- 640 X 480 @ 60Hz with a 25.175 MHz pixel clock
  constant H_ACTIVE      : integer := 640;
  constant H_FRONT_PORCH : integer := 16;
  constant H_BACK_PORCH  : integer := 48;
  constant H_TOTAL       : integer := 800;

  constant V_ACTIVE      : integer := 480;
  constant V_FRONT_PORCH : integer := 11;
  constant V_BACK_PORCH  : integer := 31;
  constant V_TOTAL       : integer := 524;

  signal line_count  : std_logic_vector (9 downto 0);   -- Y coordinate
  signal pixel_count : std_logic_vector (10 downto 0);  -- X coordinate
  
  signal h_sync : std_logic;	-- horizontal sync
  signal v_sync : std_logic;	-- vertical sync
  
  signal h_sync_delay0 : std_logic; -- h_sync delayed 1 clock
  signal v_sync_delay0 : std_logic; -- v_sync delayed 1 clock

  signal h_blank : std_logic;		-- horizontal blanking
  signal v_blank : std_logic;		-- vertical blanking

  -- flag to reset the ram address during vertical blanking
  signal reset_vga_ram_read_address : std_logic;

  -- flag to hold the address during horizontal blanking
  signal hold_vga_ram_read_address : std_logic;

  signal ram_address_counter : std_logic_vector (19 downto 0);

begin

  -- Pixel counter

  process ( pixel_clock, reset )
  begin
    if reset = '1' then
      pixel_count <= "00000000000";
    elsif pixel_clock'event and pixel_clock = '1' then
      if pixel_count = (H_TOTAL - 1) then
        pixel_count <= "00000000000";
      else
        pixel_count <= pixel_count + 1;
      end if;
    end if;    
  end process;

  -- Horizontal sync

  process ( pixel_clock, reset )
  begin
    if reset = '1' then
      h_sync <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      if pixel_count = (H_ACTIVE + H_FRONT_PORCH - 1) then
        h_sync <= '1';
      elsif pixel_count = (H_TOTAL - H_BACK_PORCH - 1) then
        h_sync <= '0';
      end if;
    end if;    
  end process;

  -- Line counter
  
  process ( pixel_clock, reset )
  begin
    if reset = '1' then
      line_count <= "0000000000";
    elsif pixel_clock'event and pixel_clock = '1' then
      if ((line_count = V_TOTAL - 1) and (pixel_count = H_TOTAL - 1)) then
        line_count <= "0000000000";
      elsif pixel_count = (H_TOTAL - 1) then
        line_count <= line_count + 1;
      end if;
    end if;
  end process;

  -- Vertical sync

  process ( pixel_clock, reset )
  begin
    if reset = '1' then
      v_sync <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      if line_count = (V_ACTIVE + V_FRONT_PORCH -1) and
         pixel_count = (H_TOTAL - 1) then
        v_sync <= '1';
      elsif line_count = (V_TOTAL - V_BACK_PORCH - 1) and
            pixel_count = (H_TOTAL - 1) then
        v_sync <= '0';
      end if;
    end if;
  end process;

  -- Add two-cycle delays to h/v_sync to compensate for the DAC pipeline
  
  process ( pixel_clock, reset )
  begin
    if reset = '1' then
      h_sync_delay0 <= '0';
      v_sync_delay0 <= '0';
      h_sync_delay  <= '0';
      v_sync_delay  <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      h_sync_delay0 <= h_sync;
      v_sync_delay0 <= v_sync;
      h_sync_delay  <= h_sync_delay0;
      v_sync_delay  <= v_sync_delay0;
    end if;
  end process;

  -- Horizontal blanking
  
  -- The constants are offset by two to compensate for the delay
  -- in the composite blanking signal
  
  process ( pixel_clock, reset )
  begin
    if reset = '1' then
      h_blank <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      if pixel_count = (H_ACTIVE - 2) then
        h_blank <= '1';
      elsif pixel_count = (H_TOTAL - 2) then
        h_blank <= '0';
      end if;
    end if;
  end process;
  
  -- Vertical Blanking

  -- The constants are offset by two to compensate for the delay
  -- in the composite blanking signal
  
  process ( pixel_clock, reset )
  begin
    if reset = '1' then
      v_blank <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      if line_count = (V_ACTIVE - 1) and pixel_count = (H_TOTAL - 2) then
        v_blank <= '1';
      elsif line_count = (V_TOTAL - 1) and pixel_count = (H_TOTAL - 2) then
        v_blank <= '0';
      end if;
    end if;
  end process;

  -- Composite blanking
  
  process ( pixel_clock, reset )
  begin
    if reset = '1' then
      blank <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      if (h_blank or v_blank) = '1' then
        blank <= '1';
      else 
        blank <= '0';
      end if;
    end if;
  end process;

  -- RAM address counter

  -- Two control signals:

  -- reset_ram_read_address is active from the end of each field until the
  -- beginning of the next

  -- hold_vga_ram_read_address is active from the end of each line to the
  -- start of the next

  process ( pixel_clock, reset )
  begin
    if reset = '1' then
      reset_vga_ram_read_address <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      if line_count = V_ACTIVE - 1 and
         pixel_count = ( (H_TOTAL - 1 ) - SRAM_DELAY ) then
        -- reset the address counter at the end of active video		
        reset_vga_ram_read_address <= '1';
      elsif line_count = V_TOTAL - 1 and
            pixel_count =((H_TOTAL -1) - SRAM_DELAY) then
        -- re-enable the address counter at the start of active video
        reset_vga_ram_read_address <= '0';
      end if;
    end if;
  end process;

  process ( pixel_clock, reset )
  begin
    if reset = '1' then
      hold_vga_ram_read_address <= '0';
    elsif pixel_clock'event and pixel_clock = '1' then
      if pixel_count = ((H_ACTIVE - 1) - SRAM_DELAY) then
        -- hold the address counter at the end of active video
        hold_vga_ram_read_address <= '1';
      elsif pixel_count = ((H_TOTAL - 1) - SRAM_DELAY) then
        -- re-enable the address counter at the start of active video
        hold_vga_ram_read_address <= '0';
      end if;
    end if;
  end process; 

  process ( pixel_clock, reset )
  begin
    if reset = '1' then
      ram_address_counter <= "00000000000000000000";
    elsif pixel_clock'event and pixel_clock = '1' then
      if reset_vga_ram_read_address = '1' then
        ram_address_counter <= "00000000000000000000";
      elsif hold_vga_ram_read_address = '0' then
        ram_address_counter <= ram_address_counter + 1;
      end if;
    end if;
  end process;

  vga_ram_read_address <= ram_address_counter;
  
end Behavioral;
