VHDL Modulo counter, how to code and test it

A modulo counter is a counter that wraps around when it reaches a certain value. For example, a counter modulo 5 will count 0, 1, 2, 3, 4, 0, 1, …; namely, after 4 it will wrap around to 0. The reason the counter wraps after 4 is that to count five clock pulses starting from zero, the maximum value of the counter must be (modulo-1), in this case, 5-1=4.

Every VHDL counter is a modulo counter. If you define a two-bit counter, it will wrap around automatically from 3 to 0 without the need of writing special logic for that.

But what if we want a modulo counter that is different from a power of 2? In that case, we have to write special logic to achieve that.

The code below implements a modulo counter. The counter width is unconstrained, so it will be defined at implementation time. The modulo value is an input to the module. Typically, this value will come from a registers block, updated by a processor host or by the top module of the VHDL hierarchy.

However, there is a limitation for unconstrained inputs (as user @PiasaChimera let me know on Reddit). If the module is instantiated with an input like “data(31 downto 24)” or “data(0 to 7)”, the logic, which is based on the data’left attribute, fails. There are several ways to overcome this pitfall. One of them is to use the VHDL alias, and that is the option I have used, and tested, in this example.

You may notice that the implementation is as a down counter and not an up counter. For FPGA devices (at least for those I have tested), the implementation as a down counter is more efficient (regarding HW resources utilization) than an implementation as an up counter.

The entity uses an unconstrained max_cnt port. The size of the port is set on instantiation (in our example, by the testbench). The max_cnt input is the modulo value. The component also has an enable input (en), and the zero output which is asserted when the counter value is zero.

Older versions of VHDL didn’t allow output signals to be used as inputs to the logic. This limitation was overruled on VHDL-2008. Since the code below uses the zero output in the logic, to work correctly, it has to be tested on a VHDL-2008 compatible simulator. If you are using Vivado, be sure to mark the type for modulo_cnt as Vivado-2008 under the “Source File Properties” window.

Here we can see the implementation architecture of the modulo counter:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity modulo_cnt is
  port (
    clk     : in  std_logic;
    rst     : in  std_logic;

    -- inputs
    max_cnt : in  std_logic_vector;
    en      : in  std_logic;

    -- outputs
    zero    : out std_logic
  );
end modulo_cnt;
architecture rtl of modulo_cnt is
  -- normalize the unconstrained input
  alias max_cnt_norm : std_logic_vector(max_cnt'length - 1 downto 0) is max_cnt; -- normalized unconstrained input
  signal cnt : unsigned(max_cnt_norm'left downto 0);

begin
  counter_pr : process (clk)
  begin
    if (rising_edge(clk)) then
      if (rst = '1') then
        cnt <= unsigned(max_cnt_norm) - 1;
        zero <= '0';
      elsif (en = '1') then -- is counting enabled?
        if (cnt = 1) then -- use pipeline to assert zero
          zero <= '1'; -- together with cnt=0
        else
          zero <= '0';
        end if;
        if (zero = '1') then -- check if counter reached zero
          cnt <= unsigned(max_cnt_norm) - 1; -- reload with modulo_value-1
        else
          cnt <= cnt - 1; -- decrement counter
        end if;
      end if;
    end if;
  end process counter_pr;

end rtl;

The logic is very simple to follow. The counting logic is enabled by the en input. If en is asserted and the counter cnt reaches zero, it is re-loaded with the (max_cnt-1) input value. Otherwise, the signal cnt is decremented.

On the following waveform from the simulation, we can see the operation of the modulo counter.

On the first marker, the en input is asserted. From the next clock, we can see that the counter counts down until it reaches zero, where the zero output is asserted and the counting resumes from the (max_cnt-1) value.

In the second figure, above this paragraph, max_cnt is six. Exactly after the zero output is asserted, the counter is disabled (second marker), so we see that for several clocks the counting value on cnt doesn’t change. The third marker indicates the time when the counter (now enabled for several clocks) reaches zero and wraps around to five (=max_cnt-1).

I have added this third waveform to show how, using alias, we solve the case where the parameter sent to the module is: data(31 downto 24). You are invited to check by yourself that the alias works also for other cases, like sending data(0 to 7), or a constant like x”0c”. In the waveform, you can see that the signal created by the logic using an alias has a range (7 downto 0) and works in exactly the same way for modulo and modulo2 instances.

The sources, testbench files, waveform, simulation project files, etc., are released under Github.


You can check other VHDL examples in the Code Snippets section. There are also complete VHDL projects for you to analyze.


Leave a Reply

Your email address will not be published. Required fields are marked *