Counter in VHDL with debouncer

        

1.1 Aim

Enjoy this course - 

The aim of this project is to implement a counter that would enable counting for one clock cycle each time a push button is pressed and display the value on LEDs in binary form. Once a button is pressed the counted value should not increase more than once until the button is released and pressed again, which is only possible by adding a debouncer to eliminate the bouncing effect necessary to cause the counter to increment more than once after a single button pressed due to bouncing effect.
https://www.maxybyte.com/p/counter-in-vhdl-with-debouncer.html
Nexys 4 Board
1.2   Background
Here we have to filter input signals with both debouncing and edge detector since the contacts of any mechanical button to convert any analogy push to 1’s and 0’s passes through a bouncing effect (bouncing provides several transitions states before performing the main task) meanwhile the process of removing this bouncing effect is important as to get an accurate result.

Learn also:Two-bit-adder

1.2.1 Counter in this laboratory is a device that counts in binary numbers by each button pushed, stores each current state and based on that produces its next state and shows its output on FPGA board LED-s.

Below shows the truth table for the binary number counted:
0
0
0
0
0
0
0
1
0
0
1
0
0
0
1
1
0
1
0
0
0
1
0
1
0
1
1
0
0
1
1
1
1
0
0
0
1
0
0
1
1
0
1
0
1
0
1
1
1
1
0
0
1
1
0
1
1
1
1
0
1
1
1
1

1.2.2   Edge detector

Whenever two sampled inputs values stored are different, it is possible to conclude that there has been a transition in the button input signal.
The rising edge detector generates a single one clock period long pulse when the button signal changes from LOW to HIGH. A falling edge detector (that shows when the button is released) can be implemented in a similar fashion by changing the button_pulse signal equation to detect button_buf1 = ’0’ and button_buf2 = ’1’ condition.
Listing 1:
Rising Edge Single Pulse Generation
button_buf1 <= button_input_signal when rising_edge(clock_100MHz);
button_buf2 <= button_buf1 when rising_edge(clock_100MHz);
button_pulse <= button_buf1 and not button_buf2;

1.2.3 Debouncing is necessary in order to achieve correctness in signal transmission since a bouncing effect may occur during a button push in an electronics circuit. This is due to the existence of metal spring buttons contains. Thus, a button push once can, therefore, make contact severally before stabilizing.  For the debouncing purpose, a  4- bit SIPO shift register built from four D-type Flip-flops below (figure 1) is considered
         
Therefore, when the Q’s signals of this 4-bit shift register equal to one, it's then finalized that input D signal has been de-bounced.

2     Workflow

 In the implementation of this Counter, I have encapsulated 3 major processes that are running concurrently within a file named counter.vhd. The processes are:
a)     Counter process
b)    Clock divider process
c)     Debouncing process


2.1 Counter Implementation process

Right here the rst (reset) button is set as a priority which therefore can put off any incremented values generated by the cnt (count) signal anytime a button is pushed.  The cnt_out here takes in the cnt values and output then whenever this cnt_out is called.
 counter_definition:process (clk)
begin
         if clk'event and clk ='1' then
 if rst ='1' then
          cnt <= "0000";
           elsif button_pulse ='1' then
cnt <= cnt + 1;
end if; 
end if;
end process;
cnt_out<=std_logic_vector(cnt);


2.2 Clock divider Implementation Process

We are able to achieve the 100Hz frequency in contrast to the 100MHz given by the Nexys 4 board with the help of the clock divider implementation process. The clk_100Hz signal here serves as an enable signal in the implementation of debouncer.

clock_divider_definition:process(clk)
begin
if clk'event and clk = '1' then
if counter < 999999 then
counter <= counter + 1;
 clk_100Hz <= '0';
else
counter <= 0;
clk_100Hz <= '1';
end if;
end if;
end process;

2.3    Debouncing implementation process

This is achievable with the usefulness of the 4-bit shift register, here it should be noted that the clk_100Hz could be seen as the enable signal in the 4-bit D-type flip-flow which we were able to generate during the implementation of clock divider in section 2.2  and use here.

shift_register_definition:process(clk)
begin
if clk'event and clk = '1' then
 if rst ='1' then
  cnt_shift <=(others=>'0');
  elsif clk_100Hz = '1' then
  cnt_shift(0) <= button;    -- button represent the D input signal from D-type Flip-flops
  cnt_shift(3 downto 1) <= cnt_shift (2 downto 0);
 end if;
 end if;
 end process;

3:  Result

