----------------------------------------------
-- MSS copyright 1999-2017
-- Filename: DIGITAL_DC3.VHD
-- Author: Alain Zarembowitch / MSS
-- Inheritance: DIGITAL_DC1C.vhd, DIGITAL_DC2
-- Edit date: 2/14/17
-- Revision: 0
--
-- Description: Frequency translator. Rotate the input vector by the specified 
-- angle. 

-- Implementation: Signed arithmetics. Relies on signed 18x18 hardware multipliers, 
-- thus the large output precision. Uses a high precision sin lookup table (4 18Kb ROM)
----------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.NUMERIC_STD.ALL;

entity DIGITAL_DC3 is
    port (
	 	--// CLOCKS
 		CLK: in std_logic;
 			-- system clock for synchronous design.

	  	--// INPUTS
      DATA_I_IN: in std_logic_vector(17 downto 0);
      DATA_Q_IN: in std_logic_vector(17 downto 0);
        	-- input sample from A/D converter. format: signed (2's complement) 
			-- read at rising edge of CLK when SAMPLE_CLK_IN = '1'	
		SAMPLE_CLK_IN: in std_logic;
			-- one CLK wide pulse.
			-- Maximum rate: one SAMPLE_CLK_IN per CLK.
		PHASE_ROTATION: in std_logic_vector(11 downto 0);
			-- Phase rotation. 
			-- angle: ]0 to 360 degree[. No 0/360 exact representation.
			-- 0x00 -> 0.01 deg
			-- Steps: 0.022 deg.
			-- Generally connected the NCO accumulator most significant bits. 
			-- The phase rotation is not strictly read at the same time as the input
			-- samples, but it does not matter much as the NCO accumulator is continuously
			-- running.
			-- ROTATION IS COUNTER CLOCK WISE (i.e. negative frequency)

 		--// OUTPUTS
		DATA_I_OUT: out std_logic_vector(17 downto 0);
		DATA_Q_OUT: out std_logic_vector(17 downto 0);
			-- signal after derotator. 
			-- read at rising edge of CLK when SAMPLE_CLK_OUT = '1'
	  	SAMPLE_CLK_OUT: out std_logic;
			-- latency is 3 CLK after SAMPLE_CLK_IN
		SATURATION: out std_logic
			-- '1' when interpolation circuit saturates. Reduce input signal level when this occurs.
			-- Important test point, as the effects of saturation (spectrum degradation) can be difficult to detect.

       );
end entity;

architecture DIGITAL_DC_ARCH of DIGITAL_DC3 is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------      
-- sin/cos look-up table, 4 quadrants, 16K steps x 18 bits precision
component SIGNED_SIN_COS_TBL3 is
	generic (
		NADDR: integer
	);
	port (
		CLK: in std_logic;
		PHASE: in std_logic_vector(NADDR+1 downto 0);
		SIN_OUT: out std_logic_vector(17 downto 0);
		COS_OUT: out std_logic_vector(17 downto 0)
	);
end component;

COMPONENT MULT18X18SIGNED
PORT(
	A : IN std_logic_vector(17 downto 0);
	B : IN std_logic_vector(17 downto 0);          
	P : OUT std_logic_vector(35 downto 0)
	);
END COMPONENT;

-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------      
signal DATA_I_IN_D: std_logic_vector(17 downto 0);
signal DATA_Q_IN_D: std_logic_vector(17 downto 0);
signal SAMPLE_CLK_IN_D: std_logic;
signal SAMPLE_CLK_IN_D2: std_logic;
signal SIN_PHI: std_logic_vector(17 downto 0);
signal COS_PHI: std_logic_vector(17 downto 0);
signal SIN_PHI_D: std_logic_vector(17 downto 0);
signal COS_PHI_D: std_logic_vector(17 downto 0);
signal DATA_I_SIN: std_logic_vector(35 downto 0);
signal DATA_I_COS: std_logic_vector(35 downto 0);
signal DATA_Q_SIN: std_logic_vector(35 downto 0);
signal DATA_Q_COS: std_logic_vector(35 downto 0);
signal DATA_I_SIN_E: std_logic_vector(18 downto 0);
signal DATA_I_COS_E: std_logic_vector(18 downto 0);
signal DATA_Q_SIN_E: std_logic_vector(18 downto 0);
signal DATA_Q_COS_E: std_logic_vector(18 downto 0);
signal DATA_I_DC: signed(18 downto 0);
signal DATA_Q_DC: signed(18 downto 0);
signal SATURATION_I: std_logic;
signal SATURATION_Q: std_logic;
-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin
-- register input signal for timing
-- No attempt is made to realign sampling clock and NCO phase.
RECLOCK_001: process(CLK)
begin
	if rising_edge(CLK) then
		SAMPLE_CLK_IN_D <= SAMPLE_CLK_IN;

		if(SAMPLE_CLK_IN = '1') then
			DATA_I_IN_D <= DATA_I_IN;
			DATA_Q_IN_D <= DATA_Q_IN;
			SIN_PHI_D <= SIN_PHI;
			COS_PHI_D <= COS_PHI;
		end if;
	end if;
end process;

-- generate sin(phi) and cos(phi) for derotator          
-- Same table used for both sin and cos:
-- Latency is 2 CLK. Output is registered for best timing. 
SIN_COS001: SIGNED_SIN_COS_TBL3 
generic map (
	NADDR => 10
)
port map (
	CLK => CLK, 
	PHASE => PHASE_ROTATION, 
	SIN_OUT => SIN_PHI, 
	COS_OUT => COS_PHI 
	);   


-- s * sin(phi) and s * cos(phi)
-- Signed multiplication. Format 1.35 
-- ROTATION IS COUNTER CLOCK WISE (i.e. negative frequency)
-- ?? this old component synthesizes better than the signed() * signed() statement. Not sure why.
MULT1: MULT18X18SIGNED PORT MAP(
	A => DATA_I_IN_D,
	B => SIN_PHI_D,	
	P => DATA_I_SIN
);
MULT2: MULT18X18SIGNED PORT MAP(
	A => DATA_I_IN_D,
	B => COS_PHI_D,	
	P => DATA_I_COS
);
MULT3: MULT18X18SIGNED PORT MAP(
	A => DATA_Q_IN_D,
	B => SIN_PHI_D,	
	P => DATA_Q_SIN
);
MULT4: MULT18X18SIGNED PORT MAP(
	A => DATA_Q_IN_D,
	B => COS_PHI_D,	
	P => DATA_Q_COS
);

--DATA_I_SIN <= DATA_I_IN_D * SIN_PHI;
--DATA_I_COS <= DATA_I_IN_D * COS_PHI;
--DATA_Q_SIN <= DATA_Q_IN_D * SIN_PHI;
--DATA_Q_COS <= DATA_Q_IN_D * COS_PHI;
			
-- reclocking and sign extension before sum
-- Truncate LSBs.
RECLOCK_002: process(CLK)
begin
	if rising_edge(CLK) then
		SAMPLE_CLK_IN_D2 <= SAMPLE_CLK_IN_D;

		if(SAMPLE_CLK_IN_D = '1') then
			DATA_I_SIN_E <= DATA_I_SIN(35 downto 17);
			DATA_Q_SIN_E <= DATA_Q_SIN(35 downto 17);
			DATA_I_COS_E <= DATA_I_COS(35 downto 17);
			DATA_Q_COS_E <= DATA_Q_COS(35 downto 17);
	  	end if;
	end if;
end process;

-- Compute rotated I samples: si * cos(phi) - sq * sin(phi)
-- Compute rotated Q samples: si * sin(phi) + sq * cos(phi) 
DATA_I_DC <= signed(DATA_I_COS_E) - signed(DATA_Q_SIN_E);
DATA_Q_DC <= signed(DATA_I_SIN_E) + signed(DATA_Q_COS_E);


-- Reclock prior to output
-- Clamp output by 1 bit
RECLOCK_003: process(CLK)
begin
	if rising_edge(CLK) then
	  	SAMPLE_CLK_OUT <= SAMPLE_CLK_IN_D2;

		if(SAMPLE_CLK_IN_D2 = '1') then
	  		if(DATA_I_DC(18 downto 17) = "10") then
				-- clamp to most negative
				DATA_I_OUT <= "100000000000000001";
				SATURATION_I <= '1';
			elsif(DATA_I_DC(18 downto 17) = "01") then
				-- clamp to most positive
				DATA_I_OUT <= "011111111111111111";
				SATURATION_I <= '1';
			else
				DATA_I_OUT <= std_logic_vector(DATA_I_DC(17 downto 0));
				SATURATION_I <= '0';
			end if;
	
			if(DATA_Q_DC(18 downto 17) = "10") then
				-- clamp to most negative
				DATA_Q_OUT <= "100000000000000001";
				SATURATION_Q <= '1';
			elsif(DATA_Q_DC(18 downto 17) = "01") then
				-- clamp to most positive
				DATA_Q_OUT <= "011111111111111111";
				SATURATION_Q <= '1';
			else
				DATA_Q_OUT <= std_logic_vector(DATA_Q_DC(17 downto 0));
				SATURATION_Q <= '0';
			end if;
		end if;
	end if;
end process; 

SATURATION <= SATURATION_I or SATURATION_Q;

 end DIGITAL_DC_ARCH;
