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 shifter IS
	Generic
	(
		data_width		: natural := 32;
		aggregate_size	: natural := 8;
		do_pipelined	: boolean := true
	);
	Port
	(
		rst			: in  STD_LOGIC;
		clk			: in  STD_LOGIC;
		sa			: in  integer;
		din_vld		: in  STD_LOGIC;
		din			: in  unsigned(data_width-1 downto 0);
		dout_vld	: out STD_LOGIC;
		dout		: out unsigned(data_width-1 downto 0)
		
	);
END shifter;

ARCHITECTURE behavior OF shifter IS

	constant num_rounds	: natural := NextExpBaseTwo(data_width/aggregate_size);
	subtype sa_rnd_t is signed(num_rounds-1 downto 0);
	subtype word_t is unsigned(data_width-1 downto 0);
	type word_array_t is array (0 to num_rounds) of word_t;

	signal rot_data			: word_array_t;
	signal sa_rnd			: sa_rnd_t;

	type sa_rnd_array_t is array (0 to num_rounds) of sa_rnd_t;
	signal sa_rnd_pipe		: sa_rnd_array_t;
	signal dout_vld_pipe	: unsigned(0 to num_rounds);
	
--------------------------------------------------------------------------
function rol_stage(x : unsigned; en : std_logic; stage_num : natural; size : natural) return unsigned is
	variable result : unsigned(x'range);
	constant sa : natural := (2**stage_num) * size;
	begin
	if en = '1' then
		result := x(x'left-sa downto 0) & x(x'left downto x'length-sa);
	else
		result := x;
	end if;	
	return result;
end rol_stage;

--------------------------------------------------------------------------
begin

--------------------------------------------------------------------------
gen_non_pipelined:
if do_pipelined = false generate
begin
	
	rot_data(0)		<= din;
	
gen_rotate_right:
	for stage in 0 to num_rounds-1 generate
	begin
		rot_data(stage+1) <= rol_stage(rot_data(stage), sa_rnd(stage), stage, aggregate_size);
	end generate;
	
	sa_rnd			<= to_signed(sa, num_rounds);
	dout			<= rot_data(num_rounds);
	dout_vld		<= din_vld;
	
end generate;
	
--------------------------------------------------------------------------
gen_pipelined:
if do_pipelined = true generate
begin
	
	rot_data(0)			<= din;
	sa_rnd_pipe(0)		<= to_signed(sa, num_rounds);
	dout_vld_pipe(0)	<= din_vld;
	
gen_rotate_right:
	for stage in 0 to num_rounds-1 generate
	begin
		process(clk)
		begin
			if rising_edge(clk) then
				if rst = '1' then
					dout_vld_pipe(stage+1)	<= '0';
				else
					dout_vld_pipe(stage+1)	<= dout_vld_pipe(stage);
					if dout_vld_pipe(stage) = '1' then
						sa_rnd_pipe(stage+1)	<= sa_rnd_pipe(stage);
						rot_data(stage+1)		<= rol_stage(rot_data(stage), sa_rnd_pipe(stage)(stage), stage, aggregate_size);
					end if;
				end if;
			end if;
		end process;
	end generate;
	
	dout			<= rot_data(num_rounds);
	dout_vld		<= dout_vld_pipe(num_rounds);
	
end generate;

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