-- EMACS settings: -*-  tab-width: 2; indent-tabs-mode: t -*-
-- vim: tabstop=2:shiftwidth=2:noexpandtab
-- kate: tab-width 2; replace-tabs off; indent-width 2;
-- =============================================================================
-- Authors:					Patrick Lehmann
--
-- Entity:					measures a input frequency relativ to a reference frequency
--
-- Description:
-- 
-- This module counts 1 second in a reference timer at reference clock. This
-- reference time is used to start and stop a timer at input clock. The counter
-- value is the measured frequency in Hz.
--
-- License:
-- =============================================================================
-- Copyright 2007-2015 Technische Universitaet Dresden - Germany
--										 Chair of VLSI-Design, Diagnostics and Architecture
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--		http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
-- =============================================================================

library IEEE;
use			IEEE.STD_LOGIC_1164.all;
use			IEEE.NUMERIC_STD.all;

library PoC;
use			PoC.utils.all;
use			PoC.vectors.all;
use			PoC.physical.all;
use			PoC.components.all;


entity [docs]misc_FrequencyMeasurement is
	generic (
		REFERENCE_CLOCK_FREQ	: FREQ			:= 100 MHz
	);
	port (
		Reference_Clock		: in	std_logic;
		Input_Clock				: in	std_logic;

		Start							: in	std_logic;
		Done							: out	std_logic;
		Result						: out	T_SLV_32
	);
end entity;


architecture [docs]rtl of misc_FrequencyMeasurement is
	constant TIMEBASE_COUNTER_MAX			: positive																:= TimingToCycles(ite(SIMULATION, 10 us, 1 sec), REFERENCE_CLOCK_FREQ);
	constant TIMEBASE_COUNTER_BITS		: positive																:= log2ceilnz(TIMEBASE_COUNTER_MAX);

	signal TimeBase_Counter_rst				: std_logic;
	signal TimeBase_Counter_s					: signed(TIMEBASE_COUNTER_BITS downto 0)	:= to_signed(-1, TIMEBASE_COUNTER_BITS + 1);
	signal TimeBase_Counter_nxt				: signed(TIMEBASE_COUNTER_BITS downto 0);
	signal TimeBase_Counter_uf				: std_logic;

	signal Stop												: std_logic;
	signal sync_Start									: std_logic;
	signal sync_Stop									: std_logic;
	signal sync1_Busy									: T_SLV_2;

	signal Frequency_Counter_en_r			: std_logic																:= '0';
	signal Frequency_Counter_us				: unsigned(31 downto 0)										:= (others => '0');

	signal CaptureResult							: std_logic;
	signal CaptureResult_d						: std_logic																:= '0';
	signal Result_en									: std_logic;
	signal Result_d										: T_SLV_32																:= (others => '0');
	signal Done_r											: std_logic																:= '0';
begin

	TimeBase_Counter_rst	<= Start;
	TimeBase_Counter_nxt	<= TimeBase_Counter_s - 1;

	process(Reference_Clock)
	begin
		if rising_edge(Reference_Clock) then
			if (TimeBase_Counter_rst = '1') then
				TimeBase_Counter_s	<= to_signed(TIMEBASE_COUNTER_MAX - 2, TimeBase_Counter_s'length);
			elsif (TimeBase_Counter_uf = '0') then
				TimeBase_Counter_s	<= TimeBase_Counter_nxt;
			end if;
		end if;
	end process;

	TimeBase_Counter_uf		<= TimeBase_Counter_s(TimeBase_Counter_s'high);
	Stop									<= not TimeBase_Counter_s(TimeBase_Counter_s'high) and TimeBase_Counter_nxt(TimeBase_Counter_nxt'high);

	sync1 : entity poc.sync_Strobe
		generic map (
			BITS			=> 2													-- number of bit to be synchronized
		)
		port map (
			Clock1		=> Reference_Clock,						-- <Clock>	input clock
			Clock2		=> Input_Clock,								-- <Clock>	output clock
			Input(0)	=> Start,											-- @Clock1	input vector
			Input(1)	=> Stop,											--
			Output(0)	=> sync_Start,								-- @Clock2:	output vector
			Output(1)	=> sync_Stop,									--
			Busy			=> sync1_Busy
		);

	Frequency_Counter_en_r	<= ffrs(q => Frequency_Counter_en_r, set => sync_Start, rst => sync_Stop) when rising_edge(Input_Clock);

	process(Input_Clock)
	begin
		if rising_edge(Input_Clock) then
			if (sync_Start = '1') then
				Frequency_Counter_us	<= to_unsigned(1, Frequency_Counter_us'length);
			elsif (Frequency_Counter_en_r = '1') then
				Frequency_Counter_us	<= Frequency_Counter_us + 1;
			end if;
		end if;
	end process;

	CaptureResult		<= sync1_Busy(1);
	CaptureResult_d	<= CaptureResult		when rising_edge(Reference_Clock);
	Result_en				<= CaptureResult_d	and not CaptureResult;

	-- Result_d can becaptured from Frequency_Counter_us, because it's stable
	-- for more than one clock cycle and will not change until the next Start
	process(Reference_Clock)
	begin
		if rising_edge(Reference_Clock) then
			if (Result_en = '1') then
				Result_d	<= std_logic_vector(Frequency_Counter_us);
				Done_r		<= '1';
			elsif (Start = '1') then
				Done_r		<= '0';
			end if;
		end if;
	end process;

	Done		<= Done_r;
	Result	<= Result_d;
end;