--------------------------------------------------------------------------------
-- Company: Chalmers University of Technology
-- Engineer: Dmitry Knyaginin
-- 
-- Create Date:    14:55:56 11/16/2012
-- Module Name:    filter - rtl 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: This is a simple counter-based mechanical switch filter 
--              (debouncer). It filters out the noisy (bouncing) part of the
--              input signal. The duration of the filtering interval is 
--              defined with the width of the counter: the input signal is 
--              forwarded to the output as soon as the counter's MSb becomes
--              '1'. The counter is reset each time the input signal changes
--              its level. 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
--------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

entity FILTER is
  port (
    clk  : in std_logic;
    rst  : in std_logic;
    sin  : in std_logic;
    sout : out std_logic
    );
end FILTER;

architecture rtl of FILTER is

  -- The counter to cover at least 10ms @ 100MHz (21 bits, only MSb is checked;
  -- e.g., if want to divide a clock by 4 (2^2), the counter should be 3 bits).
  signal cnt : unsigned(20 downto 0);
  
  -- Registers stages for the input signal
  signal sin_reg : std_logic_vector(2 downto 0);
  

begin

  -- Assign the output to the last register stage
  sout <= sin_reg(2);

  filter_ctrl : process(rst, clk)
  begin
    if (rst = '1') then
      sin_reg <= (others => '0');
      cnt <= (others => '0');
        
    elsif rising_edge(clk) then
      sin_reg(0) <= sin;
      sin_reg(1) <= sin_reg(0);

      -- As soon as the counter's MSb becomes '1', register the input
      -- signal (i.e., forward to the output)
      if (cnt(cnt'high) = '1') then
        sin_reg(2) <= sin_reg(1);
      end if;
    
      -- If the input signal changes its level, reset the counter
      if ((sin_reg(0) xor sin_reg(1)) = '1') then
        cnt <= (others => '0');
      
      -- Count up until the MSb is '1'
      elsif (cnt(cnt'high) = '0') then
        cnt <= cnt + 1;          
        
      end if;

    end if;

  end process filter_ctrl;
  
end rtl;
