library ieee;
use ieee.std_logic_1164.all; 
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity test_FFT_CONTROL2 is end test_FFT_CONTROL2;

architecture testbench of test_FFT_CONTROL2 is

-- Declaration of the component under test
component RECEIVER_1
  port (
     SAMPLE_CLK_OUT : out std_logic;
     DATA_Q_OUT : out std_logic_vector(11 downto 0);
     DATA_I_OUT : out std_logic_vector(11 downto 0);
     AGC_DAC_SAMPLE_CLK : out std_logic;
     AGC_DAC : out std_logic_vector(9 downto 0);
     REG8 : in std_logic_vector(7 downto 0);
     REG5 : in std_logic_vector(7 downto 0);
     REG4 : in std_logic_vector(7 downto 0);
     REG3 : in std_logic_vector(7 downto 0);
     REG2 : in std_logic_vector(7 downto 0);
     REG1 : in std_logic_vector(7 downto 0);
     REG0 : in std_logic_vector(7 downto 0);
     SAMPLE_CLK_IN : in std_logic;
     DATA_Q_IN : in std_logic_vector(11 downto 0);
     DATA_I_IN : in std_logic_vector(11 downto 0);
     CLK : in std_logic
      );
end component;

component FFT_CONTROL2
  port (
     OVERFLOW : out std_logic_vector(5 downto 0);
     SAMPLE_CLK_OUT_REQ : in std_logic;
     SAMPLE_CLK_OUT : out std_logic;
     FFT_INDEX_OUT : in std_logic_vector(10 downto 0);
     DATA_Q_OUT : out std_logic_vector(11 downto 0);
     DATA_I_OUT : out std_logic_vector(11 downto 0);
     FFT_BYPASS: in std_logic;
     FFT_SCALE : in std_logic_vector(11 downto 0);
     IO_ENABLED : out std_logic;
     FFT_START : in std_logic;
     FWD_INVN : in std_logic;
     SAMPLE_CLK_IN : in std_logic;
     FFT_INDEX_IN : in std_logic_vector(10 downto 0);
     DATA_Q_IN : in std_logic_vector(11 downto 0);
     DATA_I_IN : in std_logic_vector(11 downto 0);
     CLK : in std_logic
      );
end component;

component HDLC_BYTE_ENC is
    port ( 
		--GLOBAL CLOCKS, RESET
	   CLK: in std_logic;	
		ASYNC_RESET: in std_logic;	-- asynchronous reset

		--// Inputs
		DATA_IN: in std_logic_vector(23 downto 0);                 
		SAMPLE_CLK_IN: in std_logic;
			--read DATA_IN at the rising edge of CLK when SAMPLE_CLK_IN = '1'
		SAMPLE_CLK_IN_REQ: out std_logic;
			-- '1' when the input buffer still has room for more

		SOF: in std_logic;           
			-- should be alligned with SAMPLE_CLK_IN.
		EOF: in std_logic;           
			-- should be alligned with SAMPLE_CLK_IN.
		FRAME_TYPE: in std_logic_vector(15 downto 0); 
			-- MAKE SURE IT IS NOT 7E OR 7D

		--// Output: bytewise -- hdlc byte encoded 
		DATA_OUT: out std_logic_vector(7 downto 0);
		SAMPLE_CLK_OUT: out std_logic;
		SAMPLE_CLK_OUT_REQ: in std_logic;
		INTERNAL_BUFFERS_EMPTY: out std_logic
			-- '1' when both input and output buffers are empty
	);
end component;

signal   AGC_DAC_SAMPLE_CLK : std_logic;
signal   AGC_DAC : std_logic_vector(9 downto 0);
signal   REG8 : std_logic_vector(7 downto 0);
signal   REG5 : std_logic_vector(7 downto 0);
signal   REG4 : std_logic_vector(7 downto 0);
signal   REG3 : std_logic_vector(7 downto 0);
signal   REG2 : std_logic_vector(7 downto 0);
signal   REG1 : std_logic_vector(7 downto 0);
signal   REG0 : std_logic_vector(7 downto 0);
signal   SAMPLE2_CLK : std_logic:='0';
signal   DATA2_Q : std_logic_vector(11 downto 0):= (others => '0');
signal   DATA2_I : std_logic_vector(11 downto 0):= (others => '0');

