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:

  1. library ieee;
  2. use ieee.std_logic_1164.all;
  3. use ieee.numeric_std.all;
  4. entity modulo_cnt is
  5. generic (
  6. DATA_W : natural := 32
  7. );
  8. port (
  9. clk: in std_logic;
  10. rst: in std_logic;
  11. — inputs
  12. max_cnt: in std_logic_vector (DATA_W1 downto 0);
  13. en: in std_logic;
  14. — outputs
  15. zero: out std_logic
  16. );
  17. end modulo_cnt;
  18. architecture rtl of modulo_cnt is
  19. signal cnt : unsigned(DATA_W1 downto 0);
  20. signal zero_i : std_logic;
  21. begin
  22. zero_i <=1when cnt = 0 else0;
  23. zero <= zero_i;
  24. counter_pr: process (clk, rst)
  25. begin
  26. if (rst =1) then
  27. cnt <= (others =>0);
  28. elsif (rising_edge(clk)) then
  29. if (en =1) then — is counting enabled?
  30. if (zero_i =1) then — check if counter reached zero
  31. cnt <= unsigned(max_cnt) 1;
  32. — reload with modulo value
  33. else
  34. cnt <= cnt 1; — decrement counter
  35. end if;
  36. end if;
  37. end if;
  38. end process;
  39. 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 disabled (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.

Advertisements