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 sipo IS
	Generic
	(
		data_width_in	: natural := 8;
		data_width_out	: natural := 32;
		msb_first		: boolean := false
	);
	Port
	(
		rst			: in  STD_LOGIC;
		clk			: in  STD_LOGIC;
		din_en		: in  STD_LOGIC;
		din_vld		: in  STD_LOGIC;
		din			: in  unsigned(data_width_in-1 downto 0);
		shift_en	: out STD_LOGIC;
		dout_vld	: out STD_LOGIC;
		dout		: out unsigned(data_width_out-1 downto 0);
		dout_be		: out unsigned(data_width_out/data_width_in-1 downto 0)
		
	);
END sipo;

ARCHITECTURE behavior OF sipo IS

	constant num_shifts		: natural := data_width_out/data_width_in;
	signal pre_fin			: STD_LOGIC;
	signal shift_cnt_pipe	: unsigned(num_shifts-1 downto 0);
	signal shift_pipe		: unsigned(data_width_out-1 downto 0);
	signal out_en			: STD_LOGIC;
	signal out_vld			: STD_LOGIC;
	signal din_vld_r		: STD_LOGIC;
	signal abort			: STD_LOGIC;
	
--------------------------------------------------------------------------
begin

	pre_fin		<= shift_cnt_pipe(shift_cnt_pipe'left) when msb_first else shift_cnt_pipe(0);
	shift_en	<= out_en;
	dout_vld	<= out_vld;
	
--------------------------------------------------------------------------
process(clk)
begin
	if rising_edge(clk) then
		if rst = '1' or pre_fin = '1' then
			abort <= '0';
		elsif din_vld_r = '1' and din_en = '0' then	
			abort <= '1';
		end if;
	end if;
end process;

process(clk)
begin
	if rising_edge(clk) then
		din_vld_r	<= din_vld and din_en;
		if rst = '1' then
			out_en <= '0';
		elsif out_en = '0' then	
			out_en		<= pre_fin and din_en;
		elsif out_vld = '1' then
			out_en		<= din_en;
		end if;
	end if;
end process;

process(clk)
begin
	if rising_edge(clk) then
		out_vld	<= pre_fin;
		if pre_fin = '1' then
			dout		<= shift_pipe;
			dout_be		<= shift_cnt_pipe;
		end if;
	end if;
end process;

process(clk)
begin
	if rising_edge(clk) then
		if rst = '1' or pre_fin = '1' then
			if (msb_first) then
				shift_cnt_pipe	<= (shift_cnt_pipe'left downto 1 => '0') & (din_en and din_vld);
			else
				shift_cnt_pipe	<= (din_en and din_vld) & (shift_cnt_pipe'left downto 1 => '0');
			end if;
		elsif din_vld = '1' or abort = '1' then
			if (msb_first) then
				shift_cnt_pipe	<= shift_cnt_pipe(shift_cnt_pipe'left-1 downto 0)  & din_en;
			else
				shift_cnt_pipe	<= din_en & shift_cnt_pipe(shift_cnt_pipe'left downto 1);
			end if;
		end if;
	end if;
end process;

process(clk)
begin
	if rising_edge(clk) then
		if din_vld = '1' or abort = '1' then
			if (msb_first) then
				shift_pipe		<= shift_pipe(shift_pipe'left-data_width_in downto 0) & din;
			else
				shift_pipe		<= din & shift_pipe(shift_pipe'left downto data_width_in);
			end if;
		end if;
	end if;
end process;

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