----------------------------------------------
-- MSS copyright 1999-2008
-- Filename: DIGITAL_DC2.VHD
-- Author: Alain Zarembowitch / MSS
-- Inheritance: DIGITAL_DC1C.vhd
-- Edit date: 8-6-08
-- Revision: 3
--
-- 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 higher precision sin lookup table (RAMB16
-- instead of the previous RAMB4). A single RAMB16 is shared for sine and cosine tables,
-- to be able to operate at the maximum speed of one complex sample per system clock.
-- sin/cos are computed with 18 bit precision at steps of 0.35 deg.
-- Size (including embedded sin/cos table):
-- FF: 85, LUT4s: 136, Slices: 97, RAMBs: 1, MULT18x18s: 4
-- Maximum speed: 147 MHz (Spartan-3 -4 speed grade).
--
-- Rev 2 8-7-05 AZ
-- Corrected scale problem
--
-- Rev 3 8-6-08 AZ
-- corrected sign extension bug. 
----------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_SIGNED.ALL;

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

	  	--// INPUTS
      DATA_I_IN: in std_logic_vector(11 downto 0);
      DATA_Q_IN: in std_logic_vector(11 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 360 representation.
			-- 0x00 -> 0.0 deg, 0xff -> 359.912 deg.
			-- Steps: 0.088 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.

 		--// OUTPUTS
		DATA_I_OUT: out std_logic_vector(11 downto 0);
		DATA_Q_OUT: out std_logic_vector(11 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
		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_DC2 is
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------      
-- sin/cos look-up table, 4 quadrants, 4K steps x 18 bits precision
component SIGNED_SIN_COS_TBL2 is
	port (
		CLK: in std_logic;
			-- system clock
		PHASE: in std_logic_vector(11 downto 0);
			-- angle: [0 to 360 degree[. No 360 representation.
			-- 0x00 -> 0.0 deg, 0xff -> 359.912 deg.
			-- Steps: 0.088 deg.
		SIN_OUT: out std_logic_vector(17 downto 0);
		COS_OUT: out std_logic_vector(17 downto 0)
			-- sine or cosine value. range -1 to +1. 
			-- 18-bit signed (2's complement)
			-- Latency is 2 CLK  
	);
end component;

-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------      
signal ZERO: std_logic;
signal ONE: std_logic;
signal DATA_I_IN_D: std_logic_vector(11 downto 0);
signal DATA_Q_IN_D: std_logic_vector(11 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 DATA_I_SIN: std_logic_vector(29 downto 0);
signal DATA_I_COS: std_logic_vector(29 downto 0);
signal DATA_Q_SIN: std_logic_vector(29 downto 0);
signal DATA_Q_COS: std_logic_vector(29 downto 0);
signal DATA_I_SIN_E: std_logic_vector(12 downto 0);
signal DATA_I_COS_E: std_logic_vector(12 downto 0);
signal DATA_Q_SIN_E: std_logic_vector(12 downto 0);
signal DATA_Q_COS_E: std_logic_vector(12 downto 0);
signal DATA_I_DC: std_logic_vector(12 downto 0);
signal DATA_Q_DC: std_logic_vector(12 downto 0);
signal SATURATION_I: std_logic;
signal SATURATION_Q: std_logic;
-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin
ZERO <= '0';
ONE <= '1';

-- 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;
		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_TBL2 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 2.28 
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(29 downto 17);
			DATA_Q_SIN_E <= DATA_Q_SIN(29 downto 17);
			DATA_I_COS_E <= DATA_I_COS(29 downto 17);
			DATA_Q_COS_E <= DATA_Q_COS(29 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 <= DATA_I_COS_E - DATA_Q_SIN_E;
DATA_Q_DC <= DATA_I_SIN_E + 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(12 downto 11) = "10") then
				-- clamp to most negative
				DATA_I_OUT <= "100000000001";
				SATURATION_I <= '1';
			elsif(DATA_I_DC(12 downto 11) = "01") then
				-- clamp to most positive
				DATA_I_OUT <= "011111111111";
				SATURATION_I <= '1';
			else
				DATA_I_OUT <= DATA_I_DC(11 downto 0);
				SATURATION_I <= '0';
			end if;
	
			if(DATA_Q_DC(12 downto 11) = "10") then
				-- clamp to most negative
				DATA_Q_OUT <= "100000000001";
				SATURATION_Q <= '1';
			elsif(DATA_Q_DC(12 downto 11) = "01") then
				-- clamp to most positive
				DATA_Q_OUT <= "011111111111";
				SATURATION_Q <= '1';
			else
				DATA_Q_OUT <= DATA_Q_DC(11 downto 0);
				SATURATION_Q <= '0';
			end if;
		end if;
	end if;
end process; 

SATURATION <= SATURATION_I or SATURATION_Q;

 end DIGITAL_DC_ARCH;
