----------------------------------------------------------------------------------
-- 		Modul pro prijmaci cast linkove vrstvy IEEE802.3 / Ethernet II			  --
----------------------------------------------------------------------------------
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;

entity mac_rx is 
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 mac_rx;

architecture Behavioral of mac_rx is

	COMPONENT crc
	PORT(
		DATA_IN : in std_logic_vector (7 downto 0);
		CRC_EN: in std_logic; 
		RESET: in std_logic; 
		CLK: in std_logic;
		CRC_OUT: out std_logic_vector (31 downto 0)
		);
	END COMPONENT;

-- Stavovy automat
type state_type is (st_idle, st_preamble, st_destin, st_data, st_check_crc, st_correct, st_error);
						  
signal rx_state, rx_next_state : state_type								:= st_idle; 
-- Indexace bajtu paketu
signal rx_count, rx_count_next: std_logic_vector(3 downto 0)		:= (others => '0');
signal rx_count_en: std_logic													:= '0';
-- CRC32	
signal crc32_i: std_logic_vector(31 downto 0)							:= (others => '0');
signal calc_crc, rst_crc, gmii_rxdv_i: std_logic											:= '0';
-- Registrova pamet pro prvnich 5 bajtu (bufferovani kvuli vypoctu crc v realnem case)
type map_type is array(4 downto 0) of std_logic_vector(7 downto 0);
signal data_i : map_type;													
-- Vstupy vystupnich registu
signal rx_data_vld_i, rx_data_bad_i, rx_data_good_i : std_logic 	:= '0';
signal rx_data_i, gmii_rxd_i: std_logic_vector(7 downto 0)			:= (others => '0');

begin
 
	Inst_crc32: crc PORT MAP(
		CLK => RX_CLK,
		DATA_IN => gmii_rxd_i,
		CRC_EN => calc_crc,
		CRC_OUT => crc32_i,
		RESET => rst_crc
	);
	
	----------------------------------------------------------------------
	-- SEKVENCNI CAST ----------------------------------------------------
	----------------------------------------------------------------------								
	process (RX_CLK)
	begin
		if rising_edge(RX_CLK) then 
			if reset = '1' then
				rx_state <= st_idle;			
			else
				rx_state <= rx_next_state;		
				rx_count <= rx_count_next;
				gmii_rxd_i <= GMII_RXD;
				gmii_rxdv_i <= GMII_RXDV;
				
				-- Posuvny registr pro bufferovani vstupu 
				data_i(4)<=data_i(3);
				data_i(3)<=data_i(2);
				data_i(2)<=data_i(1);
				data_i(1)<=data_i(0);
				data_i(0)<= gmii_rxd_i;
				
				-- Vystupni registrove signaly --
				RX_DATA_VLD <= rx_data_vld_i;
				RX_DATA_GOOD <= rx_data_good_i;			
				RX_DATA_BAD <= rx_data_bad_i;		
				RX_DATA <= rx_data_i;
				---------------------------------
			end if;
		end if;
	end process;
	
	----------------------------------------------------------------------
	-- Pocitani bajtu ----------------------------------------------------
	rx_count_next <= (others => '0') when rx_count_en = '0' else
							rx_count + 1;
	
	----------------------------------------------------------------------	
	-- Platna data na vystupu, jinak 0 -----------------------------------
	rx_data_i <= (others => '0') when rx_data_vld_i = '0' else
					  data_i(4);
	
	----------------------------------------------------------------------
	-- KOMBINACNI CAST ---------------------------------------------------
	----------------------------------------------------------------------
	mac_rx: process (rx_state, gmii_rxd_i, gmii_rxdv_i, GMII_RXER, crc32_i, rx_count, data_i)
	begin
		calc_crc <= '0';
		rst_crc <= '0';
		rx_count_en <= '0';
		rx_data_bad_i <= '0';
		rx_data_good_i <= '0';
		rx_data_vld_i <= '0';
		rx_next_state <= st_idle;

		case (rx_state) is
			----------------------------------------------------------------------			
			-- Necinnost ---------------------------------------------------------
			when st_idle => 
				rx_next_state <= st_idle;
				rst_crc <= '1';
				if (gmii_rxdv_i = '1') then rx_next_state <= st_preamble; end if;
			
			----------------------------------------------------------------------			
			-- Orezani o preambule -----------------------------------------------				
			when st_preamble =>
				rx_next_state <= st_preamble;
				if gmii_rxd_i = x"D5" then 
					rx_next_state <= st_destin; 
					rx_count_en <= '1';
				end if;
				if GMII_RXER = '1' then rx_next_state <= st_error; end if;
			
			----------------------------------------------------------------------			
			-- Kontrola cilove mac adresy ----------------------------------------								
			when st_destin =>
				rx_next_state <= st_destin;
				rx_count_en <= '1';
				calc_crc <= '1';	
				case rx_count(3 downto 0) is
					when "0101" => 	
					when "0110" => 			
						-- Overeni MAC adresy (vcetne broadcastu)
						if (data_i(4) & data_i(3) & data_i(2) & data_i(1) & data_i(0) & gmii_rxd_i = x"FFFFFFFFFFFF") OR
							(data_i(4) & data_i(3) & data_i(2) & data_i(1) & data_i(0) & gmii_rxd_i = FPGA_MAC) then 
							rx_next_state <= st_data; 
							rx_data_vld_i <= '1';
						else rx_next_state <= st_error; end if;
					when others =>
				end case;
				if GMII_RXER = '1' then rx_next_state <= st_error;end if;
			
			----------------------------------------------------------------------			
			-- Vlastni data vcetne s. adresy a typu ------------------------------												
			when st_data =>
				rx_next_state <= st_data;
				rx_data_vld_i <= '1';
				calc_crc <= '1';
				if gmii_rxdv_i = '0' then rx_next_state <= st_check_crc; 
					calc_crc <= '0'; 
					rx_count_en <= '1';
				end if;
				if GMII_RXER = '1' then rx_next_state <= st_error; end if;

			----------------------------------------------------------------------			
			-- Kontrola CRC ------------------------------------------------------								
			when st_check_crc =>
				if crc32_i = x"2144DF1C" then	rx_next_state <= st_correct; 
				else rx_next_state <= st_error; end if;
				if GMII_RXER = '1' then rx_next_state <= st_error; end if;
				
			----------------------------------------------------------------------			
			-- Overeny ramec -----------------------------------------------------				
			when st_correct =>
				rx_next_state <= st_idle;
				rx_data_good_i <= '1';
				
			----------------------------------------------------------------------			
			-- Chybny ramec ------------------------------------------------------				
			when st_error =>
				rx_next_state <= st_error;
				if gmii_rxdv_i = '0' then 
					rx_next_state <= st_idle; 
					rx_data_bad_i <= '1';
				end if;	
				
			when others =>
		end case;
	end process;

end Behavioral;

