Tutorial 19: Up/Down Counter in VHDL

Created on: 18 March 2013

An up/down counter is written in VHDL and implemented on a CPLD. The VHDL while loop as well as VHDL generic are also demonstrated.

Four different VHDL up/down counters are created in this tutorial:

  1. Up/down counter that counts up to a maximum value and then wraps around to 0. Counts down to 0 and then wraps around to a maximum value.
  2. Up/down counter that counts up to a maximum limit then stops. Counts down to 0 then stops. Demonstrates the VHDL while loop.
  3. Up/down counter that demonstrates the use of a single VHDL generic.
  4. Up/down counter that demonstrates the use of two VHDL generic values.

Continuous Up/Down Counter

This counter will continuously count up and wrap around to 0 when the maximum value is reached if the direction input to the counter is set to count up.

The counter will continuously count down and wrap around to the maximum value when 0 is reached if the direction input to the counter is set to count down.

VHDL Code for up_dn_counter

The up/down counter is created with the following VHDL code:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity up_dn_counter_top is
    Port ( CLK : in  STD_LOGIC;     -- input clock
           -- LEDs to display count
           LED : out  STD_LOGIC_VECTOR (7 downto 0);
           DIR : in  STD_LOGIC);    -- direction of counter (up or down)
end up_dn_counter_top;

architecture Behavioral of up_dn_counter_top is
    signal clk_div : STD_LOGIC_VECTOR (3 downto 0) := X"0";
    signal count   : STD_LOGIC_VECTOR (7 downto 0) := X"00";
