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

use work.typedef_package.all;


entity feedback_top is
    port (
        --XILLYBUS
        xillybus_clk    : in std_logic;
        fifo_rd_data    : out std_logic_vector(DEBUG_FIFO_DATA_WIDTH-1 downto 0);
        fifo_ren        : in std_logic;
        fifo_empty      : out std_logic;
        mem_addr        : in std_logic_vector(CONFIG_MEM_ADDR_WIDTH downto 0);
        mem_wr_data     : in std_logic_vector(CONFIG_MEM_DATA_WIDTH-1 downto 0);
        mem_wen         : in std_logic;
        mem_full        : out std_logic;
        --FPGA
        clk_in          : in std_logic;
        areset          : in std_logic;
        areset_debug    : in std_logic;
        async_pulse     : in std_logic;
        astandby        : in std_logic;
        adc_data_in1    : in std_logic;
        adc_data_in2    : in std_logic;
        adc_cs_n        : out std_logic;
        adc_sclk        : out std_logic;
        dac_data_out    : out std_logic;
        dac_cs_n        : out std_logic;
        dac_ldac        : out std_logic;
        dac_sclk        : out std_logic;
        --DEBUG
        leds            : out std_logic_vector(LED_WIDTH-1 downto 0)
    );
end entity;

architecture arch of feedback_top is
    
    --*****COMPONENT DECLARATION*****
    component clockgen is
        generic (
            CLKFBOUT_MULT   : integer := 41;
            CLKIN1_PERIOD   : real := 10.000000;
            CLKOUT0_DIVIDE  : integer := 41;
            DIVCLK_DIVIDE   : integer := 5
        );
        port (
            clk_in  : in std_logic;
            clk_out : out std_logic
        );
    end component;
    
    component synchronizer is
        generic (
            SYNC_STAGES : integer := 2
        );
        port (
            clk     : in std_logic;
            input   : in std_logic;
            output  : out std_logic
        );
    end component;
    
    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;
    
    component feedback_controller is
        port (
            clk             : in std_logic;
            reset           : in std_logic;
            
            sync_pulse      : in std_logic;
            
            mem_addr        : out std_logic_vector(CONFIG_MEM_ADDR_WIDTH-1 downto 0);
            mem_ren         : out std_logic;
            mem_data        : in std_logic_vector(CONFIG_DATA_WIDTH-1 downto 0);
            
            addsub_mode     : out std_logic;
            add_input_mux   : out std_logic;
            delay           : out std_logic_vector(DELAY_WIDTH-1 downto 0);
            factor          : out std_logic_vector(FACTOR_WIDTH-1 downto 0)
        );
    end component;
    
    component xillybus_link is
        generic (
            DEBUG_SEND_INTERVAL : integer := 20000000
        );
        port (
            --***XILLYBUS IF***
            xillybus_clk    : in std_logic;
            -- FIFO
            fifo_rd_data    : out std_logic_vector(DEBUG_FIFO_DATA_WIDTH-1 downto 0);
            fifo_ren        : in std_logic;
            fifo_empty      : out std_logic;
            -- RAM
            mem_addr        : in std_logic_vector(CONFIG_MEM_ADDR_WIDTH downto 0);
            mem_wr_data     : in std_logic_vector(CONFIG_MEM_DATA_WIDTH-1 downto 0);
            mem_wen         : in std_logic;
            --***FPGA IF***
            fpga_clk        : in std_logic;
            reset           : in std_logic;
            -- Dynamic Configuration
            config_addr     : in std_logic_vector(CONFIG_MEM_ADDR_WIDTH-1 downto 0);
            config_ren      : in std_logic;
            config_data     : out std_logic_vector(CONFIG_DATA_WIDTH-1 downto 0);
            -- DEBUG
            adc_data1_max   : in std_logic_vector(ADC_DATA_WIDTH-1 downto 0);
            adc_data2_max   : in std_logic_vector(ADC_DATA_WIDTH-1 downto 0);
            scaler_max      : in std_logic_vector(DAC_DATA_WIDTH-1 downto 0);
            dac_max         : in std_logic_vector(DAC_DATA_WIDTH-1 downto 0)
        );
    end component;
    
    
    
    --*****SIGNAL DECLARATION*****
    signal clk_20, reset, sync_pulse, standby, reset_debug : std_logic := '0';
    signal addsub_mode, add_input_mux : std_logic := '0';
    signal delay : std_logic_vector(DELAY_WIDTH-1 downto 0) := (others => '0');
    signal factor : std_logic_vector(FACTOR_WIDTH-1 downto 0) := (others => '0');
    signal adc_data1_max, adc_data2_max : std_logic_vector(ADC_DATA_WIDTH-1 downto 0) := (others => '0');
    signal scaler_max, dac_max : std_logic_vector(DAC_DATA_WIDTH-1 downto 0) := (others => '0');
    signal config_addr : std_logic_vector(CONFIG_MEM_ADDR_WIDTH-1 downto 0) := (others => '0');
    signal config_ren : std_logic := '0';
    signal config_data : std_logic_vector(CONFIG_DATA_WIDTH-1 downto 0) := (others => '0');
    
