----------------------------------------------------------------------------------
-- 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_norder 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_a_config_tready:  in std_logic;
    dp_xfft_b_config_tready:  in std_logic;
    
    
    -- CMUL Signals
    dp_cmpy_out_tlast:      in std_logic;
    dp_cmpy_out_tvalid:     in std_logic;
    
    --- IFFT Input Control Signals    
    dp_ifft_in_tready:        in std_logic;
    dp_ifft_out_tlast:      in std_logic;
    dp_ifft_out_tvalid:     in std_logic;
    dp_ifft_config_tready:    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_ifft_in_tlast:      out std_logic;  
    fsm_ifft_in_tvalid:     out std_logic; 
    fsm_ifft_out_tready:   out std_logic;
    fsm_ifft_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; 

    -- 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_norder;

architecture Behavioral of FSM_norder 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, DoCMulState, WaitCMulState, 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, dp_xfft_a_config_tready, data_in_rdy,
        dp_xfft_b_config_tready, dp_cmpy_out_tlast, dp_cnt_tc, last_frame_reg, 
        dp_cmpy_out_tvalid, dp_ifft_config_tready, dp_ifft_out_tvalid, dp_ifft_in_tready,
        dp_ifft_out_tlast, dp_cnt_tc_reg, is_extended, dp_cnt_pad_tc_reg) 
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_ifft_in_tlast <= '0';
    fsm_ifft_in_tvalid <= '0';
    fsm_ifft_out_tready <= '0';
    fsm_ifft_config_tvalid <= '0';
    

    fsm_cnt_en <= '0';
    fsm_cnt_rst <= '0';
   
    out_en <= '0';
    data_out_last <= '0';
    data_out_valid <= '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_ifft_out_tready <= '0';
       -- Reset Signals
            fsm_cnt_rst <= '1';
    
            out_en <= '0';
            fsm_ip_rst <= '0';
            config_done <= '0';
			
			out_en <= '0';
			data_out_last <= '0';
			data_out_valid <= '0';
			done <= '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_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_ifft_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_xfft_a_config_tready='1' and dp_xfft_b_config_tready='1' and dp_ifft_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_ifft_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;
            if data_in_rdy='1' then
               fsm_cnt_rst <= '1';
               -- ip_xfft_config_tvalid <= '1';
               NextState <= DoFFTState;
            end if;
            
        when DoFFTState =>
      
            -- Enable the FFT data in.
            fsm_cnt_en <= '1';
            fsm_xfft_in_tvalid <= '1';
                       
            NextState <= DoFFTState;
            if (last_frame_reg='1') then
                 fsm_cnt_rst <= '1';
                 fsm_xfft_out_tready <= '1';
                 fsm_ifft_config_tvalid <= '1';
                 NextState <= DoCMulState;
            end if;
            
        when DoCMulState =>    
            fsm_cnt_rst <= '1';
            fsm_xfft_out_tready <= '1';
            
            -- We first configure the IP than assert tvalid=1
            NextState <= WaitCMulState;   
            
        when WaitCMulState =>
            fsm_xfft_out_tready <= '1';

            if (dp_cmpy_out_tvalid='1') then
                fsm_ifft_in_tvalid <= '1';
            end if;
			                        
            -- Start counting the frames.
			NextState <= WaitCMulState;
            if (dp_cmpy_out_tlast='1') then
				fsm_cnt_rst <= '1';
				fsm_ifft_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_ifft_out_tready <= '1';
           -- fsm_ifft_config_tvalid <= '1';
			
            NextState <= WaitIFFTState;
            if dp_ifft_out_tvalid='1' then
                -- Stream data out
                fsm_cnt_rst <= '1';
                data_out_valid <= '1';               
                NextState <= DoneState;
            end if;
  
        when DoneState =>
            
            done <= '1';
            out_en <= '1';
            fsm_cnt_en <= '1';
            data_out_valid <= '1';

            fsm_ifft_out_tready<= '1';

            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;
