-- 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;


entity [docs]net_FrameChecksum is
	generic (
		MAX_FRAMES										: positive				:= 8;
		MAX_FRAME_LENGTH							: positive				:= 2048;
		META_BITS											: T_POSVEC				:= (0 => 8);
		META_FIFO_DEPTH								: T_POSVEC				:= (0 => 16)
	);
	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_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;
		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_nxt									: in	std_logic_vector(META_BITS'length - 1 downto 0);
		Out_Meta_Data									: out	std_logic_vector(isum(META_BITS) - 1 downto 0);
		Out_Meta_Length								: out	T_SLV_16;
		Out_Meta_Checksum							: out	T_SLV_16
	);
end entity;

-- FIXME: review writer-FSM: check full signals => block incoming words/frames if datafifo or metafifo is full

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

	type T_WRITER_STATE is			(ST_IDLE, ST_FRAME, ST_CARRY_1, ST_CARRY_2);
	type T_METAWRITER_STATE is	(ST_IDLE, ST_METADATA);
	type T_READER_STATE is			(ST_IDLE, ST_FRAME);

	signal Writer_State						: T_WRITER_STATE																			:= ST_IDLE;
	signal Writer_NextState				: T_WRITER_STATE;
	signal MetaWriter_State				: T_METAWRITER_STATE																	:= ST_IDLE;
	signal MetaWriter_NextState		: T_METAWRITER_STATE;
	signal Reader_State						: T_READER_STATE																			:= ST_IDLE;
	signal Reader_NextState				: T_READER_STATE;

	signal Checksum_rst						: std_logic;
	signal Checksum_en						: std_logic;
	signal Checksum_Data_us				: unsigned(In_Data'range);
	signal Checksum0_nxt_us				: unsigned(In_Data'length downto 0);
	signal Checksum0_d_us					: unsigned(In_Data'length downto 0)										:= (others => '0');
	signal Checksum0_nxt_cy				: std_logic;
	signal Checksum1_nxt_us				: unsigned(In_Data'range);
	signal Checksum1_d_us					: unsigned(In_Data'range)															:= (others => '0');
	signal Checksum								: T_SLV_16;

	constant WORDCOUNTER_BITS			: positive																						:= log2ceilnz(MAX_FRAME_LENGTH);
	signal WordCounter_rst				: std_logic;
	signal WordCounter_en					: std_logic;
	signal WordCounter_us					: unsigned(WORDCOUNTER_BITS - 1 downto 0)							:= to_unsigned(1, log2ceilnz(MAX_FRAME_LENGTH));
	signal WordCount							: std_logic_vector(WORDCOUNTER_BITS + 15 downto 16);

	signal FrameCommit						: std_logic;

	constant DATA_BITS						: positive																						:= 8;
	constant EOF_BIT							: natural																							:= DATA_BITS;

	signal DataFIFO_put						: std_logic;
	signal DataFIFO_DataIn				: std_logic_vector(DATA_BITS downto 0);
	signal DataFIFO_Full					: std_logic;
	signal DataFIFO_got						: std_logic;
	signal DataFIFO_DataOut				: std_logic_vector(DATA_BITS downto 0);
	signal DataFIFO_Valid					: std_logic;

	constant META_MISC_BITS				: positive																						:= Checksum'length + WordCount'length;

	signal MetaFIFO_Misc_put			: std_logic;
	signal MetaFIFO_Misc_DataIn		: std_logic_vector(META_MISC_BITS - 1 downto 0);
	signal MetaFIFO_Misc_Full			: std_logic;
	signal MetaFIFO_Misc_got			: std_logic;
	signal MetaFIFO_Misc_DataOut	: std_logic_vector(META_MISC_BITS - 1 downto 0);
	signal MetaFIFO_Misc_Valid		: std_logic;

	signal Meta_rst								: std_logic_vector(META_BITS'length - 1 downto 0);

begin

	process(Clock)
	begin
		if rising_edge(Clock) then
			if (Reset = '1') then
				Writer_State					<= ST_IDLE;
				Reader_State					<= ST_IDLE;
			else
				Writer_State					<= Writer_NextState;
				Reader_State					<= Reader_NextState;
			end if;
		end if;
	end process;

	[docs]process(Writer_State,
					In_Valid, In_SOF, In_EOF, In_Data,
					WordCounter_us, Checksum0_nxt_cy,
					DataFIFO_Full, MetaFIFO_Misc_Full)
	begin
		Writer_NextState								<= Writer_State;

		DataFIFO_put										<= '0';
		MetaFIFO_Misc_put								<= '0';

		In_Ack													<= not DataFIFO_Full;

		WordCounter_rst									<= '0';
		WordCounter_en									<= '0';

		Checksum_rst										<= '0';
		Checksum_en											<= '0';
		Checksum_Data_us								<= unsigned(In_Data);

		case Writer_State is
			when ST_IDLE =>
				if ((In_Valid and In_SOF and (not DataFIFO_Full)) = '1') then
					WordCounter_en						<= '1';
					Checksum_en								<= '1';
					DataFIFO_put							<= In_Valid;

					if (In_EOF = '1') then
						MetaFIFO_Misc_put						<= '1';

						Writer_NextState				<= ST_IDLE;
					else
						Writer_NextState				<= ST_FRAME;
					end if;
				end if;

			when ST_FRAME =>
				DataFIFO_put								<= In_Valid;

				if ((In_Valid and (not DataFIFO_Full)) = '1') then
					WordCounter_en						<= '1';
					Checksum_en								<= '1';

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

						if (Checksum0_nxt_cy = '0') then
							WordCounter_rst			<= '1';
							Checksum_rst				<= '1';

							MetaFIFO_Misc_put				<= '1';

							Writer_NextState		<= ST_IDLE;
						else
							Writer_NextState		<= ST_CARRY_1;
						end if;
					end if;
				end if;

			when ST_CARRY_1 =>
				In_Ack											<= '0';

				Checksum_Data_us						<= (others => '0');

				if (Checksum0_nxt_cy = '0') then
					Checksum_rst							<= '1';
					WordCounter_rst						<= '1';

					MetaFIFO_Misc_put							<= '1';

					Writer_NextState					<= ST_IDLE;
				else
					Checksum_en								<= '1';

					Writer_NextState					<= ST_CARRY_2;
				end if;

			when ST_CARRY_2 =>
				In_Ack											<= '0';

				Checksum_Data_us						<= (others => '0');
				Checksum_rst								<= '1';
				WordCounter_rst							<= '1';

				MetaFIFO_Misc_put								<= '1';

				Writer_NextState						<= ST_IDLE;

		end case;
	end process;


	[docs]process(Reader_State,
					Out_Ack,
					DataFIFO_Valid, DataFIFO_DataOut,
					MetaFIFO_Misc_Valid, MetaFIFO_Misc_DataOut)
	begin
		Reader_NextState								<= Reader_State;

		Out_Valid												<= '0';
		Out_Data												<= DataFIFO_DataOut(Out_Data'range);
		Out_SOF													<= '0';
		Out_EOF													<= DataFIFO_DataOut(EOF_BIT);
		Out_Meta_Checksum								<= MetaFIFO_Misc_DataOut(Checksum'range);
		Out_Meta_Length									<= resize(MetaFIFO_Misc_DataOut(WordCount'range), Out_Meta_Length'length);

		DataFIFO_got										<= '0';

		case Reader_State is
			when ST_IDLE =>
				Out_SOF											<= '1';

				if ((DataFIFO_Valid and MetaFIFO_Misc_Valid) = '1') then
					Out_Valid									<= '1';

					if (Out_Ack	 = '1') then
						DataFIFO_got						<= '1';

						if (DataFIFO_DataOut(EOF_BIT) = '0') then
							Reader_NextState			<= ST_FRAME;
						end if;
					end if;
				end if;

			when ST_FRAME =>
				Out_Valid										<= DataFIFO_Valid;

				if (Out_Ack	 = '1') then
					DataFIFO_got							<= '1';

					if (DataFIFO_DataOut(EOF_BIT) = '1') then
						Reader_NextState				<= ST_IDLE;
					end if;
				end if;

		end case;
	end process;

	process(Clock)
	begin
		if rising_edge(Clock) then
			if (WordCounter_rst = '1') then
				WordCounter_us		<= to_unsigned(1, WordCounter_us'length);
			elsif (WordCounter_en = '1') then
				WordCounter_us		<= WordCounter_us + 1;
			end if;
		end if;
	end process;

	Checksum0_nxt_cy		<= Checksum0_nxt_us(Checksum0_nxt_us'high);
	Checksum0_nxt_us		<= ('0' & Checksum1_d_us) + ('0' & Checksum_Data_us) + ((Checksum1_d_us'range => '0') & Checksum0_d_us(Checksum0_d_us'high));
	Checksum1_nxt_us		<= Checksum0_d_us(Checksum1_d_us'range);

	process(Clock)
	begin
		if rising_edge(Clock) then
			if (Checksum_rst = '1') then
				Checksum0_d_us			<= (others => '0');
				Checksum1_d_us			<= (others => '0');
			elsif (Checksum_en = '1') then
				Checksum0_d_us			<= Checksum0_nxt_us;
				Checksum1_d_us			<= Checksum1_nxt_us;
			end if;
		end if;
	end process;

	Checksum			<= std_logic_vector(Checksum0_nxt_us(Checksum1_nxt_us'range)) & std_logic_vector(Checksum1_nxt_us);
	WordCount			<= std_logic_vector(WordCounter_us);

	DataFIFO_DataIn(In_Data'range)		<= In_Data;
	DataFIFO_DataIn(EOF_BIT)					<= In_EOF;

	DataFIFO : entity PoC.fifo_cc_got
		generic map (
			D_BITS							=> DataFIFO_DataIn'length,			-- Data Width
			MIN_DEPTH						=> MAX_FRAME_LENGTH,						-- Minimum FIFO Depth
			DATA_REG						=> FALSE,												-- Store Data Content in Registers
			STATE_REG						=> TRUE,												-- Registered Full/Empty Indicators
			OUTPUT_REG					=> TRUE,												-- Registered FIFO Output
			ESTATE_WR_BITS			=> 0,														-- Empty State Bits
			FSTATE_RD_BITS			=> 0														-- Full State Bits
		)
		port map (
			clk									=> Clock,
			rst									=> Reset,
			-- Write Interface
			put									=> DataFIFO_put,
			din									=> DataFIFO_DataIn,
			full								=> DataFIFO_Full,
			estate_wr						=> open,
			-- Read Interface
			got									=> DataFIFO_got,
			valid								=> DataFIFO_Valid,
			dout								=> DataFIFO_DataOut,
			fstate_rd						=> open
		);

	MetaFIFO_Misc_DataIn(Checksum'range)		<= ite((WordCounter_us(0) = to_sl(Writer_State /= ST_CARRY_1)), Checksum, swap(Checksum, 8));
	MetaFIFO_Misc_DataIn(WordCount'range)		<= WordCount;

	MetaFIFO_Misc : entity PoC.fifo_cc_got
		generic map (
			D_BITS							=> MetaFIFO_Misc_DataIn'length,							-- Data Width
			MIN_DEPTH						=> MAX_FRAMES,															-- Minimum FIFO Depth
			DATA_REG						=> TRUE,																		-- Store Data Content in Registers
			STATE_REG						=> TRUE,																		-- 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 (
			clk									=> Clock,
			rst									=> Reset,
			-- Write Interface
			put									=> MetaFIFO_Misc_put,
			din									=> MetaFIFO_Misc_DataIn,
			full								=> MetaFIFO_Misc_Full,
			estate_wr						=> open,
			-- Read Interface
			got									=> MetaFIFO_Misc_got,
			valid								=> MetaFIFO_Misc_Valid,
			dout								=> MetaFIFO_Misc_DataOut,
			fstate_rd						=> open
		);

	Out_Meta_Length					<= resize(MetaFIFO_Misc_DataOut(WordCount'range), Out_Meta_Length'length);
	Out_Meta_Checksum				<= MetaFIFO_Misc_DataOut(Checksum'range);

	FrameCommit							<= DataFIFO_Valid and DataFIFO_DataOut(EOF_BIT) and Out_Ack;
	MetaFIFO_Misc_got				<= FrameCommit;

	genMeta : for i in 0 to META_BITS'length - 1 generate
		signal MetaFIFO_put						: std_logic;
		signal MetaFIFO_DataIn				: std_logic_vector(META_BITS(i) - 1 downto 0);
		signal MetaFIFO_Full					: std_logic;
		signal MetaFIFO_got						: std_logic;
		signal MetaFIFO_DataOut				: std_logic_vector(META_BITS(i) - 1 downto 0);
		signal MetaFIFO_Valid					: std_logic;
		signal MetaFIFO_Commit				: std_logic;
		signal MetaFIFO_Rollback			: std_logic;

		signal Writer_CounterControl	: std_logic																																:= '0';

		signal Writer_Counter_rst			: std_logic;
		signal Writer_Counter_en			: std_logic;
		signal Writer_Counter_us			: unsigned(log2ceilnz(META_FIFO_DEPTH(i) * MAX_FRAMES) - 1 downto 0)			:= (others => '0');
	begin
		Writer_Counter_rst		<= '0';		-- FIXME: is this correct?

		process(Clock)
		begin
			if rising_edge(Clock) then
				if (Reset = '1') then
					Writer_CounterControl			<= '0';
				elsif ((In_Valid and In_SOF) = '1') then
					Writer_CounterControl		<= '1';
				elsif (Writer_Counter_us = (META_FIFO_DEPTH(i) - 1)) then
					Writer_CounterControl		<= '0';
				end if;
			end if;
		end process;

		Writer_Counter_en		<= (In_Valid and In_SOF) or Writer_CounterControl;

		process(Clock)
		begin
			if rising_edge(Clock) then
				if ((Reset or Writer_Counter_rst) = '1') then
					Writer_Counter_us					<= (others => '0');
				elsif (Writer_Counter_en = '1') then
					Writer_Counter_us				<= Writer_Counter_us + 1;
				end if;
			end if;
		end process;

		Meta_rst(i)					<= not Writer_Counter_en;
		In_Meta_nxt(i)			<= Writer_Counter_en;

		MetaFIFO_put				<= Writer_Counter_en;
		MetaFIFO_DataIn			<= In_Meta_Data(high(META_BITS, i) downto low(META_BITS, i));

		MetaFIFO : entity PoC.fifo_cc_got_tempgot
			generic map (
				D_BITS							=> MetaFIFO_DataIn'length,							-- Data Width
				MIN_DEPTH						=> (META_FIFO_DEPTH(i) * MAX_FRAMES),		-- Minimum FIFO Depth
				DATA_REG						=> TRUE,																-- Store Data Content in Registers
				STATE_REG						=> TRUE,																-- 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 (
				clk									=> Clock,
				rst									=> Reset,
				-- Write Interface
				put									=> MetaFIFO_put,
				din									=> MetaFIFO_DataIn,
				full								=> MetaFIFO_Full,
				estate_wr						=> open,
				-- Read Interface
				got									=> MetaFIFO_got,
				valid								=> MetaFIFO_Valid,
				dout								=> MetaFIFO_DataOut,
				fstate_rd						=> open,

				commit							=> MetaFIFO_Commit,
				rollback						=> MetaFIFO_Rollback
			);

		MetaFIFO_got				<= Out_Meta_nxt(i);
		MetaFIFO_Commit			<= FrameCommit;
		MetaFIFO_Rollback		<= Out_Meta_rst;

		Out_Meta_Data(high(META_BITS, i) downto low(META_BITS, i))	<= MetaFIFO_DataOut;
	end generate;

	In_Meta_rst						<= slv_and(Meta_rst);

end architecture;