----------------------------------------------
-- MSS copyright 1999-2014
-- filename: FFT_CONTROL2.VHD
-- Authors: 
	-- Mike Yoha/ MSS
	-- Alain Zarembowitch / MSS
-- Edit date: 5/25/14
-- Inheritance: Sirius Satellite Radio receiver, FFT_CONTROL.VHD rev0.012 6-09-01
-- Revision: 4

-- Description: 2048-ppt complex FFT with internal memory.
-- Uses 8 RAMB. User can specify the FFT index associated with 
-- each complex input and output sample, thus allowing any order at the I/O.
-- The component has two phases: a load/unload phase and a FFT computation phase.
-- The maximum FFT computation speed is one FFT every (1050*6) = 6300 CLKs
-- @120 MHz -> 53 us 
-- The minimum FFT load/unload speed is TBD
-- 
-- Comparison with previous code (FFT_CONTROL.VHD):
-- Increased precision for inputs (18 bits), outputs (18 bits), and 
-- internal multipliers (18x18). Only two CLKs per radix-4 butterfly.
--
-- Rev 2 5/12/06 AZ
-- Added bypass mode. 
--
-- Rev 3 8/14/06 AZ
-- No Spectral inversion in bypass mode 
--
-- Rev 4 5/25/14 AZ
-- signals initialization for simulation
-- encapsulated Xilinx RAMB for better portability
-- precision extension from 12 to 18 bit
-- added SYNC_RESET
-- 
--Number of Slice Registers 1297
--Number of Slice LUTs 1529
--Number of Block RAM/FIFO 8
--Number of BUFG/BUFGCTRLs 1
--Number of DSP48A1s 6
-- Xilinx Spartan-6 -3 Minimum period: 5.565ns (Maximum Frequency: 179.683MHz)

----------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity FFT_CONTROL2 is
    port (   
		--// CLOCK
		CLK: in std_logic;	-- reference clock for synchronous designs
		SYNC_RESET: in std_logic;

		--// INPUTS
		DATA_I_IN: in std_logic_vector(17 downto 0);
		DATA_Q_IN: in std_logic_vector(17 downto 0);
		FFT_INDEX_IN: in std_logic_vector(10 downto 0);
			-- FFT index. ranges from 0 to 2047
			-- 0 up to 1023 represent increasing positive frequencies
			-- 2047 downto to 1024 represent decreasing negative frequencies
		SAMPLE_CLK_IN: in std_logic;
			-- this component reads the input data samples and FFT_INDEX at the 
			-- rising edge of CLK when SAMPLE_CLK_IN = '1'
			-- Note that this component does not count the number of input
			-- samples. The user can thus write, overwrite or underwrite 
			-- the input elastic buffer. The user must check the IO_ENABLED flag
			-- before sending data. Data can be sent up to (and excluding) the
			-- FFT_START flag.

		--// CONTROLS
		FWD_INVN: in std_logic;
			-- '1' for forward FFT, '0' for inverse FFT
		FFT_START: in std_logic; 
			-- FFT start pulse. one CLK wide. Start computing an 
			-- FFT based on the input frame already loaded up.
			-- Typically, the user will send this pulse once the previous FFT 
			-- output has been read and the new FFT input data has been uploaded.
		IO_ENABLED: out std_logic;
			-- Flag tells the user whether FFT input samples can be uploaded 
			-- and FFT output data can be downloaded. 
		FFT_SCALE: in std_logic_vector(11 downto 0);
			-- allows the user to adjust the gain at each FFT stage, based
			-- on the overflow information supplied within the OVERFLOW output.
			-- This controls the gain of the next FFT, starting at FFT_START,
			-- whereas the OVERFLOW information relates to the previous FFT.
			-- bits 1-0: FFT stage 0. Shift FFT output 0,1,2,or 3 bits to the left
			-- bits 3-2: FFT stage 1. Shift FFT output 0,1,2,or 3 bits to the left
			-- bits 5-4: FFT stage 2. Shift FFT output 0,1,2,or 3 bits to the left
			-- bits 7-6: FFT stage 3. Shift FFT output 0,1,2,or 3 bits to the left
			-- bits 9-8: FFT stage 4. Shift FFT output 0,1,2,or 3 bits to the left
			-- bits 11-10: FFT stage 5. Shift FFT output 0,1,2,or 3 bits to the left
			-- Read at FFT_START = '1'.
		FFT_BYPASS: in std_logic;
			-- '1' to bypass, '0' to compute the FFT.
			-- The bypass mode is helpful in capturing contiguous data samples,
			-- while using existing RAMB resources.
			-- Note: The FFT still goes through computations, but partial outputs
			-- are not written.
			-- FFT bypass is latched in only at FFT_START, ignore at all other times.
					
		--// FFT OUTPUT 
		DATA_I_OUT: out std_logic_vector(17 downto 0);
		DATA_Q_OUT: out std_logic_vector(17 downto 0);
		FFT_INDEX_OUT: in std_logic_vector(10 downto 0);
			-- FFT index. ranges from 0 to 2047 
		SAMPLE_CLK_OUT: out std_logic;
			-- read DATA_OUT at the rising edge of CLK when SAMPLE_CLK_OUT = '1';
		SAMPLE_CLK_OUT_REQ: in std_logic;
			-- Pulse to request FFT output data.
			-- FFT_INDEX_OUT must be valid when SAMPLE_CLK_OUT_REQ = '1'. 
			-- Latency between SAMPLE_CLK_OUT_REQ and SAMPLE_CLK_OUT is 3 CLKs.

		--// Overflow Monitoring
		OVERFLOW: out std_logic_vector(5 downto 0)
			-- flag indicating overflow for each FFT stage
			-- bit 0 for stage 0, bit 1 for stage 1, etc.
			-- '1' means overflow during anyone of the butterflies for this phase
			-- OVERFLOW contents is valid when IOENABLED = '1' (contents refer
			-- to previous FFT). It is cleared at FFT_START. 


			-- TO DO AZ: CHECK IF IT IS POSSIBLE TO RESET THE INPUT RAMB
			-- SO THAT WE DON'T HAVE TO WASTE TIME LOADING ZEROED CELLS.
			-- !!!!!!!!!!!!!!!!!!!!!!!!!!

);
 end entity;
  
architecture BEHAVIOR of FFT_CONTROL2 is   

-----------------------------------------------------------------
-- COMPONENTS
-----------------------------------------------------------------
	COMPONENT BRAM_DP
	GENERIC(
		DATA_WIDTHA: integer;
		ADDR_WIDTHA: integer;
		DATA_WIDTHB: integer;
		ADDR_WIDTHB: integer
	);
	PORT(
	    CLKA   : in  std_logic;
	    WEA    : in  std_logic;
	    ADDRA  : in  std_logic_vector(ADDR_WIDTHA-1 downto 0);
	    DIA   : in  std_logic_vector(DATA_WIDTHA-1 downto 0);
	    DOA  : out std_logic_vector(DATA_WIDTHA-1 downto 0);
	    CLKB   : in  std_logic;
	    WEB    : in  std_logic;
	    ADDRB  : in  std_logic_vector(ADDR_WIDTHB-1 downto 0);
	    DIB   : in  std_logic_vector(DATA_WIDTHB-1 downto 0);
	    DOB  : out std_logic_vector(DATA_WIDTHB-1 downto 0)
		);
	END COMPONENT;

	COMPONENT FFT_RADIX2_4
	PORT(
		CLK : IN std_logic;
		SAMPLE_CLK_IN : IN std_logic;
		X1A : IN std_logic_vector(17 downto 0);
		X1B : IN std_logic_vector(17 downto 0);
		X1C : IN std_logic_vector(17 downto 0);
		X1D : IN std_logic_vector(17 downto 0);
		Y1A : IN std_logic_vector(17 downto 0);
		Y1B : IN std_logic_vector(17 downto 0);
		Y1C : IN std_logic_vector(17 downto 0);
		Y1D : IN std_logic_vector(17 downto 0);
		CB : IN std_logic_vector(17 downto 0);
		CC : IN std_logic_vector(17 downto 0);
		CD : IN std_logic_vector(17 downto 0);
		SB : IN std_logic_vector(17 downto 0);
		SC : IN std_logic_vector(17 downto 0);
		SD : IN std_logic_vector(17 downto 0);
		OUTPUT_SHIFT : IN std_logic_vector(1 downto 0);
		RADIX2_4N : IN std_logic;          
		X2A : OUT std_logic_vector(17 downto 0);
		X2B : OUT std_logic_vector(17 downto 0);
		X2C : OUT std_logic_vector(17 downto 0);
		X2D : OUT std_logic_vector(17 downto 0);
		Y2A : OUT std_logic_vector(17 downto 0);
		Y2B : OUT std_logic_vector(17 downto 0);
		Y2C : OUT std_logic_vector(17 downto 0);
		Y2D : OUT std_logic_vector(17 downto 0);
		SAMPLE_CLK_OUT : OUT std_logic;
		OVERFLOW : OUT std_logic
		);
	END COMPONENT;


