-- 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]ipv6_TX is
	generic (
		DEBUG														: boolean							:= FALSE
	);
	port (
		Clock														: in	std_logic;								--
		Reset														: in	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_SrcIPv6Address_nxt			: out	std_logic;
		In_Meta_SrcIPv6Address_Data			: in	T_SLV_8;
		In_Meta_DestIPv6Address_nxt			: out	std_logic;
		In_Meta_DestIPv6Address_Data		: in	T_SLV_8;
		In_Meta_TrafficClass						: in	T_SLV_8;
		In_Meta_FlowLabel								: in	T_SLV_24;	--STD_LOGIC_VECTOR(19 downto 0);
		In_Meta_Length									: in	T_SLV_16;
		In_Meta_NextHeader							: in	T_SLV_8;
		-- to NDP layer
		NDP_NextHop_Query								: out	std_logic;
		NDP_NextHop_IPv6Address_rst			: in	std_logic;
		NDP_NextHop_IPv6Address_nxt			: in	std_logic;
		NDP_NextHop_IPv6Address_Data		: out	T_SLV_8;
		-- from NDP layer
		NDP_NextHop_Valid								: in	std_logic;
		NDP_NextHop_MACAddress_rst			: out	std_logic;
		NDP_NextHop_MACAddress_nxt			: out	std_logic;
		NDP_NextHop_MACAddress_Data			: in	T_SLV_8;
		-- 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_DestMACAddress_nxt			: in	std_logic;
		Out_Meta_DestMACAddress_Data		: out	T_SLV_8
	);
