Timers block

Hi.
In this series of articles, we will experiment with the definition, implementation, simulation and synthesis of a block of timers in VHDL.
Along the way, we will:
  • Test the VHDL code blocks using Vivado simulator.
  • Synthesize and implement the VHDL code on Xilinx's Zynq FPGA.
Originally this project was used on a relatively small FPGA. The logic for the timers didn't fit so I used the internal memory to implement the solution. In many of my designs I have seen that it is the LUTs (and not the FFs or the internal RAM) what tends to be under heavy utilization. So using the block RAM to implement register (or timers) banks can be a way to fit a design into a device.

So first, let's start with the code for a single timer component:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity timer is
  generic (
    DATA_W  : natural := 32
  );
  port (
    clk:  in std_logic;
    rst:  in std_logic;
  
    -- inputs
    data_in: in std_logic_vector (DATA_W-1 downto 0);
    load:  in std_logic;
    en:  in std_logic;
  
    -- outputs
    done:  out std_logic
  );
end timer;

architecture rtl of timer is
  signal timer_reg : unsigned (DATA_W-1 downto 0);

begin 
  timer_pr: process (clk) 
  begin 
    if (rising_edge(clk)) then
      if (rst = '1') then 
        timer_reg   <= (others => '0');
        done    <= '0';
      else
        if (load = '1') then      
          timer_reg   <= unsigned(data_in);
          done       <= '0';            
        elsif (en = '1') then             
          if (timer_reg = 0) then            
            done      <= '1';     
          else
            timer_reg <= timer_reg - 1; 
          end if; 
        end if;   
      end if;
    end if;  
  end process timer_pr;

end rtl;

On the entity declaration we can see that the timer block has, other than the standard clock and reset inputs:
  • data_in bus and load signals to load the timer with an initial value.
  • An enable signal en to enable the timer down-counting
The width of the timer register is configurable via the generic parameter DATA_W.
Line 35 of the architecture takes care of the loading. If the timer reaches zero (line 40), the done signal is activated. To re-activate the timer after it reaches zero, it must be loaded again.
Let's have a look at the simulation. The first cursor marks the loading of the timer with the value '3'. Once enabled, the timer counts 3..2..1..0 and on the next clock the done signal is asserted.

Another timing cycle is shown following the second cursor marker. During this second timing cycle, the timer is momentarily disabled by de-asserting the en signal. It can be seen that the timer freezes at value six for one clock cycle, then resumes the countdown until done is asserted when the timer reaches the value zero.
You can download the source files for the timer block and its test bench from Github.

Go to the second part of this series

Comments

Popular posts from this blog

Xilinx AXI Stream tutorial - Part 1

Analysis, elaboration and synthesis