component 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 component;
-----------------------------------------------------------------
-- SIGNALS
-----------------------------------------------------------------
-- Suffix _D indicates a one CLK delayed version of the net with the same name
-- Suffix _E indicates an extended precision version of the net with the same name
-- Suffix _N indicates an inverted version of the net with the same name

--// RECLOCKED SIGNALS
signal DATA_I_IN_D: std_logic_vector(17 downto 0) := (others => '0');
signal DATA_Q_IN_D: std_logic_vector(17 downto 0) := (others => '0'); 
signal SAMPLE_CLK_IN_D : std_logic := '0';

--// BUFFERS -------------------------------------
signal RAMB1_DIA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB1_DIB: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB1_DOA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB1_DOB: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB1234_ADDRA: std_logic_vector(10 downto 0) := (others => '0');
signal RAMB1234_ADDRB: std_logic_vector(10 downto 0) := (others => '0');
signal RAMB1234_WEA: std_logic := '0';
signal RAMB1234_WEB: std_logic := '0';

signal RAMB2_DIA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB2_DIB: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB2_DOA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB2_DOB: std_logic_vector(8 downto 0) := (others => '0');

signal RAMB3_DIA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB3_DIB: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB3_DOA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB3_DOB: std_logic_vector(8 downto 0) := (others => '0');

signal RAMB4_DIA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB4_DIB: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB4_DOA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB4_DOB: std_logic_vector(8 downto 0) := (others => '0');

signal RAMB5_DIA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB5_DIB: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB5_DOA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB5_DOB: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB5678_ADDRA: std_logic_vector(10 downto 0) := (others => '0');
signal RAMB5678_ADDRB: std_logic_vector(10 downto 0) := (others => '0');
signal RAMB5678_WEA: std_logic := '0';
signal RAMB5678_WEB: std_logic := '0';

signal RAMB6_DIA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB6_DIB: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB6_DOA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB6_DOB: std_logic_vector(8 downto 0) := (others => '0');

signal RAMB7_DIA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB7_DIB: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB7_DOA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB7_DOB: std_logic_vector(8 downto 0) := (others => '0');

signal RAMB8_DIA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB8_DIB: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB8_DOA: std_logic_vector(8 downto 0) := (others => '0');
signal RAMB8_DOB: std_logic_vector(8 downto 0) := (others => '0');

--// FFT STAGES (STATE MACHINE) -------------------------------------
signal PHASE_COUNTER: std_logic_vector(11 downto 0) := x"000";
signal PHASE_COUNTER_D4: std_logic_vector(11 downto 0) := (others => '0');
signal PHASE_COUNTER_D11: std_logic_vector(11 downto 0) := (others => '0');
signal FFT_STAGE: std_logic_vector(2 downto 0) := "000";
signal RAMB_ALTERNATE: std_logic := '0';	-- alternate 0/1 every FFT frame
signal RAMB_WRITE_ENABLE: std_logic := '0';
signal RAMB_RW_SELECT: std_logic := '0';
signal RAMB_RPTRA: std_logic_vector(10 downto 0) := (others => '0');
signal RAMB_RPTRB: std_logic_vector(10 downto 0) := (others => '0');
signal RAMB_WPTRA: std_logic_vector(10 downto 0) := (others => '0');
signal RAMB_WPTRB: std_logic_vector(10 downto 0) := (others => '0');
signal FFT_BYPASS_D: std_logic := '0';

--// FFT butterfly interface
signal RADIX2_4N: std_logic := '0';
signal FFT_BUTTERFLY_SAMPLE_CLK_IN: std_logic := '0';
signal X1A: std_logic_vector(17 downto 0) := (others => '0');
signal X1B: std_logic_vector(17 downto 0) := (others => '0');
signal X1C: std_logic_vector(17 downto 0) := (others => '0');
signal X1D: std_logic_vector(17 downto 0) := (others => '0');
signal Y1A: std_logic_vector(17 downto 0) := (others => '0');
signal Y1B: std_logic_vector(17 downto 0) := (others => '0');
signal Y1C: std_logic_vector(17 downto 0) := (others => '0');
signal Y1D: std_logic_vector(17 downto 0) := (others => '0');  

-- SRAM READ DATA, SRAM 1,2,3,4,   FIFO depth 0(latest) - 5(oldest)
signal SRD1_0: std_logic_vector(17 downto 0) := (others => '0');
signal SRD2_0: std_logic_vector(17 downto 0) := (others => '0');
signal SRD3_0: std_logic_vector(17 downto 0) := (others => '0');
signal SRD4_0: std_logic_vector(17 downto 0) := (others => '0');
signal SRD1_1: std_logic_vector(17 downto 0) := (others => '0');
signal SRD2_1: std_logic_vector(17 downto 0) := (others => '0');
signal SRD3_1: std_logic_vector(17 downto 0) := (others => '0');
signal SRD4_1: std_logic_vector(17 downto 0) := (others => '0');  

-- SRAM WRITE DATA

		-- butterfly inputs
		-- (4 elements if radix 4, 2 elements used if radix 2) 
signal X2A: std_logic_vector(17 downto 0) := (others => '0');
signal X2B: std_logic_vector(17 downto 0) := (others => '0');
signal X2C: std_logic_vector(17 downto 0) := (others => '0');
signal X2D: std_logic_vector(17 downto 0) := (others => '0');
signal Y2A: std_logic_vector(17 downto 0) := (others => '0');
signal Y2B: std_logic_vector(17 downto 0) := (others => '0');
signal Y2C: std_logic_vector(17 downto 0) := (others => '0');
signal Y2D: std_logic_vector(17 downto 0) := (others => '0');
	-- fft butterfly outputs


--// COMPUTE FFT COEFFICIENTS ---------------------------------
signal FFT_COEFF_SAMPLE_CLK: std_logic := '0';    
signal CB: std_logic_vector(17 downto 0) := (others => '0');
signal CC: std_logic_vector(17 downto 0) := (others => '0');
signal CD: std_logic_vector(17 downto 0) := (others => '0');
signal SB: std_logic_vector(17 downto 0) := (others => '0');
signal SC: std_logic_vector(17 downto 0) := (others => '0');
signal SD: std_logic_vector(17 downto 0) := (others => '0');
	-- fft butterfly coefficients
signal K: std_logic_vector(9 downto 0) := (others => '0');
	-- sin/cos functions input value      

-- data to be written to RAMB
signal RAMB_WRITE_DATA1: std_logic_vector(17 downto 0) := (others => '0');
signal RAMB_WRITE_DATA2: std_logic_vector(17 downto 0) := (others => '0');
signal RAMB_WRITE_DATA3: std_logic_vector(17 downto 0) := (others => '0');
signal RAMB_WRITE_DATA4: std_logic_vector(17 downto 0) := (others => '0');

