-- EMACS settings: -*-  tab-width: 2; indent-tabs-mode: t -*-
-- vim: tabstop=2:shiftwidth=2:noexpandtab
-- kate: tab-width 2; replace-tabs off; indent-width 2;
-- =============================================================================
-- Authors:				 	Patrick Lehmann
--
-- Package:				 	Common primitives described as a function
--
-- Description:
-- 
--		This packages describes common primitives like flip flops and multiplexers
--		as a function to use them as one-liners.
--
--	ATTENSION:
--		The parameter 'constant INIT' of some functions is actually the reset
--		value, not the initial value after device programming (e.g. for FPGAs),
--		this value MUST be set via signal declaration!
--
-- License:
-- =============================================================================
-- Copyright 2007-2016 Technische Universitaet Dresden - Germany
--										 Chair of VLSI-Design, Diagnostics and Architecture
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--		http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- =============================================================================

library IEEE;
use			IEEE.STD_LOGIC_1164.all;
use			IEEE.NUMERIC_STD.all;

library PoC;
use			PoC.utils.all;


package [docs]components is
	-- implement an optional register stage
	function [docs]registered(signal Clock : std_logic; constant IsRegistered : boolean) return boolean;

	-- FlipFlop functions
	-- ===========================================================================
	-- RS-FlipFlops
	function [docs]ffrs(q : std_logic;	rst : std_logic := '0'; set : std_logic := '0') return std_logic;				-- RS-FlipFlop with dominant rst
	function [docs]ffsr(q : std_logic;	rst : std_logic := '0'; set : std_logic := '0') return std_logic;				-- RS-FlipFlop with dominant set
	-- D-FlipFlops (Delay)
	function [docs]ffdre(q : std_logic;					d : std_logic;				rst : std_logic := '0'; en : std_logic := '1'; constant INIT : std_logic := '0')												return std_logic;					-- D-FlipFlop with reset and enable
	function [docs]ffdre(q : std_logic_vector;	d : std_logic_vector;	rst : std_logic := '0'; en : std_logic := '1'; constant INIT : std_logic_vector := (0 to 0 => '0'))	return std_logic_vector;	-- D-FlipFlop with reset and enable
	function [docs]ffdse(q : std_logic;					d : std_logic;				set : std_logic := '0'; en : std_logic := '1')																													return std_logic;					-- D-FlipFlop with set and enable
	-- T-FlipFlops (Toggle)
	function [docs]fftre(q : std_logic;					t : std_logic;				rst : std_logic := '0'; en : std_logic := '1'; constant INIT : std_logic := '0')												return std_logic;					-- T-FlipFlop with reset and enable
	function [docs]fftse(q : std_logic;					t : std_logic;				set : std_logic := '0'; en : std_logic := '1')																													return std_logic;					-- T-FlipFlop with set and enable

	-- counter
	function [docs]upcounter_next(cnt : unsigned; rst : std_logic := '0'; en : std_logic := '1'; constant INIT : natural := 0) return unsigned;
	function [docs]upcounter_equal(cnt : unsigned; value : natural) return std_logic;
	function [docs]downcounter_next(cnt : signed; rst : std_logic := '0'; en : std_logic := '1'; constant INIT : integer := 0) return signed;
	function [docs]downcounter_equal(cnt : signed; value : integer) return std_logic;
	function [docs]downcounter_neg(cnt : signed) return std_logic;

	-- shiftregisters
	function [docs]shreg_left(q : std_logic_vector; i : std_logic; en : std_logic := '1') return std_logic_vector;
	function [docs]shreg_right(q : std_logic_vector; i : std_logic; en : std_logic := '1') return std_logic_vector;
	-- rotate registers
	function [docs]rreg_left(q : std_logic_vector; en : std_logic := '1') return std_logic_vector;
	function [docs]rreg_right(q : std_logic_vector; en : std_logic := '1') return std_logic_vector;

	-- compare
	function [docs]comp(value1 : std_logic_vector; value2 : std_logic_vector) return std_logic_vector;
	function [docs]comp(value1 : unsigned; value2 : unsigned) return unsigned;
	function [docs]comp(value1 : signed; value2 : signed) return signed;
	function [docs]comp_allzero(value	: std_logic_vector)	return std_logic;
	function [docs]comp_allzero(value	: unsigned)					return std_logic;
	function [docs]comp_allzero(value	: signed)						return std_logic;
	function [docs]comp_allone(value	: std_logic_vector)	return std_logic;
	function [docs]comp_allone(value	: unsigned)					return std_logic;
	function [docs]comp_allone(value	: signed)						return std_logic;

	-- multiplexing
	function [docs]mux(sel : std_logic; sl0		: std_logic;				sl1		: std_logic)				return std_logic;
	function [docs]mux(sel : std_logic; slv0	: std_logic_vector;	slv1	: std_logic_vector)	return std_logic_vector;
	function [docs]mux(sel : std_logic; us0		: unsigned;					us1		: unsigned)					return unsigned;
	function [docs]mux(sel : std_logic; s0		: signed;						s1		: signed)						return signed;
