-------------------------------------------------------------
-- Filename:  CPM_CARRIER_TRACKING.VHD
-- Authors: 
--		Alain Zarembowitch / MSS
-- Version: Rev 0
-- Last modified: 5/5/17

-- Inheritance: 	N/A
--
-- description:  save all MLMAX complex matched filters outputs. Delay to aligned with CPM_VA best decoded state.
---------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;
use work.CPM_MF_PKG.all;	-- defines types for this interface

entity CPM_CARRIER_TRACKING is
    Port ( 
		--GLOBAL CLOCK, RESET
		CLK   : in  std_logic;
		SYNC_RESET: in std_logic;	
			-- synchronous reset
			-- must use SYNC_RESET after changing the modulation scheme
		
		-- INPUT 
		-- matched filters complex outputs (from CPM_MF.VHD before rotation)
		-- MPOWERLMAX complex numbers. Aligned with MF_SYMBOL_CLK_IN. CPM_MF_NBITS precision on both I and Q
		MF_I_IN: in MF_TYPE;
		MF_Q_IN: in MF_TYPE;
		MF_SYMBOL_CLK_IN: in std_logic;
		
		BEST_DATA: in std_logic;
			-- Latency: 8 CLKs after MF_SYMBOL_CLK_IN

		BEST_SYMBOL_I: in std_logic_vector(17 downto 0);
		BEST_SYMBOL_Q: in  std_logic_vector(17 downto 0);
		BEST_SYMBOL_CLK: in std_logic;
			-- best known constellation point for the last received symbol 
			-- Latency: 10 CLKs after MF_SYMBOL_CLK_IN
		
		-- OUTPUT
		PE: out std_logic_vector(10 downto 0);
		PE_SYMBOL_CLK_OUT: out std_logic
			-- once per symbol, PE phase error measurement
		);
end entity;


architecture Behavioral of CPM_CARRIER_TRACKING is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------
	COMPONENT DELAY2
	 Generic(
		ADDR_WIDTH: integer;	
		DATA_WIDTH: integer;
		NSAMPLES_DELAY: integer
	);
	PORT(
		CLK : IN std_logic;
		DATA_IN : IN std_logic_vector((DATA_WIDTH-1) downto 0);
		SAMPLE_CLK_IN : IN std_logic;          
		DATA_OUT : OUT std_logic_vector((DATA_WIDTH-1) downto 0);
		SAMPLE_CLK_OUT : OUT std_logic
		);
	END COMPONENT;

--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
signal DATA1_I: std_logic_vector((MPOWERLMAX*CPM_MF_NBITS)-1 downto 0) := (others => '0');
signal DATA1_Q: std_logic_vector((MPOWERLMAX*CPM_MF_NBITS)-1 downto 0) := (others => '0');
signal DATA2_I: std_logic_vector((MPOWERLMAX*CPM_MF_NBITS)-1 downto 0) := (others => '0');
signal DATA2_Q: std_logic_vector((MPOWERLMAX*CPM_MF_NBITS)-1 downto 0) := (others => '0');
signal MF1_SYMBOL_CLK_SHIFT: std_logic_vector(8 downto 0) := (others => '0');
signal MF2_SYMBOL_CLK: std_logic := '0';
signal MF2_SYMBOL_CLK_D: std_logic := '0';
signal MF2_SYMBOL_CLK_D2: std_logic := '0';
signal MF2_SYMBOL_CLK_D3: std_logic := '0';
signal MF2_SYMBOL_CLK_D4: std_logic := '0';

signal MF2_I: MF_TYPE;
signal MF2_Q: MF_TYPE;

--// BEST MF SELECTION ----------------------------------
signal BEST_MF_I: signed((CPM_MF_NBITS-1) downto 0) := (others => '0');
signal BEST_MF_Q: signed((CPM_MF_NBITS-1) downto 0) := (others => '0');
signal BEST_DATA_D: std_logic := '0';


--// PHASE ERROR COMPUTATION --------------------------
signal BEST_SYMBOL_I_D: signed(10 downto 0) := (others => '0');
signal BEST_SYMBOL_Q_D: signed(10 downto 0) := (others => '0');
signal MULTA: signed((CPM_MF_NBITS-1) downto 0) := (others => '0');
signal MULTB: signed(10 downto 0) := (others => '0');
signal MULT: signed((CPM_MF_NBITS+10) downto 0) := (others => '0');
signal PE_local: signed(10 downto 0) := (others => '0');
--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
begin

-- delay matched filters outputs by 9 CLKs until the VA algorithm finds out the best symbol hypothesis
-- and phase rotation
DELAY2_INPUTS: process(MF_I_IN,MF_Q_IN)
begin
	for I in 0 to (MPOWERLMAX-1) loop
		DATA1_I(((CPM_MF_NBITS*(I+1)) -1) downto CPM_MF_NBITS*I) <= MF_I_IN(I);
		DATA1_Q(((CPM_MF_NBITS*(I+1)) -1) downto CPM_MF_NBITS*I) <= MF_Q_IN(I);
	end loop;
end process;

