-- EMACS settings: -*-  tab-width: 2; indent-tabs-mode: t -*-
-- vim: tabstop=2:shiftwidth=2:noexpandtab
-- kate: tab-width 2; replace-tabs off; indent-width 2;
-- =============================================================================
-- Authors:				 	Patrick Lehmann
--
-- Entity:				 	TODO
--
-- Description:
-- 
-- .. TODO:: No documentation available.
--
-- License:
-- =============================================================================
-- Copyright 2007-2015 Technische Universitaet Dresden - Germany
--										 Chair of VLSI-Design, Diagnostics and Architecture
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--		http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- =============================================================================

library IEEE;
use			IEEE.STD_LOGIC_1164.all;
use			IEEE.NUMERIC_STD.all;

library PoC;
use			PoC.config.all;
use			PoC.utils.all;
use			PoC.vectors.all;
use			PoC.net.all;


entity [docs]ipv4_RX is
	generic (
		DEBUG														: boolean							:= FALSE
	);
	port (
		Clock														: in	std_logic;									--
		Reset														: in	std_logic;									--
		-- STATUS port
		Error														: out	std_logic;
		-- IN port
		In_Valid												: in	std_logic;
		In_Data													: in	T_SLV_8;
		In_SOF													: in	std_logic;
		In_EOF													: in	std_logic;
		In_Ack													: out	std_logic;
		In_Meta_rst											: out	std_logic;
		In_Meta_SrcMACAddress_nxt				: out	std_logic;
		In_Meta_SrcMACAddress_Data			: in	T_SLV_8;
		In_Meta_DestMACAddress_nxt			: out	std_logic;
		In_Meta_DestMACAddress_Data			: in	T_SLV_8;
		In_Meta_EthType									: in	T_SLV_16;
		-- OUT port
		Out_Valid												: out	std_logic;
		Out_Data												: out	T_SLV_8;
		Out_SOF													: out	std_logic;
		Out_EOF													: out	std_logic;
		Out_Ack													: in	std_logic;
		Out_Meta_rst										: in	std_logic;
		Out_Meta_SrcMACAddress_nxt			: in	std_logic;
		Out_Meta_SrcMACAddress_Data			: out	T_SLV_8;
		Out_Meta_DestMACAddress_nxt			: in	std_logic;
		Out_Meta_DestMACAddress_Data		: out	T_SLV_8;
		Out_Meta_EthType								: out	T_SLV_16;
		Out_Meta_SrcIPv4Address_nxt			: in	std_logic;
		Out_Meta_SrcIPv4Address_Data		: out	T_SLV_8;
		Out_Meta_DestIPv4Address_nxt		: in	std_logic;
		Out_Meta_DestIPv4Address_Data		: out	T_SLV_8;
		Out_Meta_Length									: out	T_SLV_16;
		Out_Meta_Protocol								: out	T_SLV_8
	);
end entity;


architecture [docs]rtl of ipv4_RX is
	attribute FSM_ENCODING						: string;

	type T_STATE is (
		ST_IDLE,
																		ST_RECEIVE_TYPE_OF_SERVICE,		ST_RECEIVE_TOTAL_LENGTH_0,		ST_RECEIVE_TOTAL_LENGTH_1,
			ST_RECEIVE_IDENTIFICATION_0,	ST_RECEIVE_IDENTIFICATION_1,	ST_RECEIVE_FLAGS,							ST_RECEIVE_FRAGMENT_OFFSET_1,
			ST_RECEIVE_TIME_TO_LIVE,			ST_RECEIVE_PROTOCOL,					ST_RECEIVE_HEADER_CHECKSUM_0,	ST_RECEIVE_HEADER_CHECKSUM_1,
			ST_RECEIVE_SOURCE_ADDRESS,
			ST_RECEIVE_DESTINATION_ADDRESS,
--			ST_RECEIVE_OPTIONS,
			ST_RECEIVE_DATA_1,						ST_RECEIVE_DATA_N,
		ST_DISCARD_FRAME,
		ST_ERROR
	);

	signal State													: T_STATE											:= ST_IDLE;
	signal NextState											: T_STATE;
	attribute FSM_ENCODING of State				: signal is ite(DEBUG, "gray", ite((VENDOR = VENDOR_XILINX), "auto", "default"));

	signal In_Ack_i												: std_logic;
	signal Is_DataFlow										: std_logic;
	signal Is_SOF													: std_logic;
	signal Is_EOF													: std_logic;

	signal Out_Valid_i										: std_logic;
	signal Out_SOF_i											: std_logic;
	signal Out_EOF_i											: std_logic;

	subtype T_IPV4_BYTEINDEX	 						is natural range 0 to 3;
	signal IP_ByteIndex										: T_IPV4_BYTEINDEX;

	signal Register_rst										: std_logic;

	-- IPv4 Basic Header
	signal HeaderLength_en								: std_logic;
	signal TypeOfService_en								: std_logic;
	signal TotalLength_en0								: std_logic;
	signal TotalLength_en1								: std_logic;
