-- -----------------------------------------------------------------------------
-- Copyright (c) 2013 Benjamin Krill <benjamin@krll.de>
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.
-- -----------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.dvi_package.all;

library work;
use work.all;

library UNISIM;
use UNISIM.Vcomponents.all;

entity atlys is
	generic ( MODE : string := "encode");
	--generic ( MODE : string := "direct");
	port (
	rstbtn_n                   : in    std_logic; -- The pink reset button
	clk100                     : in    std_logic; -- 100 MHz osicallator

	tx0_tmds                   : out   std_logic_vector(3 downto 0);
	tx0_tmds_n                 : out   std_logic_vector(3 downto 0);
	rx0_tmds                   : in    std_logic_vector(3 downto 0);
	rx0_tmds_n                 : in    std_logic_vector(3 downto 0);
	rx0_sda                    : inout std_logic;
	rx0_scl                    : in    std_logic;

	tx1_tmds                   : out   std_logic_vector(3 downto 0);
	tx1_tmds_n                 : out   std_logic_vector(3 downto 0);
	rx1_tmds                   : in    std_logic_vector(3 downto 0);
	rx1_tmds_n                 : in    std_logic_vector(3 downto 0);

	switch                     : in    std_logic_vector(1 downto 0);
	led                        : out   std_logic_vector(7 downto 0)
	);
end atlys;

architecture top of atlys is
	signal rstbtn            : std_logic;
	signal rst_n             : std_logic;
	signal rst               : std_logic;
	signal pclk              : std_logic;
	signal pclkx2            : std_logic;
	signal pclkx10           : std_logic;
	signal pllclk1           : std_logic;
	signal pllclk1bg         : std_logic;
	signal pll_lckd          : std_logic;
	signal serdesstrobe      : std_logic;
	signal tmdsclk           : std_logic;
	signal hsync             : std_logic;
	signal vsync             : std_logic;
	signal dat_en            : std_logic;
	signal valid             : std_logic_vector(COLOR_CNT-1 downto 0);
	signal ready             : std_logic_vector(COLOR_CNT-1 downto 0);
	signal psalgnerr         : std_logic;
	signal color             : color_t(COLOR_CNT-1 downto 0);
	signal sdout             : sdat_t(COLOR_CNT-1 downto 0);
	signal rx0_sda_i         : std_logic;
	signal rx0_sda_o         : std_logic;
	signal rx0_tmds_s        : std_logic_vector(3 downto 0);

	signal toggle            : std_logic;
	signal tmdsclkint        : std_logic_vector(4 downto 0);
	type tmds_data_t is array(natural range <>) of std_logic_vector(4 downto 0);
	signal tmds_data         : tmds_data_t(COLOR_CNT-1 downto 0);
	signal tmdsint           : std_logic_vector(2 downto 0);
	signal tmds_clk          : std_logic;
	signal sdata             : std_logic_vector(29 downto 0);

	signal tx0_pclkx2        : std_logic;
	signal tx0_pclkx10       : std_logic;
	signal tx0_reset_n       : std_logic;
	signal tx0_serdesstrobe  : std_logic;
	signal tx0_clkfbout      : std_logic;
	signal tx0_clkfbin       : std_logic;
	signal tx0_plllckd       : std_logic;
	signal tx0_pllclk0       : std_logic;
	signal tx0_pllclk2       : std_logic;
	signal tx0_bufpll_lock   : std_logic;
begin
	rstbtn <= not rstbtn_n;
	rst    <= not rst_n;
	led    <= ready(RED) & ready(GREEN) & ready(BLUE) & valid(RED) & valid(GREEN) & valid(BLUE)  & dat_en & rst_n;

	dvi_decoder_0: entity work.dvi_decoder
	port map (
		ext_rst        => rstbtn,

		tmdsclk_p      => rx0_tmds(3),
		tmdsclk_n      => rx0_tmds_n(3),
		din_p          => rx0_tmds(2 downto 0),
		din_n          => rx0_tmds_n(2 downto 0),

		reset_n        => rst_n,        -- rx reset
		pclk_o         => pclk,         -- regenerated pixel clock
		pclkx2_o       => pclkx2,       -- double rate pixel clock
		pclkx10_o      => pclkx10,      -- 10x pixel as IOCLK

		pll_lckd_o     => pll_lckd,     -- send pll_lckd out so it can be fed into a different BUFPLL
		serdesstrobe_o => serdesstrobe, -- BUFPLL serdesstrobe output
		tmdsclk_o      => tmdsclk,      -- TMDS cable clock

		hsync_o        => hsync,        -- hsync data
		vsync_o        => vsync,        -- vsync data
		dat_en_o       => dat_en,       -- data enable
		valid_o        => valid,
		ready_o        => ready,
		psalgnerr_o    => psalgnerr,
		sdout_o        => sdout,
		color_o        => color
	);