end entity;


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

	type T_STATE is (
		ST_IDLE,
			ST_NDP_QUERY,								ST_NDP_QUERY_WAIT,
			ST_SEND_VERSION,
			ST_SEND_TRAFFIC_CLASS,
			ST_SEND_FLOW_LABEL_1,				ST_SEND_FLOW_LABEL_2,
			ST_SEND_In_Meta_Length_0,		ST_SEND_In_Meta_Length_1,
			ST_SEND_NEXT_HEADER,				ST_SEND_HOP_LIMIT,
			ST_SEND_SOURCE_ADDRESS,
			ST_SEND_DESTINATION_ADDRESS,
			ST_SEND_DATA,
		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 IPv6SeqCounter_rst					: std_logic;
	signal IPv6SeqCounter_en					: std_logic;
	signal IPv6SeqCounter_us					: unsigned(3 downto 0)				:= (others => '0');

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, In_Valid, In_SOF, In_EOF, In_Data,
					In_Meta_Length,
					Out_Ack, Out_Meta_rst, Out_Meta_DestMACAddress_nxt,--Out_Meta_DestMACAddress_rev,
					NDP_NextHop_Valid, NDP_NextHop_IPv6Address_rst, NDP_NextHop_IPv6Address_nxt, NDP_NextHop_MACAddress_Data,--NDP_NextHop_IPv6Address_rev,
					In_Meta_DestIPv6Address_Data, In_Meta_SrcIPv6Address_Data, In_Meta_TrafficClass, In_Meta_FlowLabel, In_Meta_NextHeader,
					IPv6SeqCounter_us)
	begin
		NextState													<= State;

		In_Ack_i												<= '0';

		Out_Valid													<= '0';
		Out_Data													<= (others => '0');
		Out_SOF														<= '0';
		Out_EOF														<= '0';

		In_Meta_rst												<= '0';

		NDP_NextHop_Query									<= '0';
		In_Meta_SrcIPv6Address_nxt				<= '0';
		In_Meta_DestIPv6Address_nxt				<= '0';
		NDP_NextHop_IPv6Address_Data			<= In_Meta_DestIPv6Address_Data;

		NDP_NextHop_MACAddress_rst				<= Out_Meta_rst;
		NDP_NextHop_MACAddress_nxt				<= Out_Meta_DestMACAddress_nxt;
		Out_Meta_DestMACAddress_Data			<= NDP_NextHop_MACAddress_Data;

		IPv6SeqCounter_rst								<= '0';
		IPv6SeqCounter_en									<= '0';

		case State is
			when ST_IDLE =>
				In_Meta_rst										<= NDP_NextHop_IPv6Address_rst;
				In_Meta_DestIPv6Address_nxt		<= NDP_NextHop_IPv6Address_nxt;

				if ((In_Valid and In_SOF) = '1') then
					NextState										<= ST_NDP_QUERY;
				end if;

			when ST_NDP_QUERY =>
				Out_Data											<= x"6" & In_Meta_TrafficClass(7 downto 4);
				Out_SOF												<= '1';

				In_Meta_rst											<= NDP_NextHop_IPv6Address_rst;
				In_Meta_DestIPv6Address_nxt		<= NDP_NextHop_IPv6Address_nxt;

				NDP_NextHop_Query							<= '1';

				if (NDP_NextHop_Valid = '1') then
					Out_Valid										<= '1';
					In_Meta_rst									<= '1';		-- reset metadata

					if (Out_Ack	 = '1') then
						NextState									<= ST_SEND_TRAFFIC_CLASS;
					else
						NextState									<= ST_SEND_VERSION;
					end if;
				else
					NextState										<= ST_NDP_QUERY_WAIT;
				end if;

			when ST_NDP_QUERY_WAIT =>
				Out_Valid											<= '0';
				Out_Data											<= x"6" & In_Meta_TrafficClass(7 downto 4);
				Out_SOF												<= '1';

				In_Meta_rst										<= NDP_NextHop_IPv6Address_rst;
				In_Meta_DestIPv6Address_nxt		<= NDP_NextHop_IPv6Address_nxt;

				if (NDP_NextHop_Valid = '1') then
					Out_Valid										<= '1';
					In_Meta_rst									<= '1';		-- reset metadata

					if (Out_Ack	 = '1') then
						NextState									<= ST_SEND_TRAFFIC_CLASS;
					else
						NextState									<= ST_SEND_VERSION;
					end if;
				end if;

			when ST_SEND_VERSION =>
				Out_Valid											<= '1';
				Out_Data											<= x"6" & In_Meta_TrafficClass(7 downto 4);
				Out_SOF												<= '1';

				if (Out_Ack	 = '1') then
					NextState										<= ST_SEND_TRAFFIC_CLASS;
				end if;

			when ST_SEND_TRAFFIC_CLASS =>
				Out_Valid											<= '1';
				Out_Data											<= In_Meta_TrafficClass(3 downto 0) & In_Meta_FlowLabel(19 downto 16);

				if (Out_Ack	 = '1') then
					NextState										<= ST_SEND_FLOW_LABEL_1;
				end if;

			when ST_SEND_FLOW_LABEL_1 =>
				Out_Valid											<= '1';
				Out_Data											<= In_Meta_FlowLabel(15 downto 8);

				if (Out_Ack	 = '1') then
					NextState										<= ST_SEND_FLOW_LABEL_2;
				end if;

			when ST_SEND_FLOW_LABEL_2 =>
				Out_Valid											<= '1';
				Out_Data											<= In_Meta_FlowLabel(7 downto 0);

				if (Out_Ack	 = '1') then
					NextState										<= ST_SEND_In_Meta_Length_0;
				end if;

			when ST_SEND_In_Meta_Length_0 =>
				Out_Valid											<= '1';
				Out_Data											<= In_Meta_Length(15 downto 8);

				if (Out_Ack	 = '1') then
					NextState										<= ST_SEND_In_Meta_Length_1;
				end if;

			when ST_SEND_In_Meta_Length_1 =>
				Out_Valid											<= '1';
				Out_Data											<= In_Meta_Length(7 downto 0);

				if (Out_Ack	 = '1') then
					NextState										<= ST_SEND_NEXT_HEADER;
				end if;

			when ST_SEND_NEXT_HEADER =>
				Out_Valid											<= '1';
				Out_Data											<= In_Meta_NextHeader;

				if (Out_Ack	 = '1') then
					NextState										<= ST_SEND_HOP_LIMIT;
				end if;

			when ST_SEND_HOP_LIMIT =>
				Out_Valid											<= '1';
				Out_Data											<= x"02";		-- TODO: read from cache / routing info

				if (Out_Ack	 = '1') then
					NextState										<= ST_SEND_SOURCE_ADDRESS;
				end if;

			when ST_SEND_SOURCE_ADDRESS =>
				Out_Valid											<= '1';
				Out_Data											<= In_Meta_SrcIPv6Address_Data;

				if (Out_Ack	 = '1') then
					In_Meta_SrcIPv6Address_nxt			<= '1';
					IPv6SeqCounter_en						<= '1';

					if (IPv6SeqCounter_us = 15) then
						NextState									<= ST_SEND_DESTINATION_ADDRESS;
					end if;
				end if;

			when ST_SEND_DESTINATION_ADDRESS =>
				Out_Valid											<= '1';
				Out_Data											<= In_Meta_DestIPv6Address_Data;

				if (Out_Ack	 = '1') then
					In_Meta_DestIPv6Address_nxt	<= '1';
					IPv6SeqCounter_en						<= '1';

					if (IPv6SeqCounter_us = 15) then
						NextState									<= ST_SEND_DATA;
					end if;
				end if;

			when ST_SEND_DATA =>
				Out_Valid												<= In_Valid;
				Out_Data												<= In_Data;
				Out_EOF													<= In_EOF;
				In_Ack_i											<= Out_Ack;

				if ((In_EOF and Out_Ack) = '1') then
					In_Meta_rst										<= '1';
					NextState											<= ST_IDLE;
				end if;

			when ST_DISCARD_FRAME =>
				null;

			when ST_ERROR =>
				null;

		end case;
	end process;

	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or IPv6SeqCounter_rst) = '1') then
				IPv6SeqCounter_us			<= (others => '0');
			elsif (IPv6SeqCounter_en = '1') then
				IPv6SeqCounter_us			<= IPv6SeqCounter_us + 1;
			end if;
		end if;
	end process;

	In_Ack													<= In_Ack_i;

end architecture;