-- 9 clk delay
DELAY2_000: process(CLK)
begin
	if rising_edge(CLK) then
		MF1_SYMBOL_CLK_SHIFT <= MF1_SYMBOL_CLK_SHIFT(MF1_SYMBOL_CLK_SHIFT'left -1 downto 0) & MF_SYMBOL_CLK_IN;
	end if;
end process;

DELAY2_001: DELAY2 
GENERIC MAP(
	ADDR_WIDTH => 4,	-- max delay 16 samples
	DATA_WIDTH => DATA1_I'length,	
	NSAMPLES_DELAY => 7	-- 9 CLK latency
)
PORT MAP(
	CLK => CLK,
	DATA_IN => DATA1_I,
	SAMPLE_CLK_IN => '1',
	DATA_OUT => DATA2_I,
	SAMPLE_CLK_OUT => open
);

DELAY2_002: DELAY2 
GENERIC MAP(
	ADDR_WIDTH => 4,	-- max delay 16 samples
	DATA_WIDTH => DATA1_Q'length,	
	NSAMPLES_DELAY => 7	-- 9 CLK latency
)
PORT MAP(
	CLK => CLK,
	DATA_IN => DATA1_Q,
	SAMPLE_CLK_IN => '1',
	DATA_OUT => DATA2_Q,
	SAMPLE_CLK_OUT => open
);

DELAY2_OUTPUTS: process(DATA2_I,DATA2_Q)
begin
	for I in 0 to (MPOWERLMAX-1) loop
		MF2_I(I) <= DATA2_I((CPM_MF_NBITS*(I+1)) -1 downto CPM_MF_NBITS*I);
		MF2_Q(I) <= DATA2_Q((CPM_MF_NBITS*(I+1)) -1 downto CPM_MF_NBITS*I);
	end loop;
end process;
MF2_SYMBOL_CLK <= MF1_SYMBOL_CLK_SHIFT(8);

--// BEST MF SELECTION ----------------------------------
-- select the best MF output (one complex number out of the M^L matched filters outputs)
-- based on the highest confidence at this time, from the VA.
BEST_MF_SEL_001: process(CLK)
begin
	if rising_edge(CLK) then
		BEST_DATA_D <= BEST_DATA;	-- aligned with MF_SYMBOL_CLK_IN_D9
		MF2_SYMBOL_CLK_D <= MF2_SYMBOL_CLK;
		
		
		if(MF2_SYMBOL_CLK = '1') then	-- aligned with MF_SYMBOL_CLK_IN_D9
			if(BEST_DATA_D = '0') then
				BEST_MF_I <= signed(MF2_I(0));
				BEST_MF_Q <= signed(MF2_Q(0));
			else
				BEST_MF_I <= signed(MF2_I(1));
				BEST_MF_Q <= signed(MF2_Q(1));
			end if;
		end if;
	end if;
end process;

--// PHASE ERROR COMPUTATION --------------------------
-- Compute the phase error between BEST_MF and closest ideal symbol.
-- First select the best phase 

-- timing: BEST_SYMBOL_X and BEST_MF_X are aligned (with BEST_SYMBOL_CLK_D2)

-- derotate BEST_MF_X. Only compute the imaginary (Q) as it represents the phase error to be fed into the tracking loop.
DEROTATE_001: process(CLK)
begin
	if rising_edge(CLK) then
		MF2_SYMBOL_CLK_D2 <= MF2_SYMBOL_CLK_D;
		MF2_SYMBOL_CLK_D3 <= MF2_SYMBOL_CLK_D2;
		
		-- delay BEST_SYMBOL_x by 1 symbol
		if(MF2_SYMBOL_CLK = '1') then	-- just before BEST_SYMBOL_x changes
			BEST_SYMBOL_I_D <= signed(BEST_SYMBOL_I(17 downto 7));
			BEST_SYMBOL_Q_D <= signed(BEST_SYMBOL_Q(17 downto 7));
		end if;
		
		if(MF2_SYMBOL_CLK_D = '1') then	-- aligned with MF_SYMBOL_CLK_IN_D10
			MULTA <= BEST_MF_I;
			MULTB <= BEST_SYMBOL_Q_D;
		elsif(MF2_SYMBOL_CLK_D2 = '1') then	-- aligned with MF_SYMBOL_CLK_IN_D11
			MULTA <= BEST_MF_Q;
			MULTB <= BEST_SYMBOL_I_D;
		end if;
	end if;
end process; 

MULT <= MULTA * MULTB;

DEROTATE_002: process(CLK)
begin
	if rising_edge(CLK) then
		MF2_SYMBOL_CLK_D4 <= MF2_SYMBOL_CLK_D3;
		
		if(MF2_SYMBOL_CLK_D2 = '1') then	
			PE_local <= MULT(MULT'left downto MULT'left-10);
		elsif(MF2_SYMBOL_CLK_D3 = '1') then
			PE_local <= PE_local - MULT(MULT'left downto MULT'left-10);
		end if;
	end if;
end process; 

DEROTATE_003: process(CLK)
begin
	if rising_edge(CLK) then
		PE_SYMBOL_CLK_OUT <= MF2_SYMBOL_CLK_D4;
		
		if(MF2_SYMBOL_CLK_D4 = '1') then
			PE <= std_logic_vector(PE_local);
		end if;
	end if;
end process; 
	

end Behavioral;