-- data read from RAMB
signal RAMB_READ_DATA1: std_logic_vector(17 downto 0) := (others => '0');
signal RAMB_READ_DATA2: std_logic_vector(17 downto 0) := (others => '0');
signal RAMB_READ_DATA3: std_logic_vector(17 downto 0) := (others => '0');
signal RAMB_READ_DATA4: std_logic_vector(17 downto 0) := (others => '0');

signal FFT_BUTTERFLY_SAMPLE_CLK_OUT: std_logic := '0';

-- overflow	& gain control
signal OVERFLOW_LOCAL: std_logic := '0';
signal OUTPUT_SHIFT: std_logic_vector(1 downto 0) := (others => '0');
signal FFT_SCALE_D: std_logic_vector(11 downto 0) := (others => '0');

-- output
signal SAMPLE_CLK_OUT_REQ_D: std_logic := '0';
signal SAMPLE_CLK_OUT_REQ_D2: std_logic := '0';

-----------------------------------------------------------------
-- IMPLEMENTATION
-----------------------------------------------------------------
begin 

--// RECLOCK INPUT DATA and SAMPLE_CLK_IN
RECLOCK_001: process(CLK)
begin       
	if rising_edge(CLK) then
		SAMPLE_CLK_IN_D <= SAMPLE_CLK_IN;
		DATA_I_IN_D <= DATA_I_IN;
		DATA_Q_IN_D <= DATA_Q_IN;
	end if;
end process;

--// BUFFERS -------------------------------------
-- Buffers are used for storing intermediate results while computing
-- an FFT in place, and for storing a complete frame of input samples
-- prior to starting the next FFT (A/B buffer type).
-- Need for storage 2 (A/B) * 2 (I/Q) * 2048 (FFT size) * 18 (bit precision)
-- = 147456 bits = 8 RAMBs.
-- 4*9-bit RAMB are used to store 2*18-bit precision samples.

RAMB16_S9_S9_001: BRAM_DP 
GENERIC MAP(
	DATA_WIDTHA => 9,		
	ADDR_WIDTHA => 11,
	DATA_WIDTHB => 9,		 
	ADDR_WIDTHB => 11

)
PORT MAP(
	CLKA => CLK,
	WEA => RAMB1234_WEA,      -- Port A Write Enable Input
	ADDRA => RAMB1234_ADDRA,  -- Port A 11-bit Address Input
	DIA => RAMB1_DIA,      -- Port A 9-bit Data Input
	DOA => RAMB1_DOA,
	CLKB => CLK,
	WEB => RAMB1234_WEB,
	ADDRB => RAMB1234_ADDRB,  -- Port B 11-bit Address Input
	DIB => RAMB1_DIB,      -- Port B 9-bit Data Input
	DOB => RAMB1_DOB      -- Port B 9-bit Data Output
);

RAMB16_S9_S9_002: BRAM_DP 
GENERIC MAP(
	DATA_WIDTHA => 9,		
	ADDR_WIDTHA => 11,
	DATA_WIDTHB => 9,		 
	ADDR_WIDTHB => 11

)
PORT MAP(
	CLKA => CLK,
	WEA => RAMB1234_WEA,      -- Port A Write Enable Input
	ADDRA => RAMB1234_ADDRA,  -- Port A 11-bit Address Input
	DIA => RAMB2_DIA,      -- Port A 9-bit Data Input
	DOA => RAMB2_DOA,
	CLKB => CLK,
	WEB => RAMB1234_WEB,
	ADDRB => RAMB1234_ADDRB,  -- Port B 11-bit Address Input
	DIB => RAMB2_DIB,      -- Port B 9-bit Data Input
	DOB => RAMB2_DOB      -- Port B 9-bit Data Output
);

RAMB16_S9_S9_003: BRAM_DP 
GENERIC MAP(
	DATA_WIDTHA => 9,		
	ADDR_WIDTHA => 11,
	DATA_WIDTHB => 9,		 
	ADDR_WIDTHB => 11

)
PORT MAP(
	CLKA => CLK,
	WEA => RAMB1234_WEA,      -- Port A Write Enable Input
	ADDRA => RAMB1234_ADDRA,  -- Port A 11-bit Address Input
	DIA => RAMB3_DIA,      -- Port A 9-bit Data Input
	DOA => RAMB3_DOA,
	CLKB => CLK,
	WEB => RAMB1234_WEB,
	ADDRB => RAMB1234_ADDRB,  -- Port B 11-bit Address Input
	DIB => RAMB3_DIB,      -- Port B 9-bit Data Input
	DOB => RAMB3_DOB      -- Port B 9-bit Data Output
);

RAMB16_S9_S9_004: BRAM_DP 
GENERIC MAP(
	DATA_WIDTHA => 9,		
	ADDR_WIDTHA => 11,
	DATA_WIDTHB => 9,		 
	ADDR_WIDTHB => 11

)
PORT MAP(
	CLKA => CLK,
	WEA => RAMB1234_WEA,      -- Port A Write Enable Input
	ADDRA => RAMB1234_ADDRA,  -- Port A 11-bit Address Input
	DIA => RAMB4_DIA,      -- Port A 9-bit Data Input
	DOA => RAMB4_DOA,
	CLKB => CLK,
	WEB => RAMB1234_WEB,
	ADDRB => RAMB1234_ADDRB,  -- Port B 11-bit Address Input
	DIB => RAMB4_DIB,      -- Port B 9-bit Data Input
	DOB => RAMB4_DOB      -- Port B 9-bit Data Output
);

RAMB16_S9_S9_005: BRAM_DP 
GENERIC MAP(
	DATA_WIDTHA => 9,		
	ADDR_WIDTHA => 11,
	DATA_WIDTHB => 9,		 
	ADDR_WIDTHB => 11

)
PORT MAP(
	CLKA => CLK,
	WEA => RAMB5678_WEA,      -- Port A Write Enable Input
	ADDRA => RAMB5678_ADDRA,  -- Port A 11-bit Address Input
	DIA => RAMB5_DIA,      -- Port A 9-bit Data Input
	DOA => RAMB5_DOA,
	CLKB => CLK,
	WEB => RAMB5678_WEB,
	ADDRB => RAMB5678_ADDRB,  -- Port B 11-bit Address Input
	DIB => RAMB5_DIB,      -- Port B 9-bit Data Input
	DOB => RAMB5_DOB      -- Port B 9-bit Data Output
);

RAMB16_S9_S9_006: BRAM_DP 
GENERIC MAP(
	DATA_WIDTHA => 9,		
	ADDR_WIDTHA => 11,
	DATA_WIDTHB => 9,		 
	ADDR_WIDTHB => 11

)
PORT MAP(
	CLKA => CLK,
	WEA => RAMB5678_WEA,      -- Port A Write Enable Input
	ADDRA => RAMB5678_ADDRA,  -- Port A 11-bit Address Input
	DIA => RAMB6_DIA,      -- Port A 9-bit Data Input
	DOA => RAMB6_DOA,
	CLKB => CLK,
	WEB => RAMB5678_WEB,
	ADDRB => RAMB5678_ADDRB,  -- Port B 11-bit Address Input
	DIB => RAMB6_DIB,      -- Port B 9-bit Data Input
	DOB => RAMB6_DOB      -- Port B 9-bit Data Output
);

RAMB16_S9_S9_007: BRAM_DP 
GENERIC MAP(
	DATA_WIDTHA => 9,		
	ADDR_WIDTHA => 11,
	DATA_WIDTHB => 9,		 
	ADDR_WIDTHB => 11

)
PORT MAP(
	CLKA => CLK,
	WEA => RAMB5678_WEA,      -- Port A Write Enable Input
	ADDRA => RAMB5678_ADDRA,  -- Port A 11-bit Address Input
	DIA => RAMB7_DIA,      -- Port A 9-bit Data Input
	DOA => RAMB7_DOA,
	CLKB => CLK,
	WEB => RAMB5678_WEB,
	ADDRB => RAMB5678_ADDRB,  -- Port B 11-bit Address Input
	DIB => RAMB7_DIB,      -- Port B 9-bit Data Input
	DOB => RAMB7_DOB      -- Port B 9-bit Data Output
);

