Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
Artem Melnykov
Published © MIT

Pulse counter implemented in FPGA: hardware (VHDL) design

A fast counter that records time intervals between successive pulses such as detected photons in life sciences

IntermediateWork in progress2 hours819
Pulse counter implemented in FPGA: hardware (VHDL) design

Things used in this project

Hardware components

Z-turn Board (Zynq-7010)
×1
Z-turn cape
×1

Software apps and online services

Vivado Design Suite
AMD Vivado Design Suite

Story

Read more

Schematics

iatcounter_state_machine_b7LwyDfYxj.png

Code

detector.vhd

VHDL
-- detector.vhdl
-- detector takes an asychronous input and makes it synchronous:
-- following a rising edge of INPUT,
-- OUTPUT will be high for the next clock cycle

library ieee;
use ieee.std_logic_1164.all;
 
entity detector is
  port(
    clk        : in std_logic;
    input      : in std_logic;
    output     : out std_logic
  );
end detector;

architecture behavior of detector is
    signal input_prev : std_logic;
    
begin    

    prDetectClk : process (clk,input)
    begin
      if (rising_edge(clk)) then 
        output <= (not input_prev) and input;          
        input_prev <= input;
      end if; --rising_edge(clk)
    end process;
        
end behavior;

tb_detector.vhd

VHDL
-- tb_detector.vhdl
-- testbench for detector design
library ieee;
use ieee.std_logic_1164.all;

-- entity declaration for your testbench. 
entity tb_detector is
end tb_detector;

architecture behavior of tb_detector is

-- component declaration
component detector is
  port(
    clk        : in std_logic;
    input      : in std_logic;
    output     : out std_logic
  );
end component;

-- definitions of constants
constant CLK_PERIOD : time := 10 ns;
constant SIGNAL_DURATION : time := 3*CLK_PERIOD;
constant SIGNAL_DELAY : time := 2 ns;

--declare inputs and initialize them to zero.
signal clk    : std_logic := '0';
signal input  : std_logic := '0';

--declare outputs
signal output : std_logic;

begin

    -- instantiate the component
   uut : detector port map (
            clk => clk,
            input => input,
            output => output
        ); 
        
   -- Clock process definitions
   Clk_process :process
   begin
        Clk <= '0';
        wait for CLK_PERIOD/2;
        Clk <= '1';
        wait for CLK_PERIOD/2;
   end process;
    
  -- Apply inputs here.
  stim_proc: process
   begin        
        wait for CLK_PERIOD*5 + SIGNAL_DELAY; --wait for 5 clock cycles.
        
        input <= '1';
        wait for SIGNAL_DURATION;
        input <= '0';
        
        wait for CLK_PERIOD*8;
        
        input <= '1';
        wait for SIGNAL_DURATION;
        input <= '0';

        wait;
  end process;

end;

iatcounter.vhd

VHDL
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
entity iatcounter is
  generic(
    NUMBER_OF_CHANNELS : integer := 2;
    DATA_WIDTH : integer := 16
  );
  port(
    clk        : in std_logic;
    enable     : in std_logic;
    stop_flags : in std_logic_vector(NUMBER_OF_CHANNELS-1 downto 0);
    max_count  : in unsigned(DATA_WIDTH - 1 downto 0);
    ready      : out std_logic;
    data       : out std_logic_vector(DATA_WIDTH-1 downto 0)
  );
end iatcounter;

architecture behavior of iatcounter is
    type state_type is (INC, INC_REP, RES, RES_REP);
    signal LAST_STATE : state_type;
    signal count : unsigned(DATA_WIDTH-NUMBER_OF_CHANNELS-1 downto 0);
    signal channel_mask : std_logic_vector(NUMBER_OF_CHANNELS-1 downto 0);
    signal data_loc : std_logic_vector(DATA_WIDTH-1 downto 0);
    signal ready_loc : std_logic;
    
    -- make the channel field
    function make_channel_mask(flags : std_logic_vector(NUMBER_OF_CHANNELS-1 downto 0))
                                return std_logic_vector is
      variable ret : std_logic_vector(NUMBER_OF_CHANNELS-1 downto 0);
    begin
      for ii in NUMBER_OF_CHANNELS-1 downto 0 loop
        if (flags(ii)='0') then
          ret(ii) := '0';
        else
          ret(ii) := '1';
        end if;
      end loop;
      return ret;
    end function;
    