begin
    
    clockgen_inst : clockgen
        generic map (
            CLKFBOUT_MULT   => CLKFBOUT_MULT,
            CLKIN1_PERIOD   => CLKIN1_PERIOD,
            CLKOUT0_DIVIDE  => CLKOUT0_DIVIDE,
            DIVCLK_DIVIDE   => DIVCLK_DIVIDE
        )
        port map(
            clk_in  => clk_in,
            clk_out => clk_20
        );
    
    reset_sync : synchronizer
        generic map (
            SYNC_STAGES => SYNC_STAGES
        )
        port map (
            clk     => clk_20,
            input   => areset,
            output  => reset
        );
    
    sync_pulse_sync : synchronizer
        generic map (
            SYNC_STAGES => SYNC_STAGES
        )
        port map (
            clk     => clk_20,
            input   => async_pulse,
            output  => sync_pulse
        );
    
    standby_sync : synchronizer
        generic map (
            SYNC_STAGES => SYNC_STAGES
        )
        port map (
            clk     => clk_20,
            input   => astandby,
            output  => standby
        );
    
    reset_debug_sync : synchronizer
        generic map (
            SYNC_STAGES => SYNC_STAGES
        )
        port map (
            clk     => clk_20,
            input   => areset_debug,
            output  => reset_debug
        );
    
    feedback_loop_inst : feedback_loop
        port map (
            clk             => clk_20,
            reset           => reset,
            adc_data_in1    => adc_data_in1,
            adc_data_in2    => adc_data_in2,
            adc_cs_n        => adc_cs_n,
            adc_sclk        => adc_sclk,
            dac_data_out    => dac_data_out,
            dac_cs_n        => dac_cs_n,
            dac_ldac        => dac_ldac,
            dac_sclk        => dac_sclk,
            addsub_mode     => addsub_mode,
            add_input_mux   => add_input_mux,
            delay           => delay,
            factor          => factor,
            reset_debug     => reset_debug,
            adc_data1_max   => adc_data1_max,
            adc_data2_max   => adc_data2_max,
            scaler_max      => scaler_max,
            dac_max         => dac_max
        );
    
    feedback_controller_inst : feedback_controller
        port map (
            clk             => clk_20,
            reset           => reset or standby,
            sync_pulse      => sync_pulse,
            mem_addr        => config_addr,
            mem_ren         => config_ren,
            mem_data        => config_data,
            addsub_mode     => addsub_mode,
            add_input_mux   => add_input_mux,
            delay           => delay,
            factor          => factor
        );
    
    xillybus_link_inst : xillybus_link
        generic map (
            DEBUG_SEND_INTERVAL => DEBUG_SEND_INTERVAL
        )
        port map (
            xillybus_clk    => xillybus_clk,
            fifo_rd_data    => fifo_rd_data,
            fifo_ren        => fifo_ren,
            fifo_empty      => fifo_empty,
            mem_addr        => mem_addr,
            mem_wr_data     => mem_wr_data,
            mem_wen         => mem_wen,
            fpga_clk        => clk_20,
            reset           => reset,
            config_addr     => config_addr,
            config_ren      => config_ren,
            config_data     => config_data,
            adc_data1_max   => adc_data1_max,
            adc_data2_max   => adc_data2_max,
            scaler_max      => scaler_max,
            dac_max         => dac_max
        );

    -- Connect standy to mem_full signal, to prevent simutanuoys read/writing on memory
    mem_full    <= not standby;
    -- Connect leds
    led_prc : process(all)
    begin
        leds                <= (others => '0');
        leds(LED_WIDTH-1)   <= standby;
    end process;
    
end architecture;