----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date: 07/06/2021 04:34:19 PM
-- Design Name: 
-- Module Name: FFT_VecByCirc5 - 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;
use ieee.numeric_std.all;

library work;
use work.Utils.all;

entity VecByCircOpt5_top_2ip is
generic (
    D_V:  integer  := 5;
    Nb:   integer  := 8;
    P_VAL: integer := 23 
);
port 
(
   Clk, Rst_n: in std_logic;
   start_in: in std_logic;
    
   done: out std_logic;
   
  
-- Message Memory
   msg_mem_en: out std_logic;
   msg_mem_adr: out std_logic_vector(my_log2(div_ceil(P_VAL, Nb))-1 downto 0);
   msg_mem_data: in std_logic_vector(0 to Nb-1);
   
-- Position Memory
   pos_mem_en: out std_logic;
   pos_mem_adr: out std_logic_vector(my_log2(D_V)-1 downto 0);
   pos_mem_data: in std_logic_vector(my_log2(P_VAL)-1 downto 0);
    
-- Out Memory    
   out_mem_en: out std_logic;
   out_mem_adr: out std_logic_vector(my_log2(div_ceil(P_VAL, Nb))-1 downto 0);
   out_mem_data: out std_logic_vector(Nb-1 downto 0)
);
end VecByCircOpt5_top_2ip;

architecture Behavioral of VecByCircOpt5_top_2ip is

constant NB_LOG2: integer := my_log2(NB);
constant DV_LOG2: integer := my_log2(D_V);
constant P_VAL_LOG2: integer := my_log2(P_VAL);
constant OUT_MEM_ROW_NUM: integer := div_ceil(P_VAL, Nb);
constant OUT_MEM_ROW_NUM_LOG2: integer := my_log2(OUT_MEM_ROW_NUM);

type FSMState is (ResetState, WaitConfig, Idle, StartSystem, WaitForFFTDone, 
    SaveMemOut, OutMemRow, WaitRemainRowBits, DoneState);
signal CurrState, LastState, NextState: FSMState;

-- DP Components
component APadder is 
generic (
-- TODO: This must be a power of two.
	constant Nb: integer := 4;
	constant P_VAL: integer := 11
);
port (
	Clk, Rst_n: in  std_logic;
	
	start_in: in std_logic; -- Start the sequence of bit

	out_bin: out std_logic;
	out_last: out std_logic;
	out_valid: out std_logic;
-- When one the circuit stop the output sequence.
	out_stop: in std_logic; 

    done: out std_logic;
	
-- 	Position memory
	mem_in_en:   out std_logic; -- Enable signal for the memory
	mem_in_adr:  out std_logic_vector(OUT_MEM_ROW_NUM_LOG2-1 downto 0);
	mem_in_data: in  std_logic_vector(0 to Nb-1)
);
end component;

component BPadder is 
generic (
	constant D_V: integer := 29;
	constant P_VAL: integer := 11
--	constant P_VAL_EXT: integer   := 32
);
port (
	Clk, Rst_n: in  std_logic;
	
	start_in: in std_logic; -- Start the sequence of bit

	out_bin: out std_logic;
	out_last: out std_logic;
	out_valid: out std_logic;
-- When one the circuit stop the output sequence.
	out_stop: in std_logic; 

    done: out std_logic;
	
-- 	Position memory
	mem_in_en:   out std_logic; -- Enable signal for the memory
	mem_in_adr:  out std_logic_vector(DV_LOG2-1 downto 0);
	mem_in_data: in  std_logic_vector(P_VAL_LOG2-1 downto 0)
);
end component;

-- Component name convention Name_{log2(Samples)}_{log2(NumOfBits)}
component main_mul5_2ip is
generic 
(
	constant P_VALUE: integer := 27;
    constant SAMPLES_LOG2: integer := 6
 );
port
(
    Clk: in std_logic;
    Rst_n: in std_logic;
    start_in: in std_logic;
-- One when all the config internal IPs are done.   
-- The data is present next clock cycle. 
    config_done: out std_logic;

    a_data: in std_logic;
--  a_last : in std_logic;     
--  a_valid: in std_logic;
  
    b_data: in std_logic;
--  b_last : in std_logic;     
--  b_valid: in std_logic;    
    
    
    last_frame: in std_logic;
    
 -- Momentetly we suppose tha the tb always sync the data in.   
    a_data_rdy: in std_logic  := '0';
    a_data_last: in std_logic  := '0';
    b_data_rdy: in std_logic := '0';
    b_data_last: in std_logic := '0';
    data_in_rdy: in std_logic := '0';

    data_out: out std_logic;
    data_out_int: out std_logic_vector((3+SAMPLES_LOG2)-1 downto 0) := 
            (others => '0');
    
    data_out_valid: out std_logic;
    data_out_last: out std_logic;
    
    done: out std_logic
);
end component;