begin    

    data <= channel_mask & std_logic_vector(count);
    ready <= ready_loc;

    prUpdateOutputs : process (clk,enable,stop_flags)
    begin
      if (enable='1') then
        channel_mask <= make_channel_mask(stop_flags);
        count <= to_unsigned(0,DATA_WIDTH-NUMBER_OF_CHANNELS);
        ready_loc <= '0';
        LAST_STATE <= RES;
      else
        if (rising_edge(clk)) then
          channel_mask <= make_channel_mask(stop_flags);
          case (LAST_STATE) is
          when (INC) =>
            count <= count + 1;
            if (unsigned(stop_flags)/=0 or count=MAX_COUNT-1) then
              ready_loc <= '1';
              LAST_STATE <= INC_REP;
            else
              ready_loc <= '0';
              LAST_STATE <= INC;
            end if;
          when (INC_REP) =>
            count <= to_unsigned(1,DATA_WIDTH-NUMBER_OF_CHANNELS);
            if (unsigned(stop_flags)/=0) then
              ready_loc <= '1';
              LAST_STATE <= RES_REP;
            else
              ready_loc <= '0';
              LAST_STATE <= RES;
            end if;
          when (RES) =>
            count <= count+1;
            if (unsigned(stop_flags)/=0) then
              ready_loc <= '1';
              LAST_STATE <= INC_REP;
            else
              ready_loc <= '0';
              LAST_STATE <= INC;
            end if;
          when (RES_REP) =>
            count <= to_unsigned(1,DATA_WIDTH-NUMBER_OF_CHANNELS);
            if (unsigned(stop_flags)/=0) then
              ready_loc <= '1';
              LAST_STATE <= RES_REP;
            else
              ready_loc <= '0';
              LAST_STATE <= RES;
            end if;
          when others =>
            count <= to_unsigned(0,DATA_WIDTH-NUMBER_OF_CHANNELS);
            ready_loc <= '0';
            LAST_STATE <= RES;
          end case;
        end if; --rising_edge(clk)
      end if; -- enable
    end process;
        
end behavior;

tb_iatcounter.vhd

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

-- entity declaration for your testbench
entity tb_iatcounter is
end tb_iatcounter;

architecture behavior of tb_iatcounter is

-- component declaration
component iatcounter is
  generic(
    NUMBER_OF_CHANNELS : integer;
    DATA_WIDTH : integer
  );
  port(
    clk        : in std_logic;
    enable     : in std_logic;
    stop_flags : in std_logic_vector(NUMBER_OF_CHANNELS-1 downto 0);
    max_count  : in unsigned(DATA_WIDTH - 1 downto 0);
    ready      : out std_logic;
    data       : out std_logic_vector(DATA_WIDTH-1 downto 0)
  );
end component;

-- define constants
constant NUMBER_OF_CHANNELS_TB : integer := 2;
constant DATA_WIDTH_TB : integer := 8;
constant MAX_COUNT_TB  : integer := 5;
constant CLK_PERIOD : time := 10 ns;
constant ENABLE_DELAY : time := 7 ns;
constant SIGNAL_DURATION : time := CLK_PERIOD;
constant STOP_FLAG_SIGNAL_ON  : std_logic_vector(NUMBER_OF_CHANNELS_TB-1 downto 0) := "11";
constant STOP_FLAG_SIGNAL_OFF : std_logic_vector(NUMBER_OF_CHANNELS_TB-1 downto 0) := "00";