The entire processes are saved in a file named counter.vhd below is the full code for the implementation of the filtered counter:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.all;
entity counter is
      Port ( clk : in STD_LOGIC;
           rst : in STD_LOGIC;
           button: in std_logic;
           cnt_out : out STD_LOGIC_VECTOR (3 downto 0));
end counter;
       architecture Behavioral of counter is
         signal cnt: signed(3 downto 0);
          signal cnt_shift: std_logic_vector(3 downto 0);
          signal button_buf1, button_buf2, button_pulse: std_logic;
          signal clk_100Hz:std_logic;
          signal counter: integer range 0 to 999999;
          signal db_button: std_logic;
           begin
counter_definition:process (clk)
begin
         if clk'event and clk ='1' then
 if rst ='1' then
          cnt <= "0000";
           elsif button_pulse ='1' then
cnt <= cnt + 1;
end if; 
end if;
end process;
cnt_out<=std_logic_vector(cnt);
clock_divider_definition:process(clk)
begin
if clk'event and clk = '1' then
if counter < 250 then
counter <= counter + 1;
 clk_100Hz <= '0';
else
counter <= 0;
clk_100Hz <= '1';
end if;
end if;
end process;
shift_register_definition:process(clk)
begin
if clk'event and clk = '1' then
 if rst ='1' then
  cnt_shift <=(others=>'0');
  elsif clk_100Hz = '1' then
  cnt_shift(0) <= button;    -- button represent the D input signal from D-type Flip-flops
  cnt_shift(3 downto 1) <= cnt_shift (2 downto 0);
 end if;
 end if;
 end process;
db_button <= '1'  when cnt_shift = "1111" else '0';  --cnt_shift represent Q's in 4 D-type Flip-flops
 button_buf1 <= db_button when rising_edge(clk);
 button_buf2 <= button_buf1 when rising_edge(clk);
 button_pulse <= button_buf1 and not button_buf2;
end Behavioral;

4 : Testbench

The test bench stimulus process help to show the logical behavior of the filtered counter code in a simulated form without been tested on FPGA board. It actually encapsulates the behavioral port of my source file as a component . Whereas, it also permits signal declaration. Mapping its declared signal with the encapsulated component of the behavioral source file values for input and output is possible right within the unit under test.
Meanwhile right under the keyword “begin” situated within the stimulus, the assignment of bits to signals begins. On the other hand the keyword “wait” stop the test indefinitely whereas the “wait for” is used to wait for a specific period of time before the continuation of the next stimulus process.  The stimuli architecture process take 3 different processes and assigns all possible input, and based on the simulated inputs it then generates the necessary outputs on the waveform.
In a nutshell, the correctness of the filtered counter was carried out by the stimulus waveform which was implemented by testbench code below:

Testbench Code

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity CounterTb is
--  Port ( );
end CounterTb;
architecture Behavioral of CounterTb is
component counter
Port ( clk: in std_logic;
       rst: in std_logic;
       button: in std_logic;
       cnt_out: out std_logic_vector(3 downto 0)
);
end component;
signal clk_o, rst_o, button_o: std_logic;
signal cnt: std_logic_vector(3 downto 0);
begin
uut: counter port map(clk => clk_o , rst=> rst_o, button => button_o, cnt_out => cnt);
clock_process : process
begin
clk_o <= '0';
wait for 10 ns;
clk_o <= '1';
wait for 10 ns;
end process;
button_proc: process
begin
button_o <= '0';
wait for 20 ms;
button_o <= '1';
wait for 20 ms;
button_o <= '0';
wait for 20 ms;
button_o <= '1';
wait for 20 ms;
button_o <= '0';
wait for 20 ms;
button_o <= '1';
wait for 20 ms;
button_o <= '0';
wait for 20 ms;
button_o <= '1';
wait for 20 ms;
end process;
reset_proc: process
begin
rst_o <= '1';
wait for 30 ns;
rst_o <= '0';
wait for 10 ns;
wait;
end process;
end Behavioral;

5 : Generated Simulation Waveform

After running the simulation testbench code, below shows the generated waveform for the filtered counter.
maxybyte.com
Figure 1: Filtered Counter Waveform

Conclusion

In this laboratory, I have shown how to filter input signals as to get correct signal output.

Conclusively, the correctness of this code has been tested both on simulation and the FPGA board given all the necessary binary outputs correctly without bouncing effect on the output signals. 

Read also:Two-bit-adder

2 comments:

Note: only a member of this blog may post a comment.

New Post

New style string formatting in Python

In this section, you will learn the usage of the new style formatting. Learn more here . Python 3 introduced a new way to do string formatti...