RAMB16_S9_S9_008: BRAM_DP 
GENERIC MAP(
	DATA_WIDTHA => 9,		
	ADDR_WIDTHA => 11,
	DATA_WIDTHB => 9,		 
	ADDR_WIDTHB => 11

)
PORT MAP(
	CLKA => CLK,
	WEA => RAMB5678_WEA,      -- Port A Write Enable Input
	ADDRA => RAMB5678_ADDRA,  -- Port A 11-bit Address Input
	DIA => RAMB8_DIA,      -- Port A 9-bit Data Input
	DOA => RAMB8_DOA,
	CLKB => CLK,
	WEB => RAMB5678_WEB,
	ADDRB => RAMB5678_ADDRB,  -- Port B 11-bit Address Input
	DIB => RAMB8_DIB,      -- Port B 9-bit Data Input
	DOB => RAMB8_DOB      -- Port B 9-bit Data Output
);


--// FFT STAGES (STATE MACHINE) -------------------------------------
-- Keep track of where we are in the FFT:
	-- 0 radix 2 computation
	-- 1,2,3,4,5 radix 4 computation
	-- 6 load/unload.
-- Duration for each stage: 1024 CLKs + processing delay (12 CLKs + margin) = 1050 CLKs
-- The maximum FFT computation speed is one FFT every (1050*6) = 6300 CLKs
-- @90 MHz -> 70 us 
STAGE_PHASE_COUNTER_GEN: process(CLK)
begin       
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			FFT_STAGE <= "110";	-- waiting for input
		elsif(FFT_START = '1') then
			-- marks the start of FFT  
			PHASE_COUNTER <= (others => '0'); 	-- reset phase counter after FFT_START
			FFT_STAGE <= (others => '0');  		-- reset stage to 0 after each FFT_START 
			RAMB_ALTERNATE <= not RAMB_ALTERNATE;	
				-- alternate RAMB sides at each new FFT. Reason: the 2048-ppt FFT has
				-- an even number of processing stages. Therefore, we could not load
				-- and unload data simultaneously if we did not switch at every frame.
			FFT_BYPASS_D <= FFT_BYPASS;
				-- remember the bypass mode.																
		else
			-- Duration for each stage: 1024 CLKs + processing delay + margin = 1050 CLKs
			if (PHASE_COUNTER = 1049) then
				-- Clear counters and go to next FFT stage 
				PHASE_COUNTER <= (others => '0');
				if(FFT_STAGE /= 6) then
					-- move to the next stage. counts from 0 to 6 and stay there
					-- until next FFT_START
					FFT_STAGE <= FFT_STAGE + 1;	
				end if;
		  	elsif(FFT_STAGE /= 6) then
				-- (no need to keep on counting during the load/unload phase)
				PHASE_COUNTER <= PHASE_COUNTER + 1;
			end if;
		end if;
 	end if;
end process;

RADIX2_4N <= '1' when (FFT_STAGE = 0) else '0';
IO_ENABLED <= '1' when (FFT_STAGE = 6) else '0';

-- instead of using 10*11 FFs to delay PHASE_COUNTER, use two delayed counters
PHASE_COUNTER_D4_GEN: process(CLK)
begin       
	if rising_edge(CLK) then
		if(PHASE_COUNTER < 4) then
			PHASE_COUNTER_D4 <= (others => '0');
		else
			PHASE_COUNTER_D4 <= PHASE_COUNTER_D4 + 1;
		end if;
	end if;
end process;

PHASE_COUNTER_D11_GEN: process(CLK)
begin       
	if rising_edge(CLK) then
		if(PHASE_COUNTER < 11) then
			PHASE_COUNTER_D11 <= (others => '0');
		else
			PHASE_COUNTER_D11 <= PHASE_COUNTER_D11 + 1;
		end if;
	end if;
end process;

--// RAMB WRITE/READ POINTERS MANAGEMENT -------------------------------------
ADDR_GEN_001: process(RAMB_RW_SELECT, RAMB_RPTRA, RAMB_RPTRB, RAMB_WPTRA, RAMB_WPTRB)
begin
	if(RAMB_RW_SELECT = '1') then
		-- write to RAMB1-4, read from RAMB5-8
		-- read/write up to two addresses simultaneously using the Dual-port RAM 
		RAMB1234_ADDRA <= RAMB_WPTRA;
		RAMB1234_ADDRB <= RAMB_WPTRB;
		RAMB5678_ADDRA <= RAMB_RPTRA;
		RAMB5678_ADDRB <= RAMB_RPTRB;
	else
		-- read from RAMB1-4, write to RAMB5-8
		RAMB1234_ADDRA <= RAMB_RPTRA;
		RAMB1234_ADDRB <= RAMB_RPTRB;
		RAMB5678_ADDRA <= RAMB_WPTRA;
		RAMB5678_ADDRB <= RAMB_WPTRB;
	end if;
end process;

WEN_GEN_001: process(FFT_STAGE, RAMB_RW_SELECT, SAMPLE_CLK_IN_D,
					RAMB_WRITE_ENABLE, FFT_BYPASS_D)
begin
	if(FFT_STAGE = 6) then
		-- Data input stage. Write only on A side of the dual-port RAM
		if(RAMB_RW_SELECT = '1') then
			-- write to RAMB1-4, read from RAMB5-8
			RAMB1234_WEA <= SAMPLE_CLK_IN_D;	 	-- can be at odd or even addresses
			RAMB1234_WEB <= '0';
			RAMB5678_WEA <= '0';
			RAMB5678_WEB <= '0';
		else 
			-- read from RAMB1-4, write to RAMB5-8
			RAMB1234_WEA <= '0';
			RAMB1234_WEB <= '0';
			RAMB5678_WEA <= SAMPLE_CLK_IN_D;	  -- can be at odd or even addresses
			RAMB5678_WEB <= '0';
		end if;
	elsif(FFT_BYPASS_D = '0') then
		-- FFT computation stages. 
		-- Simultaneously write at odd and even addresses
		if(RAMB_RW_SELECT = '1') then
			-- write to RAMB1-4, read from RAMB5-8
			RAMB1234_WEA <= RAMB_WRITE_ENABLE;	 	-- write to even address 
			RAMB1234_WEB <= RAMB_WRITE_ENABLE;		-- write to odd address
			RAMB5678_WEA <= '0';
			RAMB5678_WEB <= '0';
		else
			-- read from RAMB1-4, write to RAMB5-8
			RAMB1234_WEA <= '0';
			RAMB1234_WEB <= '0';
			RAMB5678_WEA <= RAMB_WRITE_ENABLE;	  -- write to even address
			RAMB5678_WEB <= RAMB_WRITE_ENABLE;	  -- write to odd address
		end if;
	else
		--// bypass mode. Block all writes except during IO_ENABLED.
		RAMB1234_WEA <= '0';
		RAMB1234_WEB <= '0';
		RAMB5678_WEA <= '0';
		RAMB5678_WEB <= '0';
	end if;
end process;

