------------------------------------------------------------------------------
--  Copyright (c) 2018 by Paul Scherrer Institute, Switzerland
--  All rights reserved.
--  Authors: Oliver Bruendler
------------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Libraries
------------------------------------------------------------------------------
library ieee;
	use ieee.std_logic_1164.all;
	use ieee.numeric_std.all;
	
library work;
	use work.psi_common_math_pkg.all;

------------------------------------------------------------------------------
-- Package Header
------------------------------------------------------------------------------
package psi_common_logic_pkg is

	function ZerosVector(size : in natural) return std_logic_vector;
	
	function OnesVector(size : in natural) return std_logic_vector;
	
	function ShiftLeft(	arg		: in	std_logic_vector;
						bits	: in	integer;
						fill	: in	std_logic := '0') 
						return std_logic_vector;

	function ShiftRight(arg		: in	std_logic_vector;
						bits	: in	integer;
						fill	: in	std_logic := '0') 
						return std_logic_vector;		

	function BinaryToGray(	binary	: in	std_logic_vector)
							return std_logic_vector;
							
	function GrayToBinary(	gray	: in	std_logic_vector)
							return std_logic_vector;
							
	-- Parallel Prefix Computation of the OR function
	-- Input 	--> Output
	-- 0100		--> 0111
	-- 0101		--> 0111
	-- 0011		--> 0011
	-- 0010		--> 0011
	function PpcOr(	inp	: in	std_logic_vector)
					return std_logic_vector;
					
	function IntToStdLogic(	int 	: in	integer)
							return std_logic;
							
	function ReduceOr(	vec : in std_logic_vector)
						return std_logic;
						
	function ReduceAnd(	vec : in std_logic_vector)
						return std_logic;
						
	function To01X(	inp : in std_logic) 
					return std_logic;
	
	function To01X(	inp : in std_logic_vector)
					return std_logic_vector;

end psi_common_logic_pkg;	 

------------------------------------------------------------------------------
-- Package Body
------------------------------------------------------------------------------
package body psi_common_logic_pkg is 
  
	-- *** ZerosVector ***
	function ZerosVector(size : in natural) return std_logic_vector is
		constant c : std_logic_vector(size-1 downto 0) := (others => '0');
	begin
		return c;
	end function;
	
	-- *** OnesVector ***
	function OnesVector(size : in natural) return std_logic_vector is
		constant c : std_logic_vector(size-1 downto 0) := (others => '1');
	begin
		return c;
	end function;
	
	-- *** ShiftLeft ***
	function ShiftLeft(	arg		: in	std_logic_vector;
						bits	: in	integer;
						fill	: in	std_logic := '0')
						return std_logic_vector	is
		constant argDt : std_logic_vector(arg'high downto 0) := arg;
		variable v : std_logic_vector(argDt'range);
	begin
		if bits < 0 then
			return ShiftRight(argDt, -bits, fill);
		else
			v(v'left downto bits) 	:= argDt(argDt'left-bits downto 0);
			v(bits-1 downto 0)		:= (others => fill);
			return v;
		end if;
	end function;
				
	-- *** ShiftRight ***
	function ShiftRight(	arg		: in	std_logic_vector;
							bits	: in	integer;
							fill	: in	std_logic := '0')
							return std_logic_vector	is
		constant argDt : std_logic_vector(arg'high downto 0) := arg;
		variable v : std_logic_vector(argDt'range);
	begin
		if bits < 0 then
			return ShiftLeft(argDt, -bits, fill);
		else
			v(v'left-bits downto 0) 		:= argDt(argDt'left downto bits);
			v(v'left downto v'left-bits+1)	:= (others => fill);
			return v;
		end if;
	end function;	
	
	-- *** BinaryToGray ***
	function BinaryToGray(	binary	: in	std_logic_vector)
							return std_logic_vector is
		variable Gray_v : std_logic_vector(binary'range);
	begin
		Gray_v := binary xor ('0' & binary(binary'left downto 1));
		return Gray_v;
	end function;
			
	-- *** GrayToBinary ***
	function GrayToBinary(	gray	: in	std_logic_vector)
							return std_logic_vector is
		variable Binary_v : std_logic_vector(gray'range);
	begin
		Binary_v(Binary_v'left) := gray(gray'left);
		for b in gray'left-1 downto 0 loop
			Binary_v(b) := gray(b) xor Binary_v(b+1);
		end loop;		
		return Binary_v;
	end function;

	
	-- *** PpcOr ***
	function PpcOr(	inp	: in	std_logic_vector)
					return std_logic_vector	is
		constant Stages_c		: integer	:= log2ceil(inp'length);
		constant Pwr2Width_c	: integer	:= 2**Stages_c;
		type StageOut_t	is array (natural range <>) of std_logic_vector(Pwr2Width_c-1 downto 0);
		variable StageOut_v		: StageOut_t(0 to Stages_c);			
		variable BinCnt_v		: unsigned(Pwr2Width_c-1 downto 0);
	begin
		StageOut_v(0) 							:= (others => '0');
		StageOut_v(0)(inp'length-1 downto 0)	:= inp;
		for stage in 0 to Stages_c-1 loop		
			BinCnt_v := (others => '0');
			for idx in 0 to Pwr2Width_c-1 loop
				if BinCnt_v(stage) = '0' then
					StageOut_v(stage+1)(idx) := StageOut_v(stage)(idx) or StageOut_v(stage)((idx/(2**stage)+1)*2**stage);
				else
					StageOut_v(stage+1)(idx) := StageOut_v(stage)(idx);
				end if;
				BinCnt_v := BinCnt_v+1;
			end loop;
		end loop;
		return StageOut_v(Stages_c)(inp'length-1 downto 0);
	end function;
	
	function IntToStdLogic(	int 	: in	integer)
							return std_logic is
	begin
		if int = 1 then
			return '1';
		elsif int = 0 then
			return '0';
		else
			return 'X';
		end if;
	end function;
	
	function ReduceOr(	vec : in std_logic_vector)
						return std_logic is
		variable tmp : std_logic;
	begin
		tmp := '0';
		for i in 0 to vec'high loop
			tmp := tmp or vec(i);
		end loop;
		return tmp;
	end function;
		
	function ReduceAnd(	vec : in std_logic_vector)
						return std_logic is
		variable tmp : std_logic;
	begin
		tmp := '1';
		for i in 0 to vec'high loop
			tmp := tmp and vec(i);
		end loop;
		return tmp;
	end function;
	
	function To01X(	inp : in std_logic) 
					return std_logic is
	begin
		case inp is
			when '0' | 'L' => return '0';
			when '1' | 'H' => return '1';
			when others => return 'X';
		end case;
	end function;
	
	function To01X(	inp : in std_logic_vector)
					return std_logic_vector is
		variable tmp : std_logic_vector(inp'range);
	begin
		for i in inp'low to inp'high loop
			tmp(i) := to01X(inp(i));
		end loop;
		return tmp;
	end function;
	
end psi_common_logic_pkg;