--// RESAMPLING 
signal DATA3_I_A: std_logic_vector(11 downto 0);
signal DATA3_Q_A: std_logic_vector(11 downto 0);
signal DATA3_I_B: std_logic_vector(11 downto 0);
signal DATA3_Q_B: std_logic_vector(11 downto 0);
signal DATA3_I_A_D: std_logic_vector(11 downto 0);
signal DATA3_Q_A_D: std_logic_vector(11 downto 0);
signal DATA3_I_B_D: std_logic_vector(11 downto 0);
signal DATA3_Q_B_D: std_logic_vector(11 downto 0);
signal A_BN: std_logic := '0';
signal A_BN_DF: std_logic;
signal A_BN_DF_D: std_logic;
signal A_BN_DF_D2: std_logic;
signal DATA3_I: std_logic_vector(11 downto 0);
signal DATA3_Q: std_logic_vector(11 downto 0);
signal SAMPLE3_CLK: std_logic;
signal   SAMPLE3A_CLK : std_logic:='0';

signal   FFT_OVERFLOW : std_logic_vector(5 downto 0):= (others => '0');
signal SOF4: std_logic;
signal EOF4: std_logic;
signal SOF4_D: std_logic;
signal EOF4_D: std_logic;
signal SOF4_D2: std_logic;
signal EOF4_D2: std_logic;
signal   DATA4_Q : std_logic_vector(11 downto 0):= (others => '0');
signal   DATA4_I : std_logic_vector(11 downto 0):= (others => '0');
signal SAMPLE4_CLK: std_logic;
signal   SAMPLE4_CLK_REQ : std_logic:='0';
signal   FFT_INDEX_OUT : std_logic_vector(11 downto 0):= (others => '0');
signal   FFT_INDEX_IN : std_logic_vector(11 downto 0):= (others => '0');
signal   ASYNC_RESET : std_logic;
signal   CLK_P : std_logic;
signal   CLK_ING : std_logic;
signal   FFT_IO_ENABLED : std_logic:='0';
signal   FFT_BYPASS : std_logic:='0';
signal   FFT_SCALE : std_logic_vector(11 downto 0):= (others => '0');
signal   FFT_START : std_logic:='1';
signal   FFT_START_CAPTURE : std_logic:='0';
signal   FFT_START_CAPTURE_FLAG : std_logic:='0';
signal FFT_INDEX_OUT_MSB_D: std_logic;
signal FFT_INDEX_OUT_MSB_D2: std_logic;
signal FFT_INDEX_OUT_MSB_D3: std_logic;

--// FFT POST PROCESSING
--// POLAR3
signal REG16: std_logic_vector(7 downto 0):= x"01";
signal DATA5_M: std_logic_vector(13 downto 0);
signal DATA5_P: std_logic_vector(10 downto 0);
signal SAMPLE5_CLK: std_logic;
signal SOF5: std_logic;
signal EOF5: std_logic;

signal SOF4_D3: std_logic;
signal SOF4_D4: std_logic;
signal SOF4_D5: std_logic;
signal SOF4_D6: std_logic;

signal EOF4_D3: std_logic;
signal EOF4_D4: std_logic;
signal EOF4_D5: std_logic;
signal EOF4_D6: std_logic;

signal FFT_OVERFLOW_D: std_logic_vector(5 downto 0);
signal FFT_OVERFLOW_D2: std_logic_vector(5 downto 0);
signal FFT_OVERFLOW_D3: std_logic_vector(5 downto 0);
signal FFT_OVERFLOW_D4: std_logic_vector(5 downto 0);

signal FFT_START_D: std_logic;
signal FFT_START_D2: std_logic;
signal FFT_START_D3: std_logic;
signal FFT_START_D4: std_logic;

--// FRAME SUBSAMPLING MECHANISM 
signal DATA7: std_logic_vector(23 downto 0);
signal SAMPLE7_CLK: std_logic;
signal SOF7: std_logic;
signal EOF7: std_logic;
signal SAMPLE7_CLK_REQ: std_logic;
signal DECIMATE_ON: std_logic;