begin

    -- clock divider
    process (CLK)
    begin
        if (CLK'Event and CLK = '1') then
            clk_div <= clk_div + '1';
        end if;
    end process;
    
    -- up/down counter
    process (clk_div(3), DIR)
    begin
        if (clk_div(3)'Event and clk_div(3) = '1') then
            if (DIR = '1') then
                count <= count + '1';   -- counting up
            elsif (DIR = '0') then
                count <= count - '1';   -- counting down
            end if;
        end if;
    end process;
    
    -- display count on LEDs
    LED <= not count;
    
end Behavioral;

The DIR input to the counter determines the direction of count – up or down. This VHDL code should be easy enough to understand if you have been following this VHDL CPLD course.

Up/Down Counter with Limits

This up/down counter will stop counting at an upper limit when counting up. The counter will stop at zero when counting down. The VHDL while loop is used to set the upper and lower count limits.

VHDL Code for up_dn_counter2

The VHDL code for the up/down counter with limits is shown here.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity up_down_counter2 is
    Port ( CLK : in  STD_LOGIC;
           DIR : in  STD_LOGIC;
           LED : out  STD_LOGIC_VECTOR (7 downto 0));
end up_down_counter2;

architecture Behavioral of up_down_counter2 is
    signal clk_div : STD_LOGIC_VECTOR (5 downto 0);
    signal count   : STD_LOGIC_VECTOR (7 downto 0);
begin

    -- clock divider
    process (CLK)
    begin
        if (CLK'Event and CLK = '1') then
            clk_div <= clk_div + '1';
        end if;
    end process;
    
    -- up/down counter
    process (clk_div(5))
    begin
        if (clk_div(5)'Event and clk_div(5) = '1') then
            if (DIR = '1') then
                -- count up to 15 then stop
                while (count < 15) loop
                    count <= count + '1';   -- count up
                end loop;
            else
                -- count down to 0 then stop
                while (count > 0) loop
                    count <= count - '1';   -- count down
                end loop;
            end if;
        end if;
    end process;

    LED <= not count;

end Behavioral;

This code is a modified version of the previous example, but uses a VHDL while loop to set upper and lower count limits.

The first while loop is shown here:

while (count < 15) loop
    count <= count + '1';   -- count up
end loop;

The count will only be incremented while count is less than 15. When the count value is 14, the count will be incremented only once more to 15, after this the code in the while loop will not be run again until count is made less than 15 and the direction of count is up.

The while loop runs once every clock cycle on the rising edge of clk_div(5).

Up/Down Counter using One VHDL Generic

A generic is a named value that is put in the entity part of the VHDL code. Wherever the name of the generic value is used in the VHDL code, its value will be substituted.

A generic is similar to using #define in a C program.

Generics are useful for:

  • Changing a value that occurs in more than one place in the VHDL code. By changing the value of the generic in the entity part of the code, every place that the generic occurs in the rest of the code will be changed to its new value.
  • Making it possible to change a value near the top of a file, even if the value occurs anywhere else in the file. This saves having to search for the value in the code. Because it has a name, it can also be easily found in the file using an editor's search function.

VHDL Code for up_dn_counter3

The code below is a modified version of the previous VHDL example. The code has been modified to use a single generic value.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

-- up/down counter with limits that uses one GENERIC
entity up_down_counter3_top is
    Generic (CLK_BIT : INTEGER := 3);
    Port ( CLK : in  STD_LOGIC;
           DIR : in  STD_LOGIC;
           LED : out  STD_LOGIC_VECTOR (7 downto 0));
end up_down_counter3_top;

architecture Behavioral of up_down_counter3_top is
    signal clk_div : STD_LOGIC_VECTOR (CLK_BIT downto 0);
    signal count   : STD_LOGIC_VECTOR (7 downto 0);
begin

    -- clock divider
    process (CLK)
    begin
        if (CLK'Event and CLK = '1') then
            clk_div <= clk_div + '1';
        end if;
    end process;
    
    -- up/down counter
    process (clk_div(CLK_BIT))
    begin
        if (clk_div(CLK_BIT)'Event and clk_div(CLK_BIT) = '1') then
            if (DIR = '1') then
                -- count up to 15 then stop
                while (count < 15) loop
                    count <= count + '1';   -- count up
                end loop;
            else
                -- count down to 0 then stop
                while (count > 0) loop
                    count <= count - '1';   -- count down
                end loop;
            end if;
        end if;
    end process;

    LED <= not count;

end Behavioral;

In this code, a generic is used to change a value in four different places in the VHDL code.

The generic is first created in the entity part of the VHDL code and given a name:

Generic (CLK_BIT : INTEGER := 3);

The name of the generic is CLK_BIT and it has been assigned a value of 3. Now wherever its name occurs in the code, the value of 3 will be substituted.

Everywhere that CLK_BIT occurs in the above code has been highlighted in red.

The generic has been used in this code to make it easy to change the number of times that the input clock is being divided by the clock divider.

To make the clock divider register clk_div bigger and then use the MSB of this register as the divided clock, the value of CLK_BIT only needs to be made bigger. For example, changing CLK_BIT to 5 will make clk_div six bits wide and the divided clock will be taken from bit 5 of clk_div.

Up/Down Counter using Two VHDL Generics

This example shows how to use a second generic in the same VHDL file.

VHDL Code for up_dn_counter4

The VHDL code for this example is shown here.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

-- up/down counter with limits and two generics
entity up_down_counter4_top is
    Generic (CLK_BIT      : INTEGER := 2;
             UP_CNT_LIMIT : INTEGER := 64);
    Port ( CLK : in  STD_LOGIC;
           DIR : in  STD_LOGIC;
           LED : out  STD_LOGIC_VECTOR (7 downto 0));
end up_down_counter4_top;

architecture Behavioral of up_down_counter4_top is
    signal clk_div : STD_LOGIC_VECTOR (CLK_BIT downto 0);
    signal count   : STD_LOGIC_VECTOR (7 downto 0);
begin

    -- clock divider
    process (CLK)
    begin
        if (CLK'Event and CLK = '1') then
            clk_div <= clk_div + '1';
        end if;
    end process;
    
    -- up/down counter
    process (clk_div(CLK_BIT))
    begin
        if (clk_div(CLK_BIT)'Event and clk_div(CLK_BIT) = '1') then
            if (DIR = '1') then
                -- count up to limit then stop
                while (count < UP_CNT_LIMIT) loop
                    count <= count + '1';   -- count up
                end loop;
            else
                -- count down to 0 then stop
                while (count > 0) loop
                    count <= count - '1';   -- count down
                end loop;
            end if;
        end if;
    end process;

    LED <= not count;

end Behavioral;

This code is a modified version of the previous example. The divided clock to the counter has been sped up by changing the value of CLK_BIT from 3 to 2.

A second generic named UP_CNT_LIMIT has been used to make the upper count limit of the counter easy to change. The counter has been set to count up to 64.

The code below shows that to add a second generic to the code or additional generics, they are added by separating each generic with a semicolon.

Generic (CLK_BIT      : INTEGER := 2;
         UP_CNT_LIMIT : INTEGER := 64);