----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date: 04/10/2021 07:32:42 PM
-- Design Name: 
-- Module Name: FFT_VecByCirc_FSM - Behavioral
-- Project Name: 
-- Target Devices: 
-- Tool Versions: 
-- Description: 
-- 
-- Dependencies: 
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
----------------------------------------------------------------------------------


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library work;
use work.Common.all;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity FSM_2ip is
port
( 
    Clk : in STD_LOGIC; Rst_n : in STD_LOGIC;
   
-- INPUTS   
    start_in:              in std_logic;
    data_in_rdy:           in std_logic;
    last_frame_reg:        in std_logic;

-- DP Signals
    dp_cnt_tc:             in std_logic;

-- FFT Input Control Signals
    dp_xfft_config_tready:  in std_logic;
	dp_xifft_config_tready:    in std_logic;

    dp_xfft_out_tvalid:    in std_logic;
	dp_xfft_out_tlast:  in std_logic;
	
	dp_xifft_in_tready:    in std_logic;
    dp_xifft_out_tvalid:   in std_logic;
    dp_xifft_out_tlast:    in std_logic;

-- CMUL Signals
    dp_cmpy_out_tlast:      in std_logic;
    dp_cmpy_out_tvalid:     in std_logic;
        
    dp_has_both_fft_data: in std_logic;

-- OUTPUTS    
    fsm_ip_rst:                out std_logic;   
    
-- FFT IP Outputs
    fsm_xfft_in_tvalid:     out std_logic;
    fsm_xfft_out_tready:    out std_logic;
    fsm_xfft_config_tvalid: out std_logic;
        
-- IFFT Outputs    
    fsm_xifft_in_tlast:      out std_logic;  
    fsm_xifft_in_tvalid:     out std_logic; 
    fsm_xifft_out_tready:    out std_logic;
    fsm_xifft_config_tvalid: out std_logic;

-- Counter out Signals
    fsm_cnt_en: out std_logic;
    fsm_cnt_rst: out std_logic;    
    
-- 1 Scale and FFT settings, 0 IFFT and zero scaling.
    fsm_xfft_config_data_sel: out std_logic; 

-- SRAM 
-- 1 we are using the IP for the a_data FFT, 0 the IP i used for the CMUL out IFFT.
    fsm_in_fft_mux_sel: out std_logic;
    
 -- SRAM OUTPUTS
           
    fsm_sram_we: out std_logic;                        
    fsm_sram_re: out std_logic;      
    fsm_sram_CSn: out std_logic;                   
    fsm_sram_rst_fsm: out std_logic; 
    fsm_sram_addr_sel: out std_logic_vector(1 downto 0);                      
    fsm_sram_data_sel: out std_logic; 
    
-- General Signals
   done: out std_logic;
   out_en: out std_logic;
   config_done:   out std_logic;
   data_out_last: out std_logic;
   data_out_valid: out std_logic;   
-- When one  indicate that the system is used in extended mode such that padded hase been added at the input,
-- The transform is done on 2N but the output data is only in the 1:N samples, so we stop the FSM before reaching the 
-- 2**(2N) terminal count. In the case that the fsm_cnt reached p, than dp_cnt_pad_tc is 1
	is_extended: in std_logic := '0';
	dp_cnt_pad_tc: in std_logic := '0'
);
end FSM_2ip;

architecture Behavioral of FSM_2ip is

component Reg1 is
port
(
    CLK:    in     std_logic;
    Rst_n:    in     std_logic;
    En:        in     std_logic;
    D:        in     std_logic;
    Q:        out std_logic
);
end component;

-- State Machine States
type FSM_State is (ResetS0, ResetS1, ConfigState, IdleState, WaitInData,
        DoFFTState, WaitFFTOutState, DoCMulState, LoadIFFTState, WaitIFFTState,
        SaveIFFTState, WaitBRAM0, WaitBRAM1,  DoneState);
