-------------------------------------------------------------------------------
--
-- VGA video generator
--
-- Uses the vga_timing module to generate hsync etc.
-- Massages the RAM address and requests cycles from the memory controller
-- to generate video using one byte per pixel
--
-- Cristian Soviani, Dennis Lim, and Stephen A. Edwards
--
-------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity vga is
  port (
    clk            : in std_logic;
    pix_clk        : in std_logic;
    rst            : in std_logic;
    video_data     : in std_logic_vector(15 downto 0);
    video_addr     : out std_logic_vector(19 downto 0);
    video_req      : out std_logic;
    VIDOUT_CLK     : out std_logic;
    VIDOUT_RCR     : out std_logic_vector(9 downto 0);
    VIDOUT_GY      : out std_logic_vector(9 downto 0);
    VIDOUT_BCB     : out std_logic_vector(9 downto 0);
    VIDOUT_BLANK_N : out std_logic;
    VIDOUT_HSYNC_N : out std_logic;
    VIDOUT_VSYNC_N : out std_logic);
end vga;

architecture Behavioral of vga is

  -- Fast low-voltage TTL-level I/O pad with 12 mA drive
  
  component OBUF_F_12
    port (
      O : out STD_ULOGIC;
      I : in STD_ULOGIC);
  end component;

  -- Basic edge-sensitive flip-flop
  
  component FD
    port (
      C : in std_logic;
      D : in std_logic;
      Q : out std_logic);
  end component;

  -- Force instances of FD into pads for speed
  
  attribute iob : string;
  attribute iob of FD : component is "true";

  component vga_timing
    port (
      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);
      pixel_clock          : in std_logic;
      reset                : in std_logic);
  end component;

  signal r                      : std_logic_vector (9 downto 0);
  signal g                      : std_logic_vector (9 downto 0);
  signal b                      : std_logic_vector (9 downto 0);
  signal blank                  : std_logic;
  signal hsync                  : std_logic;
  signal vsync                  : std_logic;
  signal vga_ram_read_address   : std_logic_vector(19 downto 0);
  signal vreq                   : std_logic;
  signal vreq_1                 : std_logic;
  signal load_video_word        : std_logic;
  signal vga_shreg              : std_logic_vector(15 downto 0);

begin

  st : vga_timing port map (
    pixel_clock => pix_clk,
    reset => rst,
    h_sync_delay => hsync,
    v_sync_delay => vsync,
    blank => blank,
    vga_ram_read_address => vga_ram_read_address);

  -- Video request is true when the RAM address is even
  
  -- FIXME: This should be disabled during blanking to reduce memory traffic

  vreq <= not vga_ram_read_address(0);

  -- Generate load_video_word by delaying vreq two cycles
  
  process (pix_clk)
  begin
    if pix_clk'event and pix_clk='1' then
      vreq_1 <= vreq;
      load_video_word <= vreq_1;
    end if;
  end process;

  -- Generate video_req (to the RAM controller) by delaying vreq by
  -- a cycle synchronized with the pixel clock
  
  process (clk)
  begin
    if clk'event and clk='1' then
      video_req <= pix_clk and vreq;
    end if;
  end process;

  -- The video address is the upper 19 bits from the VGA timing generator
  -- because we are using two pixels per word and the RAM address counts words

  video_addr <= '0' & vga_ram_read_address(19 downto 1);

  -- The video shift register: either load it from RAM or shift it up a byte
  
  process (pix_clk)
  begin
    if pix_clk'event and pix_clk='1' then
      if load_video_word = '1' then
        vga_shreg <= video_data;
      else
        -- Shift the low byte of read video data into the high byte
        vga_shreg <= vga_shreg(7 downto 0) & "00000000";
      end if;
    end if;
  end process;

  -- Copy the upper byte of the video word to the color signals
  -- Note that we use three bits for red and green and two for blue.

  r(9 downto 7) <= vga_shreg (15 downto 13);
  r(6 downto 0) <= "0000000";
  g(9 downto 7) <= vga_shreg (12 downto 10);
  g(6 downto 0) <= "0000000";
  b(9 downto 8) <= vga_shreg (9 downto 8);
  b(7 downto 0) <= "00000000";

  -- Video clock I/O pad to the DAC
  
  vidclk : OBUF_F_12 port map (
    O => VIDOUT_clk,
    I => pix_clk);

  -- Control signals: hsync, vsync, and blank

  hsync_ff : FD port map (
    C => pix_clk,
    D => not hsync,
    Q => VIDOUT_HSYNC_N );

  vsync_ff : FD port map (
    C => pix_clk,
    D => not vsync,
    Q => VIDOUT_VSYNC_N );

  blank_ff :  FD port map (
    C => pix_clk,
    D => not blank,
    Q => VIDOUT_BLANK_N );

  -- Three digital color signals
  
  rgb_ff : for i in 0 to 9 generate
    
    r_ff : FD port map (
      C => pix_clk,
      D => r(i),
      Q => VIDOUT_RCR(i) );
    
    g_ff : FD port map (
      C => pix_clk,
      D => g(i),
      Q => VIDOUT_GY(i) );
    
    b_ff : FD port map (
      C => pix_clk,
      D => b(i),
      Q => VIDOUT_BCB(i) );
    
  end generate;
  
end Behavioral;
