----------------------------------------------------------------------------------
--				Implementace prijimaci casti protokolu APP (vlastni protokol) 		  --
----------------------------------------------------------------------------------
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 app_rx is

	port (
		RX_CLK				: in std_logic;		
		-- uzivatelske rozhrani výstup
		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;		
		-- z UDP vrstvy
		DATA_FROM_UDP		: in std_logic_vector(7 downto 0);
		DATA_FROM_UDP_VLD	: in std_logic;
		DATA_FROM_UDP_GOOD: in std_logic;
		DATA_FROM_UDP_BAD	: in std_logic;		
		--	ovladaci signaly pro komunikaci mezi RX a TX 
		RECV_ADR				: out std_logic_vector(APP_ADDR_WIDTH-1 downto 0);		-- potvrzeni prijeti dat do tohoto ID 
		RECV_ACK				: out std_logic;								-- zadost o odeslani potvrzení	
		RECV_ERR				: out std_logic;									
		SEND_ADR				: out std_logic_vector(15 downto 0);	-- potvrzeni prijeti dat do tohoto ID 
		SEND_ACK				: out std_logic;								-- zadost o odeslani potvrzení
		SEND_SYNC			: out std_logic;								-- potvrzeni zahajeni spojeni		
		SEND_ESYN			: out std_logic;								-- potvrzeni zahajeni spojeni		
		SEND_ERR				: out std_logic;								
		-- kontroluje zda je navazano spojeni
		CONNECTED			: out std_logic;								
		-- reset
		RESET					: in std_logic
	);

end app_rx;

architecture Behavioral of app_rx is

-- Stavy pro rx automat
type rx_state_type is (st_idle, st_listening, st_err, st_err_vld, st_header, st_data, st_data_err, st_ack, 
							  st_sync, st_esyn, st_data_ack, st_ack_vld);
 
signal rx_state, rx_next_state : rx_state_type := st_idle;
-- Registrove vystupy zprracovanych dat
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';
-- Pocitani bajtu hlavicky
signal rx_count, rx_count_next: std_logic_vector(1 downto 0):=(others=>'0');
signal rx_count_en: std_logic := '0';
-- Pocitadlo pro urceni nasledujiciho ID (GLOBAL_ID_RX)
signal rx_id, rx_id_next: std_logic_vector(15 downto 0)		:= (others=>'0');
signal rx_id_en, rx_id_rst: std_logic	:= '0';
-- Signaly pro tx vrstvu
signal send_ack_i, send_sync_i, send_esyn_i, send_err_i: std_logic		:= '0';
signal recv_ack_i, recv_err_i, load_recv_addr, rst_recv_addr: std_logic	:= '0';
-- Vyzadovano potvrzeni
signal needack, needack_next, send_ack_set, send_ack_rst: std_logic		:= '0';
-- Status pripojeni
signal connected_i: std_logic	:= '0';

