----------------------------------------------
-- MSS copyright 1999-2005
-- Filename: FFT_COEFF.VHD
-- Authors: 
	-- Mike Yoha/ MSS
	-- Alain Zarembowitch / MSS
-- Edit date: 11-21-05
-- Inheritance: Sirius Satellite Radio receiver, 
--					 FFT_COEFF.VHD rev0.006 2-18-01
-- Revision: 1
--
-- If FFT_STAGE = 0 (Radix 2)  this module computes
-- Cb = cos(-2*pi*k/2048), 		Sb = sin(-2*pi*k/2048)
-- Cc = cos(-2*pi*(k+1)/2048), Sc = sin(-2*pi*(k+1)/2048)
-- Cd = Cc, Sd = Sc
-- Input K ranges from 0 to 1022 by steps of 2 (thus k+1 goes to 1023)

-- If FFT_STAGE = 1 (Radix 4), this module computes
-- Cb = cos(2*pi*k/1024), 		Sb = sin(2*pi*k/1024)
-- Cc = cos(2*pi*2*k/1024), Sc = sin(2*pi*2*k/1024)
-- Cd = cos(2*pi*3*k/1024), Sd = sin(2*pi*3*k/1024)
-- Input K ranges from 0 to 255 in steps of 1
--
-- If FFT_STAGE = 2 (Radix 4), this module computes
-- Cb = cos(2*pi*k/256), 		Sb = sin(2*pi*k/256)
-- Cc = cos(2*pi*2*k/256), Sc = sin(2*pi*2*k/256)
-- Cd = cos(2*pi*3*k/256), Sd = sin(2*pi*3*k/256)
-- Input K ranges from 0 to 63 in steps of 1
--
-- If FFT_STAGE = 3 (Radix 4), this module computes
-- Cb = cos(2*pi*k/64), 		Sb = sin(2*pi*k/64)
-- Cc = cos(2*pi*2*k/64), Sc = sin(2*pi*2*k/64)
-- Cd = cos(2*pi*3*k/64), Sd = sin(2*pi*3*k/64)
-- Input K ranges from 0 to 15 in steps of 1
--
-- If FFT_STAGE = 4 (Radix 4), this module computes
-- Cb = cos(2*pi*k/16), 		Sb = sin(2*pi*k/16)
-- Cc = cos(2*pi*2*k/16), Sc = sin(2*pi*2*k/16)
-- Cd = cos(2*pi*3*k/16), Sd = sin(2*pi*3*k/16)
-- Input K ranges from 0 to 3 in steps of 1
--
-- If FFT_STAGE = 5 (Radix 4), this module computes
-- Cb = 1, 		Sb = 0
-- Cc = 1, Sc = 0
-- Cc = 1, Sc = 0
--
-- Changes Rev 0.004 my
-- CLK is 60 MHz
--
-- Rev 0.005 az
-- Removed references to work.math and inc_slv functions.
-- Removed reference to fcadd8_2
-- Switched from active low to active high asynchronous reset.
-- 
-- Rev 0.006 az
-- Switched to synchronous table lookup for speed.
-- Latency is 6 CLK for Cb/Sb, 7 CLK for Cc/Sc and 8 CLK for Cd/Sd
--
-- Rev 1 11-21-05 AZ
--   use more accurate SIGNED_SIN_COS_TBL2 for sine/cosine lookup.
--   looks both sine and cosine at the same time. 
--   Smaller latency (6 CLKs worst case)
--   Latency is 4 CLK for Cb/Sb, 5 CLK for Cc/Sc and 6 CLK for Cd/Sd
--   Increase phase precision from 8 to 12 bits
--   Increased output coefficients precision for direct interface with 18x18 multiplier.
--   Increased speed to one set of coefficients every 2 CLKs instead of 3.
--
-- Implementation: 
-- Maximum Frequency: 121.773MHz
-- Selected Device : 3s1500fg676-4 
-- Number of Slices:                     175  out of  13312     1%  
-- Number of Slice Flip Flops:           229  out of  26624     0%  
-- Number of 4 input LUTs:               251  out of  26624     0%  
-- Number of bonded IOBs:                124  out of    487    25%  
-- Number of BRAMs:                        2  out of     32     6%  
-- Number of GCLKs:                        1  out of      8    12%  
----------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity FFT_COEFF is
    port (   
		--// CLOCK, RESET
		CLK: in std_logic;
			-- synchronous reference clock
		SAMPLE_CLK_IN: in std_logic;
			-- start of table lookup. 6 output values can be read with a latency of 5 CLKs.
			-- Maximum sample rate is once every 2 CLKs.
			
		--// INPUTS
		FWD_INVN: in std_logic;
			-- '1' for forward FFT, '0' for inverse FFT
			-- Computes coefficients in the form cos(phi),sin(phi)
			-- where phi = (2*pi/N)*n*k when inverse FFT
			--       phi = -(2*pi/N)*n*k when forward FFT
		FFT_STAGE: in std_logic_vector(2 downto 0);
			-- 0 radix 2
			-- 1,2,3,4,5 radix 4												
		K: in std_logic_vector(9 downto 0);
			-- range 0 - 1023 by steps of 2 (Radix-2) or 1 (Radix-4)
			-- Modulos are computed within.
			

		--// COEFFICIENT OUTPUTS
		-- 2's complement, -1 to +1 range
		-- Latency after SAMPLE_CLK_IN is 4 CLK for Cb/Sb, 5 CLK for Cc/Sc and 6 CLK for Cd/Sd
		CB: out std_logic_vector(17 downto 0);
 		CC: out std_logic_vector(17 downto 0);
 		CD: out std_logic_vector(17 downto 0);
 		SB: out std_logic_vector(17 downto 0);
 		SC: out std_logic_vector(17 downto 0);
 		SD: out std_logic_vector(17 downto 0);
		SAMPLE_CLK_OUT: out std_logic
			-- read coefficients at the rising edge of CLK when SAMPLE_CLK_OUT = '1'.
			-- 5 CLKs latency w.r.t. SAMPLE_CLK_IN.

				);
 end entity;
  
