Counters are a principle part of nearly every FPGA design, facilitating time tracking in logic circuits by counting clock cycles. I’m going to discuss VHDL counter construction, and I also want to share a very practical counter tip that I picked up from a colleague many years back: count backwards.
This article will cover the following concepts:
- Counter Concepts
- VHDL Implementation
- Synthesis Considerations
- Typical Uses
For a Verilog counter, see our corresponding counter article with example code.
The general idea behind the counter is pretty simple:
Start at some initial count value and store it in a register.
Increment the counter.
Save the new count value back into the register.
The counters that I’m going to implement for you in this VHDL counter example count backwards and forwards from/to 12. It may sound strange, but I have a very good reason for counting backwards, and I’ll get into that in the Synthesis Considerations section. After all, it’s just as easy to compute x-1 as it is to compute x+1.
The code example implements a 5-bit counter that counts backwards, and two 4-bit counters that count forward–one of which is allowed to free run.
[cc lang=”vhdl” noborder=”true” tab_size=”4″ lines=”-1″ width=”600″ escaped=”true”]
entity counter is
CLK : in std_logic;
RST : in std_logic;
F_COUNT_OUT : out unsigned(3 downto 0);
B_COUNT_OUT : out unsigned(3 downto 0);
FR_COUNT_OUT : out unsigned(3 downto 0)
architecture bhv of counter is
— signal definitions
constant count_stop : unsigned(3 downto 0) := X”C”;
signal counter_b : unsigned(4 downto 0);
signal counter_f : unsigned(3 downto 0);
signal counter_fr : unsigned(3 downto 0);
–intitialize counters on reset
counter_f <= (others=>‘0’);
counter_b <= '0' & count_stop; counter_fr <= (others=>‘0’);
–free running counter
counter_fr <= counter_fr + 1; --forward counter if(counter_f /= count_stop) then counter_f <= counter_f + 1; end if; --backward counter if(counter_b(4)='0') then counter_b <= counter_b - 1; end if; end if; end if; end process; ---------------------------------------------------------------- -- outputs ---------------------------------------------------------------- --module output registers process(RST,CLK) begin if(rising_edge(CLK)) then if(RST='1') then F_COUNT_OUT <= (others=>‘0’);
B_COUNT_OUT <= (others=>‘0’);
FR_COUNT_OUT <= (others=>‘0’);
F_COUNT_OUT <= counter_f; B_COUNT_OUT <= counter_b(3 downto 0); FR_COUNT_OUT <= counter_fr; end if; end if; end process; end bhv; [/cc]
Counters are Not Clocks!
Before I get into talking about counter synthesis, I want to stress how not to make a mistake that I’ve seen over and over from even experienced engineers. Don’t generate clocks directly from counters!
There are a few ways to generate other clock speeds within an FPGA. The best way to generate new clocks for logic is by using dedicated clock resources, like a PLL, DLL, DCM or other clock management tile resources. If you can’t uses those resources for whatever reason, you can use a counter to divide down the clock, but you should never drive logic directly from the counter!
FPGA manufacturers have sophisticated architectures for clock distribution within a chip to prevent clock skew and ensure that your design runs synchronously and as fast as possible. If you drive logic directly from a counter output, you will bypass all of that. Your design will compile with more difficulty, be less optimized, and run at lower speeds.
To make an analogy, normal logic connects (like those in a counter) are like backroads or neighborhood streets in a city; clock lines are like 10-lane freeways. Driving the clock pin on logic with a counter is like going from San Francisco to Los Angeles using side roads. You’ll get there, but you’re going to do a lot of zig-zagging. The clock lines in an FPGA are literally much wider than normal logic connects and have drastically reduced parasitic impedances. The figure notionally illustrates this with clock routes in green and logic routes in blue.
Try to use clock resources to derive clocks. If you use a counter to generate a clock, always run it into a clock distribution point (like a global or regional clock buffer) before driving logic with that clock.
The most lightweight clock is the free-running clock. It just continuously adds (or subtracts) from the previously registered value. It does not need supporting logic to stop at a certain number, and the counter register just rolls over (refered to as overflow condition) when the maximum value has been reached.
We naturally count in increasing order, so our natural tendency in implementing a stop counter is to increment the counter. If we want to stop the counter at 12, as in this example, we have to start at 0 and then check to see if the counter value is equal to 12 each clock cycle. Remember that the counter register is 4 bits and that 12 in binary is 1100. Let’s look at that comparison in logical form, or in a boolean equation:
check if: [counter(3)=1] AND [counter(2)=1] AND [counter(1)=0] AND [counter(0)=0].
That’s 7 logic operations to check for the number 12. If we were counting using a 32 bit register, that would be 63 logic operations!
The point is that you can use a forward counter that doesn’t free run, but depending on the size of your counter register, the compare operation to stop the counter may take a long time with multiple levels of logic. You can get away doing this with small width registers (like this example) or slow clock speeds, but you’ll take a major performance hit in high-speed designs.
The most lightweight counter to that is not free-running is a backward counter. In this example, we started the counter at 12 and counted back to zero. Instead of comparing all four bits every clock cycle, we used a 5th bit in the most significant bit (MSB) position to see if the count was complete. When the counter reaches zero and decrements again, the register rolls over (overflows) and moves to the value 31, or 11111 in binary. Examine the count sequence in binary, starting from 5:
Using this method, we can just watch for the MSB to be equal to 1 to see if we’re done counting. That’s a single logic operation no matter how wide the count register is!
Counters are used in almost every logic design. Fundamentally, they’re the only way to keep track of time in an FPGA, and the uses are endless. A few examples are keeping track of packet sizes when sending data in a protocol, response timeouts, physical button debouncing, etc.
We want to hear from you! Do you have a comment, question, or suggestion? Feel free to drop us an email or post a comment.