----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    11:12:27 10/10/2011 
-- Design Name: 
-- Module Name:    switch_debouncer_core - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- SWITCH_DEBOUNCER_CORE.VHD
--
-- This VHDL module "debounces" an array of digital input switches. The
-- INPUT_SWITCH_ARRAY must be stable for [DELAY] CLOCK cycles before it is
-- assigned to the OUTPUT_SWITCH_ARRAY.
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity SWITCH_DEBOUNCER_CORE is
   generic
      ( 
      NUM_SWITCHES : integer := 8; -- Width of input & output
      DELAY : integer := 8
      );
   port
      (
      RESET : in std_logic;
      CLOCK : in std_logic;
      INPUT_SWITCH_ARRAY : in std_logic_vector((NUM_SWITCHES-1) downto 0);
      OUTPUT_SWITCH_ARRAY : out std_logic_vector((NUM_SWITCHES-1) downto 0);
      NEW_SWITCH_STATE : out std_logic;
      ACKNOWLEDGE : in std_logic
      );
end SWITCH_DEBOUNCER_CORE;

architecture Behavioral of SWITCH_DEBOUNCER_CORE is

-- Determine the minimum counter size
   constant SIZE : integer := integer(ceil(log2(real(DELAY))));

-- Convert (DELAY-1) to an unsigned bit vector
   constant DELAY_U : unsigned((SIZE-1) downto 0) := to_unsigned((DELAY-1),SIZE);

-- The counter for the debouncer
   signal DEBOUNCE_COUNTER : unsigned((SIZE-1) downto 0);
   signal INPUT_REGISTER : std_logic_vector((NUM_SWITCHES-1) downto 0);
   signal OUTPUT_REGISTER : std_logic_vector((NUM_SWITCHES-1) downto 0);
   signal INT_NEW_SWITCH_STATE : std_logic;

-- The debouncer state machine
   type DEBOUNCE_SM is (IDLE, DEBOUNCE_IN_PROGRESS);
   signal DEBOUNCE_STATE : DEBOUNCE_SM;

   begin
	
   DEBOUNCE_INPUT_SWITCH_ARRAY : process(CLOCK) is
      begin
		
      if rising_edge(CLOCK) then
         if (RESET = '1') then
            DEBOUNCE_COUNTER <= (others => '0');
            INPUT_REGISTER <= (others => '0');
            OUTPUT_REGISTER <= (others => '0');
            INT_NEW_SWITCH_STATE <= '0';
            DEBOUNCE_STATE <= IDLE;
         else

-- If a new value is detected on the switch array then it is registered and the DEBOUNCE_STATE 
-- goes to DEBOUNCE_IN_PROGRESS.  The DEBOUNCE_COUNTER gets initialized to DELAY.
            case DEBOUNCE_STATE is
               when IDLE =>
                  if (INPUT_SWITCH_ARRAY /= INPUT_REGISTER) then
                     INPUT_REGISTER <= INPUT_SWITCH_ARRAY;
                     DEBOUNCE_STATE <= DEBOUNCE_IN_PROGRESS;
                     DEBOUNCE_COUNTER <= DELAY_U;
                  end if;

-- The value of the switch array must be checked to determine that it remains constant for [DELAY] 
-- clock cycles. Once this occurs the value is registered again and outputted while the NEW_SWITCH_STATE 
-- is set to 1. If the value of the switches changes before [DELAY] clock cycles then the DEBOUNCE_STATE
-- returns to IDLE.
               when DEBOUNCE_IN_PROGRESS =>
                  if (INPUT_SWITCH_ARRAY = INPUT_REGISTER) then
                     DEBOUNCE_COUNTER <= DEBOUNCE_COUNTER - 1;

                     if (DEBOUNCE_COUNTER = 0) then
                        OUTPUT_REGISTER <= INPUT_REGISTER;
                        INT_NEW_SWITCH_STATE <= '1';
                        DEBOUNCE_STATE <= IDLE;
                     end if;
                  else
                     DEBOUNCE_STATE <= IDLE;
                  end if;
            end case;
			
-- Clear NEW_SWITCH_STATE when ACKNOWLEDGE is asserted
            if (INT_NEW_SWITCH_STATE = '1' and ACKNOWLEDGE = '1') then
               INT_NEW_SWITCH_STATE <= '0';
            end if;
         end if; -- if(RESET = '1')
      end if; -- if rising_edge(CLOCK)
   end process DEBOUNCE_INPUT_SWITCH_ARRAY;
	
   OUTPUT_SWITCH_ARRAY <= OUTPUT_REGISTER;
   NEW_SWITCH_STATE <= INT_NEW_SWITCH_STATE;

   end architecture;
