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

use work.typedef_package.all;
use work.sine_package.all;

entity feedback_loop_tb is
end entity;

architecture beh of feedback_loop_tb is
    
    --*****COMPONENT DECLARATION*****
    component feedback_loop is
        port (
            clk             : in std_logic;
            reset           : in std_logic;
            adc_data_in1    : in std_logic;     -- PMOD-AD1
            adc_data_in2    : in std_logic;     -- PMOD-AD1
            adc_cs_n        : out std_logic;    -- PMOD-AD1
            adc_sclk        : out std_logic;    -- PMOD-AD1
            dac_data_out    : out std_logic;    -- PMOD-DA3
            dac_cs_n        : out std_logic;    -- PMOD-DA3
            dac_ldac        : out std_logic;    -- PMOD-DA3
            dac_sclk        : out std_logic;    -- PMOD-DA3
            -- DYNAMIC CONFIGURATION
            addsub_mode     : in std_logic;                                 -- (1=ADD, 0=SUB)
            add_input_mux   : in std_logic;                                 -- (1=ADC Input 2, 0=GND)
            delay           : in std_logic_vector(DELAY_WIDTH-1 downto 0);  -- unsigned delay clock count
            factor          : in std_logic_vector(FACTOR_WIDTH-1 downto 0); -- 1Q3 Fixed Point
            -- DEBUG
            reset_debug     : in std_logic;
            adc_data1_max   : out std_logic_vector(ADC_DATA_WIDTH-1 downto 0);
            adc_data2_max   : out std_logic_vector(ADC_DATA_WIDTH-1 downto 0);
            scaler_max      : out std_logic_vector(DAC_DATA_WIDTH-1 downto 0);
            dac_max         : out std_logic_vector(DAC_DATA_WIDTH-1 downto 0)
        );
    end component;
    
    type ADC_STAGE_TYPE is (ZERO, DATA);
    
    --*****SIGNAL DECLARATION*****
    signal clk, reset : std_logic := '0';
    signal adc_data_in1, adc_data_in2, adc_cs_n : std_logic := '0';
    signal factor : std_logic_vector(FACTOR_WIDTH-1 downto 0) := (others => '0');
    signal addsub_mode, add_input_mux : std_logic := '0';
    signal delay : std_logic_vector(DELAY_WIDTH-1 downto 0) := (others => '0');
    signal adc_stage : ADC_STAGE_TYPE := ZERO;
    signal cnt1, cnt2 : integer := 0;
    signal input1, input2 : std_logic_vector(ADC_DATA_WIDTH-1 downto 0) := (others => '0');
    signal output : std_logic_vector(DAC_DATA_WIDTH-1 downto 0) := (others => '0');
    signal sine_done : std_logic := '0';

