-------------------------------------------------------------
-- MSS copyright 2012
-- Filename:  MEASURE_INTERVAL2.VHD
-- Authors: 
--		Alain Zarembowitch / MSS
-- Version: Rev 1
-- Last modified: 8/13/12
-- See also: 	MEASURE_INTERVAL.VHD
--
-- description: Measure the average interval between quasi-periodic PULSE_IN pulses, expressed in number of CLK periods.
-- Alternative algorithm. 
-- Maximum distance between two pulses is 2^15-1
---------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
Library UNISIM;
use UNISIM.vcomponents.all;

entity MEASURE_INTERVAL2 is
    Port ( 
		--// CLK, RESET
		CLK: in std_logic;
			-- processing clock
		SYNC_RESET: in std_logic;
			-- synchronous reset = start a new computation
		PULSE_IN: in std_logic;
			-- quasi-periodic pulses. 1 CLK wide.
		
		PERIOD: out std_logic_vector(15 downto 0) := (others => '0');
			-- measured interval. Read at rising edge of CLK when PERIOD_SAMPLE_CLK = '1'
		PERIOD_SAMPLE_CLK: out std_logic

			);
end entity;

architecture Behavioral of MEASURE_INTERVAL2 is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
constant NCNTRBITS: integer := 16;
signal PULSE_COUNTER: std_logic_vector((NCNTRBITS-1) downto 0) := (others => '0');
signal PULSE_COUNTER_INC: std_logic_vector((NCNTRBITS-1) downto 0) := (others => '0');
signal PULSE_COUNTER_FINAL: std_logic_vector((NCNTRBITS-1) downto 0) := (others => '0');
signal PERIOD_COUNTER: std_logic_vector((NCNTRBITS+7) downto 0) := (others => '0');
signal PULSE_IN_D: std_logic := '0';
signal PULSE_IN_D2: std_logic := '0';
--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
begin

--Algorithm: count the distance in CLK periods between two successive input pulses PULSE_IN
PULSE_COUNTER_INC <= PULSE_COUNTER + 1;
PULSE_COUNTER_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') or (PULSE_IN = '1') then
			-- Start a new computation
			PULSE_COUNTER <= (others => '0');
			-- latch final count
			PULSE_COUNTER_FINAL <= PULSE_COUNTER_INC;
			PULSE_IN_D <= '1';
		elsif(PULSE_COUNTER_INC > PERIOD_COUNTER((NCNTRBITS+6) downto 7)) and (PERIOD_COUNTER(NCNTRBITS+7) = '0') then
			-- if the interval between pulses exceeds 2*average, then update average. Don't wait until the next pulse.
			-- Reason: algorithm could deadlock otherwise
			-- latch final count
			PULSE_COUNTER_FINAL <= PULSE_COUNTER_INC;
			PULSE_IN_D <= '1';		
		elsif(PULSE_COUNTER_INC(PULSE_COUNTER_INC'left) = '0') then
			-- keep increasing until we reach the ceiling (can't overflow)
			PULSE_COUNTER <= PULSE_COUNTER_INC;
			PULSE_IN_D <= '1';
		else
			PULSE_IN_D <= '0';
		end if;
	end if;
end process;

-- Average count over 256 measurements. Leaky integrator.
PERIOD_COUNTER_001: process(CLK)
begin
	if rising_edge(CLK) then
		PULSE_IN_D2 <= PULSE_IN_D;
		
		if(SYNC_RESET = '1') then
			PERIOD_COUNTER <= (others => '0');
		elsif(PULSE_IN_D = '1') then
			PERIOD_COUNTER <= PERIOD_COUNTER + ext(PULSE_COUNTER_FINAL, PERIOD_COUNTER'length) 
										- ext(PERIOD_COUNTER((NCNTRBITS+7) downto 8), PERIOD_COUNTER'length);
		end if;
	end if;
end process;

PERIOD <= PERIOD_COUNTER((NCNTRBITS+7) downto 8);
PERIOD_SAMPLE_CLK <= PULSE_IN_D2;

end Behavioral;