--// HDLC SIGNALS
signal HDLC_DATA_IN: std_logic_vector(23 downto 0);
signal HDLC_SAMPLE_CLK_IN: std_logic;
signal HDLC_SOF: std_logic;
signal HDLC_EOF: std_logic;
signal HDLC_SAMPLE_CLK_IN_REQ: std_logic;
signal HDLC_BUFFERS_EMPTY: std_logic;
signal HDLC_FRAME_TYPE: std_logic_vector(15 downto 0);

--// USB Interface High Speed data transfer
signal USB_DATA: std_logic_vector(7 downto 0);
signal USB_SAMPLE_CLK: std_logic;
signal USB_SAMPLE_CLK_REQ: std_logic;
signal USB_BUFFER_EMPTY: std_logic;

begin

-- Place stimulus and analysis statements here   
process
begin
	ASYNC_RESET <= '1';
	wait for 2 ns;
	ASYNC_RESET <= '0';
	wait;
end process;

process
begin
	CLK_P <= '1';
	wait for 6 ns;
	CLK_P <= '0';
	wait for 6 ns;
end process;

process
begin
	CLK_ING <= '1';
	wait for 12 ns;
	CLK_ING <= '0';
	wait for 12 ns;
end process;

REG0 <= (others => '0');
REG1 <= (others => '0');
REG2 <= (others => '0');
REG3 <= x"01";
REG4 <= x"01";
REG5 <= (others => '0');
REG8 <= (others => '0');

dut :  RECEIVER_1  port map (
     SAMPLE_CLK_OUT => SAMPLE2_CLK,
     DATA_Q_OUT => DATA2_Q,
     DATA_I_OUT => DATA2_I,
     AGC_DAC_SAMPLE_CLK => AGC_DAC_SAMPLE_CLK,
     AGC_DAC => AGC_DAC,
     REG8 => REG8,
     REG5 => REG5,
     REG4 => REG4,
     REG3 => REG3,
     REG2 => REG2,
     REG1 => REG1,
     REG0 => REG0,
     SAMPLE_CLK_IN => '1',
     DATA_Q_IN => x"000",
     DATA_I_IN => x"400",
     CLK => CLK_ING
      );

-- // RESAMPLING OF THE DECIMATED INPUT DATA	---------------------
-- Resample the output of the receiver to feed into the FFT
-- NOTE: MAKE SURE THAT THE SAMPLES ARE VALID FOR ATLEAST 25 ns
-- SO THAT THE FFT DOES NOT LOSE OUT ON ANY DATA

-- write to A side or B side
RECLOCK_INPUT_002: process(CLK_ING)
begin
	if rising_edge(CLK_ING) then
		if SAMPLE2_CLK = '1' then	  
			A_BN <= not A_BN;
			if(A_BN = '0') then
			  -- direct to A side
			  DATA3_I_A <= DATA2_I;
			  DATA3_Q_A <= DATA2_Q;
			else
			  -- direct to B side
			  DATA3_I_B <= DATA2_I;
			  DATA3_Q_B <= DATA2_Q;
			end if;
		end if;
	end if;
end process;

-- reclock A_BN flag at the falling edge of CLK_ING
RECLOCK_INPUT_003: process(CLK_ING)
begin
   if falling_edge(CLK_ING) then
       A_BN_DF <= A_BN;
   end if;
end process;

-- reclock input samples with processing clock 
RECLOCK_INPUT_004: process(CLK_P, A_BN_DF, DATA3_I_A, DATA3_I_B, DATA3_Q_A, DATA3_Q_B)
begin
   if rising_edge(CLK_P) then
		A_BN_DF_D <= A_BN_DF;
		A_BN_DF_D2 <= A_BN_DF_D;
		DATA3_I_A_D <= DATA3_I_A;
		DATA3_Q_A_D <= DATA3_Q_A;
		DATA3_I_B_D <= DATA3_I_B;
		DATA3_Q_B_D <= DATA3_Q_B;
   end if;
