-- 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_Mirror is
	generic (
		PORTS							: positive									:= 2;
		DATA_BITS					: positive									:= 8;
		META_BITS					: T_POSVEC									:= (0 => 8);
		META_LENGTH				: T_POSVEC									:= (0 => 16)
	);
	port (
		Clock							: in	std_logic;
		Reset							: in	std_logic;
		-- IN Port
		In_Valid					: in	std_logic;
		In_Data						: in	std_logic_vector(DATA_BITS - 1 downto 0);
		In_SOF						: in	std_logic;
		In_EOF						: in	std_logic;
		In_Ack						: out	std_logic;
		In_Meta_rst				: out	std_logic;
		In_Meta_nxt				: out	std_logic_vector(META_BITS'length - 1 downto 0);
		In_Meta_Data			: in	std_logic_vector(isum(META_BITS) - 1 downto 0);
		-- OUT Port
		Out_Valid					: out	std_logic_vector(PORTS - 1 downto 0);
		Out_Data					: out	T_SLM(PORTS - 1 downto 0, DATA_BITS - 1 downto 0);
		Out_SOF						: out	std_logic_vector(PORTS - 1 downto 0);
		Out_EOF						: out	std_logic_vector(PORTS - 1 downto 0);
		Out_Ack						: in	std_logic_vector(PORTS - 1 downto 0);
		Out_Meta_rst			: in	std_logic_vector(PORTS - 1 downto 0);
		Out_Meta_nxt			: in	T_SLM(PORTS - 1 downto 0, META_BITS'length - 1 downto 0);
		Out_Meta_Data			: out	T_SLM(PORTS - 1 downto 0, isum(META_BITS) - 1 downto 0)
	);
end entity;


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

	signal FIFOGlue_put					: std_logic;
	signal FIFOGlue_DataIn			: std_logic_vector(DATA_BITS + 1 downto 0);
	signal FIFOGlue_Full				: std_logic;
	signal FIFOGlue_Valid				: std_logic;
	signal FIFOGlue_DataOut			: std_logic_vector(DATA_BITS + 1 downto 0);
	signal FIFOGlue_got					: std_logic;

	signal Ack_i								: std_logic;
	signal Mask_r								: std_logic_vector(PORTS - 1 downto 0)												:= (others => '1');

	signal MetaOut_rst					: std_logic_vector(PORTS - 1 downto 0);

	signal Out_Data_i						: T_SLM(PORTS - 1 downto 0, DATA_BITS - 1 downto 0)						:= (others => (others => 'Z'));
	signal Out_Meta_Data_i			: T_SLM(PORTS - 1 downto 0, isum(META_BITS) - 1 downto 0)			:= (others => (others => 'Z'));
begin

	-- Data path
	-- ==========================================================================================================================================================
	FIFOGlue_put															<= In_Valid;
	FIFOGlue_DataIn(DATA_BITS - 1 downto 0)		<= In_Data;
	FIFOGlue_DataIn(DATA_BITS + 0)						<= In_SOF;
	FIFOGlue_DataIn(DATA_BITS + 1)						<= In_EOF;

	In_Ack																		<= not FIFOGlue_Full;

	FIFOGlue : entity PoC.fifo_glue
		generic map (
			D_BITS		=> DATA_BITS + 2					-- Data Width
		)
		port map (
			-- Control
			clk				=> Clock,									-- Clock
			rst				=> Reset,									-- Synchronous Reset

			-- Input
			put				=> FIFOGlue_put,					-- Put Value
			di				=> FIFOGlue_DataIn,				-- Data Input
			ful				=> FIFOGlue_Full,					-- Full

			-- Output
			vld				=> FIFOGlue_Valid,				-- Data Available
			do				=> FIFOGlue_DataOut,			-- Data Output
			got				=> FIFOGlue_got						-- Data Consumed
  );

	genPorts : for i in 0 to PORTS - 1 generate
		assign_row(Out_Data_i, FIFOGlue_DataOut(DATA_BITS - 1 downto 0), i);
	end generate;

	Ack_i					<= slv_and(Out_Ack) or slv_and(not Mask_r or Out_Ack);
	FIFOGlue_got	<= Ack_i;

	Out_Valid			<= (PORTS - 1 downto 0 => FIFOGlue_Valid) and Mask_r;
	Out_Data			<= Out_Data_i;
	Out_SOF				<= (PORTS - 1 downto 0 => FIFOGlue_DataOut(DATA_BITS + 0));
	Out_EOF				<= (PORTS - 1 downto 0 => FIFOGlue_DataOut(DATA_BITS + 1));

	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or Ack_i	) = '1') then
				Mask_r		<= (others => '1');
			else
				Mask_r		<= Mask_r and not Out_Ack;
			end if;
		end if;
	end process;

	-- Metadata path
	-- ==========================================================================================================================================================
	In_Meta_rst		<= slv_and(MetaOut_rst);

	genMeta : for i in 0 to META_BITS'length - 1 generate
		subtype T_METAMEMORY						is std_logic_vector(META_BITS(i) - 1 downto 0);
		type T_METAMEMORY_VECTOR				is array(natural range <>) of T_METAMEMORY;

	begin
		genReg : if META_LENGTH(i) = 1 generate
			signal MetaMemory_en					: std_logic;
			signal MetaMemory							: T_METAMEMORY;
		begin
			MetaMemory_en		<= In_Valid and In_SOF;

			process(Clock)
			begin
				if rising_edge(Clock) then
					if (MetaMemory_en = '1') then
						MetaMemory		<= In_Meta_Data(high(META_BITS, i) downto low(META_BITS, i));
					end if;
				end if;
			end process;

			genReader : for j in 0 to PORTS - 1 generate
				assign_row(Out_Meta_Data_i, MetaMemory, J, high(META_BITS, i), low(META_BITS, i));
			end generate;
		end generate;
		genMem : if META_LENGTH(i) > 1 generate
			signal MetaMemory_en					: std_logic;
			signal MetaMemory							: T_METAMEMORY_VECTOR(META_LENGTH(i) - 1 downto 0);

			signal Writer_CounterControl	: std_logic																						:= '0';

			signal Writer_en							: std_logic;
			signal Writer_rst							: std_logic;
			signal Writer_us							: unsigned(log2ceilnz(META_LENGTH(i)) - 1 downto 0)		:= (others => '0');
		begin
			-- MetaMemory Write Pointer Control
			process(Clock)
			begin
				if rising_edge(Clock) then
					if (Reset = '1') then
						Writer_CounterControl			<= '0';
					else
						if ((In_Valid and In_SOF) = '1') then
							Writer_CounterControl		<= '1';
						elsif Writer_us = (META_LENGTH(i) - 1) then
							Writer_CounterControl		<= '0';
						end if;
					end if;
				end if;
			end process;

			Writer_en				<= (In_Valid and In_SOF) or Writer_CounterControl;

			In_Meta_nxt(i)	<= Writer_en;
			MetaMemory_en		<= Writer_en;
			MetaOut_rst(i)	<= not Writer_en;

			-- MetaMemory - Write Pointer
			process(Clock)
			begin
				if rising_edge(Clock) then
					if (Writer_en = '0') then
						Writer_us			<= (others => '0');
					else
						Writer_us			<= Writer_us + 1;
					end if;
				end if;
			end process;

			-- MetaMemory
			process(Clock)
			begin
				if rising_edge(Clock) then
					if (MetaMemory_en = '1') then
						MetaMemory(to_integer(Writer_us))		<= In_Meta_Data(high(META_BITS, i) downto low(META_BITS, i));
					end if;
				end if;
			end process;

			genReader : for j in 0 to PORTS - 1 generate
				signal Row							: T_METAMEMORY;

				signal Reader_en				: std_logic;
				signal Reader_rst				: std_logic;
				signal Reader_us				: unsigned(log2ceilnz(META_LENGTH(i)) - 1 downto 0)		:= (others => '0');
			begin
				Reader_rst		<= Out_Meta_rst(j) or (In_Valid and In_SOF);
				Reader_en			<= Out_Meta_nxt(j, i);

				process(Clock)
				begin
					if rising_edge(Clock) then
						if (Reader_rst = '1') then
							Reader_us			<= (others => '0');
						elsif (Reader_en = '1') then
							Reader_us			<= Reader_us + 1;
						end if;
					end if;
				end process;

				Row <= MetaMemory(to_integer(Reader_us));
				assign_row(Out_Meta_Data_i, Row, j, high(META_BITS, i), low(META_BITS, i));
			end generate;		-- for each port
		end generate;		-- if length > 1
	end generate;		-- for each metadata stream

	Out_Meta_Data		<= Out_Meta_Data_i;

end architecture;