-------------------------------------------------------------
-- MSS copyright 2011
--	Filename:  PX_TO_P8_CONVERSION.VHD
-- Author: Alain Zarembowitch / MSS
--	Version: 1
--	Date last modified: 7/11/11
-- Inheritance: 	n/a
--
-- description:  short elastic buffer and packing of 1 to 7-bit input samples 
-- into output bytes.
-- General width conversion rules:
-- a) MSb are transmitted first.
-- FIFO depth : 16-bits.
---------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
Library UNISIM;
use UNISIM.vcomponents.all;

entity PX_TO_P8_CONVERSION is
    port ( 
		--GLOBAL CLOCKS, RESET
	   CLK: in std_logic;	-- master clock for this FPGA, internal to the module

		--// Input data stream
		DATA_IN: in std_logic_vector(7 downto 0);
			-- Read at rising edge of CLK when SAMPLE_CLK_IN  = '1'.
			-- lower bits (WIDTH_IN -1):0 are defined.
			-- unused upper bits unspecified.
		SAMPLE_CLK_IN: in std_logic;
			-- one CLK wide pulse.
			-- Maximum rate: one SAMPLE_CLK_IN per CLK.
		WIDTH_IN: in std_logic_vector(2 downto 0);
			-- indicates how many bits are used within DATA_IN, starting
			-- with the LSb DATA_IN(0).
			-- valid range 1 - 7


		--// Output data stream
		DATA_OUT: out std_logic_vector(7 downto 0);
			-- Read at rising edge of CLK when SAMPLE_CLK_OUT  = '1'.
		SAMPLE_CLK_OUT: out std_logic
			-- one CLK-wide pulse

		);
end entity;

architecture behavioral of PX_TO_P8_CONVERSION is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
-- Suffix _D indicates a one CLK delayed version of the net with the same name
-- Suffix _E indicates an extended precision version of the net with the same name
-- Suffix _N indicates an inverted version of the net with the same name

signal DATA_IN_BR: std_logic_vector(7 downto 0) := (others => '0');
signal SAMPLE_CLK_IN_D: std_logic := '0';
signal EBUFFER: std_logic_vector(15 downto 0) := (others => '0');
signal WPTR: std_logic_vector(3 downto 0) := (others => '0');
signal WPTR_PLUS: std_logic_vector(3 downto 0) := (others => '0');
signal DATA_OUT_local: std_logic_vector(7 downto 0);
signal SAMPLE_CLK_OUT_local: std_logic := '0';
--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
begin

-- reverse bits in DATA_IN (so that the output is MSb first)
REVERSE_BITS_001: process(CLK)
begin
	if rising_edge(CLK) then
		SAMPLE_CLK_IN_D <= SAMPLE_CLK_IN;
		
		if (SAMPLE_CLK_IN = '1') then
			case (WIDTH_IN) is
				when "001" => 
					DATA_IN_BR(0) <= DATA_IN(0);
				when "010" => 
					DATA_IN_BR(0) <= DATA_IN(1);
					DATA_IN_BR(1) <= DATA_IN(0);
				when "011" => 
					DATA_IN_BR(0) <= DATA_IN(2);
					DATA_IN_BR(1) <= DATA_IN(1);
					DATA_IN_BR(2) <= DATA_IN(0);
				when "100" => 
					DATA_IN_BR(0) <= DATA_IN(3);
					DATA_IN_BR(1) <= DATA_IN(2);
					DATA_IN_BR(2) <= DATA_IN(1);
					DATA_IN_BR(3) <= DATA_IN(0);
				when "101" => 
					DATA_IN_BR(0) <= DATA_IN(4);
					DATA_IN_BR(1) <= DATA_IN(3);
					DATA_IN_BR(2) <= DATA_IN(2);
					DATA_IN_BR(3) <= DATA_IN(1);
					DATA_IN_BR(4) <= DATA_IN(0);
				when "110" => 
					DATA_IN_BR(0) <= DATA_IN(5);
					DATA_IN_BR(1) <= DATA_IN(4);
					DATA_IN_BR(2) <= DATA_IN(3);
					DATA_IN_BR(3) <= DATA_IN(2);
					DATA_IN_BR(4) <= DATA_IN(1);
					DATA_IN_BR(5) <= DATA_IN(0);
				when others => 
					DATA_IN_BR(0) <= DATA_IN(6);
					DATA_IN_BR(1) <= DATA_IN(5);
					DATA_IN_BR(2) <= DATA_IN(4);
					DATA_IN_BR(3) <= DATA_IN(3);
					DATA_IN_BR(4) <= DATA_IN(2);
					DATA_IN_BR(5) <= DATA_IN(1);
					DATA_IN_BR(6) <= DATA_IN(0);
			end case;
		end if;
	end if;
