----------------------------------------------------------------------------------
-- 				Modul pro linkove vrstvy IEEE802.3 / Ethernet II					  --
-- Tri mode speed 10/100/1000Mb/s, pripojeni k PHY pres rozhrani GMII/MII 		  --
----------------------------------------------------------------------------------

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

use work.my_constants.all;

library UNISIM;
use UNISIM.VComponents.all;

entity mac_core is
PORT (
	-- taktovani 125Mhz !
	GTXCLK: in std_logic;

	-- GMII 
	PHY_COL: in std_logic;
	PHY_CS: in std_logic;

	PHY_TXD: out std_logic_vector(7 downto 0);
	PHY_TXEN: out std_logic;
	PHY_TXER: out std_logic;
	PHY_TXCLK: in std_logic;			 -- 100/10Mbit
	PHY_GTXCLK: out std_logic;

	PHY_RXD: in std_logic_vector(7 downto 0);
	PHY_RXDV: in std_logic;
	PHY_RXER: in std_logic;
	PHY_RXCLK: in std_logic;
	
	PHY_RESET: out std_logic;

	-- z ip vrstvy
	TX_CLK: out std_logic;
	TX_DATA: in std_logic_vector(7 downto 0);
	TX_DATA_VLD: in std_logic;
	TX_DATA_ACK: out std_logic;
	
	-- do ip vrstvy
	RX_CLK: out std_logic;
	RX_DATA: out std_logic_vector(7 downto 0);
	RX_DATA_VLD: out std_logic;
	RX_DATA_GOOD: out std_logic;
	RX_DATA_BAD: out std_logic;
	
	-- Control / info
	COLLISION: out std_logic;
	IFG: in std_logic_vector(7 downto 0);
	FULL_DUPLEX: in std_logic;	
	SPEED1G: out std_logic;			-- 1 = 1Gb, 0 = 100/10Mb	
	
	-- global reset
	RESET: in std_logic
);	
end mac_core;

architecture Behavioral of mac_core is
COMPONENT mac_tx
PORT (
	-- taktovani 
	TX_CLK: in std_logic;			
	-- GMII 
	GMII_COL: in std_logic;
	GMII_CS: in std_logic;
	GMII_TXD: out std_logic_vector(7 downto 0);
	GMII_TXEN: out std_logic;
	GMII_TXER: out std_logic;

	-- ip vrstva
	TX_DATA: in std_logic_vector(7 downto 0);
	TX_DATA_VLD: in std_logic;
	TX_DATA_ACK: out std_logic;
		
	-- Control / info
	IFG: in std_logic_vector(7 downto 0);
	FULL_DUPLEX: in std_logic;	
	COLLISION: out std_logic;
	
	-- globalni reset 
	RESET: in std_logic
);	
END COMPONENT;
	
COMPONENT mac_rx
PORT (
	-- taktovani
	RX_CLK: in std_logic;			

	-- GMII 
	GMII_RXD: in std_logic_vector(7 downto 0);
	GMII_RXDV: in std_logic; 
	GMII_RXER: in std_logic;

	-- ip vrstva 
	RX_DATA: out std_logic_vector(7 downto 0);
	RX_DATA_VLD: out std_logic;
	RX_DATA_GOOD: out std_logic;
	RX_DATA_BAD: out std_logic;
	
	-- globalni reset
	RESET: in std_logic
);
END COMPONENT;

-- Vnitrni hodinove signaly
signal lich, lich_next, rx_clk_i, tx_clk_i, tx_clk_o, mii_clk_rx_div2, mii_clk_tx_div2: std_logic 	:= '0';
-- Vnitrni GMII pro pripojeni k MAC
signal gmii_rxd, gmii_rxd_next, gmii_txd, phy_txd_next: std_logic_vector(7 downto 0) 					:= (others => '0');
signal gmii_low, gmii_low_next, gmii_high, gmii_high_next: std_logic_vector(3 downto 0) 				:= (others => '0');
signal gmii_rxer, gmii_rxdv, gmii_rxdv_next, gmii_txen, gmii_txer, gmii_col, gmii_cs: std_logic 	:= '0';
signal mii_rxdv_reg, mii_rxdv_reg2: std_logic 																		:= '0';
-- Detekce rychlosti
signal gtx_timer, gtx_timer_next: std_logic_vector(1 downto 0)													:= (others => '0');
signal speed_i: std_logic 																									:= '0';
signal low_four, low_four_next: std_logic 																			:= '0';