end process;

--RECLOCK_INPUT_005: process(CLK_P)
--begin
--   if rising_edge(CLK_P) then
--		if (A_BN_DF_D2 /= A_BN_DF_D) then
--			SAMPLE3_CLK <= '1';
--		else
--			SAMPLE3_CLK <= '0';
--		end if;
--   end if;
--end process;

-- read when input samples from A or B sides.      
--INPUT_SAMPLES_001: process(A_BN_DF_D, DATA3_I_A_D, DATA3_I_B_D, DATA3_Q_A_D, DATA3_Q_B_D)
--begin
--	if(A_BN_DF_D = '1') then
--	  -- read from stable A side
--	  DATA3_I <= DATA3_I_A_D;
--	  DATA3_Q <= DATA3_Q_A_D;
--	else
--	  -- read from stable B side
--	  DATA3_I <= DATA3_I_B_D;
--	  DATA3_Q <= DATA3_Q_B_D;
--	end if;
--end process;

--// TEST TEST TEST ---------------------
SAMPLE3_CLK <= '1';
-- maximum positive value in all bins
DATA3_I <= "011111111111" when FFT_INDEX_IN = 3 else (others => '0');        
DATA3_Q <= (others => '0');        

process
begin
	FFT_START_CAPTURE <= '0';
	wait for 29 ns;
	FFT_START_CAPTURE <= '1';
	wait for 28 ns;
	FFT_START_CAPTURE <= '0';
	wait;
end process;


--// FFT START CAPTURE ---------------------

START_CAPTURE_001:process(CLK_P)
begin
	if rising_edge(CLK_P) then 
		if FFT_START_CAPTURE = '1' then
			FFT_START_CAPTURE_FLAG <= '1';
		elsif FFT_IO_ENABLED = '1' and SAMPLE3_CLK = '1' then
			FFT_START_CAPTURE_FLAG <= '0';			
		end if;
	end if;
end process; 

FFT_INDEX_IN_001:process (CLK_P)
begin
	if rising_edge(CLK_P) then
		if FFT_IO_ENABLED = '0' then
			-- while computing the FFT, reset the input index
			FFT_INDEX_IN <= (others => '0');
		elsif(FFT_INDEX_IN = 0) and (FFT_START_CAPTURE_FLAG = '1') and (SAMPLE3_CLK = '1')  then
			-- waiting for the 1st sample 
			FFT_INDEX_IN <= "000000000001";
		elsif (FFT_INDEX_IN /= 0)and (SAMPLE3_CLK = '1') and (FFT_INDEX_IN(11) = '0') then
			FFT_INDEX_IN <= FFT_INDEX_IN + 1;
		end if;		
	end if;
end process;

-- Block the FFT input samples when not capturing
--The user must check the IO_ENABLED flag before sending data. 
--Data can be sent up to (and excluding) the FFT_START flag.
SAMPLE3A_CLK <= SAMPLE3_CLK when FFT_INDEX_IN(11) = '0' and FFT_IO_ENABLED = '1' and FFT_START = '0' else '0';
 
FFT_SCALE <= "111111111111";
FFT_BYPASS <= '1';

dut_001 :  FFT_CONTROL2  port map (
     OVERFLOW => FFT_OVERFLOW,
     SAMPLE_CLK_OUT_REQ => SAMPLE4_CLK_REQ,
     SAMPLE_CLK_OUT => SAMPLE4_CLK,
     FFT_INDEX_OUT => FFT_INDEX_OUT(10 DOWNTO 0),
     DATA_Q_OUT => DATA4_Q,
     DATA_I_OUT => DATA4_I,
     FFT_BYPASS => FFT_BYPASS,
     FFT_SCALE => FFT_SCALE,
     IO_ENABLED => FFT_IO_ENABLED,
     FFT_START => FFT_START,
     FWD_INVN => '0',
     SAMPLE_CLK_IN => SAMPLE3A_CLK,
     FFT_INDEX_IN => FFT_INDEX_IN(10 DOWNTO 0),
     DATA_Q_IN => DATA3_Q,
     DATA_I_IN => DATA3_I,
     CLK => CLK_P
      );

