-- 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]mac_TX_SrcMAC_Prepender is
	generic (
		DEBUG													: boolean													:= FALSE;
		MAC_ADDRESSES									: T_NET_MAC_ADDRESS_VECTOR				:= (0 => C_NET_MAC_ADDRESS_EMPTY)
	);
	port (
		Clock													: in	std_logic;
		Reset													: in	std_logic;
		-- IN Port
		In_Valid											: in	std_logic_vector(MAC_ADDRESSES'length - 1 downto 0);
		In_Data												: in	T_SLVV_8(MAC_ADDRESSES'length - 1 downto 0);
		In_SOF												: in	std_logic_vector(MAC_ADDRESSES'length - 1 downto 0);
		In_EOF												: in	std_logic_vector(MAC_ADDRESSES'length - 1 downto 0);
		In_Ack												: out	std_logic_vector(MAC_ADDRESSES'length - 1 downto 0);
		In_Meta_rst										: out std_logic_vector(MAC_ADDRESSES'length - 1 downto 0);
		In_Meta_DestMACAddress_nxt		: out std_logic_vector(MAC_ADDRESSES'length - 1 downto 0);
		In_Meta_DestMACAddress_Data		: in	T_SLVV_8(MAC_ADDRESSES'length - 1 downto 0);
		-- 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 mac_TX_SrcMAC_Prepender is
	attribute FSM_ENCODING					: string;

	constant PORTS									: positive				:= MAC_ADDRESSES'length;

	constant META_RST_BIT						: natural					:= 0;
	constant META_DEST_NXT_BIT			: natural					:= 1;

	constant META_BITS							: positive				:= 56;
	constant META_REV_BITS					: positive				:= 2;

	type T_STATE is (
		ST_IDLE,
			ST_PREPEND_SRC_MAC_1,
			ST_PREPEND_SRC_MAC_2,
			ST_PREPEND_SRC_MAC_3,
			ST_PREPEND_SRC_MAC_4,
			ST_PREPEND_SRC_MAC_5,
			ST_PAYLOAD
	);

	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 LLMux_In_Valid						: std_logic_vector(PORTS - 1 downto 0);
	signal LLMux_In_Data						: T_SLM(PORTS - 1 downto 0, T_SLV_8'range)								:= (others => (others => 'Z'));		-- necessary default assignment 'Z' to get correct simulation results (iSIM, vSIM, ghdl/gtkwave)
	signal LLMux_In_Meta						: T_SLM(PORTS - 1 downto 0, META_BITS - 1 downto 0)				:= (others => (others => 'Z'));		-- necessary default assignment 'Z' to get correct simulation results (iSIM, vSIM, ghdl/gtkwave)
	signal LLMux_In_Meta_rev				: T_SLM(PORTS - 1 downto 0, META_REV_BITS - 1 downto 0)		:= (others => (others => 'Z'));		-- necessary default assignment 'Z' to get correct simulation results (iSIM, vSIM, ghdl/gtkwave)
	signal LLMux_In_SOF							: std_logic_vector(PORTS - 1 downto 0);
	signal LLMux_In_EOF							: std_logic_vector(PORTS - 1 downto 0);
	signal LLMux_In_Ack							: std_logic_vector(PORTS - 1 downto 0);

	signal LLMux_Out_Valid					: std_logic;
	signal LLMux_Out_Data						: T_SLV_8;
	signal LLMux_Out_Meta						: std_logic_vector(META_BITS - 1 downto 0);
	signal LLMux_Out_Meta_rev				: std_logic_vector(META_REV_BITS - 1 downto 0);
	signal LLMux_Out_SOF						: std_logic;
	signal LLMux_Out_EOF						: std_logic;
	signal LLMux_Out_Ack						: std_logic;

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

begin

	LLMux_In_Valid		<= In_Valid;
	LLMux_In_Data			<= to_slm(In_Data);
	LLMux_In_SOF			<= In_SOF;
	LLMux_In_EOF			<= In_EOF;
	In_Ack						<= LLMux_In_Ack;

	genLLMuxIn : for i in 0 to PORTS - 1 generate
		signal Meta		: std_logic_vector(META_BITS - 1 downto 0);
	begin
		Meta(55 downto 48)	<= In_Meta_DestMACAddress_Data(i);
		Meta(47 downto 0)		<= to_slv(MAC_ADDRESSES(i));

		assign_row(LLMux_In_Meta, Meta, i);
	end generate;

	In_Meta_rst									<= get_col(LLMux_In_Meta_rev, META_RST_BIT);
	In_Meta_DestMACAddress_nxt	<= get_col(LLMux_In_Meta_rev, META_DEST_NXT_BIT);

	LLMux : entity PoC.stream_Mux
		generic map (
			PORTS									=> PORTS,
			DATA_BITS							=> LLMux_Out_Data'length,
			META_BITS							=> LLMux_Out_Meta'length,
			META_REV_BITS					=> LLMux_Out_Meta_rev'length
		)
		port map(
			Clock									=> Clock,
			Reset									=> Reset,

			In_Valid							=> LLMux_In_Valid,
			In_Data								=> LLMux_In_Data,
			In_Meta								=> LLMux_In_Meta,
			In_Meta_rev						=> LLMux_In_Meta_rev,
			In_SOF								=> LLMux_In_SOF,
			In_EOF								=> LLMux_In_EOF,
			In_Ack								=> LLMux_In_Ack,

			Out_Valid							=> LLMux_Out_Valid,
			Out_Data							=> LLMux_Out_Data,
			Out_Meta							=> LLMux_Out_Meta,
			Out_Meta_rev					=> LLMux_Out_Meta_rev,
			Out_SOF								=> LLMux_Out_SOF,
			Out_EOF								=> LLMux_Out_EOF,
			Out_Ack								=> LLMux_Out_Ack
		);

	LLMux_Out_Meta_rev(META_RST_BIT)				<= Out_Meta_rst;
	LLMux_Out_Meta_rev(META_DEST_NXT_BIT)		<= Out_Meta_DestMACAddress_nxt;

	Out_Meta_DestMACAddress_Data						<= LLMux_Out_Meta(55 downto 48);

	Is_DataFlow		<= LLMux_Out_Valid and Out_Ack;
	Is_SOF				<= LLMux_Out_Valid and LLMux_Out_SOF;
	Is_EOF				<= LLMux_Out_Valid and LLMux_Out_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, LLMux_Out_Valid, LLMux_Out_Data, LLMux_Out_Meta, LLMux_Out_EOF, Is_DataFlow, Is_SOF, Is_EOF, Out_Ack)
	begin
		NextState							<= State;

		Out_Valid							<= '0';
		Out_Data							<= LLMux_Out_Data;
		Out_SOF								<= '0';
		Out_EOF								<= '0';

		LLMux_Out_Ack				<= '0';

		case State is
			when ST_IDLE =>
				if (Is_SOF = '1') then
					Out_Valid				<= '1';
					Out_SOF					<= '1';
					Out_Data				<= LLMux_Out_Meta((5 * 8) + 7 downto (5 * 8));

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

			when ST_PREPEND_SRC_MAC_1 =>
				Out_Valid					<= '1';
				Out_Data					<= LLMux_Out_Meta((4 * 8) + 7 downto (4 * 8));

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

			when ST_PREPEND_SRC_MAC_2 =>
				Out_Valid					<= '1';
				Out_Data					<= LLMux_Out_Meta((3 * 8) + 7 downto (3 * 8));

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

						when ST_PREPEND_SRC_MAC_3 =>
				Out_Valid					<= '1';
				Out_Data					<= LLMux_Out_Meta((2 * 8) + 7 downto (2 * 8));

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

						when ST_PREPEND_SRC_MAC_4 =>
				Out_Valid					<= '1';
				Out_Data					<= LLMux_Out_Meta((1 * 8) + 7 downto (1 * 8));

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

			when ST_PREPEND_SRC_MAC_5 =>
				Out_Valid					<= '1';
				Out_Data					<= LLMux_Out_Meta((0 * 8) + 7 downto (0 * 8));

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

			when ST_PAYLOAD =>
				Out_Valid					<= LLMux_Out_Valid;
				Out_EOF						<= LLMux_Out_EOF;
				LLMux_Out_Ack		<= Out_Ack;

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

		end case;
	end process;

end architecture;