-- 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
--									Thomas B. Preusser
--
-- Entity:				 	Debounce module for BITS many bouncing input pins.
--
-- Description:
-- 
-- This module debounces several input pins preventing input changes
-- following a previous one within the configured ``BOUNCE_TIME`` to pass.
-- Internally, the forwarded state is locked for, at least, this ``BOUNCE_TIME``.
-- As the backing timer is restarted on every input fluctuation, the next
-- passing input update must have seen a stabilized input.
--
-- The parameter ``COMMON_LOCK`` uses a single internal timer for all processed
-- inputs. Thus, all inputs must stabilize before any one may pass changed.
-- This option is usually fully acceptable for user inputs such as push buttons.
--
-- The parameter ``ADD_INPUT_SYNCHRONIZERS`` triggers the optional instantiation
-- of a two-FF input synchronizer on each input bit.
--
-- License:
-- =============================================================================
-- Copyright 2007-2015 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;
use			PoC.physical.all;


entity [docs]io_Debounce is
  generic (
    CLOCK_FREQ 							: FREQ;
    BOUNCE_TIME							: time;
    BITS                    : positive := 1;
		INIT										: std_logic_vector		:= x"00000000";	-- initial state of Output
    ADD_INPUT_SYNCHRONIZERS : boolean  := true;
    COMMON_LOCK             : boolean  := false
  );
  port (
    Clock		: in	std_logic;
		Reset		: in	std_logic							:= '0';
    Input		: in	std_logic_vector(BITS-1 downto 0);
    Output	: out	std_logic_vector(BITS-1 downto 0) := resize(descend(INIT), BITS)
  );
end entity;


architecture [docs]rtl of io_Debounce is
	-- Number of required locking cycles
	constant LOCK_COUNT_X : integer := TimingToCycles(BOUNCE_TIME, CLOCK_FREQ) - 1;

	-- Input Refinements
  signal sync		: std_logic_vector(Input'range);										-- Synchronized
  signal prev		: std_logic_vector(Input'range) := (others => '0');	-- Delayed
	signal active	: std_logic_vector(Input'range);										-- Allow Output Updates

begin
  -----------------------------------------------------------------------------
  -- Input Synchronization
  genNoSync: if not ADD_INPUT_SYNCHRONIZERS generate
    sync <= Input;
  end generate;
  genSync: if ADD_INPUT_SYNCHRONIZERS generate
    sync_i : entity PoC.sync_Bits
      generic map (
        BITS => BITS,
				INIT => INIT
      )
      port map (
        Clock  => Clock,  	-- Clock to be synchronized to
        Input  => Input,  	-- Data to be synchronized
        Output => sync  		-- synchronised data
      );
  end generate;

	-----------------------------------------------------------------------------
	-- Bounce Filter
	process(Clock)
	begin
		if rising_edge(Clock) then
			prev <= sync;
			if (Reset = '1') then
				Output <= sync;
			else
				for i in Output'range loop
					if active(i) = '1' then
						Output(i) <= sync(i);
					end if;
				end loop;
			end if;
		end if;
	end process;

	genNoLock: if LOCK_COUNT_X <= 0 generate
		active <= (others => '1');
	end generate genNoLock;
	genLock: if LOCK_COUNT_X > 0 generate
		constant LOCKS	: positive := ite(COMMON_LOCK, 1, BITS);

		signal toggle		: std_logic_vector(LOCKS-1 downto 0);
		signal locked		: std_logic_vector(LOCKS-1 downto 0);
	begin

    genOneLock: if COMMON_LOCK generate
      toggle(0) <= '1' when prev /= sync else '0';
      active    <= (others => not locked(0));
    end generate genOneLock;
    genManyLocks: if not COMMON_LOCK generate
      toggle <= prev xor sync;
      active <= not locked;
    end generate genManyLocks;

		genLocks: for i in 0 to LOCKS-1 generate
			signal Lock : signed(log2ceil(LOCK_COUNT_X+1) downto 0) := (others => '0');
		begin
			process(Clock)
			begin
				if rising_edge(Clock) then
					if (Reset = '1') then
						Lock <= (others => '0');
					else
						if toggle(i) = '1' then
							Lock <= to_signed(-LOCK_COUNT_X, Lock'length);
						elsif locked(i) = '1' then
							Lock <= Lock + 1;
						end if;
					end if;
				end if;
			end process;
			locked(i) <= Lock(Lock'left);
		end generate genLocks;
	end generate genLock;

end;