--------------------------------------------------------------------------
-- 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;
	
	type fill_mask_rom_t is array (0 to 2*data_width-1) of unsigned(data_width-1 downto 0);
--------------------------------------------------------------------------
function gen_fill_mask_rom(data_width_arg : natural) return fill_mask_rom_t is
	variable result : fill_mask_rom_t;
	begin
	    result(0) := (data_width_arg-1 downto 0 => '0');
		for i in 1 to data_width_arg-1 loop
			result(i) := (data_width_arg-1-i downto 0 => '0') & (i-1 downto 0 => '1');
		end loop;	
	    result(data_width_arg) := (data_width_arg-1 downto 0 => '0');
		for i in 1 to data_width_arg-1 loop
			result(data_width_arg+i) := (i-1 downto 0 => '1') & (data_width_arg-1-i downto 0 => '0');
		end loop;	
	return result;
	
end gen_fill_mask_rom;

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;

	constant fill_mask_rom	: fill_mask_rom_t := gen_fill_mask_rom(data_width);
	signal fill_mask		: unsigned(data_width-1 downto 0);
	signal fill_mask_addr	: unsigned(5 downto 0);
	signal dout_filled		: unsigned (data_width-1 downto 0);
	
--------------------------------------------------------------------------
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);
	fill_mask_addr	<= shift_ctrl.shift_right & shift_ctrl.shamt_nrm;
	fill_mask		<= fill_mask_rom(to_integer(fill_mask_addr));

proc_fill_neu:
	process(fill_mask, fill, rot_data(5))
	begin
		if fill = '1' then
			dout_filled	<= rot_data(5) or fill_mask;
		else	
			dout_filled	<= rot_data(5) and not fill_mask;
		end if;
	end process;
	
	dout	<= dout_filled;
	
--------------------------------------------------------------------------
end Behavioral;

