In earlier tutorials we have used the wait for statement to delay time in simulation. But what about production modules? The wait for statement cannot be used for that. That only works in simulation because we can’t just tell the electrons in a circuit to pause for a given time. So how can we keep track of time in a design module?

The answer is simply counting clock cycles. Every digital design has access to a clock signal which oscillates at a fixed, known frequency. Therefore, if we know that the clock frequency is 100 MHz, we can measure one second by counting a hundred million clock cycles.

This blog post is part of the Basic VHDL Tutorials series.

To count seconds in VHDL, we can implement a counter that counts the number of clock periods which passes. When this counter reaches the value of the clock frequency, 100 million for example, we know that a second has passed and it’s time to increment another counter. Let’s call this the Seconds counter.

To count minutes, we can implement another Minutes counter which increments when 60 seconds have passed. Similarly, we can create an Hours counter for counting hours, incrementing when 60 minutes have passed.

We can continue this approach for counting days, weeks, and months too. We are limited by the available physical resources in the underlying technology as well as the length of the counter versus the clock frequency.

As the length of the counters increase, obviously it consumes more resources. But it will also react slower because the chain of events becomes longer.

Exercise

In this video tutorial we will learn how to create a timer module in VHDL:

The final code for the timer testbench:

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

entity T18_TimerTb is
end entity;

architecture sim of T18_TimerTb is

    -- We're slowing down the clock to speed up simulation time
    constant ClockFrequencyHz : integer := 10; -- 10 Hz
    constant ClockPeriod      : time := 1000 ms / ClockFrequencyHz;

    signal Clk     : std_logic := '1';
    signal nRst    : std_logic := '0';
    signal Seconds : integer;
    signal Minutes : integer;
    signal Hours   : integer;

begin

    -- The Device Under Test (DUT)
    i_Timer : entity work.T18_Timer(rtl)
    generic map(ClockFrequencyHz => ClockFrequencyHz)
    port map (
        Clk     => Clk,
        nRst    => nRst,
        Seconds => Seconds,
        Minutes => Minutes,
        Hours   => Hours);

    -- Process for generating the clock
    Clk <= not Clk after ClockPeriod / 2;

    -- Testbench sequence
    process is
    begin
        wait until rising_edge(Clk);
        wait until rising_edge(Clk);

        -- Take the DUT out of reset
        nRst <= '1';

        wait;
    end process;

end architecture;

The final code for the timer module:

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

entity T18_Timer is
generic(ClockFrequencyHz : integer);
port(
    Clk     : in std_logic;
    nRst    : in std_logic; -- Negative reset
    Seconds : inout integer;
    Minutes : inout integer;
    Hours   : inout integer);
end entity;

architecture rtl of T18_Timer is

    -- Signal for counting clock periods
    signal Ticks : integer;

begin

    process(Clk) is
    begin
        if rising_edge(Clk) then

            -- If the negative reset signal is active
            if nRst = '0' then
                Ticks   <= 0;
                Seconds <= 0;
                Minutes <= 0;
                Hours   <= 0;
            else

                -- True once every second
                if Ticks = ClockFrequencyHz - 1 then
                    Ticks <= 0;

                    -- True once every minute
                    if Seconds = 59 then
                        Seconds <= 0;

                        -- True once every hour
                        if Minutes = 59 then
                            Minutes <= 0;

                            -- True once a day
                            if Hours = 23 then
                                Hours <= 0;
                            else
                                Hours <= Hours + 1;
                            end if;

                        else
                            Minutes <= Minutes + 1;
                        end if;

                    else
                        Seconds <= Seconds + 1;
                    end if;

                else
                    Ticks <= Ticks + 1;
                end if;

            end if;
        end if;
    end process;

end architecture;

The waveform zoomed in on the Seconds signal:
timer_seconds_waveform

The waveform zoomed in on the Minutes signal:
timer_minutes_waveform

The waveform zoomed in on the Hours signal:
timer_hours_waveform

