----------------------------------------------
-- MSS copyright 2013
-- File: NCO32X.VHD
-- Author: Alain Zarembowitch / MSS
-- Inheritance: NCO32, rev4 3/28/12, RESAMPLING2 10/27/12
-- Last edit date: 2/19/13
-- Revision: 0b 
--
-- Implementation: Extended version of NCO32 with an added pulse clock generator  
-- Important includes a toggle prevention mechanism that is very useful when the PHASE_OFFSET corrections are
-- jittering (which is the case in symbol tracking loops etc).
--
-- Rev 0b 9/30/14 AZ Vivado tools misunderstood the statement (something = 'X') as true
-- had to work around it.
----------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity NCO32X is
	generic (
    SIMULATION: std_logic := '0'
        -- 1 during simulation. 
);
	port (
		--// CLOCK
 		CLK: in std_logic;
 			-- system clock
		SYNC_RESET: in std_logic;

		--// CONTROL
		NOMINAL_FREQUENCY: in std_logic_vector(31 downto 0);
			-- Desired frequency: Expressed as a function of the system clock fclk:
			-- frequency * 2^31 / fclk
			-- Value must be changed if the master clock frequency or desired frequency changes.
		PHASE_OFFSET: in std_logic_vector(31 downto 0);
			-- occasional phase corrections.
			-- 2's complement.
			-- read at rising edge of CLK when NCO_OFFSET_SAMPLE_CLK = '1'
		PHASE_OFFSET_SAMPLE_CLK: in std_logic;
			-- The NCO_OFFSET phase correction can occur anytime. No special timing
			-- requirement with respect to inputs or outputs sample clocks.

		--// OUTPUT
		PHASE: out std_logic_vector(32 downto 0);
			-- 33-bit NCO phase. For use by derotator/frequency translators.
			-- Format 1.32, modulo 4*pi!
		SAMPLE_CLK_OUT: out std_logic
			-- 1 CLK-wide pulses
	);
end entity;
    
architecture behavioral of NCO32X is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------

-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------
signal NCO_ACCUMULATOR: std_logic_vector(32 downto 0) := (others => '0');
signal NCO_ACCUMULATOR_PARTIAL: std_logic_vector(32 downto 0) := (others => '0');
	-- NCO accumulator. 
	-- 2's complement. Format 1.32, modulo 4*pi!
signal MIN_INTERVAL: integer range 0 to 511 := 0;
signal INTERVAL_CNTR: std_logic_vector(9 downto 0) := (others => '0');
signal NCO_ACCUMULATOR_MSB_D: std_logic := '0';
-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin

--34-bit accumulator
NCO_001: process (CLK)
begin 
	if rising_edge(CLK) then
		if(PHASE_OFFSET_SAMPLE_CLK = '0') then
			NCO_ACCUMULATOR_PARTIAL <= ext(NOMINAL_FREQUENCY,33);
				-- nominal NCO increment (no phase correction)
		else
			NCO_ACCUMULATOR_PARTIAL <= NOMINAL_FREQUENCY + sxt(PHASE_OFFSET,33);
				-- NCO update with phase correction
		end if;
	end if;
end process;


NCO_002: process (CLK)
begin 
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			NCO_ACCUMULATOR <= (others => '0');
		elsif((SIMULATION = '0') or (NCO_ACCUMULATOR_PARTIAL(0) /= 'X')) then
			NCO_ACCUMULATOR <= NCO_ACCUMULATOR + NCO_ACCUMULATOR_PARTIAL;

			NCO_ACCUMULATOR_MSB_D <= NCO_ACCUMULATOR(32);
				-- remember the MSb for detecting changes (a change in the MSb means
				-- a new output sample is generated).
		end if;
	end if;
end process;

PHASE <= NCO_ACCUMULATOR;

-- toggle prevention 
-- as the PHASE_OFFSET correction is positive or negative, it can occasionally creates multiple spurious 
-- output SAMPLE_CLK_OUT_E2 pulses. We add a timer to prevent successive toggling.
-- The principle is simple: we can determine the minimum number of CLKs between successive SAMPLE_CLK_OUT_E2
-- based on the NOMINAL_FREQUENCY
TOGGLE_PREVENTION_001:  process(CLK)
begin
	if rising_edge(CLK) then
		if(NOMINAL_FREQUENCY(31) = '1') then
			MIN_INTERVAL <= 0;
		elsif(NOMINAL_FREQUENCY(30) = '1') then
			MIN_INTERVAL <= 1;
		elsif(NOMINAL_FREQUENCY(29) = '1') then
			MIN_INTERVAL <= 3;
		elsif(NOMINAL_FREQUENCY(28) = '1') then
			MIN_INTERVAL <= 7;
		elsif(NOMINAL_FREQUENCY(27) = '1') then
			MIN_INTERVAL <= 15;
		elsif(NOMINAL_FREQUENCY(26) = '1') then
			MIN_INTERVAL <= 31;
		elsif(NOMINAL_FREQUENCY(25) = '1') then
			MIN_INTERVAL <= 63;
		elsif(NOMINAL_FREQUENCY(24) = '1') then
			MIN_INTERVAL <= 127;
		elsif(NOMINAL_FREQUENCY(23) = '1') then
			MIN_INTERVAL <= 255;
		else
		--elsif(NOMINAL_FREQUENCY(22) = '1') then
			MIN_INTERVAL <= 511;
		end if;
 	end if;
end process;

-- The issue is to decide which of the reclocked and interpolated samples is best?
-- Decision based on the time elapsed since last SAMPLE_CLK_OUT
RESAMPLING_001: process(CLK)
begin
	if rising_edge(CLK) then
		if (NCO_ACCUMULATOR(32) /= NCO_ACCUMULATOR_MSB_D) 
		and (INTERVAL_CNTR >= MIN_INTERVAL) then 
			-- take a new sample
			SAMPLE_CLK_OUT <= '1';	
			INTERVAL_CNTR <= (others => '0');
		else
			SAMPLE_CLK_OUT <= '0';
			-- interval counter 0 to 512, then stay there.
			if(INTERVAL_CNTR(9) = '0') then
				INTERVAL_CNTR <= INTERVAL_CNTR + 1;
			end if;
		end if;
 	end if;
end process;

end behavioral;