--// FFT START COMPUTATION ---------------------
START_COMPUTATION_001:process(CLK_P, FFT_INDEX_IN(11))
begin
	if rising_edge(CLK_P) then 
		-- 1. data capture at the input is complete
		-- 2. finished reading from the previous FFT
		-- 3. fft is currently not computing
		-- key assumption is that as soon as FFT_START goes high
		-- FFT_IO_ENABLED goes low
		if FFT_START = '1' then
			 FFT_START <= '0';
		elsif FFT_INDEX_IN(11) = '1' and FFT_INDEX_OUT_MSB_D3 = '1' and FFT_IO_ENABLED = '1' then
			 FFT_START <= '1';
		end if;
	end if;
end process; 

--// FFT READ ---------------------
FFT_INDEX_OUT_001:process (CLK_P)
begin
	if rising_edge(CLK_P) then
		if FFT_IO_ENABLED = '0' then
			FFT_INDEX_OUT <= (others => '0');
		elsif SAMPLE4_CLK_REQ = '1' and FFT_INDEX_OUT(11) = '0' then
			FFT_INDEX_OUT <= FFT_INDEX_OUT + 1;
		end if;		
	end if;
end process; 

FFT_INDEX_OUT_D3_MSB_001:process (CLK_P)
begin
	if rising_edge(CLK_P) then
		FFT_INDEX_OUT_MSB_D <= FFT_INDEX_OUT(11);
		FFT_INDEX_OUT_MSB_D2 <= FFT_INDEX_OUT_MSB_D;
		FFT_INDEX_OUT_MSB_D3 <= FFT_INDEX_OUT_MSB_D2;
	end if;
end process; 

SOF4_001:process (CLK_P)
begin
	if rising_edge(CLK_P) then
		SOF4_D <= SOF4;
		SOF4_D2 <= SOF4_D;
		if FFT_IO_ENABLED = '1' and FFT_INDEX_OUT = 0 and SAMPLE4_CLK_REQ = '1' then
			SOF4 <= '1';
		else
			SOF4 <= '0';
		end if;		
	end if;
end process;

EOF4_001:process (CLK_P)
begin
	if rising_edge(CLK_P) then
		EOF4_D <= EOF4;
		EOF4_D2 <= EOF4_D;
		if  FFT_IO_ENABLED = '1' and FFT_INDEX_OUT = 2047 and SAMPLE4_CLK_REQ = '1' then
			EOF4 <= '1';
		else
			EOF4 <= '0';
		end if;		
	end if;
end process;

OVERFLOW_001:process (CLK_P)
begin
	if rising_edge(CLK_P) then
		FFT_OVERFLOW_D <= FFT_OVERFLOW;
		FFT_OVERFLOW_D2 <= FFT_OVERFLOW_D;
		FFT_OVERFLOW_D3 <= FFT_OVERFLOW_D2;
		FFT_OVERFLOW_D4 <= FFT_OVERFLOW_D3;

		FFT_START_D <= FFT_START;
		FFT_START_D2 <= FFT_START_D;
		FFT_START_D3 <= FFT_START_D2;
		FFT_START_D4 <= FFT_START_D3;

		if REG16(1) = '1' then	-- i and q of FFT
			if FFT_START = '1' then
				HDLC_FRAME_TYPE(15 downto 8) <= "00" & FFT_OVERFLOW;
			end if;
		else	  -- mag and phase of FFT
			if FFT_START_D4 = '1' then
				HDLC_FRAME_TYPE(15 downto 8) <= "00" & FFT_OVERFLOW_D4;
			end if;
		end if;		
	end if;
end process;

--// POST PROCESSING ----------------------------------
-- Frame type
HDLC_FRAME_TYPE(7 downto 0) <= REG16;			

-- polar3 has a latency of 5 clks
POLAR_001: POLAR3 port map(
	CLK => CLK_P,
	X_IN => DATA4_I, 
	Y_IN => DATA4_Q, 
	SAMPLE_CLK_IN => SAMPLE4_CLK,
	PHASE_OUT => DATA5_P, 	
	MAGNITUDE_OUT => DATA5_M,
	SAMPLE_CLK_OUT => SAMPLE5_CLK 
	);

