Coding and testing a Generic VHDL Downcounter

For synchronous logic, a timer and a counter are almost the same. After all, a timer counts clock units. That is why in many digital applications we see them called timers/counters.

The code below models a generic timer/counter, using unconstrained ports:

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

entity downcounter is

  port (
    clk      : in  std_logic;
    rstn     : in  std_logic;

    -- inputs
    data_in  : in  unsigned;
    load     : in  std_logic;
    en       : in  std_logic;

    -- outputs
    data_out : out unsigned;
    done     : out std_logic
  );
end downcounter;
architecture rtl of downcounter is
  -- normalize the unconstrained input
  alias data_in_norm : std_logic_vector(data_in'length - 1 downto 0) is data_in; -- normalized unconstrained input

begin

  counter_pr : process (clk)
  begin
    if (rising_edge(clk)) then
      if (rstn = '0') then
        data_out <= (others => '0');
        done <= '0';
      elsif (load = '1') then           -- load counter
        data_out <= unsigned(data_in_norm);
        done <= '0';                    -- start a new countdown - deassert done signal
      elsif (en = '1') then             -- is counting enabled?
        if (data_out = 0) then          -- check if counter reached zero
          done <= '1';                  -- set done output
        else
          data_out <= data_out - 1;     -- decrement counter
        end if;
      end if;
    end if;
  end process counter_pr;

end rtl;

In the following waveform we analyze the counter behavior:

At t=45 ns, the counter is loaded. One clock later, the internal register value of the counter counter_reg shows us that it is counting down. At t=85 ns, en is de-asserted (goes low), so the counter stops. Two clocks later it reassumes the count and at t=125 ns the done output is asserted.

Question 1: counter_reg reaches zero one clock before done goes high. Why is that?

Finally, at t=185 ns the counter is loaded again but it doesn’t reach zero since at t=225 ns is reloaded.

Question 2: at t=225 ns, both load and en and asserted. The counter does not count, it is loaded. Can you tell why load has priority over en?

Proposed exercise – Make logic that will assert done exactly on the same clock that the counter reaches zero (see Question 1) – Hint: You have to check when counter_reg reaches the value one, and if en signal is asserted, on the next clock the counter will have the value zero.

The source code and the testbench used to generate the waveform can be found on GitHub

Leave a Reply

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