Need the ModelSim project files?

Let me send you a Zip with everything you need to get started in 30 seconds

How does it work?

Tested on Windows and Linux How it works

    Unsubscribe at any time

    Analysis

    To run a 50 hour simulation we gave the command run 50 hr in the ModelSim console. Fifty hours is a really long simulation, and therefore we had to lower the clock frequency in the testbench to 10 Hz. If we had left it at 100 MHz, the simulation would have taken days. Such adaptations are sometimes necessary to allow us to simulate a design.

    We right-clicked the timeline in the waveform and selected “Grid, Timeline & Cursor Control”. When changing the time unit from ns to seconds, minute, and hours, we could see that the timer was indeed working in real-time.

    The timer time is slightly offset from simulation time because of the reset of the module at the beginning of the simulation. It’s visible in the first waveform where the 60-second mark on the timeline is slightly before when the Seconds signal wraps to 0.

    Note that in simulation, the counter values are updated in zero time at the rising edge of the clock. In the real world, the counter value will need some time to propagate from the first bit of the counter to the last one. As we increase the length of the counters, we consume of the available time of a clock period.

    Ripple counter delay in VHDL

    If the accumulated length of all the cascaded counters become too long, an error will be produced in the place and route step after compilation. How long a counter you can implement before consuming the entire clock period depends on the FPGA or ASIC architecture and clock speed.

    An increased clock speed means that the counter chain will be longer. It also means that the clock period time will be shorter, giving the counter chain even less time to complete.

    Get exclusive access to exercises and answers!

    Takeaway

    • Measuring time in VHDL modules is achieved by counting clock cycles
    • Lowering the clock frequency in the testbench will speed up the simulation

    Go to the next tutorial »

    Leave a Reply

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

    This site uses Akismet to reduce spam. Learn how your comment data is processed.

    3 thoughts on “How to create a timer in VHDL

    1. Correct the error in the code: 100e6; — 100 MHz

      Posted on May 18, 2018 at 6:10 pm
    2. hi i tried to compiled by using quartus but i had a problem in this code

      library ieee;
      use ieee.std_logic_1164.all;
      use ieee.numeric_std.all;
       
      entity T18_TimerTb is
      end entity;
       
      architecture sim of T18_TimerTb is
       
          -- We're slowing down the clock to speed up simulation time
          constant ClockFrequencyHz : integer := 10; -- 10 Hz
          constant ClockPeriod      : time := 1000 ms / ClockFrequencyHz;
       
          signal Clk     : std_logic := '1';
          signal nRst    : std_logic := '0';
          signal Seconds : integer;
          signal Minutes : integer;
          signal Hours   : integer;
       
      begin
       
          -- The Device Under Test (DUT)
          i_Timer : entity work.T18_Timer(rtl)
          generic map(ClockFrequencyHz => ClockFrequencyHz)
          port map (
              Clk     => Clk,
              nRst    => nRst,
              Seconds => Seconds,
              Minutes => Minutes,
              Hours   => Hours);
       
          -- Process for generating the clock
          Clk <= not Clk after ClockPeriod / 2;
       
          -- Testbench sequence
          process is
          begin
              wait until rising_edge(Clk);
      
              wait until rising_edge(Clk);
       
              -- Take the DUT out of reset
              nRst <= '1';
              wait;
          
      	end process;
       
      end architecture;
      

      it says
      Error (10398): VHDL Process Statement error at T18_TimerTb.vhd(40): Process Statement must contain only one Wait Statement

      Posted on May 19, 2019 at 3:38 pm
      1. You are probably trying to synthesize the testbench. It’s only the Timer module that’s synthesizable, not the testbench. The testbench can only be run in a VHDL simulator.

        To run the simulation, you will have to open the design in the VHDL simulator that you are using with Quartus, for example ModelSim. I don’t have Quartus at hand, so I don’t know the exact steps required.

        Posted on May 20, 2019 at 7:44 am