-- Need a signal to block RAMB write cycles before the data is ready (delay due to 
-- (a) reading the butterfly input samples and (b) processing through the butterfly.	
-- Earliest write cycle is at PHASE_COUNTER = 11 (-1 for delay in this process) for radix-2 and radix-4. 
RAMB_WRITE_ENABLE_GEN: process(CLK) 
begin       
	if rising_edge(CLK) then
		if(FFT_STAGE < 6) and (PHASE_COUNTER_D11 <1024) and (PHASE_COUNTER >= 11) then
			RAMB_WRITE_ENABLE <= '1';
		else
			RAMB_WRITE_ENABLE <= '0';
		end if;
	end if;
end process;

--// RAMB READ POINTERS MANAGEMENT -------------------------------------
-- Addressing rules:
-- RAMB_RW_SELECT: 
	-- At each phase, we switch reading from one set of RAMB1-4 or the other set RAMB5-8
	-- memory as specified by RAMB_ALTERNATE.
-- RAMB_RPTR(10 downto 0) = K
-- We are reading K and K+1 at the same time using the -A and -B sides of the dual port RAM
RPTR_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if (FFT_STAGE = 0) then  	
			-- radix-2
			-- Address sequence (0,1 (same clock cycle), (1024, 1025), (2,3),(1026, 1027), etc.
			RAMB_RPTRA(0) <= '0';	-- read even and odd addresses at the same time
			RAMB_RPTRA(9 downto 1) <= PHASE_COUNTER(9 downto 1); 	-- see matlab fftsim k=1:1024/2
			RAMB_RPTRA(10) <= PHASE_COUNTER(0);
			RAMB_RPTRB(0) <= '1';	-- read even and odd addresses at the same time
			RAMB_RPTRB(9 downto 1) <= PHASE_COUNTER(9 downto 1); 	-- see matlab fftsim k=1:1024/2
			RAMB_RPTRB(10) <= PHASE_COUNTER(0);
			RAMB_RW_SELECT <= RAMB_ALTERNATE;	-- has just switched at the start of new frame
		elsif(FFT_STAGE = 1) then  
			-- radix-4
			-- Address sequence (0, 256, 512, 768), (1, 257, 513, 769), etc       
			-- two 1024-ppt FFT, l = 1024/2. see matlab fftsim k=1:256/2
			RAMB_RPTRA(7 downto 0) <= PHASE_COUNTER(8 downto 1);				
			RAMB_RPTRA(8) <= '0';
			RAMB_RPTRA(9) <= PHASE_COUNTER(0);
			RAMB_RPTRA(10) <= PHASE_COUNTER(9);							
			RAMB_RPTRB(7 downto 0) <= PHASE_COUNTER(8 downto 1);	
			RAMB_RPTRB(8) <= '1';
			RAMB_RPTRB(9) <= PHASE_COUNTER(0);
			RAMB_RPTRB(10) <= PHASE_COUNTER(9);							
			RAMB_RW_SELECT <= not RAMB_ALTERNATE;	
		elsif(FFT_STAGE = 2) then
			-- radix-4
			-- Address sequence (0, 64, 128, 192), (1, 65, 129, 193), etc       
			-- eight 256 ppt FFT, l = 256/2. see matlab fftsim k=1:64/2
			RAMB_RPTRA(5 downto 0) <= PHASE_COUNTER(6 downto 1);				
			RAMB_RPTRA(6) <= '0';
			RAMB_RPTRA(7) <= PHASE_COUNTER(0);
			RAMB_RPTRA(10 downto 8) <= PHASE_COUNTER(9 downto 7);							
			RAMB_RPTRB(5 downto 0) <= PHASE_COUNTER(6 downto 1);				
			RAMB_RPTRB(6) <= '1';
			RAMB_RPTRB(7) <= PHASE_COUNTER(0);
			RAMB_RPTRB(10 downto 8) <= PHASE_COUNTER(9 downto 7);							
			RAMB_RW_SELECT <= RAMB_ALTERNATE;	
		elsif(FFT_STAGE = 3) then
			-- radix-4
			-- Address sequence (0, 16, 32, 48), (1, 17, 33, 49), etc       
			-- 32 x 64 ppt FFT, l = 64/2. see matlab fftsim k=1:16/2
			RAMB_RPTRA(3 downto 0) <= PHASE_COUNTER(4 downto 1);				
			RAMB_RPTRA(4) <= '0';
			RAMB_RPTRA(5) <= PHASE_COUNTER(0);
			RAMB_RPTRA(10 downto 6) <= PHASE_COUNTER(9 downto 5);							
			RAMB_RPTRB(3 downto 0) <= PHASE_COUNTER(4 downto 1);				
			RAMB_RPTRB(4) <= '1';
			RAMB_RPTRB(5) <= PHASE_COUNTER(0);
			RAMB_RPTRB(10 downto 6) <= PHASE_COUNTER(9 downto 5);							
			RAMB_RW_SELECT <= not RAMB_ALTERNATE;	
		elsif(FFT_STAGE = 4) then
			-- radix-4
			-- Address sequence (0, 4, 8, 12), (1, 5, 9, 13), etc       
			-- 128 x 16 ppt FFT, l = 16/2. see matlab fftsim k=1:4/2
			RAMB_RPTRA(1 downto 0) <= PHASE_COUNTER(2 downto 1);				
			RAMB_RPTRA(2) <= '0';
			RAMB_RPTRA(3) <= PHASE_COUNTER(0);
			RAMB_RPTRA(10 downto 4) <= PHASE_COUNTER(9 downto 3);							
			RAMB_RPTRB(1 downto 0) <= PHASE_COUNTER(2 downto 1);				
			RAMB_RPTRB(2) <= '1';
			RAMB_RPTRB(3) <= PHASE_COUNTER(0);
			RAMB_RPTRB(10 downto 4) <= PHASE_COUNTER(9 downto 3);							
			RAMB_RW_SELECT <= RAMB_ALTERNATE;	
		elsif(FFT_STAGE = 5) then
			-- radix-4
			-- Address sequence (0, 1, 2, 3), (4, 5, 6, 7), etc       
			RAMB_RPTRA(0) <= '0';
			RAMB_RPTRA(10 downto 1) <= PHASE_COUNTER(9 downto 0);							
			RAMB_RPTRB(0) <= '1';
			RAMB_RPTRB(10 downto 1) <= PHASE_COUNTER(9 downto 0);							
			RAMB_RW_SELECT <= not RAMB_ALTERNATE;	
		elsif (FFT_STAGE = 6) then
			if(SAMPLE_CLK_OUT_REQ = '1') then 	
				if(FFT_BYPASS_D = '0') then  		
					-- Reading is to unload the FFT data
					-- reading one address at a time 
					-- use reshuffled index
					RAMB_RPTRA(0) <= FFT_INDEX_OUT(9); 		-- user-specify read sequence. 
					RAMB_RPTRA(1) <= FFT_INDEX_OUT(10); 		-- user-specify read sequence. 
					RAMB_RPTRA(2) <= FFT_INDEX_OUT(7); 		-- user-specify read sequence. 
					RAMB_RPTRA(3) <= FFT_INDEX_OUT(8); 		-- user-specify read sequence. 
					RAMB_RPTRA(4) <= FFT_INDEX_OUT(5); 		-- user-specify read sequence. 
					RAMB_RPTRA(5) <= FFT_INDEX_OUT(6); 		-- user-specify read sequence. 
					RAMB_RPTRA(6) <= FFT_INDEX_OUT(3); 		-- user-specify read sequence. 
					RAMB_RPTRA(7) <= FFT_INDEX_OUT(4); 		-- user-specify read sequence. 
					RAMB_RPTRA(8) <= FFT_INDEX_OUT(1); 		-- user-specify read sequence. 
					RAMB_RPTRA(9) <= FFT_INDEX_OUT(2); 		-- user-specify read sequence. 
					RAMB_RPTRA(10) <= FFT_INDEX_OUT(0); 		-- user-specify read sequence. 
				else				
					-- FFT bypass mode
					-- do not reshuffle the index in the bypass mode
					-- since no FFT computation took place
					RAMB_RPTRA <= FFT_INDEX_OUT;
				end if;
			end if;
			RAMB_RW_SELECT <= RAMB_ALTERNATE;	
		end if;
	end if;
end process;


--// RAMB WRITE POINTERS MANAGEMENT -------------------------------------
-- Write address. Depends on the stage.
-- Each phase requires 2 (TBC) CLK cycles to read/write data to memory.
-- Same logic for Read and Write addresses, only one counter is delayed to
-- compensate for the processing delay in the Radix-2/4 butterflies.
-- Note that the RAMB_RW_SELECT flag directs read and write to different halves of
-- the memory.
WPTR_GEN_001: process(CLK)
begin 
	if rising_edge(CLK) then
		if (FFT_STAGE = 0) then  	
			-- radix-2 
			-- Address sequence (0,1 (same clock cycle), (1024, 1025), (2,3),(1026, 1027), etc.
			RAMB_WPTRA(0) <= '0'; -- write even and odd addresses at the same time 
			RAMB_WPTRA(9 downto 1) <= PHASE_COUNTER_D11(9 downto 1); 	-- see matlab fftsim k=1:1024/2
			RAMB_WPTRA(10) <= PHASE_COUNTER_D11(0);
			RAMB_WPTRB(0) <= '1'; -- write even and odd addresses at the same time 
			RAMB_WPTRB(9 downto 1) <= PHASE_COUNTER_D11(9 downto 1); 	-- see matlab fftsim k=1:1024/2
			RAMB_WPTRB(10) <= PHASE_COUNTER_D11(0);
		elsif(FFT_STAGE = 1) then  
			-- radix-4
			-- Address sequence (0, 256, 512, 768), (1, 257, 513, 769), etc       
			-- two 1024-ppt FFT, l = 1024/2. see matlab fftsim k=1:256/2
			RAMB_WPTRA(7 downto 0) <= PHASE_COUNTER_D11(8 downto 1);				
			RAMB_WPTRA(8) <= '0';
			RAMB_WPTRA(9) <= PHASE_COUNTER_D11(0);
			RAMB_WPTRA(10) <= PHASE_COUNTER_D11(9);							
			RAMB_WPTRB(7 downto 0) <= PHASE_COUNTER_D11(8 downto 1);	
			RAMB_WPTRB(8) <= '1';
			RAMB_WPTRB(9) <= PHASE_COUNTER_D11(0);
			RAMB_WPTRB(10) <= PHASE_COUNTER_D11(9);							
		elsif(FFT_STAGE = 2) then
			-- radix-4
			-- Address sequence (0, 64, 128, 192), (1, 65, 129, 193), etc       
			-- eight 256 ppt FFT, l = 256/2. see matlab fftsim k=1:64/2
			RAMB_WPTRA(5 downto 0) <= PHASE_COUNTER_D11(6 downto 1);				
			RAMB_WPTRA(6) <= '0';
			RAMB_WPTRA(7) <= PHASE_COUNTER_D11(0);
			RAMB_WPTRA(10 downto 8) <= PHASE_COUNTER_D11(9 downto 7);							
			RAMB_WPTRB(5 downto 0) <= PHASE_COUNTER_D11(6 downto 1);				
			RAMB_WPTRB(6) <= '1';
			RAMB_WPTRB(7) <= PHASE_COUNTER_D11(0);
			RAMB_WPTRB(10 downto 8) <= PHASE_COUNTER_D11(9 downto 7);							
		elsif(FFT_STAGE = 3) then
			-- radix-4
			-- Address sequence (0, 16, 32, 48), (1, 17, 33, 49), etc       
			-- 32 x 64 ppt FFT, l = 64/2. see matlab fftsim k=1:16/2
			RAMB_WPTRA(3 downto 0) <= PHASE_COUNTER_D11(4 downto 1);				
			RAMB_WPTRA(4) <= '0';
			RAMB_WPTRA(5) <= PHASE_COUNTER_D11(0);
			RAMB_WPTRA(10 downto 6) <= PHASE_COUNTER_D11(9 downto 5);							
			RAMB_WPTRB(3 downto 0) <= PHASE_COUNTER_D11(4 downto 1);				
			RAMB_WPTRB(4) <= '1';
			RAMB_WPTRB(5) <= PHASE_COUNTER_D11(0);
			RAMB_WPTRB(10 downto 6) <= PHASE_COUNTER_D11(9 downto 5);							
		elsif(FFT_STAGE = 4) then
			-- radix-4
			-- Address sequence (0, 4, 8, 12), (1, 5, 9, 13), etc       
			-- 128 x 16 ppt FFT, l = 16/2. see matlab fftsim k=1:4/2
			RAMB_WPTRA(1 downto 0) <= PHASE_COUNTER_D11(2 downto 1);				
			RAMB_WPTRA(2) <= '0';
			RAMB_WPTRA(3) <= PHASE_COUNTER_D11(0);
			RAMB_WPTRA(10 downto 4) <= PHASE_COUNTER_D11(9 downto 3);							
			RAMB_WPTRB(1 downto 0) <= PHASE_COUNTER_D11(2 downto 1);				
			RAMB_WPTRB(2) <= '1';
			RAMB_WPTRB(3) <= PHASE_COUNTER_D11(0);
			RAMB_WPTRB(10 downto 4) <= PHASE_COUNTER_D11(9 downto 3);							
		elsif(FFT_STAGE = 5) then
			-- radix-4
			-- Address sequence (0, 1, 2, 3), (4, 5, 6, 7), etc       
			RAMB_WPTRA(0) <= '0';
			RAMB_WPTRA(10 downto 1) <= PHASE_COUNTER_D11(9 downto 0);							
			RAMB_WPTRB(0) <= '1';
			RAMB_WPTRB(10 downto 1) <= PHASE_COUNTER_D11(9 downto 0);							
		else 
		--if (FFT_STAGE = 6) then
			-- FFT input data upload
			RAMB_WPTRA(10 downto 0) <=  FFT_INDEX_IN; 
		end if;
	end if;
end process;

--// RAMB INPUT DATA PATH MULTIPLEXERS -------------------------------------
-- Combine 2*12-bit precision complex samples into 3*8 bit words
-- Select the RAMB inputs depending on the FFT phase.
RAMB_DIA_SELECT: process(RAMB_RW_SELECT, FFT_STAGE, DATA_I_IN_D, DATA_Q_IN_D,
	RAMB_WRITE_DATA1, RAMB_WRITE_DATA2, RAMB_WRITE_DATA3, RAMB_WRITE_DATA4)
begin
	if(RAMB_RW_SELECT = '1') then
		-- write to RAMB1-4, read from RAMB5-8
		-- read/write up to two addresses simultaneously using the Dual-port RAM 
		if(FFT_STAGE = 6) then
			-- external input
			RAMB1_DIA <= DATA_I_IN_D(8 downto 0);
			RAMB2_DIA <= DATA_I_IN_D(17 downto 9);
			RAMB3_DIA <= DATA_Q_IN_D(8 downto 0);
			RAMB4_DIA <= DATA_Q_IN_D(17 downto 9);
			-- nothing else written
			RAMB5_DIA <= (others => '0');
			RAMB6_DIA <= (others => '0');
			RAMB7_DIA <= (others => '0');
			RAMB8_DIA <= (others => '0');
			RAMB1_DIB <= (others => '0');
			RAMB2_DIB <= (others => '0');
			RAMB3_DIB <= (others => '0');
			RAMB4_DIB <= (others => '0');
			RAMB5_DIB <= (others => '0');
			RAMB6_DIB <= (others => '0');
			RAMB7_DIB <= (others => '0');
			RAMB8_DIB <= (others => '0');
		else
			-- write result from radix2/4 processing
			RAMB1_DIA <= RAMB_WRITE_DATA1(8 downto 0);
			RAMB2_DIA <= RAMB_WRITE_DATA1(17 downto 9);
			RAMB3_DIA <= RAMB_WRITE_DATA2(8 downto 0);
			RAMB4_DIA <= RAMB_WRITE_DATA2(17 downto 9);
			RAMB5_DIA <= (others => '0');
			RAMB6_DIA <= (others => '0');
			RAMB7_DIA <= (others => '0');
			RAMB8_DIA <= (others => '0');
			RAMB1_DIB <= RAMB_WRITE_DATA3(8 downto 0);
			RAMB2_DIB <= RAMB_WRITE_DATA3(17 downto 9);
			RAMB3_DIB <= RAMB_WRITE_DATA4(8 downto 0);
			RAMB4_DIB <= RAMB_WRITE_DATA4(17 downto 9);
			RAMB5_DIB <= (others => '0');
			RAMB6_DIB <= (others => '0');
			RAMB7_DIB <= (others => '0');
			RAMB8_DIB <= (others => '0');
		end if;
	else
		if(FFT_STAGE = 6) then
			-- external input
			RAMB5_DIA <= DATA_I_IN_D(8 downto 0);
			RAMB6_DIA <= DATA_I_IN_D(17 downto 9);
			RAMB7_DIA <= DATA_Q_IN_D(8 downto 0);
			RAMB8_DIA <= DATA_Q_IN_D(17 downto 9);
			-- nothing else written
			RAMB1_DIA <= (others => '0');
			RAMB2_DIA <= (others => '0');
			RAMB3_DIA <= (others => '0');
			RAMB4_DIA <= (others => '0');
			RAMB5_DIB <= (others => '0');
			RAMB6_DIB <= (others => '0');
			RAMB7_DIB <= (others => '0');
			RAMB8_DIB <= (others => '0');
			RAMB1_DIB <= (others => '0');
			RAMB2_DIB <= (others => '0');
			RAMB3_DIB <= (others => '0');
			RAMB4_DIB <= (others => '0');
		else
			-- write result from radix2/4 processing
			RAMB1_DIA <= (others => '0');
			RAMB2_DIA <= (others => '0');
			RAMB3_DIA <= (others => '0');
			RAMB4_DIA <= (others => '0');
			RAMB5_DIA <= RAMB_WRITE_DATA1(8 downto 0);
			RAMB6_DIA <= RAMB_WRITE_DATA1(17 downto 9);
			RAMB7_DIA <= RAMB_WRITE_DATA2(8 downto 0);
			RAMB8_DIA <= RAMB_WRITE_DATA2(17 downto 9);
			RAMB1_DIB <= (others => '0');
			RAMB2_DIB <= (others => '0');
			RAMB3_DIB <= (others => '0');
			RAMB4_DIB <= (others => '0');
			RAMB5_DIB <= RAMB_WRITE_DATA3(8 downto 0);
			RAMB6_DIB <= RAMB_WRITE_DATA3(17 downto 9);
			RAMB7_DIB <= RAMB_WRITE_DATA4(8 downto 0);
			RAMB8_DIB <= RAMB_WRITE_DATA4(17 downto 9);
		end if;
	end if;
end process;

--// RAMB OUTPUT DATA PATH DEMULTIPLEXERS -------------------------------------
-- Reconstruct original 2*12-bit precision complex samples from 3*8 bit words
-- Select the RAMB outputs depending on the FFT phase.
RAMB_DOA_SELECT: process(RAMB_RW_SELECT, RAMB1_DOA, RAMB2_DOA, RAMB3_DOA,
	RAMB5_DOA, RAMB6_DOA, RAMB7_DOA, RAMB1_DOB, RAMB2_DOB, RAMB3_DOB, 
	RAMB5_DOB, RAMB6_DOB, RAMB7_DOB)
begin
	if(RAMB_RW_SELECT = '1') then
		-- read from RAMB5-8
		-- read up to two addresses simultaneously using the Dual-port RAM 
		RAMB_READ_DATA1 <= RAMB6_DOA & RAMB5_DOA;
		RAMB_READ_DATA2 <= RAMB8_DOA & RAMB7_DOA;

		RAMB_READ_DATA3 <= RAMB6_DOB & RAMB5_DOB;
		RAMB_READ_DATA4 <= RAMB8_DOB & RAMB7_DOB;

	else
		-- read from RAMB1-4
		-- read up to two addresses simultaneously using the Dual-port RAM 
		RAMB_READ_DATA1 <= RAMB2_DOA & RAMB1_DOA;
		RAMB_READ_DATA2 <= RAMB4_DOA & RAMB3_DOA;

		RAMB_READ_DATA3 <= RAMB2_DOB & RAMB1_DOB;
		RAMB_READ_DATA4 <= RAMB4_DOB & RAMB3_DOB;
	end if;
end process;

-- Read 2 complex samples from RAMB at rising edge of CLK 
-- Push into STACK
-- Adjust gain at FFT stages TBD and TBD
BUTTERFLY_INPUT_001: process(CLK)
begin       
	if rising_edge(CLK) then
		-- over the next 2 CLKs, read 4 complex samples from the SRAM
		-- Radix-2: read 4 complex samples as input to two radix-2 butterflies
		-- Radix-4: read 4 complex samples as input to a radix-4 butterfly
		-- Push into complex samples STACK

		-- TOP OF STACK
		SRD1_0 <= RAMB_READ_DATA1;	-- real even
		SRD2_0 <= RAMB_READ_DATA2;	-- imaginary even
		SRD3_0 <= RAMB_READ_DATA3;	-- real odd
		SRD4_0 <= RAMB_READ_DATA4;	-- imaginary odd

		SRD1_1 <= SRD1_0;
		SRD2_1 <= SRD2_0;
		SRD3_1 <= SRD3_0;
		SRD4_1 <= SRD4_0;

	end if;    
end process;

OVERFLOW_001: process(CLK)
begin       
	if rising_edge(CLK) then
		if(FFT_START = '1') then
			-- clear 
		   OVERFLOW <= (others => '0');
		elsif(FFT_BUTTERFLY_SAMPLE_CLK_OUT = '1') and (OVERFLOW_LOCAL = '1') then
			-- Overflow detection. Valid only when flag at FFT_BUTTERFLY_SAMPLE_CLK_OUT = '1'
			if(FFT_STAGE = 0) then 
				OVERFLOW(0) <= '1';
			end if;
			if(FFT_STAGE = 1) then 
				OVERFLOW(1) <= '1';
			end if;
			if(FFT_STAGE = 2) then 
				OVERFLOW(2) <= '1';
			end if;
			if(FFT_STAGE = 3) then 
				OVERFLOW(3) <= '1';
			end if;
			if(FFT_STAGE = 4) then 
				OVERFLOW(4) <= '1';
			end if;
			if(FFT_STAGE = 5) then 
				OVERFLOW(5) <= '1';
			end if;
		end if;
	end if;
end process;

-- Condition FFT butterfly inputs
-- X,Y samples have to be ready at PHASE_COUNTER_D5 to be aligned with the coefficients.
FFT_BUTTERFLY_INPUT_001: process(CLK)
begin       
	if rising_edge(CLK) then  
		if (RADIX2_4N = '1') and (PHASE_COUNTER_D4(0) = '0') then
			-- radix2 FFT
			-- 2 butterflies computed every 2 CLKs
			-- 	1st Radix2 butterfly inputs (X1A + jY1A) and (X1B + jY1B)
			X1A <= SRD1_1;	-- (oldest in stack) complex sample read from RAM 0,2,..
			Y1A <= SRD2_1;
			X1B <= SRD1_0;	-- (newest in stack) complex sample read from RAM 1024,1026,...
			Y1B <= SRD2_0;
			-- 	2nd Radix2 butterfly inputs (X1C + jY1C) and (X1D + jY1D)
			X1C <= SRD3_1;	-- (oldest in stack) complex sample read from RAM 1,3,.
			Y1C <= SRD4_1;
			X1D <= SRD3_0; -- (newest in stack) complex sample read from RAM 1025,1027,...
			Y1D <= SRD4_0;
		elsif (RADIX2_4N = '0') and (PHASE_COUNTER_D4(0) = '0') 
					and (FFT_STAGE /= 6) then
			-- radix4 FFT based on the last 4 complex samples read from RAM
			X1A <= SRD1_1;	-- (oldest in stack) complex samples read from RAM 0,256,..
			Y1A <= SRD2_1;
			X1B <= SRD3_1; 
			Y1B <= SRD4_1;
			X1C <= SRD1_0;  -- latest complex samples read from RAM 512, 768
			Y1C <= SRD2_0;
			X1D <= SRD3_0; 			
			Y1D <= SRD4_0;
		end if;
	end if;    
end process;

--// COMPUTE FFT COEFFICIENTS ---------------------------------
-- 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)
-- Cc = cos(2*pi*3*k/1024), Sc = 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)
-- Cc = cos(2*pi*3*k/256), Sc = 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)
-- Cc = cos(2*pi*3*k/64), Sc = 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)
-- Cc = cos(2*pi*3*k/16), Sc = 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