direct: if MODE = "direct" generate
	-- ----------------------------------------------------------
	-- Forward TMDS Clock Using OSERDES2 block
	-- ----------------------------------------------------------
	process (pclkx2, rst_n)
	begin
	if rst_n = '0' then
		toggle     <= '0';
		tmdsclkint <= (others => '0');
	elsif rising_edge(pclkx2) then
		toggle <= not toggle;
		if toggle = '1' then
			tmdsclkint <= (others => '1');
		else
			tmdsclkint <= (others => '0');
		end if;
	end if;
	end process;

	clkout: entity work.serdes_n_to_1
	generic map (SF => 5)
	port map (
		ioclk          => pclkx10,
		gclk           => pclkx2,
		rst            => rst,
		serdesstrobe_i => serdesstrobe,
		data_i         => tmdsclkint,
		data_o         => tmds_clk
	);

  	TMDS3: OBUFDS port map (I => tmds_clk, O => tx0_tmds(3), OB => tx0_tmds_n(3)); -- clock

	d: for I in 0 to COLOR_CNT-1 generate
		outI: entity work.serdes_n_to_1
		generic map (SF => 5)
		port map (
			ioclk          => pclkx10,
			gclk           => pclkx2,
			rst            => rst,
			serdesstrobe_i => serdesstrobe,
			data_i         => tmds_data(I),
			data_o         => tmdsint(I)
		);
		tx_tmdsI: OBUFDS port map (i => tmdsint(I), o => tx0_tmds(I), ob => tx0_tmds_n(I));
	end generate;

	sdata <= sdout(RED)(9 downto 5) & sdout(GREEN)(9 downto 5) & sdout(BLUE)(9 downto 5)
	       & sdout(RED)(4 downto 0) & sdout(GREEN)(4 downto 0) & sdout(BLUE)(4 downto 0);
	pixel2x: entity work.convert_30to15_fifo
	port map (
		rst     => rst,
		clk     => pclk,
		clkx2   => pclkx2,
		data_i  => sdata,
		data_o( 4 downto  0) => tmds_data(0),
		data_o( 9 downto  5) => tmds_data(1),
		data_o(14 downto 10) => tmds_data(2)
	);
end generate;

decode: if MODE = "encode" generate
	-- ----------------------------------------------------------------------------
	-- Instantiate a dedicate PLL for output port
	-- ----------------------------------------------------------------------------
	pll_oserdes_0: PLL_BASE
	generic map (
		CLKIN_PERIOD   => 10.0,
		CLKFBOUT_MULT  => 10, --set VCO to 10x of CLKIN
		CLKOUT0_DIVIDE => 1,
		CLKOUT1_DIVIDE => 10,
		CLKOUT2_DIVIDE => 5,
		COMPENSATION   => "SOURCE_SYNCHRONOUS"
	)
	port map (
		CLKFBOUT  => tx0_clkfbout,
		CLKOUT0   => tx0_pllclk0,
		CLKOUT1   => open,
		CLKOUT2   => tx0_pllclk2,
		CLKOUT3   => open,
		CLKOUT4   => open,
		CLKOUT5   => open,
		LOCKED    => tx0_plllckd,
		CLKFBIN   => tx0_clkfbin,
		CLKIN     => pclk, -- pllclk1bg,
		RST       => rst
	);

	-- ----------------------------------------------------------------------------
	-- This BUFG is needed in order to deskew between PLL clkin and clkout
	-- So the tx0 pclkx2 and pclkx10 will have the same phase as the pclk input
	-- ----------------------------------------------------------------------------
	tx0_clkfb_buf: BUFG port map(I => tx0_clkfbout, O => tx0_clkfbin);

	-- --------------------------------
	-- regenerate pclkx2 for TX
	-- --------------------------------
	tx0_pclkx2_buf: BUFG port map(I => tx0_pllclk2, O => tx0_pclkx2);

	-- --------------------------------
	-- regenerate pclkx10 for TX
	-- --------------------------------
	tx0_ioclk_buf: BUFPLL
	generic map ( DIVIDE => 5 )
	port map (
		PLLIN        => tx0_pllclk0,
		GCLK         => tx0_pclkx2,
		LOCKED       => tx0_plllckd,
		IOCLK        => tx0_pclkx10,
		SERDESSTROBE => tx0_serdesstrobe,
		LOCK         => tx0_bufpll_lock
	);
	tx0_reset_n <= tx0_bufpll_lock;

	dvi_encoder_0: entity work.dvi_encoder
	port map (
		rst_n          => tx0_reset_n,
		pclk           => pclk,
		pclkx2         => tx0_pclkx2,
		pclkx10        => tx0_pclkx10,
		serdesstrobe_i => tx0_serdesstrobe,
		color_i        => color,
		hsync_i        => hsync,
		vsync_i        => vsync,
		dat_en_i       => dat_en,
		tmds_p         => tx0_tmds,
		tmds_n         => tx0_tmds_n
	);
end generate;

	-- ----------------------------------------------------------------------------
	-- I2C EDID Emulation
	-- ----------------------------------------------------------------------------
	rx0_sda_i <= rx0_sda;
	rx0_sda   <= '0' when rx0_sda_o = '0' else 'Z';
	i2c_rom_0: entity work.i2c_rom
	port map (
		clk    => clk100,
		rst_n  => rstbtn_n,
		sda_i  => rx0_sda_i,
		sda_o  => rx0_sda_o,
		scl_i  => rx0_scl
	);

end top;