end process;

-- Push data in
PUSH_001: process(CLK)
begin
	if rising_edge(CLK) then
		if (SAMPLE_CLK_IN_D = '1') and (SAMPLE_CLK_OUT_local = '0') then
			-- new input symbol
			case(WPTR) is
				when "0000" => 
					EBUFFER(7 downto 0) <= DATA_IN_BR;
				when "0001" => 
					EBUFFER(8 downto 1) <= DATA_IN_BR;
				when "0010" => 
					EBUFFER(9 downto 2) <= DATA_IN_BR;
				when "0011" => 
					EBUFFER(10 downto 3) <= DATA_IN_BR;
				when "0100" => 
					EBUFFER(11 downto 4) <= DATA_IN_BR;
				when "0101" => 
					EBUFFER(12 downto 5) <= DATA_IN_BR;
				when "0110" => 
					EBUFFER(13 downto 6) <= DATA_IN_BR;
				when "0111" => 
					EBUFFER(14 downto 7) <= DATA_IN_BR;
				--when "1000" =>
				when others => 
					EBUFFER(15 downto 8) <= DATA_IN_BR;
			end case;
		elsif (SAMPLE_CLK_IN_D = '1') and (SAMPLE_CLK_OUT_local = '1') then
			-- new input symbol while sending byte out at this very clock
			-- shift 8 bits to the right.
			case(WPTR) is
				when "0000" => 
					EBUFFER(7 downto 0) <= DATA_IN_BR;
				when "0001" => 
					EBUFFER(0) <= EBUFFER(8);
					EBUFFER(8 downto 1) <= DATA_IN_BR;
				when "0010" => 
					EBUFFER(1 downto 0) <= EBUFFER(9 downto 8);
					EBUFFER(9 downto 2) <= DATA_IN_BR;
				when "0011" => 
					EBUFFER(2 downto 0) <= EBUFFER(10 downto 8);
					EBUFFER(10 downto 3) <= DATA_IN_BR;
				when "0100" => 
					EBUFFER(3 downto 0) <= EBUFFER(11 downto 8);
					EBUFFER(11 downto 4) <= DATA_IN_BR;
				when "0101" => 
					EBUFFER(4 downto 0) <= EBUFFER(12 downto 8);
					EBUFFER(12 downto 5) <= DATA_IN_BR;
				when "0110" => 
					EBUFFER(5 downto 0) <= EBUFFER(13 downto 8);
					EBUFFER(13 downto 6) <= DATA_IN_BR;
				when "0111" => 
					EBUFFER(6 downto 0) <= EBUFFER(14 downto 8);
					EBUFFER(14 downto 7) <= DATA_IN_BR;
				--when "1000" => 
				when others => 
					EBUFFER(7 downto 0) <= EBUFFER(15 downto 8);
					EBUFFER(15 downto 8) <= DATA_IN_BR;
			end case;
		elsif(SAMPLE_CLK_OUT_local = '1') then
			-- no new input symbol, sending byte out at this very clock
			-- shift 8 bits to the right.
			EBUFFER(7 downto 0) <= EBUFFER(15 downto 8);
		end if;	 
	end if;
end process;

-- MSb received first
DATA_OUT_local(0) <= EBUFFER(7);
DATA_OUT_local(1) <= EBUFFER(6);
DATA_OUT_local(2) <= EBUFFER(5);
DATA_OUT_local(3) <= EBUFFER(4);
DATA_OUT_local(4) <= EBUFFER(3);
DATA_OUT_local(5) <= EBUFFER(2);
DATA_OUT_local(6) <= EBUFFER(1);
DATA_OUT_local(7) <= EBUFFER(0);

WPTR_PLUS <= WPTR + ext(WIDTH_IN, WPTR'length);

-- Manage write pointer
WPTR_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if (SAMPLE_CLK_IN_D = '1') then
			if(WPTR_PLUS(3) = '1') then
				-- ready to send 8 bits to the output
				SAMPLE_CLK_OUT_local <= '1';
				WPTR <= "0" & WPTR_PLUS(2 downto 0);
			else
				-- keep on reading. still incomplete byte. 
				WPTR <= WPTR_PLUS;
				SAMPLE_CLK_OUT_local <= '0';
			end if;	
		else
			SAMPLE_CLK_OUT_local <= '0';
		end if;	 
	end if;
end process;

OUT_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		SAMPLE_CLK_OUT <= SAMPLE_CLK_OUT_local;
		
		if(SAMPLE_CLK_OUT_local = '1') then
			DATA_OUT <= DATA_OUT_local;
		end if;	 
	end if;
end process;


end architecture;