-- K goes from 0 to 1023 by steps of 2 (Radix-2) or 1 (Radix-4)
-- One computation every 2 CLKs
K <= PHASE_COUNTER(10 downto 1) when (RADIX2_4N = '0') else PHASE_COUNTER(9 downto 0); 

-- Call FFT_COEFF and FFT_RADIX2_4 512 times during each FFT phase, 
-- once every 2 CLKs. 
FFT_COEFF_SAMPLE_CLK <= '1' when (PHASE_COUNTER(0) = '0') and (FFT_STAGE < 6) 
							and (PHASE_COUNTER(10) = '0') else '0';

-- Latency is 5 CLKs.
-- Coefficients for inverse FFT (IFFT)
FFT_COEFF_001: FFT_COEFF port map(
	CLK => CLK,
	SAMPLE_CLK_IN => FFT_COEFF_SAMPLE_CLK, 
	FWD_INVN => FWD_INVN,
	FFT_STAGE => FFT_STAGE, 
	K => K, 		
	CB => CB, 
	CC => CC, 
	CD => CD, 
	SB => SB, 
	SC => SC, 
	SD => SD,
	SAMPLE_CLK_OUT => FFT_BUTTERFLY_SAMPLE_CLK_IN
);

-- FFT gain under user control
OUTPUT_SHIFT_GEN: process(CLK)
begin
	if rising_edge(CLK) then
		if(FFT_START = '1') then
			-- freeze the scaling requirements at FFT start.
			FFT_SCALE_D <= FFT_SCALE;
		end if;
	end if;
