----------------------------------------------------------------------------------
--						Implementace prijimaci casti protokolu IPv4  			  	 	  --
----------------------------------------------------------------------------------

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 ip_rx is
	PORT(
		RX_CLK : 					in std_logic;

		-- do transportni vrstvy
		RX_DATA : 			out std_logic_vector(7 downto 0);
		RX_DATA_VLD :		out std_logic;
		RX_DATA_BAD: 		out std_logic;
		RX_DATA_GOOD: 		out std_logic;

		-- Info o prijatem paketu
		SOURCE_IP:					out std_logic_vector(31 downto 0):= (others => '0');
		SOURCE_PROTOCOL:			out std_logic_vector(7 downto 0)	:= (others => '0');
	
		-- ARP tabulka 
		ARP_MAC:						out std_logic_vector(47 downto 0);
		ARP_REPLY:					out std_logic;
		ARP_REQUEST:				out std_logic;

		-- z MAC vrstvy
		DATA_FROM_MAC : 			in std_logic_vector(7 downto 0);
		DATA_FROM_MAC_VLD : 		in std_logic;
		DATA_FROM_MAC_BAD: 		in std_logic;
		DATA_FROM_MAC_GOOD: 		in std_logic;

		-- reset
		RESET : 						in std_logic 
	);
end ip_rx;

architecture Behavioral of ip_rx is

	COMPONENT checksum
	PORT(
		data_in : IN std_logic_vector(7 downto 0);
		reset : IN std_logic;
		calc : IN std_logic;
		clk : IN std_logic;          
		sum_out : OUT std_logic_vector(15 downto 0)
		);
	END COMPONENT;

-- stavovy automat
type state_type is (st_idle, st_detect_type, st_arp_type ,st_ip_header, st_ip_data, st_reply,
						  st_request, st_ip_ack, st_request_ack, st_reply_ack, st_wait_end); 						  

-- stavovy automat
signal state, next_state : state_type 													:= st_idle; 

-- Vystupni reg. signaly
signal rx_data_i: std_logic_vector(7 downto 0)										:= (others => '0');
signal rx_data_vld_i, rx_data_bad_i, rx_data_good_i: std_logic					:= '0';
-- Pocitani bajtu
signal rx_count, rx_count_next: std_logic_vector(UDP_ADDR_WIDTH downto 0)	:= (others => '0');
signal rx_count_en: std_logic																:= '0';
-- nacteni zdrojove ip z hdr
signal load_ip_8, load_ip_16, load_ip_24, load_ip_32: std_logic				:= '0';
-- nacteni protokolu z hdr
signal load_protocol: std_logic															:= '0';
-- info o delce
signal ip_len: std_logic_vector(UDP_ADDR_WIDTH downto 0)							:= (others => '0');
signal load_lenght_16, load_lenght_8: std_logic										:= '0';

-- Checksuma
signal checksum_i: std_logic_vector(15 downto 0)									:= (others => '0');
signal calc_sum, rst_sum: std_logic														:= '0';
-- nacteni mac a ip z arp
signal load_mac_48, load_mac_40, load_mac_32, 
		 load_mac_24, load_mac_16, load_mac_8: std_logic 							:= '0';

