-- 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]icmpv4_RX is
	generic (
		DEBUG													: boolean											:= FALSE
	);
	port (
		Clock													: in	std_logic;																	--
		Reset													: in	std_logic;																	--
		-- CSE interface
		Command												: in	T_NET_ICMPV4_RX_COMMAND;
		Status												: out	T_NET_ICMPV4_RX_STATUS;
		Error													: out	T_NET_ICMPV4_RX_ERROR;
		-- 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_SrcIPv4Address_nxt		: out	std_logic;
		In_Meta_SrcIPv4Address_Data		: in	T_SLV_8;
		In_Meta_DestIPv4Address_nxt		: out	std_logic;
		In_Meta_DestIPv4Address_Data	: in	T_SLV_8;
		In_Meta_Length								: in	T_SLV_16;
		-- OUT Port
		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_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_Type									: out	T_SLV_8;
		Out_Meta_Code									: out	T_SLV_8;
		Out_Meta_Identification				: out	T_SLV_16;
		Out_Meta_SequenceNumber				: out	T_SLV_16;
		Out_Meta_Payload_nxt					: in	std_logic;
		Out_Meta_Payload_last					: out	std_logic;
		Out_Meta_Payload_Data					: out	T_SLV_8
	);
end entity;


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

	type T_STATE		is (
		ST_IDLE,
			ST_RECEIVE_ECHO_CODE,
				ST_RECEIVE_ECHO_CHECKSUM_0,
				ST_RECEIVE_ECHO_CHECKSUM_1,
				ST_RECEIVE_ECHO_IDENTIFIER_0,
				ST_RECEIVE_ECHO_IDENTIFIER_1,
				ST_RECEIVE_ECHO_SEQ_NUMBER_0,
				ST_RECEIVE_ECHO_SEQ_NUMBER_1,
				ST_RECEIVE_ECHO_DATA,
				ST_RECEIVE_ECHO_COMPLETE,
		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 Register_rst										: std_logic;

	-- UDP header fields
	signal Type_en												: std_logic;
	signal Code_en												: std_logic;
	signal Checksum_en0										: std_logic;
	signal Checksum_en1										: std_logic;
	signal Identification_en0							: std_logic;
	signal Identification_en1							: std_logic;
	signal SequenceNumber_en0							: std_logic;
	signal SequenceNumber_en1							: std_logic;

	signal Type_d													: T_SLV_8											:= (others => '0');
	signal Code_d													: T_SLV_8											:= (others => '0');
	signal Checksum_d											: T_SLV_16										:= (others => '0');
	signal Identification_d								: T_SLV_16										:= (others => '0');
	signal SequenceNumber_d								: T_SLV_16										:= (others => '0');

	signal MetaFIFO_put										: std_logic;
	signal MetaFIFO_DataIn								: std_logic_vector(8 downto 0);
	signal MetaFIFO_Full									: std_logic;
	signal MetaFIFO_Commit								: std_logic;
	signal MetaFIFO_Rollback							: std_logic;
--	signal MetaFIFO_Valid									: STD_LOGIC;
	signal MetaFIFO_DataOut								: std_logic_vector(8 downto 0);
	signal MetaFIFO_got										: std_logic;

begin

	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, Command, In_Valid, In_Data, In_SOF, In_EOF, Out_Meta_rst, Out_Meta_Payload_nxt)
	begin
		NextState													<= State;

		Status														<= NET_ICMPV4_RX_STATUS_IDLE;
		Error															<= NET_ICMPV4_RX_ERROR_NONE;

		In_Ack														<= '0';
		In_Meta_rst												<= '0';
		In_Meta_SrcMACAddress_nxt					<= '0';
		In_Meta_DestMACAddress_nxt				<= '0';
		In_Meta_SrcIPv4Address_nxt				<= '0';
		In_Meta_DestIPv4Address_nxt				<= '0';

		Register_rst											<= to_sl(Command = NET_ICMPV4_RX_CMD_CLEAR);
		Type_en														<= '0';
		Code_en														<= '0';
		Checksum_en0											<= '0';
		Checksum_en1											<= '0';
		Identification_en0								<= '0';
		Identification_en1								<= '0';
		SequenceNumber_en0								<= '0';
		SequenceNumber_en1								<= '0';

		MetaFIFO_put											<= '0';
		MetaFIFO_DataIn(In_Data'range)		<= In_Data;
		MetaFIFO_DataIn(In_Data'length)		<= In_EOF;
		MetaFIFO_got											<= '0';
		MetaFIFO_Commit										<= '0';
		MetaFIFO_Rollback									<= '0';

		case State is
			when ST_IDLE =>
				if ((In_Valid and In_SOF) = '1') then
					In_Ack											<= '1';

					if (In_EOF = '0') then
						Type_en										<= '1';

						if (In_Data = C_NET_ICMPV4_TYPE_ECHO_REPLY) then
							NextState								<= ST_RECEIVE_ECHO_CODE;
						elsif (In_Data = C_NET_ICMPV4_TYPE_ECHO_REQUEST) then
							NextState								<= ST_RECEIVE_ECHO_CODE;
						else
							NextState								<= ST_DISCARD_FRAME;
						end if;
					else
						NextState									<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_ECHO_CODE =>
				Status												<= NET_ICMPV4_RX_STATUS_RECEIVING;

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

					if (In_EOF = '0') then
						Code_en										<= '1';

						if (In_Data = C_NET_ICMPV4_CODE_ECHO_REPLY) then
							NextState								<= ST_RECEIVE_ECHO_CHECKSUM_0;
						elsif (In_Data = C_NET_ICMPV4_CODE_ECHO_REQUEST) then
							NextState								<= ST_RECEIVE_ECHO_CHECKSUM_0;
						else
							NextState								<= ST_DISCARD_FRAME;
						end if;
					else
						NextState									<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_ECHO_CHECKSUM_0 =>
				Status												<= NET_ICMPV4_RX_STATUS_RECEIVING;

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

					if (In_EOF = '0') then
						Checksum_en0							<= '1';
						NextState									<= ST_RECEIVE_ECHO_CHECKSUM_1;
					else
						NextState									<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_ECHO_CHECKSUM_1 =>
				Status												<= NET_ICMPV4_RX_STATUS_RECEIVING;

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

					if (In_EOF = '0') then
						Checksum_en1							<= '1';
						NextState									<= ST_RECEIVE_ECHO_IDENTIFIER_0;
					else
						NextState									<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_ECHO_IDENTIFIER_0 =>
				Status												<= NET_ICMPV4_RX_STATUS_RECEIVING;

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

					if (In_EOF = '0') then
						Identification_en0				<= '1';
						NextState									<= ST_RECEIVE_ECHO_IDENTIFIER_1;
					else
						NextState									<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_ECHO_IDENTIFIER_1 =>
				Status												<= NET_ICMPV4_RX_STATUS_RECEIVING;

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

					if (In_EOF = '0') then
						Identification_en1				<= '1';
						NextState									<= ST_RECEIVE_ECHO_SEQ_NUMBER_0;
					else
						NextState									<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_ECHO_SEQ_NUMBER_0 =>
				Status												<= NET_ICMPV4_RX_STATUS_RECEIVING;

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

					if (In_EOF = '0') then
						SequenceNumber_en0				<= '1';
						NextState									<= ST_RECEIVE_ECHO_SEQ_NUMBER_1;
					else
						NextState									<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_ECHO_SEQ_NUMBER_1 =>
				Status												<= NET_ICMPV4_RX_STATUS_RECEIVING;

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

					if (In_EOF = '0') then
						SequenceNumber_en1				<= '1';
						NextState									<= ST_RECEIVE_ECHO_DATA;
					else
						NextState									<= ST_ERROR;
					end if;
				end if;

			when ST_RECEIVE_ECHO_DATA =>
				Status												<= NET_ICMPV4_RX_STATUS_RECEIVING;

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

					MetaFIFO_put								<= '1';

					if (In_EOF = '1') then
						MetaFIFO_Commit						<= '1';
						NextState									<= ST_RECEIVE_ECHO_COMPLETE;
					end if;
				end if;

			when ST_RECEIVE_ECHO_COMPLETE =>
				if (Code_d = C_NET_ICMPV4_TYPE_ECHO_REPLY) then
					Status											<= NET_ICMPV4_RX_STATUS_RECEIVED_ECHO_REPLY;
				elsif (Code_d = C_NET_ICMPV4_TYPE_ECHO_REQUEST) then
					Status											<= NET_ICMPV4_RX_STATUS_RECEIVED_ECHO_REQUEST;
				end if;

				In_Meta_SrcMACAddress_nxt			<= Out_Meta_SrcMACAddress_nxt;
				In_Meta_DestMACAddress_nxt		<= Out_Meta_DestMACAddress_nxt;
				In_Meta_SrcIPv4Address_nxt		<= Out_Meta_SrcIPv4Address_nxt;
				In_Meta_DestIPv4Address_nxt		<= Out_Meta_DestIPv4Address_nxt;

				MetaFIFO_got									<= Out_Meta_Payload_nxt;
				MetaFIFO_Rollback							<= Out_Meta_rst;

				if (Command = NET_ICMPV4_RX_CMD_CLEAR) then
					NextState										<= ST_IDLE;
				end if;

			when ST_DISCARD_FRAME =>
				In_Ack												<= '1';

				if ((In_Valid and In_EOF) = '1') then
					NextState										<= ST_ERROR;
				end if;

			when ST_ERROR =>
				Status												<= NET_ICMPV4_RX_STATUS_ERROR;
				Error													<= NET_ICMPV4_RX_ERROR_FSM;

				NextState											<= ST_IDLE;

		end case;
	end process;

	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or Register_rst) = '1') then
				Type_d															<= (others => '0');
				Code_d															<= (others => '0');
				Checksum_d													<= (others => '0');
				Identification_d										<= (others => '0');
				SequenceNumber_d										<= (others => '0');
			else
				if (Type_en = '1') then
					Type_d														<= In_Data;
				end if;
				if (Code_en = '1') then
					Code_d														<= In_Data;
				end if;

				if (Checksum_en0 = '1') then
					Checksum_d(7 downto 0)						<= In_Data;
				end if;
				if (Checksum_en1 = '1') then
					Checksum_d(15 downto 8)						<= In_Data;
				end if;

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

				if (SequenceNumber_en1 = '1') then
					SequenceNumber_d(7 downto 0)			<= In_Data;
				end if;
				if (SequenceNumber_en1 = '1') then
					SequenceNumber_d(15 downto 8)			<= In_Data;
				end if;
			end if;
		end if;
	end process;

	-- FIXME: monitor MetaFIFO_Full signal

	PayloadFIFO : entity PoC.fifo_cc_got_tempgot
		generic map (
			D_BITS							=> MetaFIFO_DataIn'length,	-- Data Width
			MIN_DEPTH						=> 64,											-- Minimum FIFO Depth
			DATA_REG						=> TRUE,										-- Store Data Content in Registers
			STATE_REG						=> FALSE,										-- Registered Full/Empty Indicators
			OUTPUT_REG					=> FALSE,										-- Registered FIFO Output
			ESTATE_WR_BITS			=> 0,												-- Empty State Bits
			FSTATE_RD_BITS			=> 0												-- Full State Bits
		)
		port map (
			-- Global Reset and Clock
			clk									=> Clock,
			rst									=> Reset,

			-- Writing Interface
			put									=> MetaFIFO_put,
			din									=> MetaFIFO_DataIn,
			full								=> MetaFIFO_Full,
			estate_wr						=> open,

			-- Reading Interface
			got									=> MetaFIFO_got,
			dout								=> MetaFIFO_DataOut,
			valid								=> open,	--MetaFIFO_Valid,
			fstate_rd						=> open,

			commit							=> MetaFIFO_Commit,
			rollback						=> MetaFIFO_Rollback
		);

	Out_Meta_SrcMACAddress_Data			<= In_Meta_SrcMACAddress_Data;
	Out_Meta_DestMACAddress_Data		<= In_Meta_DestMACAddress_Data;
	Out_Meta_SrcIPv4Address_Data		<= In_Meta_SrcIPv4Address_Data;
	Out_Meta_DestIPv4Address_Data		<= In_Meta_DestIPv4Address_Data;
	Out_Meta_Length									<= In_Meta_Length;
	Out_Meta_Type										<= Type_d;
	Out_Meta_Code										<= Code_d;
	Out_Meta_Identification					<= Identification_d;
	Out_Meta_SequenceNumber					<= SequenceNumber_d;
	Out_Meta_Payload_last						<= MetaFIFO_DataOut(Out_Meta_Payload_Data'length);
	Out_Meta_Payload_Data						<= MetaFIFO_DataOut(Out_Meta_Payload_Data'range);
end architecture;