end process;

process(FFT_STAGE, FFT_SCALE_D)
begin
	if(FFT_STAGE = 0) then
		OUTPUT_SHIFT <= FFT_SCALE_D(1 downto 0);
	elsif(FFT_STAGE = 1) then
		OUTPUT_SHIFT <= FFT_SCALE_D(3 downto 2);
	elsif(FFT_STAGE = 2) then
		OUTPUT_SHIFT <= FFT_SCALE_D(5 downto 4);
	elsif(FFT_STAGE = 3) then
		OUTPUT_SHIFT <= FFT_SCALE_D(7 downto 6);
	elsif(FFT_STAGE = 4) then
		OUTPUT_SHIFT <= FFT_SCALE_D(9 downto 8);
	else
	--elsif(FFT_STAGE = 5) then
		OUTPUT_SHIFT <= FFT_SCALE_D(11 downto 10);
	end if;	
end process;
		
-- Implement FFT butterfly
-- Compute a new set of FFT butterflies every 2 CLKs 
-- (X1 + jY1) inputs, (X2 + jY2) for outputs
-- Latency is 6 CLKs
FFT_RADIX2_4_001: FFT_RADIX2_4 port map (CLK => CLK, 
		SAMPLE_CLK_IN => FFT_BUTTERFLY_SAMPLE_CLK_IN,
		X1A => X1A, X1B => X1B, X1C => X1C, X1D => X1D, 
		Y1A => Y1A, Y1B => Y1B, Y1C => Y1C, Y1D => Y1D, 
		RADIX2_4N => RADIX2_4N,
        CB => CB, CC => CC, CD => CD, SB => SB, SC => SC, SD => SD,
		OUTPUT_SHIFT => OUTPUT_SHIFT,
		X2A => X2A, X2B => X2B, X2C => X2C, X2D => X2D,  
        Y2A => Y2A, Y2B => Y2B, Y2C => Y2C, Y2D => Y2D, 
		SAMPLE_CLK_OUT => FFT_BUTTERFLY_SAMPLE_CLK_OUT,
		OVERFLOW => OVERFLOW_LOCAL
);