signal CurrState, NextState: FSM_State;

signal dp_cnt_tc_reg, dp_cnt_pad_tc_reg: std_logic;

begin



FSM_State_Update: process(Clk, Rst_n)
begin
    if Rst_n='0' then
        CurrState <= ResetS0;
    elsif rising_edge(Clk) then
        CurrState <= NextState;
    end if;
end process;

-- we need a dounter tc delayed by one due to BRAM....
TCReg: Reg1 port map (
    Clk => Clk, Rst_n => Rst_n,
    En => '1', 
    D  => dp_cnt_tc,
    Q  => dp_cnt_tc_reg
);

-- we need a dounter tc delayed by one due to BRAM....
TC_Pad_Reg: Reg1 port map (
    Clk => Clk, Rst_n => Rst_n,
    En => '1', 
    D  => dp_cnt_pad_tc,
    Q  => dp_cnt_pad_tc_reg
);

FSM_Out_Update: process(CurrState, start_in, data_in_rdy, dp_xifft_config_tready, 
        dp_xfft_config_tready, dp_cmpy_out_tlast, dp_cnt_tc, last_frame_reg, dp_cmpy_out_tvalid, 
        dp_xifft_out_tvalid, dp_xifft_out_tlast, dp_xfft_out_tlast, dp_cnt_tc_reg, is_extended,
        dp_cnt_pad_tc_reg, dp_has_both_fft_data) 
	variable has_ended_xfft_out_flag, has_ended_xifft_out_flag: std_logic;
begin
    fsm_ip_rst <= '1';
    fsm_xfft_in_tvalid <= '0';
    fsm_xfft_out_tready <= '0';
    fsm_xfft_config_data_sel <= '0';
    fsm_xfft_config_tvalid <= '0';
    
    fsm_xifft_in_tlast <= '0';
    fsm_xifft_in_tvalid <= '0';
    fsm_xifft_out_tready <= '0';
    fsm_xifft_config_tvalid <= '0';
    
    fsm_sram_data_sel <= '0';   
    fsm_cnt_en <= '0';
    fsm_cnt_rst <= '0';
    
---- SRAM is salways select, this ignal is active low
    fsm_sram_CSn <= '0';
    fsm_sram_we <= '0';
    fsm_sram_re <= '0';
    fsm_sram_addr_sel <= SRAM_ADR_SEL_NONE;
    fsm_sram_rst_fsm <= '1';
    
    out_en <= '0';
    data_out_last <= '0';
    fsm_sram_data_sel <= '0';  
    data_out_valid <= '0';
	
	fsm_in_fft_mux_sel <= '0';

    case CurrState is
        when ResetS0 =>
        -- Default signal values
            fsm_xfft_in_tvalid <= '0';
            fsm_xfft_out_tready <= '0';
            fsm_xfft_config_tvalid <= '0';
            fsm_xifft_out_tready <= '0';
            fsm_sram_data_sel <= '0';
            fsm_sram_CSn <= '1';
            fsm_sram_addr_sel <= (others => '0');
       -- Reset Signals
            fsm_cnt_rst <= '1';
    
            out_en <= '0';
            fsm_ip_rst <= '0';
            config_done <= '0';
            fsm_sram_data_sel <= '0'; 
            fsm_sram_rst_fsm <= '0';
			done <= '0';