--	signal Identification_en0							: STD_LOGIC;
--	signal Identification_en1							: STD_LOGIC;
	signal Flags_en												: std_logic;
--	signal FragmentOffset_en0							: STD_LOGIC;
--	signal FragmentOffset_en1							: STD_LOGIC;
	signal TimeToLive_en									: std_logic;
	signal Protocol_en										: std_logic;
	signal HeaderChecksum_en0							: std_logic;
	signal HeaderChecksum_en1							: std_logic;
	signal SourceIPv4Address_en						: std_logic;
	signal DestIPv4Address_en							: std_logic;

	signal HeaderLength_d									: T_SLV_4													:= (others => '0');
	signal TypeOfService_d								: T_SLV_8													:= (others => '0');
	signal TotalLength_d									: T_SLV_16												:= (others => '0');
--	signal Identification_d								: T_SLV_16												:= (others => '0');
	signal Flag_DontFragment_d						: std_logic												:= '0';
	signal Flag_MoreFragmenta_d						: std_logic												:= '0';
--	signal FragmentOffset_d								: STD_LOGIC_VECTOR(12 downto 0)		:= (others => '0');
	signal TimeToLive_d										: T_SLV_8													:= (others => '0');
	signal Protocol_d											: T_SLV_8													:= (others => '0');
	signal HeaderChecksum_d								: T_SLV_16												:= (others => '0');
	signal SourceIPv4Address_d						: T_NET_IPV4_ADDRESS							:= (others => (others => '0'));
	signal DestIPv4Address_d							: T_NET_IPV4_ADDRESS							:= (others => (others => '0'));

	constant IPV4_ADDRESS_LENGTH					: positive												:= 4;			-- IPv4 -> 4 bytes
	constant IPV4_ADDRESS_READER_BITS			: positive												:= log2ceilnz(IPV4_ADDRESS_LENGTH);

	signal IPv4SeqCounter_rst							: std_logic;
	signal IPv4SeqCounter_en							: std_logic;
	signal IPv4SeqCounter_us							: unsigned(IPV4_ADDRESS_READER_BITS - 1 downto 0)		:= to_unsigned(IPV4_ADDRESS_LENGTH - 1, IPV4_ADDRESS_READER_BITS);

	signal SrcIPv4Address_Reader_rst			: std_logic;
	signal SrcIPv4Address_Reader_en				: std_logic;
	signal SrcIPv4Address_Reader_us				: unsigned(IPV4_ADDRESS_READER_BITS - 1 downto 0)		:= to_unsigned(IPV4_ADDRESS_LENGTH - 1, IPV4_ADDRESS_READER_BITS);
	signal DestIPv4Address_Reader_rst			: std_logic;
	signal DestIPv4Address_Reader_en			: std_logic;
	signal DestIPv4Address_Reader_us			: unsigned(IPV4_ADDRESS_READER_BITS - 1 downto 0)		:= to_unsigned(IPV4_ADDRESS_LENGTH - 1, IPV4_ADDRESS_READER_BITS);

begin

	In_Ack				<= In_Ack_i;
	Is_DataFlow		<= In_Valid and In_Ack_i;
	Is_SOF				<= In_Valid and In_SOF;
	Is_EOF				<= In_Valid and In_EOF;

	process(Clock)
	begin
		if rising_edge(Clock) then
			if (Reset = '1') then
				State			<= ST_IDLE;
			else
				State			<= NextState;
			end if;
		end if;
	end process;

	[docs]process(State, Is_DataFlow, Is_SOF, Is_EOF, In_Valid, In_Data, In_EOF, IPv4SeqCounter_us, Out_Ack)
	begin
		NextState									<= State;

		Error											<= '0';

		In_Ack_i									<= '0';
		Out_Valid_i								<= '0';
		Out_SOF_i									<= '0';
		Out_EOF_i									<= In_EOF;

		IPv4SeqCounter_rst				<= '0';
		IPv4SeqCounter_en					<= '0';

		-- IPv4 Basic Header
		Register_rst							<= '0';
		HeaderLength_en						<= '0';
		TypeOfService_en					<= '0';
		TotalLength_en0						<= '0';
		TotalLength_en1						<= '0';
--		Identification_en0				<= '0';
--		Identification_en1				<= '0';
		Flags_en									<= '0';
