-------------------------------------------------------------------------------
--
-- Simple VGA raster display
--
-- Stephen A. Edwards
-- sedwards@cs.columbia.edu
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity vga_controller is
  
  port (
    
    reset : in std_logic;
    clk   : in std_logic;                    -- Should be 25.125 MHz  
    rgb : in std_logic_vector(29 downto 0);
    buffer_read_addr : out unsigned(15 downto 0);
    hs_in : in std_logic; 
    vs_in : in std_logic;
    sync_in : in std_logic;
    blank_in : in std_logic;
    line_sel : out std_logic;
    
    VGA_CLK,                         -- Clock
    VGA_HS,                          -- H_SYNC
    VGA_VS,                          -- V_SYNC
    VGA_BLANK,                       -- BLANK
    VGA_SYNC : out std_logic;        -- SYNC
    VGA_R,                           -- Red[9:0]
    VGA_G,                           -- Green[9:0]
    VGA_B : out unsigned(9 downto 0) -- Blue[9:0]
    );

end vga_controller;

architecture rtl of vga_controller is
  
  -- Video parameters
  
  constant HTOTAL       : integer := 800;
  constant HSYNC        : integer := 96;
  constant HBACK_PORCH  : integer := 48;
  constant HACTIVE      : integer := 640;
  constant HFRONT_PORCH : integer := 16;
  
  constant VTOTAL       : integer := 525;
  constant VSYNC        : integer := 2;
  constant VBACK_PORCH  : integer := 33;
  constant VACTIVE      : integer := 480;
  constant VFRONT_PORCH : integer := 10;

  -- Signals for the video controller
  signal Hcount : unsigned(15 downto 0);  -- Horizontal position (0-800)
  signal Vcount : unsigned(15 downto 0);  -- Vertical position (0-524)
  signal EndOfLine, EndOfField : std_logic;
  signal buff_read_addr : unsigned(15 downto 0) := "0000000000000000";
  signal line_s : std_logic := '0';

  signal vga_hblank, vga_hsync,
    vga_vblank, vga_vsync : std_logic;  -- Sync. signals

  signal pre_hs : std_logic;
  signal hs_2 : std_logic_vector(1 downto 0);
  
  signal pre_vs : std_logic;
  signal vs_2 : std_logic_vector(1 downto 0);
  
  signal mACT_HS, mACT_VS : std_logic;
  signal hc, vc : unsigned(15 downto 0) := x"0000";
  
  signal L_COUNTER, RL_COUNTER : unsigned(10 downto 0);
  signal delay : unsigned(7 downto 0);
  signal sync_en : std_logic;
  signal sync_reset : std_logic;

begin

  LSEL : process(clk)
  begin
	if Vcount(0) = '1' then
		line_s <= not line_s;
	end if;
  end process LSEL;

  -- Horizontal and vertical counters

  HCounter : process (clk)
  begin
    if rising_edge(clk) then      
      if reset = '1' then
        Hcount <= (others => '0');
        buff_read_addr <= (others => '0');
      elsif EndOfLine = '1' then
        Hcount <= (others => '0');
        buff_read_addr <= (others => '0');
      else
        buff_read_addr <= buff_read_addr + 1;
        Hcount <= Hcount + 1;
      end if;      
    end if;
  end process HCounter;
  
--  Haddr : process (clk)
--  begin
--	if rising_edge(clk) then
--	  if reset = '1' or EndOfLine <= '1' then
--		buff_read_addr <= (others => '0');
--	  else
--		buff_read_addr <= buff_read_addr + 1;
--	  end if;
--	end if;
--  end process Haddr;
		
  buffer_read_addr <= buff_read_addr;
--  EndOfLine <= '1' when Hcount = HTOTAL - 1 else '0';
  EndOfLine <= '1' when Hcount = 852 else '0';
  
  VCounter: process (clk)
  begin
    if rising_edge(clk) then      
      if reset = '1' then
        Vcount <= (others => '0');
      elsif EndOfLine = '1' then
        if EndOfField = '1' then
          Vcount <= (others => '0');
        else
          Vcount <= Vcount + 1;
        end if;
      end if;
    end if;
  end process VCounter;