--declare inputs and initialize them to zero.
signal clk        : std_logic := '0';
signal enable     : std_logic := '1'; -- disabled by default
signal stop_flags : std_logic_vector(NUMBER_OF_CHANNELS_TB-1 downto 0) := "00";
signal max_count  : unsigned(DATA_WIDTH_TB - 1 downto 0) := to_unsigned(MAX_COUNT_TB,DATA_WIDTH_TB);

--declare outputs
signal ready : std_logic;
signal data  : std_logic_vector(DATA_WIDTH_TB-1 downto 0);

begin

    -- instantiate the unit under test (uut)
   uut : iatcounter generic map (NUMBER_OF_CHANNELS_TB, DATA_WIDTH_TB) port map (
            clk => clk,
            enable => enable, 
            stop_flags => stop_flags,
            max_count => max_count,
            ready => ready,
            data => data
        ); 
        
   -- Clock process definitions
   Clk_process :process
   begin
        Clk <= '0';
        wait for CLK_PERIOD/2;
        Clk <= '1';
        wait for CLK_PERIOD/2;
   end process;
    
   -- Apply inputs here.
  stim_proc: process
   begin        
        wait for CLK_PERIOD*5 + ENABLE_DELAY; -- wait for 5 clock cycles
                                              -- +2 ns for starting while clk is low
                                              -- +7 ns for starting while clk is high
        
        enable <='0';                  --enable the counter

        -- event generation upon reaching max count
        wait for CLK_PERIOD*(MAX_COUNT_TB+3)-ENABLE_DELAY+CLK_PERIOD/2;
        
        -- event generation upon detection in a channel
        stop_flags <= STOP_FLAG_SIGNAL_ON;
        wait for SIGNAL_DURATION;
        stop_flags <= STOP_FLAG_SIGNAL_OFF;
        
        -- event generation upon detection in a channel at the same time as the maximum count has been reached
        wait for CLK_PERIOD*(MAX_COUNT_TB-1);
        stop_flags <= STOP_FLAG_SIGNAL_ON;
        wait for SIGNAL_DURATION;
        stop_flags <= STOP_FLAG_SIGNAL_OFF;

        -- event generation upon detection in a channel immediately after the maximum count has been reached
        wait for CLK_PERIOD*MAX_COUNT_TB;
        stop_flags <= STOP_FLAG_SIGNAL_ON;
        wait for SIGNAL_DURATION;
        stop_flags <= STOP_FLAG_SIGNAL_OFF;
        
        -- event generation when second ch2 is detected immediately after ch1
        wait for CLK_PERIOD*7;
        stop_flags <= "10";
        wait for SIGNAL_DURATION;
        stop_flags <= STOP_FLAG_SIGNAL_OFF;
        stop_flags <= "01";
        wait for SIGNAL_DURATION;
        stop_flags <= STOP_FLAG_SIGNAL_OFF;
        
        wait;

  end process;

end;

iatcollector2ch.vhd

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

entity iatcollector2ch is
    generic(
      DATA_WIDTH : integer := 16
    );
	port (
        clk        : in std_logic;
        enable     : in std_logic;
        channel1   : in std_logic;
        channel2   : in std_logic;
        max_count  : in std_logic_vector(DATA_WIDTH - 1 downto 0);
        ready      : out std_logic;
        data       : out std_logic_vector(DATA_WIDTH - 1 downto 0)
	);
end iatcollector2ch;

architecture behavior of iatcollector2ch is

    -- detectors
    component detector is
    port(
      clk        : in std_logic;
      input      : in std_logic;
      output     : out std_logic
    );
    end component;
    
    -- iat counter for all detectors
    component iatcounter is
    generic(
      NUMBER_OF_CHANNELS : integer;
      DATA_WIDTH : integer
    );
    port(
      clk        : in std_logic;
      enable     : in std_logic;
      stop_flags : in std_logic_vector(NUMBER_OF_CHANNELS-1 downto 0);
      max_count  : in unsigned(DATA_WIDTH-1 downto 0);
      ready      : out std_logic;
      data       : out std_logic_vector(DATA_WIDTH-1 downto 0)
    );
    end component;
        
    -- constanats
    constant NUM_CH : integer := 2;    
    -- intermediate signals
    signal stop_flags_i : std_logic_vector(NUM_CH-1 downto 0);
    signal max_count_i  : unsigned(DATA_WIDTH-1 downto 0);

