Modulo counter

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 at 4 is because, to count five clock pulses starting from zero, the maximum value of the counter must be (modulo-1).

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 defined as a generic parameter. The modulo value is an input to the module. Tipically, this value will come from a registers blocks, updated by a processor host.

We may notice that the implementation is as a down-counter and not an up-counter. For Altera 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 up-counter.

The entity includes the DATA_W parameter, namely, the size of the counter. 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.

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
   generic (
      DATA_W   : natural := 32
   );
   port (
      clk:     in std_logic;
      rst:     in std_logic;
      
      -- inputs
      max_cnt: in std_logic_vector (DATA_W-1 downto 0);
      en:      in std_logic;

      -- outputs
      zero:    out std_logic
   );
end modulo_cnt;


architecture rtl of modulo_cnt is
   signal   cnt       : unsigned(DATA_W-1 downto 0);
   signal   zero_i   : std_logic;

begin 
zero_i   <= '1' when cnt = 0 else '0';
zero  <= zero_i;

counter_pr: process (clk, rst) 
   begin 
    if (rst = '1') then 
        cnt       <= (others => '0');
    elsif (rising_edge(clk)) then   
      if (en = '1') then            -- is counting enabled?
         if (zero_i = '1') then     -- check if counter reached zero
            cnt   <= unsigned(max_cnt) - 1;     
                                    -- reload with modulo value
         else
            cnt   <= cnt - 1;       -- decrement counter
         end if;  
      end if;  
    end if;
end process;

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 input value. Otherwise, the cnt is decremented.

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

mod_wf1

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

mod_wf2

On the second figure, the max_cnt is six. Exactly after the zero output is asserted the counter is disables (first cursor), so we see that for several clocks the counting value on cnt doesn’t change. The second cursor marks the time where the counter (now enabled for several clocks) reaches zero and wraps around to five (=max_cnt-1).

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

Link for this project on Github

Link for all releases of this project under Github