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

entity tb_vec_by_circ14_opt is
generic (SAMPLES_LOG2: integer := 14);
end tb_vec_by_circ14_opt;

architecture Behavioral of tb_vec_by_circ14_opt is

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;

-----------------------------------------------------------------------
-- Timing constants
-----------------------------------------------------------------------
constant CLOCK_PERIOD : time := 5 ns; -- 500Mhz Clock
constant T_HOLD       : time := 200 ps;
constant T_STROBE     : time := CLOCK_PERIOD - (2 ps);

-- TB Memory definition
type dut_data_memory_type is array(0 to 2**SAMPLES_LOG2-1) of integer range 0 to 1;


-- TB Signals
signal Clk : std_logic := '0';  -- the master clock
signal Rst_n: std_logic := '0';

signal f_cnt_en:  std_logic := '0';
signal f_cnt_tc:  std_logic := '0';
signal f_cnt_rst: std_logic := '0';
signal f_cnt_val: unsigned(SAMPLES_LOG2-1 downto 0) := (others => '0'); 

signal data_in_rdy: std_logic := '0';

signal dut_a_inputs: std_logic_vector(0 to 2**SAMPLES_LOG2-1) := (others => '0');
signal dut_b_inputs: std_logic_vector(0 to 2**SAMPLES_LOG2-1) := (others => '0');


-- TB state machine
type FSM_State is (ResetState, IdleState, WaitConfigState, SendDataState, WaitOutState, StreamOutState, DoneState);
signal CurrState, NextState: FSM_State := ResetState;

-- DUT signals
signal dut_start: std_logic := '0';
signal dut_config_done: std_logic := '0';
signal dut_last_frame: std_logic := '0';



signal dut_a_data, dut_b_data: std_logic := '0';
signal dut_done, dut_fft_done, dut_ifft_done: std_logic := '0';
signal dut_error: std_logic := '0';

signal dut_bin_out: std_logic := '0';
signal dut_out_valid: std_logic := '0';
signal dut_out_last: std_logic := '0';
signal dut_data_in_rdy: std_logic := '0';
signal dut_bin_val: unsigned(1 downto 0) := (others => '0');

begin

-- CLock And reset generation
-----------------------------------------------------------------------
-- Generate clock
-----------------------------------------------------------------------
clock_gen : process
begin
    clk <= '0';
    wait for CLOCK_PERIOD;
    loop
        clk <= '0';
        wait for CLOCK_PERIOD/2;
        clk <= '1';
        wait for CLOCK_PERIOD/2;
    end loop;
end process clock_gen;

-- Generate the Reset signal
process
begin  -- process
	Rst_n <= '0';
    wait for 3*CLOCK_PERIOD/2;
	Rst_n <= '1';
    wait;
end process;

dut_start <= '1' after 300 ns, '0' after 500 ns;


--- State Machine Update circuit
process(Clk, Rst_n) 
begin
    if Rst_n='0' then
        CurrState <= ResetState;
    elsif rising_edge(Clk) then
        CurrState <= NextState;
    end if;
end process;

dut_a_inputs(0 to 31) <= B"1010_0000_0000_0000_0000_0000_0000_0000";  
dut_b_inputs(0 to 31) <= B"1001_0001_1001_1000_1001_0000_0010_1010";  


-- State Machine Logic
process (CurrState, f_cnt_val, f_cnt_tc, dut_start, dut_config_done,
         dut_out_valid, dut_done, dut_bin_out, dut_out_last, 
         dut_last_frame)
    variable tmpA_Val, tmpB_Val: std_logic := '0';
begin
    f_cnt_en <= '0';
    dut_a_data <= '0';
    dut_b_data <= '0';
    -- dut_bin_val <= (others => '1');
    dut_last_frame <= '0';
    
    case CurrState is
        when ResetState =>
            f_cnt_rst <= '1';
            NextState <= IdleState;
        when IdleState =>
            NextState <= IdleState;
            if dut_start='1' then
                NextState <= WaitConfigState;
             end if;
        when WaitConfigState =>
            NextState <= WaitConfigState;
            if dut_config_done='1' then
               NextState <= SendDataState;
            end if;
        when SendDataState =>
            f_cnt_en <= '1';
            data_in_rdy <= '1';
 --           dut_data_in_rdy <= '1';
            
            tmpA_Val := dut_a_inputs(to_integer(f_cnt_val));
			dut_a_data <= tmpA_Val;

            tmpB_Val := dut_b_inputs(to_integer(f_cnt_val));
			dut_b_data <= tmpB_Val;       

            
            NextState <= SendDataState;
            if f_cnt_tc='1' then
               f_cnt_rst <= '1';
               dut_last_frame <= '1';
               NextState <= WaitOutState;
            end if;      
            
        when WaitOutState =>
            NextState <= WaitOutState;
            if dut_out_valid='1' then 
                NextState <= StreamOutState;
            end if;
                        
        when StreamOutState => 
             NextState <= StreamOutState;

             if dut_out_last='1' then 
                NextState <= DoneState;
             end if;           
        when DoneState =>
            report "Not a real failure. Simulation finished successfully. Test completed successfully" severity failure;
            NextState <= IdleState;
        when others => NextState <= IdleState;
    end case;
end process;

DUT: entity work.FFT_VecByCirc14_opt port map (
    Clk => Clk, Rst_n => Rst_n,
    start_in => dut_start,
    config_done => dut_config_done,

    a_data      => dut_a_data,
    b_data      => dut_b_data,
    last_frame  => dut_last_frame,
-- NOTE: This would be a good syn interface but I guess it require 
-- some sort of FIFO/RAM at the input?
--    a_data_rdy    => dut_a_data_rdy,
--    a_data_last   => dut_a_last,
--    b_data_rdy    => dut_b_data_rdy,
--    b_data_last   => dut_b_last,
    data_in_rdy   => data_in_rdy,

    data_out      => dut_bin_out,
    data_out_valid  => dut_out_valid,
    data_out_last => dut_out_last
);

process(Rst_n, dut_bin_out)
begin
    if Rst_n='0' then
        dut_bin_val <= (others => '0');
    else
-- If the data out is avaible read it.
       if dut_bin_out='1' then
           dut_bin_val <= "01";
       else
           dut_bin_val <= "00"; 
       end if;
   end if;
end process;

F_IDX_CNT: Counter generic map(N => SAMPLES_LOG2) 
port map (Clk => Clk, Rst_n => Rst_n, 
          En => f_cnt_en, TC => f_cnt_tc,
          CntValue => f_cnt_val,
          CntRst => f_cnt_rst
);

end Behavioral;

