-- 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:				 	TODO
--
-- Description:
-- 
-- .. TODO:: No documentation available.
--
-- License:
-- =============================================================================
-- Copyright 2007-2014 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.physical.all;


entity [docs]io_FrequencyCounter is
	generic (
		CLOCK_FREQ								: FREQ									:= 100 MHz;
		TIMEBASE									: time									:= 1 sec;
		RESOLUTION								: positive							:= 8
	);
	port (
		Clock				: in	std_logic;
		Reset				: in	std_logic;
    FreqIn			: in	std_logic;
		FreqOut			: out	std_logic_vector(RESOLUTION - 1 downto 0)
	);
end entity;


architecture [docs]rtl of io_FrequencyCounter is
	constant TIMEBASECOUNTER_MAX				: positive																		:= TimingToCycles(TIMEBASE, CLOCK_FREQ);
	constant TIMEBASECOUNTER_BITS				: positive																		:= log2ceilnz(TIMEBASECOUNTER_MAX);
	constant REQUENCYCOUNTER_MAX				: positive																		:= 2**RESOLUTION;
	constant FREQUENCYCOUNTER_BITS			: positive																		:= RESOLUTION;

	signal TimeBaseCounter_us						: unsigned(TIMEBASECOUNTER_BITS - 1 downto 0)	:= (others => '0');
	signal TimeBaseCounter_ov						: std_logic;
	signal FrequencyCounter_us					: unsigned(FREQUENCYCOUNTER_BITS downto 0)		:= (others => '0');
	signal FrequencyCounter_ov					: std_logic;

	signal FreqIn_d											: std_logic																		:= '0';
	signal FreqIn_re										: std_logic;

	signal FreqOut_d										: std_logic_vector(RESOLUTION - 1 downto 0)		:= (others => '0');

begin
	FreqIn_d	<= FreqIn when rising_edge(Clock);
	FreqIn_re	<= not FreqIn_d and FreqIn;

	-- timebase counter
	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or TimeBaseCounter_ov) = '1') then
				TimeBaseCounter_us		<= (others => '0');
			else
				TimeBaseCounter_us		<= TimeBaseCounter_us + 1;
			end if;
		end if;
	end process;

	TimeBaseCounter_ov	<= to_sl(TimeBaseCounter_us = TIMEBASECOUNTER_MAX);

	-- frequency counter
	process(Clock)
	begin
		if rising_edge(Clock) then
			if ((Reset or TimeBaseCounter_ov) = '1') then
				FrequencyCounter_us		<= (others => '0');
			elsif (FrequencyCounter_ov = '0') and (FreqIn_re = '1') then
				FrequencyCounter_us		<= FrequencyCounter_us + 1;
			end if;
		end if;
	end process;

	FrequencyCounter_ov	<= FrequencyCounter_us(FrequencyCounter_us'high);

	-- hold counter value until next TimeBaseCounter event
	process(Clock)
	begin
		if rising_edge(Clock) then
			if (Reset = '1') then
				FreqOut_d			<= (others => '0');
			elsif (TimeBaseCounter_ov = '1') then
				if (FrequencyCounter_ov = '1') then
					FreqOut_d	<= (others => '1');
				else
					FreqOut_d	<= std_logic_vector(FrequencyCounter_us(FreqOut_d'range));
				end if;
			end if;
		end if;
	end process;

	FreqOut		<= FreqOut_d;
end;