I've read through the other posts but can't seem to fix mine. I'm new to VHDL so I'm sure it's a simple fix.
In short, the button isn't debouncing. The code compiles and the bitstream programs. In the testbench, button presses work, but the output LEDs don't change. On the board, pressing a button makes random LEDs light up (I presume because of bouncing). According to the schematic the inputs are going through the debouncers.
Can anyone identify the issue? And any other hints and tips are always appreciated :)
Thanks!
EDIT1: Added rising_edge(clk). Also note, when I press either button, at the time it's depressed all the LEDs light up.
button_counter.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity button_counter is
port( clk : in std_logic;
btnU : in std_logic;
btnD : in std_logic;
led : out std_logic_vector (15 downto 0));
end button_counter;
architecture behavioral of button_counter is
component debouncer is
port( clk : in std_logic;
btn : in std_logic;
btn_clr : out std_logic);
end component;
signal btnU_clr : std_logic;
signal btnD_clr : std_logic;
begin
debouncer_btnU : debouncer port map (clk => clk, btn => btnU, btn_clr => btnU_clr);
debouncer_btnD : debouncer port map (clk => clk, btn => btnD, btn_clr => btnD_clr);
process(clk)
variable count : integer := 0;
begin
if (rising_edge(clk)) then
if(btnU_clr = '1') then count := count + 1;
elsif(btnD_clr = '1') then count := count - 1;
end if;
led <= std_logic_vector(to_unsigned(count, led'length));
end if;
end process;
end behavioral;
Debouncer.vhd
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity debouncer is
port( clk : in std_logic;
btn : in std_logic;
btn_clr : out std_logic);
end debouncer;
architecture behavioural of debouncer is
constant delay : integer := 650000; -- 6.5ms
signal count : integer := 0;
signal btn_tmp : std_logic := '0';
begin
process(clk)
begin
if rising_edge(clk) then
if (btn /= btn_tmp) then
btn_tmp <= btn;
count <= 0;
elsif (count = delay) then
btn_clr <= btn_tmp;
else
count <= count + 1;
end if;
end if;
end process;
end behavioural;
button_counter_tb.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity button_counter_tb is
end button_counter_tb;
architecture behavioral of button_counter_tb is
signal clk_tb : std_logic;
signal btnU_tb : std_logic;
signal btnD_tb : std_logic;
signal led_tb : std_logic_vector (15 downto 0);
component button_counter
port(clk : in std_logic;
btnU : in std_logic;
btnD : in std_logic;
led : out std_logic_vector (15 downto 0));
end component;
begin
UUT: button_counter port map (clk => clk_tb, btnU => btnU_tb, btnD => btnD_tb, led => led_tb);
process
begin
btnU_tb <= '0';
btnD_tb <= '0';
wait for 100ns;
btnU_tb <= '1';
wait for 100ns;
btnU_tb <= '0';
wait for 100ns;
btnU_tb <= '1';
wait for 100ns;
btnD_tb <= '1';
wait for 100ns;
btnU_tb <= '0';
wait for 100ns;
btnD_tb <= '0';
end process;
end behavioral;
After your code update there are several issues remaining:
The clock isn't being generated in the testbench
The stimuli (button presses) aren't adequately timed in the testbench
The debouncer doesn't produce an enable for a single clock
To facilitate simulation for design validation your design has been modified to allow a slower clock (it appears you're actually using a 100 MHz clock). The idea is to reduce the computation requirements and display waveform storage.
The first two points are addressed in the testbench:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity button_counter_tb is
end entity button_counter_tb;
architecture behavioral of button_counter_tb is
-- NOTE: suffix _tb has been removed, it's annoying to type over and over
signal clk: std_logic := '0'; -- ADDED default value '0'
signal btnU: std_logic;
signal btnD: std_logic;
signal led: std_logic_vector (15 downto 0);
component button_counter
generic ( -- ADDED generic
CLKP: time := 10 ns;
DEBT: time := 6.5 ms -- debounce time supports different
); -- mechanical buttons/switches
port (
clk: in std_logic;
btnU: in std_logic;
btnD: in std_logic;
led: out std_logic_vector (15 downto 0)
);
end component;
constant CLKP: time := 12.5 us; -- ADDED just long enough to show debounce
constant DEBT: time := 6.5 ms; -- ADDED
begin
CLOCK: -- ADDED clock process
process
begin
wait for CLKP/2;
clk <= not clk;
if now > 2 sec then -- stop simulation
wait;
end if;
end process;
UUT:
button_counter
generic map ( -- ADDED generic map
CLKP => CLKP,
DEBT => DEBT
)
port map (
clk => clk,
btnU => btnU,
btnD => btnD,
led => led
);
-- STIMULI:
-- process
-- begin
-- btnU_tb <= '0';
-- btnD_tb <= '0';
-- wait for 100 ns;
-- btnU_tb <= '1';
-- wait for 100 ns;
-- btnU_tb <= '0';
-- wait for 100 ns;
-- btnU_tb <= '1';
-- wait for 100 ns;
-- btnD_tb <= '1';
-- wait for 100 ns;
-- btnU_tb <= '0';
-- wait for 100 ns;
-- btnD_tb <= '0';
-- wait; -- ADDED -- stops simulation
-- end process;
UP_BUTTON:
process
begin
btnU <= '0';
wait for 2 ms;
btnU <= '1'; -- first button press
wait for 0.5 ms;
btnU <= '0';
wait for 0.25 ms;
btnU <= '1';
wait for 7 ms;
btnU <= '0';
wait for 100 us;
btnU <= '1';
wait for 20 us;
btnU <= '0';
wait for 200 ms;
btnU <= '1'; -- second button press
wait for 20 us;
btnU <= '0';
wait for 20 us;
btnU <= '1';
wait for 6.6 ms;
btnU <= '0';
wait for 250 ms;
btnU <= '1'; -- third button press
wait for 20 us;
btnU <= '0';
wait for 20 us;
btnU <= '1';
wait for 6.6 ms;
btnU <= '0';
wait for 200 ms;
btnU <= '1'; -- second button press
wait for 20 us;
btnU <= '0';
wait for 20 us;
btnU <= '1';
wait for 6.6 ms;
btnU <= '0';
wait for 50 us;
btnU <= '1';
wait for 1 ms;
btnU <= '0';
wait;
end process;
DOWN_BUTTON:
process
begin
btnD <= '0';
wait for 800 ms;
btnD <= '1'; -- first button press
wait for 0.5 ms;
btnD <= '0';
wait for 0.25 ms;
btnD <= '1';
wait for 0.5 ms;
btnD <= '0';
wait for 1 ms;
btnD <= '1';
wait for 7 ms;
btnD <= '0';
wait for 100 us;
btnD <= '1';
wait for 20 us;
btnD <= '0';
wait for 200 ms;
btnD <= '1'; -- second button press
wait for 20 us;
btnD <= '0';
wait for 20 us;
btnD <= '1';
wait for 6.6 ms;
btnD <= '0';
wait for 250 ms;
wait;
end process;
end architecture behavioral;
The _tb
suffix to signal names has been removed (it was painful to type in repetitively).
A clock period has been picked with a ratio of bounce period to clk period guaranteed to allow dropping 'bounces'. The stimului button presses can be extended as can the simulation which is arbitrary here.
Note the button press values are guaranteed to span one or more clock intervals.
These should tolerate the clock period being changed by modifying CLKP
.
The debounce interval DEBT can be modified to reflect the use of different switches or buttons, including membrane switches with severe aging. The debounce interval is a consequence of mechanical characteristics of the particular switches or buttons. Passing these generic constants allows a degree of platform independence.
The third point is addressed by changes to the debouncer:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity debouncer is
generic ( -- ADDED GENERICS to speed up simulation
CLKP: time := 10 ns;
DEBT: time := 6.5 ms
);
port (
clk: in std_logic;
btn: in std_logic;
btn_clr: out std_logic
);
end entity debouncer;
architecture behavioural of debouncer is
-- constant delay: integer := 650000; -- 6.5ms
constant DELAY: integer := DEBT/CLKP;
signal count: integer := 0;
signal b_enab: std_logic := '0'; -- RENAMED, WAS btn_tmp
signal btnd0: std_logic; -- ADDED for clock domain crossing
signal btnd1: std_logic; -- DITTO
begin
CLK_DOMAIN_CROSS: -- ADDED process
process (clk)
begin
if rising_edge(clk) then
btnd0 <= btn;
btnd1 <= btnd0;
end if;
end process;
DEBOUNCE_COUNTER: -- ADDED LABEL
process (clk)
begin
if rising_edge(clk) then
-- if btn /= btn_tmp then -- REWRITTEN
-- btn_tmp <= btn;
-- count <= 0;
-- elsif count = DELAY then
-- btn_clr <= btn_tmp;
-- else
-- count <= count + 1;
-- end if;
btn_clr <= '0'; -- btn_clr for only one clock, used as enable
if btnd1 = '0' then -- test for btn inactive state
count <= 0;
elsif count < DELAY then -- while btn remains in active state
count <= count + 1;
end if;
if count = DELAY - 1 then -- why btn_clr '1' or 1 clock
btn_clr <= '1';
end if;
end if;
end process;
end architecture behavioural;
The debouncer has been modified to get a clock domain button value which is used to reset and enable the counter count
. The output btn_clr
name has been left intact and is true for only one clock and can be used as an enable.
CLKP
and DEBT
are used together to allow faster simulation execution while passing the same simulation time.
Note the active state of the button input is hard coded. These would be connected to device pins where the input polarity can be specified.
Modifications to button_counter pass generic constants CLKP
and DEBT
to the debouncers:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity button_counter is
generic (
CLKP: time := 10 ns; -- GENERIC CONSTANTS for faster simulation
DEBT: time := 6.5 ms -- supports diffeent switches/buttons
);
port (
clk: in std_logic;
btnU: in std_logic;
btnD: in std_logic;
led: out std_logic_vector (15 downto 0)
);
end entity button_counter;
architecture behavioral of button_counter is
component debouncer is
generic (
CLKP: time := 10 ns;
DEBT: time := 6.5 ms
);
port (
clk: in std_logic;
btn: in std_logic;
btn_clr: out std_logic
);
end component;
signal btnU_clr: std_logic;
signal btnD_clr: std_logic;
begin
debouncer_btnU:
debouncer
generic map (
CLKP => CLKP,
DEBT => DEBT
)
port map (
clk => clk,
btn => btnU,
btn_clr => btnU_clr
);
debouncer_btnD:
debouncer
generic map (
CLKP => CLKP,
DEBT => DEBT
)
port map (
clk => clk,
btn => btnD,
btn_clr => btnD_clr
);
process (clk)
variable count: integer := 0;
begin
if rising_edge(clk) then
if btnU_clr = '1' then
count := count + 1;
elsif btnD_clr = '1'then
count := count - 1;
end if;
led <= std_logic_vector(to_unsigned(count, led'length));
end if;
end process;
end architecture behavioral;
And when simulated we now see the LEDs count up and down:
Running the testbench and displaying the various waveforms would allow 'zooming in' to display glitch handling in the two debouncers.
The modifications to pass the clock period and debounce interval through the design hierarchy wouldn't be strictly essential. They facilitate simulation which is used as here for design validation. (The stimuli shown in the testbench don't exhaustively verify the design).
By using the generic defaults (with a 100MHz clock) there's a very good chance the design will function when implemented in a target platform. (The active polarity of button inputs is selected in the debouncer to support the original implementation. if you suspect button bounces while getting increments or decrements you can increase the DEBT value.)
If a particular synthesis tool can't handle value of type time
passed as generic constants you can convert the various declarations of CLKP
and DEBT
to type integer
or simply pass the maximum count.