--		FragmentOffset_en0				<= '0';
--		FragmentOffset_en1				<= '0';
		TimeToLive_en							<= '0';
		Protocol_en								<= '0';
		HeaderChecksum_en0				<= '0';
		HeaderChecksum_en1				<= '0';
		SourceIPv4Address_en			<= '0';
		DestIPv4Address_en				<= '0';

		case State is
			when ST_IDLE =>
				if (Is_SOF = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
						if (In_Data(3 downto 0) = x"4") then
							HeaderLength_en			<= '1';
							NextState						<= ST_RECEIVE_TYPE_OF_SERVICE;
						else
							NextState						<= ST_DISCARD_FRAME;
						end if;
					else  -- EOF
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_TYPE_OF_SERVICE =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
						TypeOfService_en			<= '1';
						NextState							<= ST_RECEIVE_TOTAL_LENGTH_0;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_TOTAL_LENGTH_0 =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
						TotalLength_en0				<= '1';
						NextState							<= ST_RECEIVE_TOTAL_LENGTH_1;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_TOTAL_LENGTH_1 =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
						TotalLength_en1				<= '1';
						NextState							<= ST_RECEIVE_IDENTIFICATION_0;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_IDENTIFICATION_0 =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
--						Identification_en0		<= '1';
						NextState							<= ST_RECEIVE_IDENTIFICATION_1;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_IDENTIFICATION_1 =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
--						Identification_en1		<= '1';
						NextState							<= ST_RECEIVE_FLAGS;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_FLAGS =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
						Flags_en							<= '1';
--						FragmentOffset_en0		<= '1';
						NextState							<= ST_RECEIVE_FRAGMENT_OFFSET_1;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_FRAGMENT_OFFSET_1 =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
--						FragmentOffset_en1		<= '1';
						NextState							<= ST_RECEIVE_TIME_TO_LIVE;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_TIME_TO_LIVE =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
						TimeToLive_en					<= '1';
						NextState							<= ST_RECEIVE_PROTOCOL;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_PROTOCOL =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
						Protocol_en						<= '1';
						NextState							<= ST_RECEIVE_HEADER_CHECKSUM_0;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_HEADER_CHECKSUM_0 =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
						HeaderChecksum_en0		<= '1';
						NextState							<= ST_RECEIVE_HEADER_CHECKSUM_1;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_HEADER_CHECKSUM_1 =>
				IPv4SeqCounter_rst				<= '1';

				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					if (Is_EOF = '0') then
						HeaderChecksum_en1		<= '1';
						NextState							<= ST_RECEIVE_SOURCE_ADDRESS;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_SOURCE_ADDRESS =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					SourceIPv4Address_en		<= '1';
					IPv4SeqCounter_en				<= '1';

					if (Is_EOF = '0') then
						if (IPv4SeqCounter_us = 0) then
							IPv4SeqCounter_rst	<= '1';
							NextState						<= ST_RECEIVE_DESTINATION_ADDRESS;
						end if;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_DESTINATION_ADDRESS =>
				if (In_Valid = '1') then
					In_Ack_i								<= '1';

					DestIPv4Address_en			<= '1';
					IPv4SeqCounter_en				<= '1';

					if (Is_EOF = '0') then
						if (IPv4SeqCounter_us = 0) then
							IPv4SeqCounter_rst	<= '1';
							NextState						<= ST_RECEIVE_DATA_1;
						end if;
					else
						NextState							<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_DATA_1 =>
				In_Ack_i									<= Out_Ack;
				Out_Valid_i								<= In_Valid;
				Out_SOF_i									<= '1';
				Out_EOF_i									<= In_EOF;

				if (Is_DataFlow = '1') then
					if (Is_EOF = '0') then
						NextState							<= ST_RECEIVE_DATA_N;
					else
						NextState							<= ST_IDLE;
					end if;
				end if;

			when ST_RECEIVE_DATA_N =>
				In_Ack_i									<= Out_Ack;
				Out_Valid_i								<= In_Valid;
				Out_EOF_i									<= In_EOF;

				if (Is_EOF = '1') then
					NextState								<= ST_IDLE;
				end if;

			when ST_DISCARD_FRAME =>
				In_Ack_i									<= '1';

				if (Is_EOF = '1') then
					NextState								<= ST_ERROR;
				end if;

			when ST_ERROR =>
				Error											<= '1';
				NextState									<= ST_IDLE;

		end case;
	end process;


	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or Register_rst) = '1') then
				HeaderLength_d						<= (others => '0');
				TypeOfService_d						<= (others => '0');
				TotalLength_d							<= (others => '0');
--				Identification_d					<= (others => '0');
				Flag_DontFragment_d				<= '0';
				Flag_MoreFragmenta_d			<= '0';