end package;


package body components is
	-- implement an optional register stage
	-- ===========================================================================
	function registered(signal Clock : std_logic; constant IsRegistered : boolean) return boolean is
	begin
		return ite(IsRegistered, rising_edge(Clock), TRUE);
	end function;

	-- FlipFlops
	-- ===========================================================================
	-- D-flipflop with reset and enable
	function ffdre(q : std_logic; d : std_logic; rst : std_logic := '0'; en : std_logic := '1'; constant INIT : std_logic := '0') return std_logic is
	begin
		if not SIMULATION then
			if (INIT = '0') then
				return ((d and en) or (q and not en)) and not rst;
			elsif (INIT = '1') then
				return ((d and en) or (q and not en)) or rst;
			else
				report "Unsupported INIT value for synthesis." severity FAILURE;
				return 'X';
			end if;
		elsif (rst = '1') then
			return INIT;
		else
			return ((d and en) or (q and not en));
		end if;
	end function;

	function ffdre(q : std_logic_vector; d : std_logic_vector; rst : std_logic := '0'; en : std_logic := '1'; constant INIT : std_logic_vector := (0 to 0 => '0')) return std_logic_vector is
		constant INIT_I		: std_logic_vector(q'range)		:= resize(INIT, q'length);
		variable Result		: std_logic_vector(q'range);
	begin
		for i in q'range loop
			Result(i)		:= ffdre(q => q(i), d => d(i), rst => rst, en => en, INIT => INIT_I(i));
		end loop;
		return Result;
	end function;

	-- D-flipflop with set and enable
	function ffdse(q : std_logic; d : std_logic; set : std_logic := '0'; en : std_logic := '1') return std_logic is
	begin
		return ffdre(q => q, d => d, rst => set, en => en, INIT => '1');
	end function;

	-- T-flipflop with reset and enable
	function fftre(q : std_logic; t : std_logic; rst : std_logic := '0'; en : std_logic := '1'; constant INIT : std_logic := '0') return std_logic is
	begin
		if not SIMULATION then
			if (INIT = '0') then
				return ((not q and (t and en)) or (q and not (t and en))) and not rst;
			elsif (INIT = '1') then
				return ((not q and (t and en)) or (q and not (t and en))) or rst;
			else
				report "Unsupported INIT value for synthesis." severity FAILURE;
				return 'X';
			end if;
		elsif (rst = '1') then
			return INIT;
		else
			return ((not q and (t and en)) or (q and not (t and en)));
		end if;
	end function;

	-- T-flipflop with set and enable
	function fftse(q : std_logic; t : std_logic; set : std_logic := '0'; en : std_logic := '1') return std_logic is
	begin
		return fftre(q => q, t => t, rst => set, en => en, INIT => '1');
	end function;

	-- RS-flipflop with dominant rst
	function ffrs(q : std_logic; rst : std_logic := '0'; set : std_logic := '0') return std_logic is
	begin
		return (q or set) and not rst;
	end function;

	-- RS-flipflop with dominant set
	function ffsr(q : std_logic; rst : std_logic := '0'; set : std_logic := '0') return std_logic is
	begin
		return (q and not rst) or set;
	end function;


	-- Counters
	-- ===========================================================================
	-- up-counter
	function upcounter_next(cnt : unsigned; rst : std_logic := '0'; en : std_logic := '1'; constant INIT : natural := 0) return unsigned is
	begin
		if (rst = '1') then
			return to_unsigned(INIT, cnt'length);
		elsif (en = '1') then
			return cnt + 1;
		else
			return cnt;
		end if;
	end function;

	function upcounter_equal(cnt : unsigned; value : natural) return std_logic is
	begin
		-- optimized comparison for only up counting values
		return to_sl((cnt and to_unsigned(value, cnt'length)) = value);
	end function;

	-- down-counter
	function downcounter_next(cnt : signed; rst : std_logic := '0'; en : std_logic := '1'; constant INIT : integer := 0) return signed is
	begin
		if (rst = '1') then
			return to_signed(INIT, cnt'length);
		elsif (en = '1') then
			return cnt - 1;
		else
			return cnt;
		end if;
	end function;

	function downcounter_equal(cnt : signed; value : integer) return std_logic is
	begin
		-- optimized comparison for only down counting values
		return to_sl((cnt nor to_signed(value, cnt'length)) /= value);
	end function;

	function downcounter_neg(cnt : signed) return std_logic is
	begin
		return cnt(cnt'high);
	end function;

	-- Shift/Rotate Registers
	-- ===========================================================================
	function shreg_left(q : std_logic_vector; i : std_logic; en : std_logic := '1') return std_logic_vector is
	begin
		return mux(en, q, q(q'left - 1 downto q'right) & i);
	end function;

	function shreg_right(q : std_logic_vector; i : std_logic; en : std_logic := '1') return std_logic_vector is
	begin
		return mux(en, q, i & q(q'left downto q'right - 1));
	end function;

	function rreg_left(q : std_logic_vector; en : std_logic := '1') return std_logic_vector is
	begin
		return mux(en, q, q(q'left - 1 downto q'right) & q(q'left));
	end function;

	function rreg_right(q : std_logic_vector; en : std_logic := '1') return std_logic_vector is
	begin
		return mux(en, q, q(q'right) & q(q'left downto q'right - 1));
	end function;

	-- compare functions
	-- ===========================================================================
	-- Returns, when
	--	1-		=> value1 < value2 (difference is negative)
	--	00		=> value1 = value2 (difference is zero)
	--	-1		=> value1 > value2 (difference is positive)
	function comp(value1 : std_logic_vector; value2 : std_logic_vector) return std_logic_vector is
	begin
		report "Comparing two STD_LOGIC_VECTORs - implicit conversion to UNSIGNED" severity WARNING;
		return std_logic_vector(comp(unsigned(value1), unsigned(value2)));
	end function;

	function comp(value1 : unsigned; value2 : unsigned) return unsigned is
	begin
		if value1 < value2 then
			return "10";
		elsif value1 = value2 then
			return "00";
		else
			return "01";
		end if;
	end function;

	function comp(value1 : signed; value2 : signed) return signed is
	begin
		if value1 < value2 then
			return "10";
		elsif value1 = value2 then
			return "00";
		else
			return "01";
		end if;
	end function;

	function comp_allzero(value	: std_logic_vector) return std_logic is
	begin
		return comp_allzero(unsigned(value));
	end function;

	function comp_allzero(value	: unsigned) return std_logic is
	begin
		return to_sl(value = (value'range => '0'));
	end function;

	function comp_allzero(value	: signed) return std_logic is
	begin
		return to_sl(value = (value'range => '0'));
	end function;

	function comp_allone(value	: std_logic_vector) return std_logic is
	begin
		return comp_allone(unsigned(value));
	end function;

	function comp_allone(value	: unsigned) return std_logic is
	begin
		return to_sl(value = (value'range => '1'));
	end function;

	function comp_allone(value	: signed) return std_logic is
	begin
		return to_sl(value = (value'range => '1'));
	end function;


	-- multiplexers
	function mux(sel : std_logic; sl0 : std_logic; sl1 : std_logic) return std_logic is
	begin
		return (sl0 and not sel) or (sl1 and sel);
	end function;

	function mux(sel : std_logic; slv0 : std_logic_vector; slv1 : std_logic_vector) return std_logic_vector is
	begin
		return (slv0 and not (slv0'range => sel)) or (slv1 and (slv1'range => sel));
	end function;

	function mux(sel : std_logic; us0 : unsigned; us1 : unsigned) return unsigned is
	begin
		return (us0 and not (us0'range => sel)) or (us1 and (us1'range => sel));
	end function;

	function mux(sel : std_logic; s0 : signed; s1 : signed) return signed is
	begin
		return (s0 and not (s0'range => sel)) or (s1 and (s1'range => sel));
	end function;
end package body;