LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
use std.textio.all; --  Imports the standard textio package.

use work.utils_pkg.all; --  Imports the standard textio package.

ENTITY piso IS
	Generic
	(
		data_width_in	: natural := 32;
		data_width_out	: natural := 8;
		msb_first		: boolean := false
	);
	Port
	(
		rst			: in  STD_LOGIC;
		clk			: in  STD_LOGIC;
		din_vld		: in  STD_LOGIC;
		din_re		: out STD_LOGIC;
		din			: in  unsigned(data_width_in-1 downto 0);
		din_be		: in  unsigned(data_width_in/data_width_out-1 downto 0);
		dout_vld	: out STD_LOGIC;
		shift_en	: in  STD_LOGIC;
		dout		: out unsigned(data_width_out-1 downto 0)
		
	);
END piso;

ARCHITECTURE behavior OF piso IS

	constant num_shifts		: natural := data_width_in/data_width_out;
	signal pre_fin			: STD_LOGIC;
	signal shift_cnt_pipe	: unsigned(num_shifts-1 downto 0);
	signal shift_pipe		: unsigned(data_width_in-1 downto 0);
	signal vld_pipe			: unsigned(num_shifts-1 downto 0);
	signal shift_reg_we		: STD_LOGIC;
	
	type state_t is (idle, shift, finish);
	signal state		: state_t;
	signal state_next	: state_t;
	
--------------------------------------------------------------------------
begin

	pre_fin		<= shift_cnt_pipe(shift_cnt_pipe'left-1);
	dout_vld	<= vld_pipe(vld_pipe'left) when msb_first else vld_pipe(0);
	dout		<= 		shift_pipe(shift_pipe'left downto shift_pipe'left-data_width_out+1) when msb_first
				else	shift_pipe(data_width_out-1 downto 0);

--------------------------------------------------------------------------
proc_state_next:
process(clk)
begin
	if rising_edge(clk) then
		if rst = '1' then
			state	<= idle;
		else
			state	<= state_next;
		end if;
	end if;
end process;

proc_fsm:
process(state, din_vld, shift_en, pre_fin)
begin
	state_next		<= state;
	shift_reg_we	<= '0';
	din_re			<= '0';
	case state is
	
	when idle =>
		din_re		<= '1';
		if din_vld = '1' then
			shift_reg_we	<= '1';
			state_next		<= shift;
		end if;

	when shift =>
		if pre_fin = '1' then
			if din_vld = '1' then
				if shift_en = '1' then
					din_re			<= '1';
					shift_reg_we	<= '1';
					state_next		<= shift;
				end if;
			else
				state_next	<= idle;
			end if;
		end if;

	when finish =>
		state_next	<= idle;

	when others =>
		state_next	<= idle;

	end case;	
end process;

process(clk)
begin
	if rising_edge(clk) then
		if rst = '1' then
			shift_cnt_pipe	<= (others => '1');
		elsif shift_reg_we = '1' then
			shift_cnt_pipe	<= (others => '0');
		elsif shift_en = '1' then
			shift_cnt_pipe	<= shift_cnt_pipe(shift_cnt_pipe'left-1 downto 0)  & '1';
		end if;
	end if;
end process;

--------------------------------------------------------------------------
process(clk)
begin
	if rising_edge(clk) then
		if shift_reg_we = '1' then
			shift_pipe		<= din;
		elsif shift_en = '1' then
			if (msb_first) then
				shift_pipe		<= shift_pipe(shift_pipe'left-data_width_out downto 0) & (data_width_out-1 downto 0 => '0');
			else
				shift_pipe		<= (data_width_out-1 downto 0 => '0') & shift_pipe(shift_pipe'left downto data_width_out);
			end if;
		end if;
	end if;
end process;

--------------------------------------------------------------------------
process(clk)
begin
	if rising_edge(clk) then
		if rst = '1' then
			vld_pipe	<= (others => '0');
		elsif shift_reg_we = '1' then
			vld_pipe		<= din_be;
		elsif shift_en = '1' then
			if (msb_first) then
				vld_pipe		<= vld_pipe(vld_pipe'left-1 downto 0) & '0';
			else
				vld_pipe		<= '0' & vld_pipe(vld_pipe'left downto 1);
			end if;
		end if;
	end if;
end process;

--------------------------------------------------------------------------
end behavior;