component SIPO is
generic (N: integer); 
port 
(
    Clk, Rst_n: in std_logic;
    En: in std_logic;
    Clr: in std_logic := '0';
    D: in std_logic;
    Q: out std_logic_vector(N-1 downto 0)
);
end component;

component Reg1 is
port 
(
    Clk, Rst_n: in std_logic;
    En: in std_logic;
    Clr: in std_logic := '0';
    D: in std_logic;
    Q: out std_logic
);
end component;


component Counter is
generic (constant N: integer := 8);
port 
( 
    Rst_n : in std_logic;
    Clk : in std_logic;
    En : in std_logic;
    CntRst: in std_logic; -- When 1 reset the counte to 0 sync reset.
    CntValue : out unsigned(N-1 downto 0);
    TC : out std_logic
);
end component;

-- DP Signals

signal a_start, b_start, fft_start : std_logic;

signal out_bin: std_logic;
signal out_last: std_logic;
signal out_valid: std_logic;

signal fft_rst, fft_done, fft_done_config: std_logic;

-- A padder signals
signal a_out, a_done, a_out_last, a_out_valid: std_logic;
signal b_out, b_done, b_out_last, b_out_valid: std_logic;

-- Out Memory CNT
signal out_cnt_tc:  std_logic;
signal out_cnt_en:  std_logic;
signal out_cnt_rst: std_logic;
signal out_cnt_val: unsigned(OUT_MEM_ROW_NUM_LOG2-1 downto 0);

signal currMemRow: std_logic_vector(0 to Nb-1);

-- If Nb is always a poer of two we can do the mod easilly?? 
-- Any way for now we have a small Nb counter can be removed.
signal r_cnt_tc:  std_logic;
signal r_cnt_en:  std_logic;
signal r_cnt_rst: std_logic;
signal r_cnt_val: unsigned(NB_LOG2-1 downto 0); 

signal sipo_out: std_logic_vector(Nb-1 downto 0);
signal out_bin_reg: std_logic;
signal sipo_out_sm: std_logic_vector(Nb-1 downto 0);
signal sipo_clr: std_logic;
signal sipo_in: std_logic;

begin

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

FSM_Update: process(CurrState, LastState, start_in, sipo_out, 
    out_bin, fft_done_config, r_cnt_tc, out_last, out_valid)
begin
    r_cnt_en <= '0';
    r_cnt_rst <= '0';
    
    out_cnt_en <= '0';
    out_cnt_rst <= '0';
    
    out_mem_en   <= '0';
--    out_mem_rst  <= '0';
    fft_rst <= '1';
    
    a_start <=  '0';
    b_start <=  '0';
    fft_start <= '0';
    sipo_clr <= '0';
    sipo_in <= '0';
    sipo_out_sm <= (others => '0');
    
    done <= '0'; 
    case CurrState is
        when ResetState => 
           r_cnt_en <= '0'; 
           r_cnt_rst <= '1';
           currMemRow <= (others => '0');
           
           out_cnt_rst <= '1';
           out_cnt_en <= '0';
           
           a_start <=  '0';
           b_start <=  '0';
           fft_start <= '0';
           
           fft_rst <= '0';
                          
           done <= '0';
           NextState <= Idle;
        when Idle =>
            NextState <= Idle;
            if start_in='1' then
                NextState <= WaitConfig;
            end if;
        when WaitConfig  =>
            NextState <= WaitConfig;
            if fft_done_config='1' then 
                a_start <=  '1';
                b_start <=  '1';
                fft_start <= '1';
            
            
                NextState <= WaitForFFTDone;
            end if;
        when StartSystem => 
            a_start <=  '1';
            b_start <=  '1';
            fft_start <= '1';
            NextState <= WaitForFFTDone;
        when WaitForFFTDone => 
            NextState <= WaitForFFTDone;
            if out_valid='1' then
               --  currMemRow(0) <= out_bin;
                r_cnt_en  <= '1'; 
                sipo_in   <= out_bin;
                NextState <= SaveMemOut;
            end if;
        when SaveMemOut =>
            r_cnt_en <= '1';
            sipo_in <= out_bin;
            
            --out_cnt_en <= '0';
            --out_mem_en <= '0'; 
            NextState <= SaveMemOut;
            if r_cnt_tc='1' then
                NextState <= OutMemRow;
            else
                if out_last='1' then
                    -- We have to wait for the remaining Nb bits to be written to memory,
                    -- if by chance we are not at the end of Nb frame.
                    if r_cnt_tc='1' then
                        NextState <= DoneState;
                    else
                        NextState <= WaitRemainRowBits;
                    end if;
                end if;
            end if;
         when OutMemRow =>
              r_cnt_en <= '1'; 
               sipo_in <= out_bin;
               
              sipo_out_sm <= sipo_out; 
              out_mem_en   <= '1';
              out_cnt_en   <= '1'; 
              
              if LastState=WaitRemainRowBits then
                NextState <= DoneState;
              else
                NextState <= SaveMemOut;
              end if;
               
         when WaitRemainRowBits =>
            r_cnt_en <= '1';
            -- Fill the SIPO with 0s
            sipo_in <= '0';
            
            NextState <= WaitRemainRowBits;
            if r_cnt_tc='1' then
               NextState <= OutMemRow;
            end if;
        when DoneState =>
            done <= '1';
            NextState <= Idle;
        when others => NextState <= Idle;
   end case;