-- Write data to memory
FFT_OUT_002: process(CLK)
begin
	if rising_edge(CLK) then
		if(FFT_STAGE = 0) then
			-- radix 2
			-- Data for successive RAMB addresses k, k+1 are available at the same time.
			if (PHASE_COUNTER_D11(0) = '0') then  	
				-- same as FFT_BUTTERFLY_SAMPLE_CLK_OUT = '1'
				RAMB_WRITE_DATA1 <= X2A; -- real even
				RAMB_WRITE_DATA2 <= Y2A; -- imaginary even
				RAMB_WRITE_DATA3 <= X2C; -- real odd
				RAMB_WRITE_DATA4 <= Y2C; -- imaginary odd
			else  
				-- one CLK later....
				-- FFT output is 'frozen' for two CLKs. Thus, no need to reclock
				RAMB_WRITE_DATA1 <= X2B; -- real even
				RAMB_WRITE_DATA2 <= Y2B; -- imaginary even
				RAMB_WRITE_DATA3 <= X2D; -- real odd
				RAMB_WRITE_DATA4 <= Y2D; -- imaginary odd 
			end if;
		elsif(FFT_STAGE < 6) then
			-- radix 4
			if (PHASE_COUNTER_D11(0) = '0') then
				-- same as FFT_BUTTERFLY_SAMPLE_CLK_OUT = '1'
				RAMB_WRITE_DATA1 <= X2A; 
				RAMB_WRITE_DATA2 <= Y2A; 
				RAMB_WRITE_DATA3 <= X2B; 
				RAMB_WRITE_DATA4 <= Y2B; 
			else
				-- one CLK later....
				RAMB_WRITE_DATA1 <= X2C; 
				RAMB_WRITE_DATA2 <= Y2C; 
				RAMB_WRITE_DATA3 <= X2D; 
				RAMB_WRITE_DATA4 <= Y2D; 
			end if;
	  	end if;
	end if;
end process;

--// FFT OUTPUT -------------------------------------
-- Reconstruct original 2*18-bit precision complex samples from 4*9 bit words
-- Select the RAMB outputs depending on the FFT phase.
-- Only use the A side of the RAMB here.
FFT_OUTPUT_001: process(CLK)
begin
	if rising_edge(CLK) then
		-- block FFT output read requests if not in FFT_STAGE 6
		if(FFT_STAGE = 6) then
			SAMPLE_CLK_OUT_REQ_D <= SAMPLE_CLK_OUT_REQ;
		else
			SAMPLE_CLK_OUT_REQ_D <= '0';
		end if;
		SAMPLE_CLK_OUT_REQ_D2 <= SAMPLE_CLK_OUT_REQ_D;
		SAMPLE_CLK_OUT <= SAMPLE_CLK_OUT_REQ_D2;
        
        if(SAMPLE_CLK_OUT_REQ_D2 = '1') then    
        	-- update output only if valid output sample (i.e. not during FFT intermediate 
        	-- computation stages).
			if(RAMB_RW_SELECT = '1') then
				-- read from RAMB5-8
				DATA_I_OUT <= RAMB6_DOA & RAMB5_DOA;
				if(FFT_BYPASS_D = '1') then				
					DATA_Q_OUT <= RAMB8_DOA & RAMB7_DOA;
					-- no spectral inversion for bypass mode
				else 
					DATA_Q_OUT <= not(RAMB8_DOA & RAMB7_DOA) + 1;
					-- spectral inversion
				end if;
			else
				-- read from RAMB1-4
				DATA_I_OUT <= RAMB2_DOA & RAMB1_DOA;
				if(FFT_BYPASS_D = '1') then				
					DATA_Q_OUT <= RAMB4_DOA & RAMB3_DOA;
					-- no spectral inversion for bypass mode
				else 
					DATA_Q_OUT <= not(RAMB4_DOA & RAMB3_DOA) + 1;
					-- spectral inversion
				end if;
			end if;
		end if;
	end if;
end process;



end  BEHAVIOR;