----------------------------------------------------------------------------------
--							Implementace prijimaci casti protokolu UDP  			  		  --
----------------------------------------------------------------------------------
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 udp_rx is
	port (
		CLK_RX : IN std_logic;
		DATA_FROM_IP : IN std_logic_vector(7 downto 0);
		DATA_FROM_IP_VLD : IN std_logic;
		DATA_FROM_IP_GOOD : IN std_logic;
		DATA_FROM_IP_BAD : IN 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;
		
		SOURCE_IP : IN std_logic_vector(31 downto 0);	
		RESET : IN std_logic          
	);
end udp_rx;

architecture Behavioral of udp_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 rx_state_type is (st_idle, st_header, st_data, st_wait, st_add_hdr, st_check_sum, st_error); 
signal rx_state, rx_next_state : rx_state_type;
-- Indexace bajtu paketu
signal rx_count, rx_count_next: std_logic_vector(UDP_ADDR_WIDTH-1 downto 0):= (others=>'0');
signal rx_data_len: std_logic_vector(UDP_ADDR_WIDTH-1 downto 0)				:= (others=>'0');
signal rx_count_en: std_logic																:= '0';	
-- Checksuma
signal data_to_sum: std_logic_vector(7 downto 0)									:= (others=>'0');
signal rx_checksum: std_logic_vector(15 downto 0)									:= (others=>'0');
signal sum_reset, compute_en: std_logic												:= '0';
-- Vstupy vystupnich registu
signal rx_data_i: std_logic_vector(7 downto 0) 										:= (others=>'0');
signal rx_data_vld_i, rx_data_good_i, rx_data_bad_i: std_logic					:= '0';
-- doplneni vektoru delky do 16bit slova
signal data_len_pad: std_logic_vector(15 - UDP_ADDR_WIDTH downto 0)			:= (others=>'0'); 
-- identifikace zakazane checksumy z paketu
signal sum_enabled, sum_en_next, sum_en_set: std_logic							:= '0'; 