--  EndOfField <= '1' when Vcount = VTOTAL - 1 else '0';
  EndOfField <= '1' when Vcount = 525 else '0';
  -- State machines to generate HSYNC, VSYNC, HBLANK, and VBLANK

  HSyncGen : process (clk, sync_en)
  begin
    if clk'event and clk = '1' then 
      if sync_en = '0' then
		Pre_HS <= '0';
		mACT_HS <= '0';
		hc <= x"0000";
		vga_hsync <= '0';
	  else
        pre_hs <= hs_in;
        hs_2 <= pre_hs & hs_in;
		if reset = '1' then
		  vga_hsync <= '0';
		elsif hs_2 = "10" then
		  mACT_HS <= '1';
		  if mACT_HS = '1' then
		    if hc < 852 then 
			  hc <= hc + 1;
			else
			  hc <= x"0000";
			  mACT_HS <= '0';
			end if;
			if hc < HSYNC then
			  vga_hsync <= '0';
			else
			  vga_hsync <= '1';
			end if;
		  end if;  
		end if;
	  end if;
    end if;
  end process HSyncGen;

  HBlankGen : process (clk)
  begin
    if rising_edge(clk) then
      if reset = '1' then
        vga_hblank <= '1';
      elsif Hcount = HSYNC + HBACK_PORCH then
        vga_hblank <= '0';
      elsif Hcount = HSYNC + HBACK_PORCH + HACTIVE then
        vga_hblank <= '1';
      end if;      
    end if;
  end process HBlankGen;

  VSyncGen : process (clk)
  begin
    if rising_edge(clk) then
      if reset = '1' then
        vga_vsync <= '1';
      elsif EndOfLine ='1' then
        if EndOfField = '1' then
          vga_vsync <= '0';
        elsif Vcount = VSYNC then
          vga_vsync <= '1';
        end if;
      end if;      
    end if;
  end process VSyncGen;

--  VSyncGen : process (clk, sync_en)
--  begin
--    if clk'event and clk = '1' then
--      if sync_en = '0' then
--		Pre_VS <= '1';
--		mACT_VS <= '0';
--		vc <= x"0000";
--		vga_vsync <= '0';
--	  else
--        pre_vs <= vs_in;
--        vs_2 <= pre_vs & vs_in;
--		if reset = '1' then
--		  vga_vsync <= '0';
--		elsif vs_2 = "01" then
--		  mACT_VS <= '1';
--		  if hc = "0000000000000001" and mACT_VS = '1' then
--	        if vc < 524 then
--			  vc <= vc + 1;
--			else
--	          vc <= x"0000";
--	        end if;
--			if vc < VSYNC then
--			  vga_vsync <= '0';
--			else
--			  vga_vsync <= '1';
--			end if;
--		  end if;
--		end if;
--	  end if;      
--    end if;
--  end process VSyncGen;
  
  LCOUNT : process(hs_in)
  begin
	if hs_in = '1' then
	  L_COUNTER <= "00000000000";
	else
	  L_COUNTER <= L_COUNTER + 1;
	end if;
  end process LCOUNT;
  
  RLCOUNT : process(vs_in)
  begin
    if vs_in = '1' then
	  RL_COUNTER <= L_COUNTER;
	end if;
  end process RLCOUNT;
  
  DELAYGen : process(sync_reset, vs_in)
  begin
	if sync_reset = '0' then
	  delay <= x"00";
	elsif vs_in = '1' and delay < 250 then
	  delay <= delay + 1;
	end if;
  end process DELAYGen;
  
  SYNCENGen : process(sync_reset, vs_in)
  begin
	if sync_reset = '0' then
	  sync_en <= '0';
	elsif vs_in = '0' and delay < 100 then
	  sync_en <= '0';
	else
	  sync_en <= '1';
	end if;
  end process SYNCENGen;

  SYNCRESETGen: process(clk)
  begin
    if RL_COUNTER = 9 then
      sync_reset <= '1';
    else
      sync_reset <= '0';
    end if;
  end process SYNCRESETGen;


  VBlankGen : process (clk)
  begin
    if rising_edge(clk) then    
      if reset = '1' then
        vga_vblank <= '1';
      elsif EndOfLine = '1' then
        if Vcount = VSYNC + VBACK_PORCH - 1 then
          vga_vblank <= '0';
        elsif Vcount = VSYNC + VBACK_PORCH + VACTIVE - 1 then
          vga_vblank <= '1';
        end if;
      end if;
    end if;
  end process VBlankGen;

  -- Registered video signals going to the video DAC

  VideoOut: process (clk, reset)
  begin
    if reset = '1' then
      VGA_R <= "0000000000";
      VGA_G <= "0000000000";
      VGA_B <= "0000000000";
    elsif clk'event and clk = '1' then
	  if Hcount >= 0 then
			VGA_R <= unsigned(rgb(29 downto 20));
			VGA_G <= unsigned(rgb(19 downto 10));
			VGA_B <= unsigned(rgb(9 downto 0));
      elsif vga_hblank = '0' and vga_vblank ='0' then
        VGA_R <= "0000000000";
        VGA_G <= "1111111111";
        VGA_B <= "0000000000";
      else
        VGA_R <= "1111111111";
        VGA_G <= "0000000000";
        VGA_B <= "0000000000";    
      end if;
    end if;
  end process VideoOut;

  
  line_sel <= line_s;
  VGA_CLK <= clk;
  VGA_HS <= hs_in;
  VGA_VS <= vs_in;
  VGA_SYNC <= '0';
  VGA_BLANK <= not (vga_vsync or vga_hsync);

end rtl;