architecture BEHAVIOR of FFT_COEFF is   
-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------    
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 SAMPLE_CLK_IN_D: std_logic;
signal SAMPLE_CLK_IN_D2: std_logic;
signal SAMPLE_CLK_IN_D3: std_logic;
signal SAMPLE_CLK_IN_D4: std_logic;

signal P1: std_logic_vector(11 downto 0);
signal P2: std_logic_vector(11 downto 0);
signal P3: std_logic_vector(11 downto 0);
signal P3A: std_logic_vector(11 downto 0);
signal P12: std_logic_vector(11 downto 0);
signal P12A: std_logic_vector(11 downto 0);
signal COS12: std_logic_vector(17 downto 0);
signal SIN12: std_logic_vector(17 downto 0);
signal COS3: std_logic_vector(17 downto 0);
signal SIN3: std_logic_vector(17 downto 0);
signal KPLUS1: std_logic_vector(9 downto 0);      
signal P1_2: std_logic_vector(11 downto 0);
signal P1_4: std_logic_vector(11 downto 0);
signal P1_6: std_logic_vector(11 downto 0);
signal P1_8: std_logic_vector(11 downto 0);
signal P2_2: std_logic_vector(11 downto 0);
signal P2_4: std_logic_vector(11 downto 0);
signal P2_6: std_logic_vector(11 downto 0);
signal P2_8: std_logic_vector(11 downto 0);
signal P3_2: std_logic_vector(11 downto 0);
signal P3_4: std_logic_vector(11 downto 0);
signal P3_6: std_logic_vector(11 downto 0);
signal P3_8: std_logic_vector(11 downto 0);
-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin

--// compute signal clock phase
-- 0,1,2,3,3,3,3,3 until next cycle
CLK_PHASE_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		SAMPLE_CLK_IN_D <= SAMPLE_CLK_IN;
		SAMPLE_CLK_IN_D2 <= SAMPLE_CLK_IN_D;
		SAMPLE_CLK_IN_D3 <= SAMPLE_CLK_IN_D2;
		SAMPLE_CLK_IN_D4 <= SAMPLE_CLK_IN_D3;
	end if;
end process;

-- look ahead for radix2 because of the delay in generating the coefficients
KPLUS1 <= K + 1;	-- for use by radix 2 coefficients. Note: barely does anything since KPLUS is truncated immediately after
                   
-- compute 3*k, beware of the hidden modulus which is different for each stage                   
P1_2 <= "00" & K(7 downto 0) & "00";
P2_2 <= "0" & K(7 downto 0) & "000";       
P3_2 <= P1_2 + P2_2;

