How to count pressed keys on FPGA spartan board

The effect you are witnessing is called “bouncing” of the switch.
You need to “debounce” the external input.


How to synchronize an external input

An external input is not synchronous to the internal clock domain. Thus signal edges within the setup or hold time of a register could cause metastability. You need to synchronize your input to the clock domain using a synchronizer. A two-stage synchronizer is usually sufficient.

Example code:

library ieee;
use ieee.std_logic_1164.all;

entity synchronizer is
    generic(
        nr_of_stages : natural := 2
        );
    port(
        clk : in std_logic;
        asynchronous_input : in std_logic;
        synchronous_output : out std_logic
        );
end entity;

architecture rtl of synchronizer is
    signal registers : std_logic_vector(nr_of_stages-1 downto 0);
    -- no intialization as this could give a false edge further in the chain.
begin
    -- build the registers
    register_proc : process(clk)
    begin
        -- connect the registers end to end
        if rising_edge(clk) then
            for i in nr_of_stages-1 downto 1 loop
                registers(i) <= registers(i-1);
            end loop;
            registers(0) <= asynchronous_input;
        end if;
    end process;
    -- connect the output to the last register
    synchronous_output <= registers(nr_of_stages-1);
end architecture;

Debouncing the signal

Assuming the input is clock synchronous (or synchronized, as described above). You can debounce the signal by ensuring it is stable for a prolonged period. I.e. start a counter when a button is pressed and forward the input when the counter reaches a value.

Example code:

library ieee;
use ieee.std_logic_1164.all;

entity debouncer is
    generic(
        clock_frequency : positive := 20e6; -- e.g. 20 MHz
        settle_time : time := 100 ms
        );
    port(
        clk : in std_logic;
        input : in std_logic;
        output : out std_logic
        );
end entity;

architecture rtl of debouncer is
    constant settle_time_in_clocks : positive := integer(real(clock_frequency) * settle_time / 1 sec); -- MHz to ms
    signal timer : natural range settle_time_in_clocks-1 downto 0 := settle_time_in_clocks-1;
begin
    timer_proc : process(clk)
    begin
        if rising_edge(clk) then
            if input="0" then
                -- not asserted: reset the timer and output
                timer <= settle_time_in_clocks-1;
                output <= '0';
            elsif timer = 0 then
                -- timer finished, set the output
                output <= '1';
            else
                -- count down
                timer <= timer - 1;
            end if;
        end if;
    end process;
end architecture;

How to count a key press

You detect a key press by detecting a 0-to-1 transition of the input.

Example code:

library ieee;
use ieee.std_logic_1164.all;

entity kpcnt is
    port(
        clk : in std_logic;
        rst : in std_logic;
        input_from_debouncer : in std_logic -- assumed to be synchronous to clk
        -- some output to be defined
        );
end entity;

architecture rtl of kpcnt is
    signal input_delay : std_logic;
    signal input_rising_edge : std_logic;
    use ieee.numeric_std.all;
    signal kpcounter : unsigned(7 downto 0) := (others => '0');
begin
    -- create delayed input signal
    delay_input : process(clk)
    begin
        if rising_edge(clk) then
            input_delay <= input_from_debouncer;
        end if;
    end process;
    -- detect 0->1 transition
    input_rising_edge <= '1' when input_from_debouncer="1" and input_delay = '0' else '0';
    -- count the number of 0->1 transitions
    kpcounter_proc : process(clk)
    begin
        if rising_edge(clk) then
            if rst="1" then
                kpcounter <= (others => '0');
            elsif input_rising_edge="1" then
                kpcounter <= kpcounter + 1;
            end if;
        end if;
    end process;    
end architecture;

Links

Here are some links with additional examples:

Leave a Comment