RECLOCK_000: process(CLK_P)
begin
	if rising_edge(CLK_P) then
		SOF4_D3 <= SOF4_D2;
		SOF4_D4 <= SOF4_D3;
		SOF4_D5 <= SOF4_D4;
		SOF4_D6 <= SOF4_D5;
		SOF5 <= SOF4_D6;

		EOF4_D3 <= EOF4_D2;
		EOF4_D4 <= EOF4_D3;
		EOF4_D5 <= EOF4_D4;
		EOF4_D6 <= EOF4_D5;
		EOF5 <= EOF4_D6;
	end if;
end process;

-- REG16(1) = '0' for phase and magnitude of fft output
-- 			  '1' for I/Q of fft output
DATA7 <= (DATA5_M(13 downto 1) & DATA5_P) when (REG16(1) = '0') else (DATA4_I & DATA4_Q);
SAMPLE7_CLK <= SAMPLE5_CLK when (REG16(1) = '0') else SAMPLE4_CLK;
SOF7 <= SOF5 when (REG16(1) = '0') else SOF4_D2;
EOF7 <= EOF5 when (REG16(1) = '0') else EOF4_D2;
SAMPLE4_CLK_REQ <= SAMPLE7_CLK_REQ;


--// FRAME SUBSAMPLING MECHANISM ----------------------------------
-- Allow one frame at a time into the HDLC/USB transmit components.
-- Enable when both USB & HDLC buffers are empty and start of frame.
-- Disable when end of frame.
SUBSAMPLE_001: process(CLK_P)
begin
	if rising_edge(CLK_P) then 
		if (SOF7 = '1') and (HDLC_BUFFERS_EMPTY = '1') and (USB_BUFFER_EMPTY = '1') then
			DECIMATE_ON <= '1';
		elsif (EOF7 = '1') then
			DECIMATE_ON <= '0';
		end if;
	end if;
end process; 

SUBSAMPLE_002: process(DECIMATE_ON, SOF7, HDLC_BUFFERS_EMPTY, USB_BUFFER_EMPTY, DATA7, 
	SAMPLE7_CLK, EOF7)
begin
	if(DECIMATE_ON = '1') or 
	((SOF7 = '1') and (HDLC_BUFFERS_EMPTY = '1') and (USB_BUFFER_EMPTY = '1')) then
		-- enable the data path to the HDLC encoder
		HDLC_DATA_IN <= DATA7;
		HDLC_SAMPLE_CLK_IN <= SAMPLE7_CLK;
		HDLC_SOF <= SOF7;
		HDLC_EOF <= EOF7;
	else
		-- block the data path to the HDLC encoder
		HDLC_DATA_IN <= (others => '0');
		HDLC_SAMPLE_CLK_IN <= '0';
		HDLC_SOF <= '0';
		HDLC_EOF <= '0';
	end if;
end process;

SAMPLE7_CLK_REQ <= HDLC_SAMPLE_CLK_IN_REQ;       


--// HDLC BYTE ENCODING -----------------------------------
HDLC_001: HDLC_BYTE_ENC port map(
   CLK => CLK_P,
	ASYNC_RESET => ASYNC_RESET,
	DATA_IN => HDLC_DATA_IN,
	SAMPLE_CLK_IN => HDLC_SAMPLE_CLK_IN,
	SAMPLE_CLK_IN_REQ => HDLC_SAMPLE_CLK_IN_REQ,
	SOF => HDLC_SOF,
	EOF => HDLC_EOF,
	FRAME_TYPE => HDLC_FRAME_TYPE,		
	DATA_OUT => USB_DATA,	
	SAMPLE_CLK_OUT => USB_SAMPLE_CLK,	 
	SAMPLE_CLK_OUT_REQ => USB_SAMPLE_CLK_REQ,
	INTERNAL_BUFFERS_EMPTY => HDLC_BUFFERS_EMPTY	
	);

USB_SAMPLE_CLK_REQ <= '1'; 
USB_BUFFER_EMPTY <= '1';

end testbench;