end process;

---- First allcoate the two padders
APadder_Comp: APadder generic map (Nb => Nb, P_VAL => P_VAL) 
    port map (
        clk => clk, Rst_n => Rst_n,
        start_in  => a_start,
        out_bin   => a_out,
        out_last  => a_out_last,
        out_valid => a_out_valid,
        
        done => a_done,
        out_stop  => '0',
       
        
        mem_in_en => msg_mem_en,
        mem_in_adr => msg_mem_adr,
        mem_in_data => msg_mem_data
);
      

BPadderComp: BPadder generic map (D_V => D_V, P_VAL => P_VAL) 
    port map (
        clk => clk, Rst_n => Rst_n,
        start_in   => b_start,
        
        out_bin    => b_out,
        out_last   => b_out_last,
        out_valid  => b_out_valid,
        
        done => b_done,
        
        out_stop => '0',
        
        mem_in_en => pos_mem_en,
        mem_in_adr => pos_mem_adr,
        mem_in_data => pos_mem_data
);

-- Main multiplier
MainMul: main_mul5_2ip generic map (P_VALUE => P_VAL) 
port map (
    Clk => Clk, Rst_n => fft_rst,
    start_in => fft_start,
    config_done => fft_done_config,

    a_data       => a_out,
--    a_data_last  => a_out_last,
--    a_data_valid => a_out_valid,
      
    b_data       => b_out,
--    b_data_last  => b_out_last,
--    b_data_valid => b_out_valid,
    
    last_frame  => a_out_last,
    
    data_in_rdy   => a_out_valid,

    data_out        => out_bin,
    data_out_last   => out_last,
    data_out_valid  => out_valid,
    
    done => fft_done
    
);

--OutRegB: Reg1 port map (Clk => Clk, Rst_n => Rst_n,
--    En => '1',D => out_bin, Q => out_bin_reg);

-- At the output instead of using the FSM to save the bit 
-- at the i pos, we use a SIPO (Serial input Parallel output)
-- shift register of size Nb, and a mux to select when r_cnt_tc='1'
-- the data to send to the memory. 
SIPO_Comp: SIPO generic map (N => Nb) 
port map (
    Clk => Clk, Rst_n => Rst_n,
    En  =>  '1', 
    Clr => sipo_clr,
    D   => sipo_in,
    Q   => sipo_out 
);

--sipo_out_sm <= sipo_out when r_cnt_tc='1' else
--               (others => '0');

-- Set ourput ADR                               
out_mem_adr <= std_logic_vector(out_cnt_val);   
 out_mem_data <= sipo_out_sm;                     

R_IDX_CNT: Counter generic map(N => NB_LOG2) 
port map (
	Clk => Clk, Rst_n => Rst_n, 
    En  => r_cnt_en, TC => r_cnt_tc,
    CntValue => r_cnt_val,
    CntRst   => r_cnt_rst
);

OUT_IDX_CNT: Counter generic map(N => OUT_MEM_ROW_NUM_LOG2) 
port map (
	Clk => Clk, Rst_n => Rst_n, 
    En  => out_cnt_en, TC => out_cnt_tc,
    CntValue => out_cnt_val,
    CntRst   => out_cnt_rst
);

end Behavioral;