--				FragmentOffset_d					<= (others => '0');
				TimeToLive_d							<= (others => '0');
				Protocol_d								<= (others => '0');
				HeaderChecksum_d					<= (others => '0');
				SourceIPv4Address_d				<= (others => (others => '0'));
				DestIPv4Address_d					<= (others => (others => '0'));
			else
				if (HeaderLength_en = '1') then
					HeaderLength_d									<= In_Data(3 downto 0);
				end if;

				if (TypeOfService_en = '1') then
					TypeOfService_d									<= In_Data;
				end if;

				if (TotalLength_en0 = '1') then
					TotalLength_d(15 downto 8)			<= In_Data;
				end if;
				if (TotalLength_en1 = '1') then
					TotalLength_d(7 downto 0)				<= In_Data;
				end if;

--				if (Identification_en0 = '1') then
--					Identification_d(15 downto 8)		<= In_Data;
--				end if;
--				if (Identification_en1 = '1') then
--					Identification_d(7 downto 0)		<= In_Data;
--				end if;

				if (Flags_en = '1') then
					Flag_DontFragment_d							<= In_Data(6);
					Flag_MoreFragmenta_d						<= In_Data(5);
				end if;

--				if (FragmentOffset_en0 = '1') then
--					FragmentOffset_d(12 downto 8)		<= In_Data(4 downto 0);
--				end if;
--				if (FragmentOffset_en1 = '1') then
--					FragmentOffset_d(7 downto 0)		<= In_Data;
--				end if;

				if (TimeToLive_en = '1') then
					TimeToLive_d										<= In_Data;
				end if;

				if (Protocol_en = '1') then
					Protocol_d											<= In_Data;
				end if;

				if (HeaderChecksum_en0 = '1') then
					HeaderChecksum_d(15 downto 8)		<= In_Data;
				end if;
				if (HeaderChecksum_en1 = '1') then
					HeaderChecksum_d(7 downto 0)		<= In_Data;
				end if;

				if (SourceIPv4Address_en = '1') then
					SourceIPv4Address_d(to_integer(IPv4SeqCounter_us))	<= In_Data;
				end if;

				if (DestIPv4Address_en = '1') then
					DestIPv4Address_d(to_integer(IPv4SeqCounter_us))		<= In_Data;
				end if;
			end if;
		end if;
	end process;

	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or IPv4SeqCounter_rst) = '1') then
				IPv4SeqCounter_us				<= to_unsigned(IPV4_ADDRESS_LENGTH - 1, IPV4_ADDRESS_READER_BITS);
			elsif (IPv4SeqCounter_en = '1') then
				IPv4SeqCounter_us				<= IPv4SeqCounter_us - 1;
			end if;
		end if;
	end process;

	SrcIPv4Address_Reader_rst		<= Out_Meta_rst;
	SrcIPv4Address_Reader_en		<= Out_Meta_SrcIPv4Address_nxt;
	DestIPv4Address_Reader_rst	<= Out_Meta_rst;
	DestIPv4Address_Reader_en		<= Out_Meta_DestIPv4Address_nxt;

	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or SrcIPv4Address_Reader_rst) = '1') then
				SrcIPv4Address_Reader_us		<= to_unsigned(IPV4_ADDRESS_LENGTH - 1, IPV4_ADDRESS_READER_BITS);
			elsif (SrcIPv4Address_Reader_en = '1') then
				SrcIPv4Address_Reader_us		<= SrcIPv4Address_Reader_us - 1;
			end if;
		end if;
	end process;

	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or DestIPv4Address_Reader_rst) = '1') then
				DestIPv4Address_Reader_us		<= to_unsigned(IPV4_ADDRESS_LENGTH - 1, IPV4_ADDRESS_READER_BITS);
			elsif (DestIPv4Address_Reader_en = '1') then
				DestIPv4Address_Reader_us		<= DestIPv4Address_Reader_us - 1;
			end if;
		end if;
	end process;

	In_Meta_rst												<= Out_Meta_rst;
	In_Meta_SrcMACAddress_nxt					<= Out_Meta_SrcMACAddress_nxt;
	In_Meta_DestMACAddress_nxt				<= Out_Meta_DestMACAddress_nxt;

	Out_Valid													<= Out_Valid_i;
	Out_Data													<= In_Data;
	Out_SOF														<= Out_SOF_i;
	Out_EOF														<= Out_EOF_i;
	Out_Meta_SrcMACAddress_Data				<= In_Meta_SrcMACAddress_Data;
	Out_Meta_DestMACAddress_Data			<= In_Meta_DestMACAddress_Data;
	Out_Meta_EthType									<= In_Meta_EthType;
	Out_Meta_SrcIPv4Address_Data			<= SourceIPv4Address_d(to_integer(SrcIPv4Address_Reader_us));
	Out_Meta_DestIPv4Address_Data			<= DestIPv4Address_d(to_integer(DestIPv4Address_Reader_us));
	Out_Meta_Length										<= TotalLength_d;
	Out_Meta_Protocol									<= Protocol_d;

end architecture;