begin

    --*****COMPONENT INSTANTIATION*****
    uut : feedback_loop
    port map(
        clk             => clk,
        reset           => reset,
        adc_data_in1    => adc_data_in1,
        adc_data_in2    => adc_data_in2,
        adc_cs_n        => adc_cs_n,
        adc_sclk        => open,
        dac_data_out    => open,
        dac_cs_n        => open,
        dac_ldac        => open,
        dac_sclk        => open,
        addsub_mode     => addsub_mode,
        add_input_mux   => add_input_mux,
        delay           => delay,
        factor          => factor,
        reset_debug     => '0',
        adc_data1_max   => open,
        adc_data2_max   => open,
        scaler_max      => open,
        dac_max         => open
    );

    clk_prc : process
    begin
        clk <= '1';
        wait for 25 ns;
        clk <= '0';
        wait for 25 ns;
    end process;
    
    process
    begin
        --INITIALISE SIGNALS
        reset   <= '1';
        wait until rising_edge(clk);
        wait until rising_edge(clk);
        reset   <= '0';
        --report "Single Input, Positive Feedback, Scale 1.32, Delay 0";
        --add_input_mux   <= '0';
        --addsub_mode     <= '1';
        --factor          <= "10101"; --1.32
        --delay           <= std_logic_vector(to_unsigned(0,DELAY_WIDTH));
        --wait until sine_done = '1';
        --report "Single Input, Negative Feedback, Scale 1.32, Delay 0";
        --add_input_mux   <= '0';
        --addsub_mode     <= '0';
        --factor          <= "10101"; --1.32
        --delay           <= std_logic_vector(to_unsigned(0,DELAY_WIDTH));
        --wait until sine_done = '1';
        --report "Single Input, Positive Feedback, Scale 0.66, Delay 0";
        --add_input_mux   <= '0';
        --addsub_mode     <= '1';
        --factor          <= "01010"; --0.66
        --delay           <= std_logic_vector(to_unsigned(0,DELAY_WIDTH));
        --wait until sine_done = '1';
        --report "Single Input, Positive Feedback, Scale 1.32, Delay 500";
        --add_input_mux   <= '0';
        --addsub_mode     <= '1';
        --factor          <= "10101"; --1.32
        --delay           <= std_logic_vector(to_unsigned(500,DELAY_WIDTH));
        --wait until sine_done = '1';
        --report "Double Input, Positive Feedback, Scale 0.66, Delay 0";
        --add_input_mux   <= '1';
        --addsub_mode     <= '1';
        --factor          <= "01010"; --0.66
        --delay           <= std_logic_vector(to_unsigned(0,DELAY_WIDTH));
        --wait until sine_done = '1';
        report "Double Input, Negative Feedback, Scale 1.32, Delay 0";
        add_input_mux   <= '1';
        addsub_mode     <= '0';
        --factor          <= "01010"; --0.66
        factor          <= "10101"; --1.32
        delay           <= std_logic_vector(to_unsigned(500,DELAY_WIDTH));
        wait until sine_done = '1';
        wait;
    end process;
    
    adc_prc : process (all)
    begin
        if rising_edge(clk) then
            sine_done   <= '0';
            case (adc_stage) is
                when ZERO =>
                    if (adc_cs_n = '0') then
                        cnt1            <= cnt1 + 1;
                        if (cnt1 = 3) then
                            cnt1        <= 11;
                            adc_stage   <= DATA;
                        end if;
                    end if;
                when DATA =>
                    if (adc_cs_n = '0') then
                        cnt1            <= cnt1 - 1;
                        if (cnt1 = 0) then
                            if (cnt2 = sine1'length-1) then
                                cnt2        <= 0;
                                sine_done   <= '1';
                            else
                                cnt2    <= cnt2 + 1;
                            end if;
                            cnt1        <= 0;
                            adc_stage   <= ZERO;
                        end if;
                    end if;
            end case;
        end if;
        
        case (adc_stage) is
            when ZERO =>
                adc_data_in1    <= '0';
                adc_data_in2    <= '0';
            when DATA =>
                adc_data_in1    <= sine1(cnt2)(cnt1);
                adc_data_in2    <= sine2(cnt2)(cnt1);
        end case;
    end process;
    
    io_prc : process (all)
        --alias in1 is <<signal uut.adc_data1 : std_logic_vector(ADC_DATA_WIDTH-1 downto 0)>>;
        --alias in2 is <<signal uut.adc_data2 : std_logic_vector(ADC_DATA_WIDTH-1 downto 0)>>;
        --alias in_done is <<signal uut.adc_done : std_logic>>;
        alias in1 is <<signal uut.delay_out : std_logic_vector(ADC_DATA_WIDTH downto 0)>>;
        alias in2 is <<signal uut.tmp2 : std_logic_vector(ADC_DATA_WIDTH-1 downto 0)>>;
        alias out1 is <<signal uut.addsub_out : std_logic_vector(DAC_DATA_WIDTH-1 downto 0)>>;
        alias out_done is <<signal uut.addsub_done : std_logic>>;
    begin
        if rising_edge(clk) then
            if (in1(ADC_DATA_WIDTH) = '1') then
                input1  <= in1(ADC_DATA_WIDTH-1 downto 0);
                input2  <= in2;
            end if;
            if (out_done = '1') then
                output  <= out1;
            end if;
        end if;
    end process;
    
end architecture;