-- 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:				 	A generic buffer module for the PoC.Stream protocol.
--
-- 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;


entity [docs]stream_Mux is
	generic (
		PORTS							: positive									:= 2;
		DATA_BITS					: positive									:= 8;
		META_BITS					: natural										:= 8;
		META_REV_BITS			: natural										:= 2--;
--		WEIGHTS						: T_INTVEC									:= (1, 1)
	);
	port (
		Clock							: in	std_logic;
		Reset							: in	std_logic;
		-- IN Ports
		In_Valid					: in	std_logic_vector(PORTS - 1 downto 0);
		In_Data						: in	T_SLM(PORTS - 1 downto 0, DATA_BITS - 1 downto 0);
		In_Meta						: in	T_SLM(PORTS - 1 downto 0, META_BITS - 1 downto 0);
		In_Meta_rev				: out	T_SLM(PORTS - 1 downto 0, META_REV_BITS - 1 downto 0);
		In_SOF						: in	std_logic_vector(PORTS - 1 downto 0);
		In_EOF						: in	std_logic_vector(PORTS - 1 downto 0);
		In_Ack						: out	std_logic_vector(PORTS - 1 downto 0);
		-- OUT Port
		Out_Valid					: out	std_logic;
		Out_Data					: out	std_logic_vector(DATA_BITS - 1 downto 0);
		Out_Meta					: out	std_logic_vector(META_BITS - 1 downto 0);
		Out_Meta_rev			: in	std_logic_vector(META_REV_BITS - 1 downto 0);
		Out_SOF						: out	std_logic;
		Out_EOF						: out	std_logic;
		Out_Ack						: in	std_logic
	);
end entity;


architecture [docs]rtl of stream_Mux is
	attribute KEEP										: boolean;
	attribute FSM_ENCODING						: string;

	subtype T_CHANNEL_INDEX is natural range 0 to PORTS - 1;

	type T_STATE is (ST_IDLE, ST_DATAFLOW);

	signal State											: T_STATE					:= ST_IDLE;
	signal NextState									: T_STATE;

	signal FSM_Dataflow_en						: std_logic;

	signal RequestVector							: std_logic_vector(PORTS - 1 downto 0);
	signal RequestWithSelf						: std_logic;
	signal RequestWithoutSelf					: std_logic;

	signal RequestLeft								: unsigned(PORTS - 1 downto 0);
	signal SelectLeft									: unsigned(PORTS - 1 downto 0);
	signal SelectRight								: unsigned(PORTS - 1 downto 0);

	signal ChannelPointer_en					: std_logic;
	signal ChannelPointer							: std_logic_vector(PORTS - 1 downto 0);
	signal ChannelPointer_d						: std_logic_vector(PORTS - 1 downto 0)						:= to_slv(2 ** (PORTS - 1), PORTS);
	signal ChannelPointer_nxt					: std_logic_vector(PORTS - 1 downto 0);
	signal ChannelPointer_bin					: unsigned(log2ceilnz(PORTS) - 1 downto 0);

	signal idx												: T_CHANNEL_INDEX;

	signal Out_EOF_i									: std_logic;

begin
	RequestVector				<= In_Valid and In_SOF;
	RequestWithSelf			<= slv_or(RequestVector);
	RequestWithoutSelf	<= slv_or(RequestVector and not ChannelPointer_d);

	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, RequestWithSelf, RequestWithoutSelf, Out_Ack, Out_EOF_i, ChannelPointer_d, ChannelPointer_nxt)
	begin
		NextState									<= State;

		FSM_Dataflow_en						<= '0';

		ChannelPointer_en					<= '0';
		ChannelPointer						<= ChannelPointer_d;

		case State is
			when ST_IDLE =>
				if (RequestWithSelf = '1') then
					ChannelPointer_en		<= '1';

					NextState						<= ST_DATAFLOW;
				end if;

			when ST_DATAFLOW =>
				FSM_Dataflow_en				<= '1';

				if ((Out_Ack and Out_EOF_i) = '1') then
					if (RequestWithoutSelf = '0') then
						NextState					<= ST_IDLE;
					else
						ChannelPointer_en	<= '1';
					end if;
				end if;
		end case;
	end process;

	process(Clock)
	begin
		if rising_edge(Clock) then
			if (Reset = '1') then
				ChannelPointer_d			<= to_slv(2 ** (PORTS - 1), PORTS);
			elsif (ChannelPointer_en = '1') then
				ChannelPointer_d		<= ChannelPointer_nxt;
			end if;
		end if;
	end process;

	RequestLeft					<= (not ((unsigned(ChannelPointer_d) - 1) or unsigned(ChannelPointer_d))) and unsigned(RequestVector);
	SelectLeft					<= (unsigned(not RequestLeft) + 1)		and RequestLeft;
	SelectRight					<= (unsigned(not RequestVector) + 1)	and unsigned(RequestVector);
	ChannelPointer_nxt	<= std_logic_vector(ite((RequestLeft = (RequestLeft'range => '0')), SelectRight, SelectLeft));

	ChannelPointer_bin	<= onehot2bin(ChannelPointer);
	idx									<= to_integer(ChannelPointer_bin);

	Out_Data						<= get_row(In_Data, idx);
	Out_Meta						<= get_row(In_Meta, idx);

	Out_SOF							<= In_SOF(to_integer(ChannelPointer_bin));
	Out_EOF_i						<= In_EOF(to_integer(ChannelPointer_bin));
	Out_Valid						<= In_Valid(to_integer(ChannelPointer_bin)) and FSM_Dataflow_en;
	Out_EOF							<= Out_EOF_i;

	In_Ack							<= (In_Ack	'range => (Out_Ack	 and FSM_Dataflow_en)) and ChannelPointer;

	genMetaReverse_0 : if META_REV_BITS = 0 generate
		In_Meta_rev		<= (others => (others => '0'));
	end generate;
	genMetaReverse_1 : if META_REV_BITS > 0 generate
		signal Temp_Meta_rev : T_SLM(PORTS - 1 downto 0, META_REV_BITS - 1 downto 0)		:= (others => (others => 'Z'));
	begin
		genAssign : for i in 0 to PORTS - 1 generate
			signal row	: std_logic_vector(META_REV_BITS - 1 downto 0);
		begin
			row		<= Out_Meta_rev and (row'range => ChannelPointer(i));
			assign_row(Temp_Meta_rev, row, i);
		end generate;
		In_Meta_rev		<= Temp_Meta_rev;
	end generate;

end architecture;