begin

	process (RX_CLK)
	begin
		if (RX_CLK'event and RX_CLK = '1') then
			if (RESET = '1') then
				rx_state <= st_idle;

			else 
				rx_state <= rx_next_state;
				rx_count <= rx_count_next; 
				rx_id <= rx_id_next;
						
				needack <= needack_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;
				
				------------------------------------------------------------------------				
				-- nacteni globalnich ID pro ack nebo err			
				if load_recv_addr = '1' then 
					RECV_ADR <=  DATA_FROM_UDP(APP_ADDR_WIDTH-1 downto 0);
				elsif rst_recv_addr = '1' then 
					RECV_ADR <= (others => '0');
				end if;
				
				if send_ack_i = '1' or send_err_i = '1' then
					SEND_ADR <= rx_id;
				end if;
				
				------------------------------------------------------------------------	
				-- Info o prijeti ack nebo err -----------------------------------------
				RECV_ACK <= recv_ack_i;
				RECV_ERR <= recv_err_i;
				------------------------------------------------------------------------
				-- Prikazy pro odeslani ------------------------------------------------
				SEND_ACK <= send_ack_i;
				SEND_SYNC <= send_sync_i;
				SEND_ESYN <= send_esyn_i;
				SEND_ERR <= send_err_i;
			end if;
		end if;
	end process;
						 
							 
	needack_next <= '1' when send_ack_set = '1' else
							  '0' when send_ack_rst = '1' else 
							  needack;
				
	CONNECTED <= connected_i;
	
	------------------------------------------------------------------------	
	-- Pocitani prijatych bajtu hlavicky -----------------------------------
	rx_count_next <= (others => '0') when rx_count_en = '0' else
							rx_count + 1;
	
	------------------------------------------------------------------------	
	-- Pocitani nasledujiciho ID -------------------------------------------
	rx_id_next <= (others => '0') when rx_id_rst = '1' else
					   rx_id + 1 when rx_id_en = '1' else 
						rx_id;

	app_rx: process (rx_state, rx_count, rx_id, needack, DATA_FROM_UDP_VLD, DATA_FROM_UDP, DATA_FROM_UDP_GOOD, DATA_FROM_UDP_BAD)
	begin
		rx_next_state <= st_idle;
		rx_data_i <= x"00";
		rx_data_vld_i <= '0';
		rx_data_good_i <= '0';
		rx_data_bad_i <= '0';

		rx_count_en <= '0';
		rx_id_en <= '0';
		rx_id_rst <= '0';
	
		send_ack_i <= '0';
		send_sync_i	<= '0';
		send_esyn_i <= '0';
		send_err_i	<= '0';
	
		recv_ack_i <= '0';
		recv_err_i	<= '0';
		load_recv_addr <= '0';
		rst_recv_addr <= '0';

		send_ack_set <= '0';
		send_ack_rst <= '0';

		connected_i <= '1';	
		
		case rx_state is 		
		
			---------------------------------------------------------------------------------
			-- Stav necinnosti = odpojeno ---------------------------------------------------
			when st_idle =>
				connected_i <= '0';
				rx_id_rst <= '1';
				if DATA_FROM_UDP_VLD = '1' and DATA_FROM_UDP = x"AA" then rx_next_state <= st_sync; end if;

			---------------------------------------------------------------------------------
			-- Spojeni navazano, naslouchani na portu ---------------------------------------
			when st_listening =>
				rx_next_state <= st_listening;
				if DATA_FROM_UDP_VLD = '1' then 
					case DATA_FROM_UDP is 
						when x"AA" =>	-- sync paket
							rx_next_state <= st_sync;
							
						when x"BB" => 	-- ack  paket
							rx_next_state <= st_ack;
							
						when x"CC" =>	-- data paket
							rx_next_state <= st_header;
							
						when x"DD" =>	-- esync paket
							rx_next_state <= st_esyn;
							
						when x"EE" => 	-- error paket
							rx_next_state <= st_err;

						when others =>
					end case;
				end if;
			
			--------------------------------------------------------------------------------
			-- Zahajeni spojeni + inicializace pocatecni sekvence --------------------------
			when st_sync =>
				rx_next_state <= st_sync;
				if DATA_FROM_UDP_BAD = '1' then rx_next_state <= st_listening;
				elsif DATA_FROM_UDP_GOOD = '1' then
					send_sync_i <= '1';
					rx_id_rst <= '1';
					rst_recv_addr <= '1';
					rx_next_state <= st_listening;
				end if;
				
			-----------------------------------------------------------------------------------	
			--	Prisel potvrzovaci paket		
			when st_ack =>
				rx_count_en <= '1';
				rx_next_state <= st_ack;
				case rx_count is 
					when "01" => rx_next_state <= st_ack_vld; load_recv_addr <= '1';
					when "10" => 
					when others =>
				end case;
			
			when st_ack_vld =>
				rx_next_state <= st_ack_vld;
				if DATA_FROM_UDP_BAD = '1' then rx_next_state <= st_listening;
				elsif DATA_FROM_UDP_GOOD = '1' then
					recv_ack_i <= '1';
					rx_next_state <= st_listening;
				end if;

			-----------------------------------------------------------------------------------
			-- Hlavicka - kontrola ID + okna pro ACK
			when st_header =>
				rx_next_state <= st_header;
				rx_count_en <= '1';
				case rx_count is 
					when "00" =>
						if DATA_FROM_UDP = x"01" then send_ack_set <= '1'; end if;
					when "01" =>
						if DATA_FROM_UDP /= rx_id(15 downto 8) then rx_next_state <= st_data_err; end if;				
					when "10" =>
						rx_next_state <= st_data;
						if DATA_FROM_UDP /= rx_id(7 downto 0) then rx_next_state <= st_data_err; end if;
					when others =>
				end case;
				
			-----------------------------------------------------------------------------------
			-- Data pro vyssi vrstvu ----------------------------------------------------------
			when st_data =>	
				rx_next_state <= st_data;
				rx_data_i <= DATA_FROM_UDP;
				rx_data_vld_i <= '1';
				if DATA_FROM_UDP_VLD = '0' then 
					rx_next_state <= st_data_ack;
					rx_data_vld_i <= '0';					
				end if;
		
			------------------------------------------------------------------------------------
			-- Potvrzeni platnosti prijatych dat -----------------------------------------------
			when st_data_ack =>
				rx_next_state <= st_data_ack;
				if DATA_FROM_UDP_BAD = '1' then 
					rx_next_state <= st_listening;
					rx_data_bad_i <= '1';
					send_ack_rst <= '1';	

				elsif DATA_FROM_UDP_GOOD = '1' then
					rx_next_state <= st_listening;
					rx_data_good_i <= '1';
					rx_id_en <= '1';
					send_ack_i <= needack;
					send_ack_rst <= '1';
				end if;	
					
			------------------------------------------------------------------------------------
			-- Ukonceni spojení, dalsi prichozi data nejsou relevantni do prichodu SYNC sekvence
			when st_esyn =>	
				rx_next_state <= st_esyn;
				if DATA_FROM_UDP_BAD = '1' then rx_next_state <= st_listening;
				elsif DATA_FROM_UDP_GOOD = '1' then
					send_esyn_i <= '1';
					rx_next_state <= st_idle;
				end if;
				
			------------------------------------------------------------------------------------
			-- Prisel ERR paket s indexem poslednich spravnych dat -----------------------------
			when st_err =>
				rx_count_en <= '1';
				rx_next_state <= st_err;
				case rx_count is 
					when "01" => rx_next_state <= st_err_vld; load_recv_addr <= '1';
					when others =>
				end case;
			
			when st_err_vld =>
				rx_next_state <= st_err_vld;
				if DATA_FROM_UDP_BAD = '1' then rx_next_state <= st_listening;
				elsif DATA_FROM_UDP_GOOD = '1' then
					rx_next_state <= st_listening;
					recv_err_i <= '1';
				end if;

			------------------------------------------------------------------------------------		
			-- Prisla data s chybnym ID - odeslat ERR paket ------------------------------------
			when st_data_err =>
				rx_next_state <= st_data_err;
				if DATA_FROM_UDP_BAD = '1' then rx_next_state <= st_listening;
				elsif DATA_FROM_UDP_GOOD = '1' then
					rx_next_state <= st_listening;
					send_err_i <= '1';
				end if;

		end case;			
	end process;

end Behavioral;