begin
	-- Kontrolni soucet
	Inst_checksum: checksum PORT MAP(
		sum_out => checksum_i,
		data_in => DATA_FROM_MAC,
		reset => rst_sum,
		calc => calc_sum,
		clk => RX_CLK
	);
	
	
	SYNC_PROC: process (RX_CLK)
   begin
      if (RX_CLK'event and RX_CLK = '1') then
			if reset = '1' then 
				state <= st_idle;

			else
				state <= next_state;				
				rx_count <= rx_count_next;
				
				-- Nactení potrebných hodnot z hlavicky							
				if load_lenght_16 = '1' then ip_len(UDP_ADDR_WIDTH downto 8) <=  DATA_FROM_MAC(UDP_ADDR_WIDTH-8 downto 0); end if;
				if load_lenght_8 = '1' then ip_len(7 downto 0) <=  DATA_FROM_MAC; end if;
				-- zdrojova adresa
				if load_ip_32 = '1' then SOURCE_IP(31 downto 24) <= DATA_FROM_MAC; end if;
				if load_ip_24 = '1' then SOURCE_IP(23 downto 16) <= DATA_FROM_MAC; end if;
				if load_ip_16 = '1' then SOURCE_IP(15 downto 8) <= DATA_FROM_MAC; end if;
				if load_ip_8 = '1' then SOURCE_IP(7 downto 0) <= DATA_FROM_MAC;	end if;			
				-- protokol
				if load_protocol = '1' then SOURCE_PROTOCOL <= DATA_FROM_MAC; end if;
				-- arp mac
				if load_mac_48 = '1' then ARP_MAC(47 downto 40) <= DATA_FROM_MAC; end if;
				if load_mac_40 = '1' then ARP_MAC(39 downto 32) <= DATA_FROM_MAC; end if;
				if load_mac_32 = '1' then ARP_MAC(31 downto 24) <= DATA_FROM_MAC; end if;
				if load_mac_24 = '1' then ARP_MAC(23 downto 16) <= DATA_FROM_MAC; end if;
				if	load_mac_16 = '1' then ARP_MAC(15 downto 8) <= DATA_FROM_MAC; end if;
				if load_mac_8 = '1' then ARP_MAC(7 downto 0) <= DATA_FROM_MAC; end if;

				-- Výstupní registry
				RX_DATA <= rx_data_i;
				RX_DATA_VLD <= rx_data_vld_i;
				RX_DATA_GOOD <= rx_data_good_i;
				RX_DATA_BAD <= rx_data_bad_i;
			
			end if;
      end if;
   end process;

	-- vstup vystupniho registru ovladany vld signalem
	rx_data_i <= DATA_FROM_MAC when rx_data_vld_i = '1' else (others => '0');

	-- Citac pro pocitani bajtu paketu 
	rx_count_next <= rx_count + 1 when rx_count_en = '1' else 
						  (others => '0');							
	
	---------------------------------------------------------------------
	-- FSM pro prijem dat -----------------------------------------------		
	---------------------------------------------------------------------	
	ip_rx: process (state, rx_count, ip_len, checksum_i,
						 DATA_FROM_MAC, DATA_FROM_MAC_VLD, DATA_FROM_MAC_BAD, DATA_FROM_MAC_GOOD)
	begin
		next_state <= st_idle;
		rx_data_vld_i <= '0';
		rx_data_bad_i <= '0';
		rx_data_good_i <= '0';
		rx_count_en <= '0';
		calc_sum <= '0';
		rst_sum <= '0';
		ARP_REPLY <= '0';
		ARP_REQUEST <= '0';

		load_mac_48 <= '0';
		load_mac_40 <= '0';
		load_mac_32 <= '0';
		load_mac_24 <= '0';
		load_mac_16 <= '0';
		load_mac_8 <= '0';
		
		load_ip_32 <= '0';
		load_ip_24 <= '0';
		load_ip_16 <= '0';
		load_ip_8 <= '0';
		
		load_lenght_16 <= '0';
		load_lenght_8 <= '0';
		
		load_protocol <= '0';

		case (state) is         
			---------------------------------------------------------------------
			-- Idle -------------------------------------------------------------
			when st_idle => 
				next_state <= st_idle;
				rst_sum <= '1';
				if DATA_FROM_MAC_VLD = '1' then 
					next_state <= st_detect_type; 
					rx_count_en <= '1';
				end if;
		
			---------------------------------------------------------------------
			-- Detekce typu (MAC adresa nemusi overovat, filtruje to MAC core) --
			when st_detect_type =>
				next_state <= st_detect_type;
				rx_count_en <= '1';
				case rx_count(4 downto 0) is 
					when "01110" => 
						if DATA_FROM_MAC = IP_VERSION & IP_IHL then 
							next_state <= st_ip_header; 
							rx_count_en <= '0'; 
							calc_sum <= '1';
						elsif DATA_FROM_MAC = x"00" then next_state <= st_arp_type;
						else next_state <= st_wait_end; end if;				
					when others =>
				end case;

			---------------------------------------------------------------------
			-- detekuje typ dotazu (request, reply) + overeni hlavicky ----------
			when st_arp_type =>
				next_state <= st_arp_type;
				rx_count_en <= '1';
				case rx_count(4 downto 0) is 
					when "01111" => -- typ protokolu x0800 - IP
						if DATA_FROM_MAC /= x"01" then next_state <= st_wait_end; end if;				
					when "10000" => 
						if DATA_FROM_MAC /= x"08" then next_state <= st_wait_end; end if;
					when "10001" => -- delka MAC adresy = 06
						if DATA_FROM_MAC /= x"00" then next_state <= st_wait_end; end if;					
					when "10010" => -- delka IP adresy = 04
						if DATA_FROM_MAC /= x"06" then next_state <= st_wait_end; end if;
					when "10011" => -- typ operace = x0001 pro ARP request
						if DATA_FROM_MAC /= x"04" then next_state <= st_wait_end; end if;					
					when "10100" => 
						if DATA_FROM_MAC /= x"00" then next_state <= st_wait_end; end if;
					when "10101" => -- typ dotazu
						rx_count_en <= '0';
						if DATA_FROM_MAC = x"01" then next_state <= st_request;
						elsif DATA_FROM_MAC = x"02" then next_state <= st_reply;
						else next_state <= st_wait_end; end if;
					
					when others =>
				end case;
			
			---------------------------------------------------------------------
			-- ARP dotaz - overeni zda je smerovan na tuto IP adresu ------------
			when st_request =>
				rx_count_en <= '1';
				next_state <= st_request;
				case rx_count(4 downto 0) is
					-- Nacteni zdrojove MAC adresy
					when "00000" => load_mac_48 <= '1';
					when "00001" => load_mac_40 <= '1';
					when "00010" => load_mac_32 <= '1';
					when "00011" => load_mac_24 <= '1';
					when "00100" => load_mac_16 <= '1';
					when "00101" => load_mac_8 <= '1';
					-- Macteni zdrojove IP adresy
					when "00110" => load_ip_32 <= '1';
					when "00111" => load_ip_24 <= '1';
					when "01000" => load_ip_16 <= '1';
					when "01001" => load_ip_8 <= '1';
					-- Test cilove ip (01011 - 10000 je mac)
					when "10000" =>
						if DATA_FROM_MAC /= FPGA_IP(31 downto 24) then next_state <= st_wait_end;end if;
					when "10001" => 
						if DATA_FROM_MAC /= FPGA_IP(23 downto 16) then next_state <= st_wait_end;end if;
					when "10010" => 
						if DATA_FROM_MAC /= FPGA_IP(15 downto 8) then next_state <= st_wait_end;end if;
					when "10011" => 
						if DATA_FROM_MAC /= FPGA_IP(7 downto 0) then next_state <= st_wait_end;
						else next_state <= st_request_ack; end if;			
					when others =>
				end case;
				
			---------------------------------------------------------------------
			-- ARP odpoved ------------------------------------------------------
			when st_reply =>
				rx_count_en <= '1';
				next_state <= st_reply;					
				case rx_count(4 downto 0) is
					-- Nacteni zdrojove MAC adresy
					when "00000" => load_mac_48 <= '1';
					when "00001" => load_mac_40 <= '1';
					when "00010" => load_mac_32 <= '1';
					when "00011" => load_mac_24 <= '1';
					when "00100" => load_mac_16 <= '1';
					when "00101" => load_mac_8 <= '1';
					-- Macteni zdrojove IP adresy
					when "00110" => load_ip_32 <= '1';
					when "00111" => load_ip_24 <= '1';
					when "01000" => load_ip_16 <= '1';
					when "01001" => load_ip_8 <= '1';
										 next_state <= st_reply_ack;			
					when others =>
				end case;
			
			---------------------------------------------------------------------
			-- IP paket - hlavicka ----------------------------------------------
			when st_ip_header =>
				next_state <= st_ip_header;
				rx_count_en <= '1';
				calc_sum <= '1';
				
				case rx_count(4 downto 0) is
					when "00001" => load_lenght_16 <= '1';
					when "00010" => load_lenght_8 <= '1';
					when "01000" => load_protocol <= '1';
					when "01001" => 
					when "01010" => 				
					-- Nacteni zdrojove IP adresy
					when "01011" => load_ip_32 <= '1';
					when "01100" => load_ip_24 <= '1';
					when "01101" => load_ip_16 <= '1';
					when "01110" => load_ip_8 <= '1';
					-- Overeni IP cilove ip adresy

					when "01111" => 
						if DATA_FROM_MAC /= FPGA_IP(31 downto 24) then next_state <= st_wait_end;end if;
					when "10000" => 
						if DATA_FROM_MAC /= FPGA_IP(23 downto 16) then next_state <= st_wait_end;end if;
					when "10001" => 
						if DATA_FROM_MAC /= FPGA_IP(15 downto 8) then next_state <= st_wait_end;end if;
					when "10010" => 
						if DATA_FROM_MAC /= FPGA_IP(7 downto 0) then next_state <= st_wait_end;
						else next_state <= st_ip_data; end if;
					when others =>
				end case;
					
			---------------------------------------------------------------------
			-- IP paket - data --------------------------------------------------
			when st_ip_data =>
				next_state <= st_ip_data;
				rx_count_en <= '1';
				rx_data_vld_i <= '1';
				if (rx_count = ip_len - 1) or DATA_FROM_MAC_VLD = '0' then 
					next_state <= st_ip_ack; 
					rx_data_vld_i <= '0'; 
				end if;
		
			---------------------------------------------------------------------
			-- Cekat na potvrzeni framu -----------------------------------------
			when st_ip_ack =>
				next_state <= st_ip_ack;
				if checksum_i /= x"FFFF" or DATA_FROM_MAC_BAD = '1' then 
					next_state <= st_wait_end;
					rx_data_bad_i <= '1';
					
				elsif DATA_FROM_MAC_GOOD = '1' then 
					next_state <= st_wait_end;
					rx_data_good_i <= '1';
				end if;
						
			---------------------------------------------------------------------				
			-- cekat na konec framu --------------------------------------------- 
         when st_request_ack => 
				next_state <= st_request_ack;
				if DATA_FROM_MAC_GOOD = '1' then 
					next_state <= st_wait_end;
					arp_request <= '1';
				elsif DATA_FROM_MAC_BAD = '1' then 
					next_state <= st_wait_end;
				end if;
				
			---------------------------------------------------------------------							
			-- Cekat na potvrzeni framu (bad/good frame) ------------------------
         when st_reply_ack => 
				next_state <= st_reply_ack;
				if DATA_FROM_MAC_GOOD = '1' then 
					next_state <= st_wait_end;
					ARP_REPLY <= '1';
				elsif DATA_FROM_MAC_BAD = '1' then 
					next_state <= st_wait_end;
				end if;
			
			---------------------------------------------------------------------				
			-- Cekat na konec framu ---------------------------------------------
         when st_wait_end => 
				next_state <= st_wait_end;
				if DATA_FROM_MAC_VLD = '0' then next_state <= st_idle; end if;

      end case;      
   end process;

end Behavioral;