-- 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.utils.all;
use			PoC.vectors.all;


entity [docs]stream_FrameGenerator is
  generic (
    DATA_BITS					: positive														:= 8;
		WORD_BITS					: positive														:= 16;
		APPEND						: T_FRAMEGEN_APPEND										:= FRAMEGEN_APP_NONE;
		FRAMEGROUPS				: T_FRAMEGEN_FRAMEGROUP_VECTOR_8			:= (0 => C_FRAMEGEN_FRAMEGROUP_EMPTY)
  );
	port (
		Clock							: in	std_logic;
		Reset							: in	std_logic;
		-- CSE interface
		Command						: in	T_FRAMEGEN_COMMAND;
		Status						: out	T_FRAMEGEN_STATUS;
		-- Control interface
		Pause							: in	T_SLV_16;
		FrameGroupIndex		: in	T_SLV_8;
		FrameIndex				: in	T_SLV_8;
		Sequences					: in	T_SLV_16;
		FrameLength				: in	T_SLV_16;
		-- OUT Port
		Out_Valid					: out	std_logic;
		Out_Data					: out	std_logic_vector(DATA_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_FrameGenerator is
	type T_STATE is (
		ST_IDLE,
			ST_SEQUENCE_SOF,	ST_SEQUENCE_DATA,	ST_SEQUENCE_EOF,
			ST_RANDOM_SOF,		ST_RANDOM_DATA,		ST_RANDOM_EOF,
		ST_ERROR
	);

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

	signal FrameLengthCounter_rst			: std_logic;
	signal FrameLengthCounter_en			: std_logic;
	signal FrameLengthCounter_us			: unsigned(15 downto 0)							:= (others => '0');

	signal SequencesCounter_rst				: std_logic;
	signal SequencesCounter_en				: std_logic;
	signal SequencesCounter_us				: unsigned(15 downto 0)							:= (others => '0');
	signal ContentCounter_rst					: std_logic;
	signal ContentCounter_en					: std_logic;
	signal ContentCounter_us					: unsigned(WORD_BITS - 1 downto 0)	:= (others => '0');

	signal PRNG_rst										: std_logic;
	signal PRNG_got										: std_logic;
	signal PRNG_Data									: std_logic_vector(DATA_BITS - 1 downto 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, Command, Out_Ack,
					Sequences, FrameLength,
					FrameLengthCounter_us,
					SequencesCounter_us, ContentCounter_us,
					PRNG_Data)
	begin
		NextState													<= State;

		Status														<= FRAMEGEN_STATUS_GENERATING;

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

		FrameLengthCounter_rst						<= '0';
		FrameLengthCounter_en							<= '0';
		SequencesCounter_rst							<= '0';
		SequencesCounter_en								<= '0';
		ContentCounter_rst								<= '0';
		ContentCounter_en									<= '0';

		PRNG_rst													<= '0';
		PRNG_got													<= '0';

		case State is
			when ST_IDLE =>
				Status												<= FRAMEGEN_STATUS_IDLE;

				FrameLengthCounter_rst				<= '1';
				SequencesCounter_rst					<= '1';
				ContentCounter_rst						<= '1';
				PRNG_rst											<= '1';

				case Command is
					when FRAMEGEN_CMD_NONE =>
						null;

					when FRAMEGEN_CMD_SEQUENCE =>
						NextState									<= ST_SEQUENCE_SOF;

					when FRAMEGEN_CMD_RANDOM =>
						NextState									<= ST_RANDOM_SOF;

					when FRAMEGEN_CMD_SINGLE_FRAME =>
						NextState									<= ST_ERROR;

					when FRAMEGEN_CMD_SINGLE_FRAMEGROUP =>
						NextState									<= ST_ERROR;

					when FRAMEGEN_CMD_ALL_FRAMES =>
						NextState									<= ST_ERROR;

					when others =>
						NextState									<= ST_ERROR;
				end case;

			-- generate sequential numbers
			-- ----------------------------------------------------------------------
			when ST_SEQUENCE_SOF =>
				Out_Valid											<= '1';
				Out_Data											<= std_logic_vector(ContentCounter_us);
				Out_SOF												<= '1';

				if (Out_Ack	 = '1') then
					FrameLengthCounter_en				<= '1';
					ContentCounter_en						<= '1';

					NextState										<= ST_SEQUENCE_DATA;
				end if;

			when ST_SEQUENCE_DATA =>
				Out_Valid											<= '1';
				Out_Data											<= std_logic_vector(ContentCounter_us);

				if (Out_Ack	 = '1') then
					FrameLengthCounter_en				<= '1';
					ContentCounter_en						<= '1';

					if FrameLengthCounter_us = (unsigned(FrameLength) - 2) then
						NextState									<= ST_SEQUENCE_EOF;
					end if;
				end if;

			when ST_SEQUENCE_EOF =>
				Out_Valid											<= '1';
				Out_Data											<= std_logic_vector(ContentCounter_us);
				Out_EOF												<= '1';

				if (Out_Ack	 = '1') then
					FrameLengthCounter_rst			<= '1';
					ContentCounter_en						<= '1';
					SequencesCounter_en					<= '1';

--					if (Pause = (Pause'range => '0')) then
					if SequencesCounter_us = (unsigned(Sequences) - 1) then
						Status										<= FRAMEGEN_STATUS_COMPLETE;
						NextState									<= ST_IDLE;
					else
						NextState									<= ST_SEQUENCE_SOF;
					end if;
--					end if;
				end if;

			-- generate random numbers
			-- ----------------------------------------------------------------------
			when ST_RANDOM_SOF =>
				Out_Valid									<= '1';
				Out_Data									<= PRNG_Data;
				Out_SOF										<= '1';

				if (Out_Ack	 = '1') then
					FrameLengthCounter_en		<= '1';
					PRNG_got								<= '1';
					NextState								<= ST_RANDOM_DATA;
				end if;

			when ST_RANDOM_DATA =>
				Out_Valid									<= '1';
				Out_Data									<= PRNG_Data;

				if (Out_Ack	 = '1') then
					FrameLengthCounter_en		<= '1';
					PRNG_got								<= '1';

					if FrameLengthCounter_us = (unsigned(FrameLength) - 2) then
						NextState							<= ST_RANDOM_EOF;
					end if;
				end if;

			when ST_RANDOM_EOF =>
				Out_Valid									<= '1';
				Out_Data									<= PRNG_Data;
				Out_EOF										<= '1';

				FrameLengthCounter_rst		<= '1';

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

			when ST_ERROR =>
				Status										<= FRAMEGEN_STATUS_ERROR;
				NextState									<= ST_IDLE;

		end case;
	end process;

	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or FrameLengthCounter_rst) = '1') then
				FrameLengthCounter_us			<= (others => '0');
			elsif (FrameLengthCounter_en = '1') then
				FrameLengthCounter_us			<= FrameLengthCounter_us + 1;
			end if;
		end if;
	end process;

	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or SequencesCounter_rst) = '1') then
				SequencesCounter_us			<= (others => '0');
			elsif (SequencesCounter_en = '1') then
				SequencesCounter_us			<= SequencesCounter_us + 1;
			end if;
		end if;
	end process;

	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or ContentCounter_rst) = '1') then
				ContentCounter_us				<= (others => '0');
			elsif (ContentCounter_en = '1') then
				ContentCounter_us				<= ContentCounter_us + 1;
			end if;
		end if;
	end process;

	PRNG : entity PoC.arith_prng
    generic map (
      BITS		=> DATA_BITS
		)
    port map (
      clk			=> Clock,
      rst			=> PRNG_rst,
      got			=> PRNG_got,
      val			=> PRNG_Data
     );

end architecture;