begin
	
	-- Vysilaci cast MAC jadra
	Inst_mac_tx: mac_tx PORT MAP(
		TX_CLK => tx_clk_i,
		
		GMII_COL => gmii_col,
		GMII_CS => gmii_cs,
		GMII_TXD => gmii_txd,
		GMII_TXEN => gmii_txen,
		GMII_TXER => gmii_txer,
		
		TX_DATA => TX_DATA,
		TX_DATA_VLD => TX_DATA_VLD,
		TX_DATA_ACK => TX_DATA_ACK,
		
		COLLISION => COLLISION,
		IFG => IFG,
		FULL_DUPLEX => FULL_DUPLEX,
		
		RESET => reset
	);
	
	-- Prijmaci cast MAC jadra
	Inst_mac_rx: mac_rx PORT MAP(
		RX_CLK => rx_clk_i,
		
		GMII_RXD => gmii_rxd,
		GMII_RXDV => gmii_rxdv,
		GMII_RXER => gmii_rxer,
		
		RX_DATA => RX_DATA,
		RX_DATA_VLD => RX_DATA_VLD,
		RX_DATA_GOOD => RX_DATA_GOOD,
		RX_DATA_BAD => RX_DATA_BAD,

		RESET => RESET
	);

	--------------------------------------------------------------------------------------
	-- Detekce rychlosti pripojeneho ethernetu 1Gb -> speed_i = 1, 100/10Mb -> speed_i = 0 
	--------------------------------------------------------------------------------------
	process (GTXCLK, mii_clk_rx_div2)
	begin
		if mii_clk_rx_div2 = '0' then
			gtx_timer <= (others => '0');
		elsif rising_edge(GTXCLK) then
			gtx_timer <= gtx_timer_next;
		end if;
	end process;
	
	gtx_timer_next <= gtx_timer when gtx_timer = "11" else
							gtx_timer + 1;

	SPEED1G <= speed_i;
	
	--------------------------------------------------------------------------------------
	-- Zpracování přijatých MII / GMII dat -----------------------------------------------
	--------------------------------------------------------------------------------------
	process (PHY_RXCLK)
	begin 
		if rising_edge(PHY_RXCLK) then 			
			-- urceni rychlosti ---------------------------
			if mii_clk_rx_div2 = '1' then --> sestupna hrana
				if gtx_timer = "11" then speed_i <= '0';
				else speed_i <= '1'; end if;
			end if;
			-----------------------------------------------
			
			mii_rxdv_reg <= PHY_RXDV;
			mii_rxdv_reg2 <= mii_rxdv_reg;
			
			-- MII rx clk = 25MHz potrebujem 12.5MHz
			mii_clk_rx_div2 <= not mii_clk_rx_div2; 
						
			low_four <= low_four_next;			
			gmii_low <= gmii_low_next;
			gmii_high <= gmii_high_next;
			
			gmii_col <= PHY_COL;
			gmii_cs <= PHY_CS;
			gmii_rxer <= PHY_RXER;
			gmii_rxd <= gmii_rxd_next;
			gmii_rxdv <= gmii_rxdv_next;
						
		end if;
	end process;

	-------------------------------------------------------------------------------------	
	-- Prevod MII -> GMII ---------------------------------------------------------------
	low_four_next <= '0' when PHY_RXDV = '0' and  mii_rxdv_reg2 = '0' else not low_four;
	
	gmii_low_next <= PHY_RXD(3 downto 0) when low_four = '0' else gmii_low;	
	gmii_high_next <= PHY_RXD(3 downto 0) when low_four = '1' else gmii_high;
	
	gmii_rxd_next <= PHY_RXD when speed_i = '1' else
						  gmii_high & gmii_low when mii_clk_rx_div2 = '0' and low_four = '0' else
						  PHY_RXD(3 downto 0) & gmii_low when mii_clk_rx_div2 = '0' and low_four = '1' else
						  gmii_rxd;

	gmii_rxdv_next <= PHY_RXDV when speed_i = '1' else
						   mii_rxdv_reg2 when mii_clk_rx_div2 = '0' and low_four = '0' else
						   mii_rxdv_reg when mii_clk_rx_div2 = '0' and low_four = '1' else
						   gmii_rxdv;
	-------------------------------------------------------------------------------------
	-- ridici hodinove signaly pro vsechny vyssi vrstvy
	TX_CLK <= tx_clk_i;
	RX_CLK <= rx_clk_i;
	

	--------------------------------------------------------------------------------------
	-- Zpracování dat pro odeslání MII / GMII --------------------------------------------
	--------------------------------------------------------------------------------------
	process (tx_clk_o)
	begin
		if rising_edge (tx_clk_o) then
		
			-- MII tx clk (delicka 2) = 25MHz potrebujem 12.5MHz
			mii_clk_tx_div2 <= not mii_clk_tx_div2;
			lich <= lich_next;
		
			-- do PHY
			PHY_TXER <= GMII_TXER;
			PHY_TXEN <= GMII_TXEN;
			PHY_TXD <= phy_txd_next;
			
		end if;
	end process;
	
	lich_next <= '0' when GMII_TXEN = '0' else not lich;
	
	phy_txd_next <= gmii_txd when speed_i = '1' else 
						 "0000" & gmii_txd(3 downto 0) when lich = '0' else
						 "0000" & gmii_txd(7 downto 4) when lich = '1';
	
						 
	-- tx clk pro taktovani PHY 10/100Mb
	TX_O_CLOCK_MUX : BUFGMUX
			port map (O => tx_clk_o,
						I0 => PHY_TXCLK,
						I1 => GTXCLK,
						S => speed_i);
	
	-- tx clk pro taktovani MAC jadra				 
	TX_I_CLOCK_MUX : BUFGMUX
			port map (O => tx_clk_i,
						I0 => mii_clk_tx_div2,
						I1 => tx_clk_o,
						S => speed_i);
	
	-- rx clk pro takovani MAC jadra
	RX_CLOCK_MUX : BUFGMUX
			port map (O => rx_clk_i,
						I0 => mii_clk_rx_div2,
						I1 => PHY_RXCLK,
						S => speed_i); 
	
	-- tx clk pro 1000Mb
	PHY_GTXCLK <= GTXCLK when speed_i = '1' else '0';
	
	-- reset phy
	PHY_RESET <= not RESET;

end Behavioral;

