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:
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.
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.
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.
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).
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:
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.
This example shows how to use a second generic in the same VHDL file.
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);