-- file Freqprobe_v1_0.vhd
-- Freqprobe_v1_0 module implementation
-- author: Alexander Wirthmueller
-- copyright: (C) 2020 MPSI Technologies GmbH
-- date created: 6 Jan 2020
-- date modified: 4 Feb 2020
-- IP header --- ABOVE

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

entity Freqprobe_v1_0 is
	generic (
		fMclk: natural := 50000 -- in kHz
	);
	port (
		reset: in std_logic;
		mclk: in std_logic;

		bcdNotHex: in std_logic;

		capt: in std_logic_vector(15 downto 0);
		
		freq: out std_logic
	);
end Freqprobe_v1_0;

architecture Rtl of Freqprobe_v1_0 is

	------------------------------------------------------------------------
	-- signal declarations
	------------------------------------------------------------------------

	---- main operation (op)
	type stateOp_t is (
		stateOpInit,
		stateOpLoad,
		stateOpStep,
		stateOpRun
	);
	signal stateOp: stateOp_t;

	signal freq_sig: std_logic;

begin

	------------------------------------------------------------------------
	-- implementation: main operation (op)
	------------------------------------------------------------------------

	freq <= freq_sig;

	process (reset, mclk, stateOp)
		-- assume abcd (four nibbles, least significant / fastest: d)
		variable abcd: natural range 0 to 4; -- 4 invalid

		constant amax: natural := fMclk/2; -- 500us wait for thousands
		constant bmax: natural := fMclk/10/2; -- 50us wait for hundreds
		constant cmax: natural := fMclk/100/2; -- 5us wait for tens
		constant dmax: natural := fMclk/1000/2; -- 500ns wait for ones

		variable bcdNotHex_lcl: boolean;
		variable capt_lcl_init: std_logic_vector(15 downto 0);
		variable capt_lcl: std_logic_vector(15 downto 0);

		variable cnt: natural range 0 to 65535; -- compatibility with hex and with (wrong) BCD values A..F
		variable i: natural range 0 to amax;

	begin
		if reset='1' then
			stateOp <= stateOpInit;

			freq_sig <= '0';
			
			abcd := 0;
			bcdNotHex_lcl := false;
			capt_lcl_init := (others => '0');
			capt_lcl := (others => '0');
			cnt := 0;
			i := 0;

		elsif rising_edge(mclk) then
			if stateOp=stateOpInit then
				freq_sig <= '0';
				
				abcd := 0;
				bcdNotHex_lcl := false;
				capt_lcl_init := (others => '0');
				capt_lcl := (others => '0');
				cnt := 0;
				i := 0;

				stateOp <= stateOpLoad;

			elsif stateOp=stateOpLoad then
				freq_sig <= '0';
				
				bcdNotHex_lcl := (bcdNotHex='1');

				capt_lcl_init := capt;
				capt_lcl := capt_lcl_init;

				if capt_lcl_init/=x"0000" then
					stateOp <= stateOpStep;
				end if;

			elsif stateOp=stateOpStep then
				if not bcdNotHex_lcl and capt_lcl/=x"0000" then
					abcd := 3;
				elsif capt_lcl(15 downto 12)/="0000" then
					abcd := 0;
				elsif capt_lcl(11 downto 8)/="0000" then
					abcd := 1;
				elsif capt_lcl(7 downto 4)/="0000" then
					abcd := 2;
				elsif capt_lcl(3 downto 0)/="0000" then
					abcd := 3;
				else
					abcd := 4;
				end if;

				if abcd=4 then
					if freq_sig='0' then
						freq_sig <= '1';

						capt_lcl := capt_lcl_init;

						stateOp <= stateOpStep;

					else
						stateOp <= stateOpLoad;
					end if;

				else
					cnt := 0;
					i := 0;

					stateOp <= stateOpRun;
				end if;

			elsif stateOp=stateOpRun then
				i := i + 1;

				if (abcd=0 and i=amax) or (abcd=1 and i=bmax) or (abcd=2 and i=cmax) or (abcd=3 and i=dmax) then
					i := 0;

					cnt := cnt + 1;

					if not bcdNotHex_lcl and cnt>=to_integer(unsigned(capt_lcl)) then
						capt_lcl := (others => '0');
						stateOp <= stateOpStep;
					
					elsif abcd=0 and cnt>=to_integer(unsigned(capt_lcl(15 downto 12))) then
						capt_lcl(15 downto 12) := (others => '0');
						stateOp <= stateOpStep;

					elsif abcd=1 and cnt>=to_integer(unsigned(capt_lcl(11 downto 8))) then
						capt_lcl(11 downto 8) := (others => '0');
						stateOp <= stateOpStep;

					elsif abcd=2 and cnt>=to_integer(unsigned(capt_lcl(7 downto 4))) then
						capt_lcl(7 downto 4) := (others => '0');
						stateOp <= stateOpStep;

					elsif bcdNotHex_lcl and abcd=3 and cnt>=to_integer(unsigned(capt_lcl(3 downto 0))) then
						capt_lcl(3 downto 0) := (others => '0');
						stateOp <= stateOpStep;
					end if;
				end if;
			end if;
		end if;
	end process;

end Rtl;