P1_4 <= "00" & K(5 downto 0) & "0000";
P2_4 <= "0" & K(5 downto 0) & "00000";       
P3_4 <= P1_4 + P2_4;

P1_6 <= "00" & K(3 downto 0) & "000000";
P2_6 <= "0" & K(3 downto 0) & "0000000";
P3_6 <= P1_6 + P2_6;      

P1_8 <= "00" & K(1 downto 0) & "00000000";
P2_8 <= "0" & K(1 downto 0) & "000000000";
P3_8 <= P1_8 + P2_8;      

-- compute the LUT inputs for each phase. 8 bit each
LUT_INPUT_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SAMPLE_CLK_IN = '1') then
			if(FFT_STAGE = 0) then
				-- radix 2 K/2048, K ranges from 0 to 1023
				P1 <= "0" & K & "0";
				P2 <= "0" & KPLUS1 & "0";
				P3 <= "0" & KPLUS1 & "0";
			elsif (FFT_STAGE = 1) then
				-- radix 4: K/1024, 2K/1024, 3K/1024, K ranges from 0 to 255 
				P1 <= "00" & K(7 downto 0) & "00";
				P2 <= "0" & K(7 downto 0) & "000";
				P3 <= P3_2;
			elsif (FFT_STAGE = 2) then
				-- radix 4: K/256, 2K/256, 3K/256, K ranges from 0 to 63 
				P1 <= "00" & K(5 downto 0) & "0000";
				P2 <= "0" & K(5 downto 0) & "00000";
				P3 <= P3_4;
			elsif (FFT_STAGE = 3) then
				-- radix 4: K/64, 2K/64, 3K/64, K ranges from 0 to 15     
				P1 <= "00" & K(3 downto 0) & "000000";
				P2 <= "0" & K(3 downto 0) & "0000000";
				P3 <= P3_6;
			elsif (FFT_STAGE = 4) then
				-- radix 4: K/16, 2K/16, 3K/16, K ranges from 0 to 3
				P1 <= "00" & K(1 downto 0) & "00000000";
				P2 <= "0" & K(1 downto 0) & "000000000";
				P3 <= P3_8;
			else
				-- radix 4: cos = 1, sin = 0
				P1 <= (others => '0');
				P2 <= (others => '0');
				P3 <= (others => '0');
			end if;
		end if;
	end if;
end process; 

INPUT_MUX_001: process(SAMPLE_CLK_IN_D, P1, P2)
begin
	if(SAMPLE_CLK_IN_D = '1') then
		P12 <= P1;
	else
	--elsif(SAMPLE_CLK_IN_D2 = '1') then
		P12 <= P2;
	end if;
end process;

-- Change sign of angle, depending on FFT or IFFT
P12A <= P12 when (FWD_INVN = '0') else (not(P12) + 1);
P3A <= P3 when (FWD_INVN = '0') else (not(P3) + 1);

-- 2 table to find 3 sin + 3 cos within two CLK clocks   (one clock idle)   
-- Latency is 2 CLK  
SIGNED_SIN_COS_TBL2_001: SIGNED_SIN_COS_TBL2 port map(
	CLK => CLK,
	PHASE => P12A,	-- 12 bit precision
	SIN_OUT => SIN12,	-- 18 bit precision
	COS_OUT => COS12
	);

SIGNED_SIN_COS_TBL2_002: SIGNED_SIN_COS_TBL2 port map(
	CLK => CLK,
	PHASE => P3A,	-- 12 bit precision
	SIN_OUT => SIN3,	-- 18 bit precision
	COS_OUT => COS3
	);

-- Assign sin and cos to outputs
DEMUX_001: process(CLK)
 begin  
 	if rising_edge(CLK) then  
		SAMPLE_CLK_OUT <= SAMPLE_CLK_IN_D4;

		if(SAMPLE_CLK_IN_D3 = '1') then
			CB <= COS12;
			SB <= SIN12;
		elsif(SAMPLE_CLK_IN_D4 = '1') then
			CC <= COS12;
			SC <= SIN12; 
			if(FFT_STAGE = 0) then
				-- special case Radix 2. Use Cd/Sd (make it earlier for timing purposes)
				CD <= COS12;
				SD <= SIN12;
			else
				-- radix 4
				CD <= COS3;
				SD <= SIN3;
			end if;				
		end if;
	end if;
end process;


end BEHAVIOR;