--------------------------------------------------------------------------
-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL
-- This file: The shifter unit
--
-- Copyright (C) 2008 J. Ahrensfeld
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
-- For questions and ideas, please contact the author at jens@jayfield.org
--
--------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;

use work.mips_types.all;

entity shifter is
	Generic 
	(
		data_width : integer := 8
	);
	Port
	(
		shift_ctrl	: in  shift_ctrl_t;
		din			: in  unsigned (data_width-1 downto 0);
		dout		: out unsigned (data_width-1 downto 0)
	);
end shifter;

architecture Behavioral of shifter is

	subtype word_t is unsigned(data_width-1 downto 0);
	type word_array_t is array (0 to 5) of word_t;

	signal rot_data			: word_array_t;
	signal sa_rnd			: unsigned (4 downto 0);
	signal fill	: std_logic;
	
--------------------------------------------------------------------------
function rot_stage(x : unsigned; en : std_logic; stage_num : natural) return unsigned is
	variable result : unsigned(x'range);
	constant sa : natural := 2**stage_num;
	begin
	if en = '1' then
		result := x(sa-1 downto 0) & x(x'left downto sa);
	else
		result := x;
	end if;	
	return result;
	
end rot_stage;

function rot_fill(x : unsigned; fill : std_logic; shift_right : std_logic; sa : natural) return unsigned is
	variable result : unsigned(x'range) := (others => '0');
	variable j : integer;
	begin
		
	if shift_right = '0' then
		for i in 0 to x'left loop
			if i >= sa then
				result(i) := x(i);
			else
				result(i) := fill;
			end if;	
		end loop;
	else	
		j := x'left;
		for i in 0 to x'left loop
			if i < sa then
				result(j) := fill;
			else
				result(j) := x(j);
			end if;	
			j := j - 1;
		end loop;
	end if;	
	return result;
	
end rot_fill;

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

	rot_data(0)	<= din;

gen_rotate_right:
	for stage in 0 to 4 generate
	begin
		rot_data(stage+1) <= rot_stage(rot_data(stage), sa_rnd(stage), stage);
	end generate;
	
	sa_rnd	<= shift_ctrl.shamt_rnd;
	fill	<= shift_ctrl.shift_arith and din(data_width-1);
	dout	<= rot_fill(rot_data(5), fill, shift_ctrl.shift_right, to_integer(shift_ctrl.shamt_nrm)) after 5 ns;
--	dout	<= rot_data(5);
	
	

--------------------------------------------------------------------------
end Behavioral;