begin

	Inst_checksum: checksum PORT MAP(
		sum_out => rx_checksum,
		data_in => data_to_sum,
		reset => sum_reset,
		calc => compute_en,
		clk => CLK_RX
	);
	
	data_len_pad <= (others => '0');
	
	--------------------------------------------------------------------------
	-- Sekvencni cast --------------------------------------------------------
	--------------------------------------------------------------------------		
	process (CLK_RX)
	begin
		if (CLK_RX'event and CLK_RX = '1') then
			if (RESET = '1') then
				rx_state <= st_idle;
			else 
				-- Stavovy automat
				rx_state <= rx_next_state;
				
				-- Pametova cast pro udaje z hlavicky
				if rx_count = 4 then rx_data_len(UDP_ADDR_WIDTH-1 downto 8) <= DATA_FROM_IP(UDP_ADDR_WIDTH-9 downto 0);	-- delka H
				elsif rx_count = 5 then rx_data_len(7 downto 0) <= DATA_FROM_IP;					-- delka L
				end if;
				
				rx_count <= rx_count_next;
				sum_enabled <= sum_en_next;
				
				-- Output reg. 
				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;
	
	rx_data_i <= (others => '0') when rx_data_vld_i = '0' else DATA_FROM_IP;
	
	sum_en_next <= '1' when sum_en_set = '1' else
						'0' when sum_reset = '1' else
						sum_enabled;
	
	---------------------------------------------------------------------
	-- Pocitani bajtu paketu --------------------------------------------			
	rx_count_next <= (others => '0') when rx_count_en = '0' else
							rx_count + 1;
						

	--------------------------------------------------------------------------
	-- Kombinacni cast - stavovy automat ypracovani prijatych dat ------------
	--------------------------------------------------------------------------		
	UDP_rx: process (rx_state, rx_checksum, rx_data_len, rx_count, sum_enabled,
						  DATA_FROM_IP_VLD, DATA_FROM_IP, DATA_FROM_IP_GOOD, DATA_FROM_IP_BAD, SOURCE_IP)
	begin
		rx_next_state <= st_idle;
		rx_data_vld_i <= '0';
		rx_data_good_i <= '0';
		rx_data_bad_i <= '0';
		rx_count_en <= '0';
		sum_reset <= '0';
		compute_en <= '0';
		data_to_sum <= x"00";
		sum_en_set <= '0';
	
		case rx_state is 		
		
			---------------------------------------------------------------------
			-- Idle -------------------------------------------------------------		
			when st_idle =>
				rx_next_state <= st_idle; 
				sum_reset <= '1';
				if DATA_FROM_IP_VLD = '1' then 
					rx_next_state <= st_header; 
					data_to_sum <= DATA_FROM_IP;
					rx_count_en <= '1'; 
					sum_reset <= '0';
					compute_en <= '1';
				end if;		
			
			---------------------------------------------------------------------
			-- Vyhodnoceni hlavicky (cilovy port + summ) ------------------------		
			when st_header =>		
				rx_next_state <= st_header;
				data_to_sum <= DATA_FROM_IP;
				rx_count_en <= '1';
				compute_en <= '1';			
				case rx_count(2 downto 0) is 
					when "010" =>
						if DATA_FROM_IP /= FPGA_PORT(15 downto 8) then rx_next_state <= st_error; end if;	
					when "011" =>
						if DATA_FROM_IP /= FPGA_PORT(7 downto 0) then rx_next_state <= st_error; end if;	
					when "110" => 
						if DATA_FROM_IP /= x"00" then sum_en_set <= '1'; end if;	
					when "111" =>
						if DATA_FROM_IP /= x"00" then sum_en_set <= '1'; end if;						
						rx_next_state <= st_data;		
					when others =>
				end case;	
			
			---------------------------------------------------------------------
			-- Vlastni UDP data  ------------------------------------------------			
			when st_data =>	
				rx_next_state <= st_data;
				data_to_sum <= DATA_FROM_IP;
				rx_data_vld_i <= '1';
				rx_count_en <= '1';
				compute_en <= '1';
				if rx_count = rx_data_len then 
					rx_data_vld_i <= '0';
					rx_next_state <= st_wait; 
					data_to_sum <= x"00";
					rx_count_en <= '0'; 			
					compute_en <= '0';
				end if;
				
			---------------------------------------------------------------------
			-- Cekat na potvrzeni z IP vrstvy -----------------------------------
			when st_wait =>
				rx_next_state <= st_wait;
				if DATA_FROM_IP_BAD = '1' then 
					rx_next_state <= st_idle;
					rx_data_bad_i <= '1';
				elsif DATA_FROM_IP_GOOD = '1' then	
					if sum_enabled = '0' then 
						rx_next_state <= st_idle;
						rx_data_good_i <= '1';
					else rx_next_state <= st_add_hdr; end if;
				end if;

			---------------------------------------------------------------------
			-- Dopocitat do checksumy data z pseudohlavicky ---------------------
			when st_add_hdr =>
				rx_next_state <= st_add_hdr;
				rx_count_en <= '1';
				compute_en <= '0';
				case rx_count(3 downto 0) is 
					when x"0" =>	-- prvne se yapocita delka protoze se prepise na 4,5 hodnote citace
					when x"1" => data_to_sum <= data_len_pad&rx_data_len(UDP_ADDR_WIDTH-1 downto 8);compute_en <= '1';					
					when x"2" => data_to_sum <= rx_data_len(7 downto 0);compute_en <= '1';
					when x"3" => data_to_sum <= SOURCE_IP(31 downto 24);compute_en <= '1';
					when x"4" => data_to_sum <= SOURCE_IP(23 downto 16);compute_en <= '1';
					when x"5" => data_to_sum <= SOURCE_IP(15 downto 8);compute_en <= '1';					
					when x"6" => data_to_sum <= SOURCE_IP(7 downto 0);compute_en <= '1';	
					when x"7" => data_to_sum <= FPGA_IP(31 downto 24);compute_en <= '1';
					when x"8" => data_to_sum <= FPGA_IP(23 downto 16);compute_en <= '1';
					when x"9" => data_to_sum <= FPGA_IP(15 downto 8);compute_en <= '1';					
					when x"A" => data_to_sum <= FPGA_IP(7 downto 0);compute_en <= '1';	
					when x"B" => data_to_sum <= x"00";compute_en <= '1';					
					when x"C" => data_to_sum <= x"11";compute_en <= '1';	
					when x"D" =>   -- cekat 3 takty na vysledek
					when x"E" =>
					when x"F" =>  rx_next_state <= st_check_sum;
					when others =>
				end case;	
		
			---------------------------------------------------------------------
			-- Otestovat checksumu ----------------------------------------------	
			when st_check_sum =>
				rx_next_state <= st_idle;
				if rx_checksum = x"FFFF" then rx_data_good_i <= '1'; 
				else rx_data_bad_i <= '1';	end if;
			
			---------------------------------------------------------------------
			-- Nesedi cilovy port - cekat na konec ------------------------------
			when st_error =>
				rx_next_state <= st_error;
				sum_reset <= '1';
				if DATA_FROM_IP_VLD = '0' then rx_next_state <= st_idle; end if;	
			
		end case;			
	end process;

end Behavioral;