---- SRAM is salways select, this ignal is active low
			fsm_sram_CSn <= '0';
			fsm_sram_we <= '0';
			fsm_sram_re <= '0';
			fsm_sram_addr_sel <= SRAM_ADR_SEL_NONE;
			
			out_en <= '0';
			data_out_last <= '0';
			fsm_sram_data_sel <= '0';  
			data_out_valid <= '0';
			
			fsm_in_fft_mux_sel <= '0';
			
			has_ended_xfft_out_flag  := '0';
			has_ended_xifft_out_flag  := '0';
            
            NextState <= ResetS1;
        when ResetS1 => 
            fsm_ip_rst <= '0';
            NextState <= ConfigState;
        when ConfigState =>
        
            -- Given the fact that we are using two identical IP for the FFT_A/FFT_B we use the same signal to 
            -- drive the inputs.  
			fsm_in_fft_mux_sel <= '1';
            fsm_xfft_config_tvalid <= '1';
            -- The ifft scaling is never used as an IFFT and we wanna avoid the block to apply
            -- more scale than the intrinsic one associated to the FFT algo.
            fsm_xifft_config_tvalid <= '1';
            
            -- axi_fft_config_tdata <= '0' & "101011" & '1';
            -- axi_ifft_config_tdata <= (others => '0');
    
            -- the axi trady remain='1' when the config is done
            NextState <= ConfigState; 
            if dp_xifft_config_tready='1' and dp_xfft_config_tready='1' then
                           -- Disable the config channel as we are not sending any more config data
               config_done <= '1';
               NextState <= IdleState; 
               fsm_xfft_config_tvalid <= '0';
               fsm_xifft_config_tvalid <= '0';
            end if;
        when IdleState =>
            NextState <= IdleState;
            if (start_in='1') then
                done <= '0';
                fsm_cnt_rst <= '1';
                NextState <= WaitInData;
             end if;
                        
        when WaitInData =>
            NextState <= WaitInData;
			fsm_in_fft_mux_sel <= '1';
            if data_in_rdy='1' then
               fsm_cnt_rst <= '1';
               -- ip_xfft_config_tvalid <= '1';
			   fsm_in_fft_mux_sel <= '1';
               NextState <= DoFFTState;
            end if;
            
        when DoFFTState =>
			fsm_in_fft_mux_sel <= '1';
			
            -- Enable the FFT data in.
            fsm_cnt_en <= '1';
            fsm_xfft_in_tvalid <= '1';
			fsm_xifft_in_tvalid <= '1';
            
            NextState <= DoFFTState;
            if (last_frame_reg='1') then
                 fsm_cnt_rst <= '1';
                -- fsm_xfft_out_tready <= '1';
				-- fsm_xifft_out_tready<= '1';
                 NextState <= WaitFFTOutState;
            end if;
                       
        when WaitFFTOutState =>    
            fsm_cnt_rst <= '1';
			fsm_in_fft_mux_sel <= '1'; -- Need this to one to select proper SRAM data input.

            NextState <= WaitFFTOutState;  
            if dp_has_both_fft_data='1' then
				fsm_xfft_out_tready <= '1';
				fsm_xifft_out_tready<= '1';
                NextState <= DoCMulState;
            end if;
            
        when DoCMulState =>
			fsm_in_fft_mux_sel <= '1'; -- Need this to one to select proper SRAM data input.
		--	fsm_xfft_out_tready <= '1';
		--	fsm_xifft_out_tready <= '1';
            
			
			-- We need to disable the FFT out when we have ended the sequence and got 
			--  tlast, this happen before cmul_tlast, need this for both AXI channel! 
			fsm_xfft_out_tready <= '0';
			if has_ended_xfft_out_flag='0' then 
			   fsm_xfft_out_tready <= '1';
			   if dp_xfft_out_tlast='1' then
				  has_ended_xfft_out_flag := '1';
				end if;
			end if;
			
			fsm_xifft_out_tready <= '0';
			if has_ended_xifft_out_flag='0' then 
			   fsm_xifft_out_tready <= '1';
			   if dp_xifft_out_tlast='1' then
				  has_ended_xifft_out_flag := '1';
				end if;
			end if;
                        
            if (dp_cmpy_out_tvalid='1') then
                fsm_cnt_en <= '1';
                fsm_sram_we <= '1';
                fsm_sram_addr_sel <= SRAM_ADR_SEL_CNT_VAL_REV;
            end if;
                        
            -- Start counting the frames.
			NextState <= DoCMulState;
			if (dp_cmpy_out_tlast='1') then
                NextState <= WaitBRAM0;
            end if;
         -- The BRAM have a 1 clock latency we need to wait one clock here.
         when WaitBRAM0 =>
            fsm_cnt_rst <= '1';
            fsm_cnt_en <= '1';
            fsm_xifft_config_tvalid <= '1';
			fsm_in_fft_mux_sel <= '0'; 
            NextState <= LoadIFFTState;
                       
         when LoadIFFTState =>
            -- Stram data in
            fsm_sram_re <= '1';
            fsm_cnt_en <= '1';
			fsm_in_fft_mux_sel <= '0'; 
            fsm_sram_addr_sel <= SRAM_ADR_SEL_CNT_VAL; -- Normal index             
              
            if dp_xifft_in_tready = '1' then
               fsm_xifft_in_tvalid <= '1';
            end if;
            
            NextState <= LoadIFFTState;
            if dp_cnt_tc_reg='1' then
               fsm_cnt_rst <= '1';
               fsm_xifft_in_tlast <= '1';
               NextState <= WaitIFFTState;
            end if; 
            
        when WaitIFFTState =>  
            -- NOTE: Resetting the SRAM async crate a problem here because the FSM output is one clock behind,
            -- as we are working with a moore machine the FSM signal change on the clock rising edge.
            -- The problem here is that if we reset the FSM we cannot save the data in the same time.
            fsm_xifft_out_tready<= '1';
			fsm_in_fft_mux_sel <= '0'; 
            NextState <= WaitIFFTState;
            if dp_xifft_out_tvalid='1' then
                -- Stream data out
                fsm_sram_we <= '1';
                fsm_cnt_rst <= '1';
                fsm_sram_data_sel <= '1';  
                fsm_sram_addr_sel <= SRAM_ADR_SEL_IFFT_TUSER; -- IFFT Index  
                
                NextState <= SaveIFFTState;
            else 
                fsm_sram_rst_fsm <= '0';
            end if;
                    
         when SaveIFFTState =>
            -- This state contain bot the read of the SRAM to 
            -- stream data into the IFFT IP and the code that write back the 
            -- IFFT Results in. There is a delay between when all sample are in the core
            -- and when the IP begin to stream data.
            --  f_cnt_en <= '1';
            fsm_xifft_out_tready <= '1';
			fsm_in_fft_mux_sel <= '0'; 
            
            fsm_sram_we <= '1';
            fsm_sram_data_sel <= '1'; 
            fsm_sram_addr_sel <= SRAM_ADR_SEL_IFFT_TUSER; -- IFFT Index  
                      
            NextState <= SaveIFFTState;
            if dp_xifft_out_tlast='1' then
                NextState <= WaitBRAM1;
            end if;
            
        when WaitBRAM1 =>    
            fsm_cnt_rst <= '1';    
            fsm_cnt_en <= '1';       
            NextState <= DoneState;
            
        when DoneState =>
            
            done <= '1';
            out_en <= '1';
            fsm_sram_re <= '1';
            fsm_cnt_en <= '1';
            data_out_valid <= '1';
            fsm_sram_addr_sel <= SRAM_ADR_SEL_CNT_VAL; 
            
            NextState <= DoneState;
-- If this circuit is used in extened mode, we have only 1:N real output data not the entire 2**SAMPLES_LOG2
			if is_extended='1' then
				if dp_cnt_pad_tc_reg='1' then
				   fsm_cnt_rst <= '1';
				
				   NextState <= IdleState;
				   data_out_last <= '1';
				end if;
			else
				if dp_cnt_tc_reg='1' then
				   fsm_cnt_rst <= '1';
				
				   NextState <= IdleState;
				   data_out_last <= '1';
				end if;
			end if;
			
        when others => NextState <= IdleState;            
    end case;
end process;    

end Behavioral;
