-----------------------------------------------------------------------------
---------- Top FFT module with serial input/output data format --------------
-----------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

use work.fft_module_pkg.all;

entity top_fft is
 Port ( 
    -- input data serial - time domain
       DATA_in  : in  std_logic_vector (c_DATA_WIDTH-1 downto 0);  
    -- clock and reset signals
       clk         : in  std_logic;
       clk_data_en : in  std_logic;
       rst         : in  std_logic;
    -- output data serial - frequency domain
       DATA_out : out  std_logic_vector (c_DATA_WIDTH-1 downto 0)
      );

end top_fft;

architecture Structural of top_fft is

-------------------------------------------------------------------------------
--------------------------- Signal declaration --------------------------------
-------------------------------------------------------------------------------

  signal load_done          : std_logic;
  signal load_out_reg       : std_logic;

 --signals - input register
  signal input_reg_in       : t_LOAD_REG;
  signal input_reg_out      : t_LOAD_REG;

  signal input_reg_out_temp : t_DATA_IN_OUT_FFT;

 --signals - output register
  signal output_reg_in      : t_LOAD_REG; 
  signal output_reg_out     : t_LOAD_REG;

  signal output_reg_in_temp : t_DATA_IN_OUT_FFT;

 --signals - output register multiplexors - load data or shift out data
  signal output_reg_mul1    : t_LOAD_REG;
  signal output_reg_mul2    : t_LOAD_REG;

-------------------------------------------------------------------------------
-------------------------- Component declaration ------------------------------
-------------------------------------------------------------------------------

component loading_logic
  port(
      -- clock and reset signals
       clk             : in  std_logic;
       clk_data_en     : in  std_logic;
       rst             : in  std_logic;
      -- load done
       load_done       : out std_logic;
       load_out_reg    : out std_logic
       );
end component;

component fft_module
  port(
      -- input data serial - time domain
       DATA_in     : in  t_DATA_IN_OUT_FFT; 
      -- clock and reset signals
       clk         : in  std_logic;
       rst         : in  std_logic;
      -- load enable
       load_en     : in  std_logic;
      -- output data serial - frequency domain
       DATA_out    : out t_DATA_IN_OUT_FFT
       );
end component;

begin

-------------------------------------------------------------------------------
-------------------------- Component connection -------------------------------
-------------------------------------------------------------------------------


 input_reg: process (clk, clk_data_en, rst)
  begin
   if (rst = '1') then
     input_reg_out <= (others => (others =>'0'));
   elsif (rising_edge(clk) and clk_data_en = '1') then
     input_reg_out <= input_reg_in;
   end if;
  end process;

 output_reg: process (clk, clk_data_en, rst)
  begin
   if (rst = '1') then
     output_reg_out <= (others => (others =>'0'));
   elsif (rising_edge(clk) and clk_data_en = '1') then
     output_reg_out <= output_reg_in;
   end if;
  end process;


 loading_logic_1: loading_logic
  port map(
       -- clock and reset signals
        clk             => clk,
        clk_data_en     => clk_data_en,
        rst             => rst,
       -- load done pulse after load all data
        load_done       => load_done,
       -- load data to output register enable
        load_out_reg    => load_out_reg
           );

 fft_module_1: fft_module
  port map(
       -- input data serial - time domain
        DATA_in     => input_reg_out_temp,
       -- clock and reset signals
        clk         => clk,
        rst         => rst,
       -- load enable
        load_en     => load_done,
       -- output data serial - frequency domain
        DATA_out    => output_reg_in_temp
          );

-------------------------------------------------------------------------------
--------- Generate input register and output register connections -------------
-------------------------------------------------------------------------------

-----  INPUT REGISTER ------
 
 -- serial input register - set as FIFO
  input_reg_load:
   for addr in 1 to c_FFT_SIZE - 1 generate
     input_reg_in (addr-1)  <= input_reg_out (addr);
   end generate;

 -- connect input pin with first input register
 input_reg_in (c_FFT_SIZE - 1) <= DATA_in;

 -- generate connection with FFT_module component
  input_reg_send: 
   for addr in 0 to c_FFT_SIZE- 1 generate
     -- real part
     input_reg_out_temp(addr * 2) <= input_reg_out(addr);
     -- imaginary part
     input_reg_out_temp(addr * 2 + 1) <= (others => '0');
   end generate;


 -----  OUTPUT REGISTER ------

 -- serial output register - set as FIFO
  output_reg_serial: 
   for addr in 0 to c_FFT_SIZE - 2 generate
    output_reg_mul2 (addr) <= output_reg_out (addr+1);
   end generate;

 -- when shifting data out, zeros are loaded into empty registers
  output_reg_mul2(c_FFT_SIZE-1) <= (others=>('0'));

 -- data from data register to output register multiplexor
 -- when only real input number, output is complex but symmetric
 -- data format: Re(0) Re(1) Im(1) Re(2) Im(2) ... 
 -- ... Re(c_FFT_SIZE/2 -1) Im(c_FFT_SIZE/2 -1) Re(c_FFT_SIZE/2)
   output_reg_load: for addr in 1 to c_FFT_SIZE - 1
    generate
     output_reg_mul1 (addr)   <= output_reg_in_temp(addr + 1);
    end generate;
     output_reg_mul1 (0)      <= output_reg_in_temp(0);

 -- output register multiplexer
  output_reg_mul:    
   for addr in 0 to c_FFT_SIZE - 1 generate
     output_reg_in (addr) <= output_reg_mul1 (addr)
       when load_out_reg = '1' else
                             output_reg_mul2 (addr);
   end generate;

 -- drive output pin from output register
 DATA_out <= output_reg_out(0);

end Structural;