begin
    
    max_count_i <= unsigned(max_count);

    -- instantiate the detectors
    detector1 : detector
      port map (
        clk => clk,
        input => channel1,
        output => stop_flags_i(0)
     ); 
    detector2 : detector
      port map (
        clk => clk,
        input => channel2,
        output => stop_flags_i(1)
     ); 

    -- instantiate the counter
    counter1: iatcounter
      generic map (
        NUM_CH,
        DATA_WIDTH
      )
      port map(
        clk => clk,
        enable => enable,
        stop_flags => stop_flags_i,
        max_count => max_count_i,
        ready => ready,
        data => data
    );

end behavior;

tb_iatcollector2ch.vhd

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

-- entity declaration
entity tb_iatcollector2ch is
end tb_iatcollector2ch;

architecture behavior of tb_iatcollector2ch is

-- component declaration
component iatcollector2ch is
    generic(
      DATA_WIDTH : integer
    );
	port (
        clk        : in std_logic;
        enable     : in std_logic;
        channel1   : in std_logic;
        channel2   : in std_logic;
        max_count  : in std_logic_vector(DATA_WIDTH - 1 downto 0);
        ready      : out std_logic;
        data       : out std_logic_vector(DATA_WIDTH - 1 downto 0)
	);
end component;

-- define constants
constant DATA_WIDTH_TB : integer := 8;
constant MAX_COUNT_TB  : integer := 5; 
constant CLK_PERIOD : time := 10 ns;
constant SIGNAL_DURATION : time := 3*CLK_PERIOD;
constant ENABLE_DELAY : time := 2 ns;

--declare inputs and initialize them
signal clk       : std_logic := '0';
signal enable    : std_logic := '1';
signal channel1  : std_logic := '0';
signal channel2  : std_logic := '0';
signal max_count : std_logic_vector(DATA_WIDTH_TB - 1 downto 0) := std_logic_vector(to_unsigned(MAX_COUNT_TB,DATA_WIDTH_TB));

--declare outputs
signal ready      : std_logic;
signal data       : std_logic_vector(DATA_WIDTH_TB - 1 downto 0);

begin

    -- instantiate the unit under test (uut)
   uut : iatcollector2ch generic map (DATA_WIDTH_TB) port map (
            clk => clk,
            enable => enable,
            channel1 => channel1,
            channel2 => channel2,
            max_count => max_count,
            ready => ready,
            data => data
        ); 
        
   -- Clock process definition
   Clk_process :process
   begin
        Clk <= '0';
        wait for CLK_PERIOD/2;
        Clk <= '1';
        wait for CLK_PERIOD/2;
   end process;
    
   -- simulate inputs
  stim_proc: process
   begin        
        wait for CLK_PERIOD*5 + ENABLE_DELAY; -- wait for 5 clock cycles
                                              -- +2 ns for starting while clk is low
                                              -- +7 ns for starting while clk is high
        
        enable <='0';                  --enable the counter

        -- event generation upon reching max count
        wait for CLK_PERIOD*(MAX_COUNT_TB+1);--ENABLE_DELAY+CLK_PERIOD/2;
        
        
        channel1 <= '1';
        wait for SIGNAL_DURATION;
        channel1 <= '0';
        
        wait for CLK_PERIOD*8;
        channel1 <= '1';
        wait for SIGNAL_DURATION;
        channel1 <= '0';

        wait for CLK_PERIOD*4;
        channel1 <= '1';
        wait for CLK_PERIOD;
        channel2 <= '1';
        wait for 2*CLK_PERIOD;
        channel1 <= '0';
        wait for CLK_PERIOD;
        channel2 <= '0';

        wait;

  end process;

end;

Credits

Artem Melnykov
4 projects • 2 followers
Contact

Comments

Please log in or sign up to comment.