library ieee;
use ieee.std_logic_1164.all;

library UNISIM;
use UNISIM.vcomponents.all;

library sychro1;
library utilities;
library comm;

library kakona;
use kakona.kakona_package.all;

entity xilly_toplevel is
        
  port (
                -- FMC & other ports:
                
                -- local oscillator to be divided
                IN_CLK_LO_N : IN std_logic;
                IN_CLK_LO_P : IN std_logic;  
                -- divided clock
                OUT_CLK_LO_DIVIDED_N : OUT std_logic;
                OUT_CLK_LO_DIVIDED_P : OUT std_logic;
                
                -- input data:
                -- clock:
                IN_CLK_FOR_DATA_P : IN std_logic;
                IN_CLK_FOR_DATA_N : IN std_logic;
                -- frame signal:
                IN_FRAME_FOR_DATA_N : IN std_logic;
                IN_FRAME_FOR_DATA_P : IN std_logic;
                
                -- data from ADCs:
                IN_DATA_ADC_P : IN std_logic_vector( C_NUM_INPUT_ADC_DATA_PORTS - 1 downto 0 );
                IN_DATA_ADC_N : IN std_logic_vector( C_NUM_INPUT_ADC_DATA_PORTS - 1 downto 0 );
                
                -- our LEDs:
                GPIO_LED2 : OUT std_logic_vector(3 DOWNTO 0);
                
                -- SPI communication block:
                OUT_SPI_N_CE : OUT std_logic_vector( C_NUM_INPUT_ADC_MODULES-1 downto 0 );
                OUT_SPI_DOUT : OUT std_logic;
                OUT_SPI_CLK  : OUT std_logic;
                                
                -- test:
                --OUT_TEST1 : OUT std_logic;
                
                -- dummy inputs due to incorrect soldering -- pins are hardconnected to ground.
                IN_DUMMY : IN std_logic_vector( 1 downto 0 );
                
                -- GPIO_DIP_SWITCH:
                GPIO_DIP_SW : IN std_logic_vector( 7 downto 0 );
                
                -- original xillybus-only ports:
    PCIE_PERST_B_LS : IN std_logic;
    PCIE_REFCLK_N : IN std_logic;
    PCIE_REFCLK_P : IN std_logic;
    PCIE_RX_N : IN std_logic_vector(3 DOWNTO 0);
    PCIE_RX_P : IN std_logic_vector(3 DOWNTO 0);
    GPIO_LED : OUT std_logic_vector(3 DOWNTO 0);
    PCIE_TX_N : OUT std_logic_vector(3 DOWNTO 0);
    PCIE_TX_P : OUT std_logic_vector(3 DOWNTO 0));
end xilly_toplevel;

architecture behavioral of xilly_toplevel is

        component multiplexer_from_fifos
generic
        ( G_NUM_CHANNELS : natural :=  2;  -- number of channels
                G_DATA_WIDTH   : natural := 32   -- data width of individual packets
        );
port (
        
        clk : in std_logic;
        rst : in std_logic;
        
        -- input side
        i_data  : in std_logic_vector( G_DATA_WIDTH*G_NUM_CHANNELS - 1 downto 0 );
        i_valid : in std_logic_vector( G_NUM_CHANNELS - 1 downto 0 );
        o_rden  : out std_logic_vector( G_NUM_CHANNELS - 1 downto 0 );
        
        -- output side
        o_data  : out std_logic_vector( G_DATA_WIDTH - 1 downto 0 );
        o_valid : out std_logic;
        i_full  : in std_logic

);
end component;

  COMPONENT fifo_32x512_walmostfull
  PORT (
    clk : IN STD_LOGIC;
    srst : IN STD_LOGIC;
    din : IN STD_LOGIC_VECTOR(31 DOWNTO 0);
    wr_en : IN STD_LOGIC;
    rd_en : IN STD_LOGIC;
    dout : OUT STD_LOGIC_VECTOR(31 DOWNTO 0);
    full : OUT STD_LOGIC;
    empty : OUT STD_LOGIC;
    valid : OUT STD_LOGIC;
    prog_full : OUT STD_LOGIC
  );
        END COMPONENT;

 component bitslip_compensation
  port (
  
                clk : in std_logic;
                rst : in std_logic;
                
                i_data : in std_logic_vector( 15 downto 0 );
                i_valid : in std_logic;
                
                o_bitslip            : out std_logic;
                o_bitslip_done       : out std_logic;
                o_bitslip_drop_byte  : out std_logic;
                o_bitslip_failed     : out std_logic
                
        );
        end component;

component iserdes_clock_generator
port
 (
        -- Clock and reset signals
  CLK_IN_P                : in    std_logic;  -- Differential fast clock from IOB
  CLK_IN_N                : in    std_logic;
  CLK_OUT                                                     : out   std_logic;  -- Fast clock output (synchronous to data)
  CLK_DIV_OUT             : out   std_logic;  -- Slow clock output
  
  CLK_RESET               : in    std_logic); -- Reset signal for Clock circuit

end component;


  component xillybus
    port (
      PCIE_PERST_B_LS : IN std_logic;
      PCIE_REFCLK_N : IN std_logic;
      PCIE_REFCLK_P : IN std_logic;
      PCIE_RX_N : IN std_logic_vector(3 DOWNTO 0);
      PCIE_RX_P : IN std_logic_vector(3 DOWNTO 0);
      GPIO_LED : OUT std_logic_vector(3 DOWNTO 0);
      PCIE_TX_N : OUT std_logic_vector(3 DOWNTO 0);
      PCIE_TX_P : OUT std_logic_vector(3 DOWNTO 0);
      bus_clk : OUT std_logic;
      quiesce : OUT std_logic;
      user_r_control_r_rden : OUT std_logic;
      user_r_control_r_empty : IN std_logic := '0';
      user_r_control_r_data : IN std_logic_vector(31 DOWNTO 0) := ( others => '0' );
      user_r_control_r_eof : IN std_logic := '0';
      user_r_control_r_open : OUT std_logic;
      user_w_control_w_wren : OUT std_logic;
      user_w_control_w_full : IN std_logic := '0';
      user_w_control_w_data : OUT std_logic_vector(31 DOWNTO 0);
      user_w_control_w_open : OUT std_logic;
      user_r_data1_r_rden : OUT std_logic;
      user_r_data1_r_empty : IN std_logic;
      user_r_data1_r_data : IN std_logic_vector(31 DOWNTO 0);
      user_r_data1_r_eof : IN std_logic;
      user_r_data1_r_open : OUT std_logic;
      user_w_data1_w_wren : OUT std_logic;
      user_w_data1_w_full : IN std_logic;
      user_w_data1_w_data : OUT std_logic_vector(31 DOWNTO 0);
      user_w_data1_w_open : OUT std_logic;
      user_r_data2_r_rden : OUT std_logic;
      user_r_data2_r_empty : IN std_logic := '0';
      user_r_data2_r_data : IN std_logic_vector(31 DOWNTO 0) := ( others => '0' );
      user_r_data2_r_eof : IN std_logic := '0';
      user_r_data2_r_open : OUT std_logic;
      user_w_data2_w_wren : OUT std_logic;
      user_w_data2_w_full : IN std_logic := '0';
      user_w_data2_w_data : OUT std_logic_vector(31 DOWNTO 0);
      user_w_data2_w_open : OUT std_logic);
  end component;

        component xilly_userlogiccmp_wrapper
  port (
                i_clk : in std_logic;
                i_rst : in std_logic;
                
                user_r_control_r_rden : in std_logic := '0';
                user_r_control_r_empty : out std_logic := '1';
                user_r_control_r_data : out std_logic_vector(31 DOWNTO 0);
                
                user_w_control_w_wren : in std_logic := '0';
                user_w_control_w_full : out std_logic := '0';
                user_w_control_w_data : in std_logic_vector(31 DOWNTO 0) := ( others => '0' );
                
                user_r_data1_r_rden : in std_logic := '0';
                user_r_data1_r_empty : out std_logic := '1';
                user_r_data1_r_data : out std_logic_vector(31 DOWNTO 0);
                
                user_w_data1_w_wren : in std_logic := '0';
                user_w_data1_w_full : out std_logic := '0';
                user_w_data1_w_data : in std_logic_vector(31 DOWNTO 0) := ( others => '0' );
                
                user_r_data2_r_rden : in std_logic := '0';
                user_r_data2_r_empty : out std_logic := '1';
                user_r_data2_r_data : out std_logic_vector(31 DOWNTO 0);
                
                user_w_data2_w_wren : in std_logic := '0';
                user_w_data2_w_full : out std_logic := '0';
                user_w_data2_w_data : in std_logic_vector(31 DOWNTO 0) := ( others => '0' )
        );
        end component;
        
  signal bus_clk :  std_logic;
  signal quiesce : std_logic;

        signal user_r_control_r_rden : std_logic;
        signal user_r_control_r_empty : std_logic;
        signal user_r_control_r_data : std_logic_vector(31 DOWNTO 0);
        --signal user_r_control_r_eof : std_logic;
  signal user_r_control_r_open : std_logic;
  signal user_w_control_w_wren : std_logic;
  signal user_w_control_w_full : std_logic;
  signal user_w_control_w_data : std_logic_vector(31 DOWNTO 0);
  signal user_w_control_w_open : std_logic;
 
  signal user_r_data1_r_rden : std_logic;
  signal user_r_data1_r_empty : std_logic;
  signal user_r_data1_r_data : std_logic_vector(31 DOWNTO 0);
  --signal user_r_data1_r_eof : std_logic;
  signal user_r_data1_r_open : std_logic;
  signal user_w_data1_w_wren : std_logic;
  signal user_w_data1_w_full : std_logic;
  signal user_w_data1_w_data : std_logic_vector(31 DOWNTO 0);
  signal user_w_data1_w_open : std_logic;
  
        signal user_r_data2_r_rden : std_logic;
  signal user_r_data2_r_empty : std_logic;
  signal user_r_data2_r_data : std_logic_vector(31 DOWNTO 0);
  --signal user_r_data2_r_eof : std_logic;
  signal user_r_data2_r_open : std_logic;
  signal user_w_data2_w_wren : std_logic;
  signal user_w_data2_w_full : std_logic;
  signal user_w_data2_w_data : std_logic_vector(31 DOWNTO 0);
  signal user_w_data2_w_open : std_logic;
        
        -- reset signal from xillybus. '1' when no device is open
        signal s_reset : std_logic;
        

        -- generated clock from ADC by iserdes_clock_generator: 
        signal s_iserdes_clk     : std_logic;
        signal s_iserdes_clk_div : std_logic;
                
        -- Frame signal
        signal s_data16_to_bitslip       : std_logic_vector( 15 downto 0 );
        signal s_data16_to_bitslip_valid : std_logic;
        signal s_bitslip            : std_logic;
        signal s_bitslip_done       : std_logic;
        signal s_bitslip_drop_byte  : std_logic;
        signal s_bitslip_failed     : std_logic;
        
        signal s_bitslip_regged  : std_logic;
        signal s_bitslip_drop_byte_regged  : std_logic;
        
        -- from all ADC processing blocks:
        signal s_from_processing_blocks_data : std_logic_vector( (C_NUM_INPUT_ADC_DATA_PORTS+1)*32 - 1 downto 0 ); -- +1 is space for output from frame
        signal s_from_processing_blocks_valid : std_logic_vector( C_NUM_INPUT_ADC_DATA_PORTS+1 - 1 downto 0 );
        signal s_from_processing_blocks_rden : std_logic_vector( C_NUM_INPUT_ADC_DATA_PORTS+1 - 1 downto 0 );
        
        -- from multiplexer:
        signal s_from_multiplexer_data : std_logic_vector( 31 downto 0 );
        signal s_from_multiplexer_valid : std_logic;
        signal s_from_multiplexer_full : std_logic;
                                                                        
        -- SPI communication module:
        signal s_spi_done : std_logic;
        
        -- GPIO_DIP_SW register
        signal s_gpio_dip_sw : std_logic_vector( 7 downto 0 );
        
        signal s_valid_for_bitslip_processing : std_logic;
                                                                                
begin
        
        -- Xillybus instantiation:
  xillybus_ins : xillybus
    port map (
      -- Ports related to /dev/xillybus_control_r
      -- FPGA to CPU signals:
      user_r_control_r_rden => user_r_control_r_rden,
      user_r_control_r_empty => user_r_control_r_empty,
      user_r_control_r_data => user_r_control_r_data,
      user_r_control_r_eof => '0',
      user_r_control_r_open => user_r_control_r_open,

      -- Ports related to /dev/xillybus_control_w
      -- CPU to FPGA signals:
      user_w_control_w_wren => user_w_control_w_wren,
      user_w_control_w_full => user_w_control_w_full,
      user_w_control_w_data => user_w_control_w_data,
      user_w_control_w_open => user_w_control_w_open,
                        
      -- Ports related to /dev/xillybus_data1_r
      -- FPGA to CPU signals:
      user_r_data1_r_rden => user_r_data1_r_rden,
      user_r_data1_r_empty => user_r_data1_r_empty,
      user_r_data1_r_data => user_r_data1_r_data,
      user_r_data1_r_eof => '0',
      user_r_data1_r_open => user_r_data1_r_open,

      -- Ports related to /dev/xillybus_data1_w
      -- CPU to FPGA signals:
      user_w_data1_w_wren => user_w_data1_w_wren,
      user_w_data1_w_full => user_w_data1_w_full,
      user_w_data1_w_data => user_w_data1_w_data,
      user_w_data1_w_open => user_w_data1_w_open,

      -- Ports related to /dev/xillybus_data2_r
      -- FPGA to CPU signals:
      user_r_data2_r_rden => user_r_data2_r_rden,
      user_r_data2_r_empty => user_r_data2_r_empty,
      user_r_data2_r_data => user_r_data2_r_data,
      user_r_data2_r_eof => '0',
      user_r_data2_r_open => user_r_data2_r_open,

      -- Ports related to /dev/xillybus_data2_w
      -- CPU to FPGA signals:
      user_w_data2_w_wren => user_w_data2_w_wren,
      user_w_data2_w_full => user_w_data2_w_full,
      user_w_data2_w_data => user_w_data2_w_data,
      user_w_data2_w_open => user_w_data2_w_open,

      -- General signals
      PCIE_PERST_B_LS => PCIE_PERST_B_LS,
      PCIE_REFCLK_N => PCIE_REFCLK_N,
      PCIE_REFCLK_P => PCIE_REFCLK_P,
      PCIE_RX_N => PCIE_RX_N,
      PCIE_RX_P => PCIE_RX_P,
      GPIO_LED => GPIO_LED,
      PCIE_TX_N => PCIE_TX_N,
      PCIE_TX_P => PCIE_TX_P,
      bus_clk => bus_clk,
      quiesce => quiesce
  ); 

        s_reset <= '0' when user_r_control_r_open = '1' or
                                                                                        user_w_control_w_open = '1' or 
                                                                                        user_r_data1_r_open = '1' or
                                                                                        user_w_data1_w_open = '1' or
                                                                                        user_r_data2_r_open = '1' or
                                                                                        user_w_data2_w_open = '1' or
                                                                                        s_gpio_dip_sw(0) = '0' else
                                                 '1';
        
        -- register the gpio_dip_sw(0) with the 125MHz clock:
        registers_for_gpio0 : process( bus_clk )
        begin
                if( rising_edge( bus_clk ) ) then
                        s_gpio_dip_sw(0) <= gpio_dip_sw(0);
                        s_gpio_dip_sw(2) <= gpio_dip_sw(2); -- used for SPI confifuration block that is clocked with bus_clk
                end if; 
        end process;
        
        -- register the gpio_dip_sw(1) with the clk_div clock:
        registers_for_gpio1 : process( s_iserdes_clk_div )
        begin
                if( rising_edge( s_iserdes_clk_div ) ) then
                        s_gpio_dip_sw(1) <= gpio_dip_sw(1);
                end if; 
        end process;
        
        
        -- xilly_userlogiccmp_wrapper instantiation:
        xilly_userlogiccmp_wrapper_inst : xilly_userlogiccmp_wrapper
  port map (
                i_clk => bus_clk,
                i_rst => s_reset,
                
                user_r_control_r_rden  => user_r_control_r_rden,
                user_r_control_r_empty => user_r_control_r_empty,
                user_r_control_r_data  => user_r_control_r_data,
                
                user_w_control_w_wren => user_w_control_w_wren,
                user_w_control_w_full => user_w_control_w_full,
                user_w_control_w_data => user_w_control_w_data,
                
                user_r_data1_r_rden  => user_r_data1_r_rden,
                user_r_data1_r_empty => user_r_data1_r_empty,
                user_r_data1_r_data  => user_r_data1_r_data,
                
                user_w_data1_w_wren => user_w_data1_w_wren,
                user_w_data1_w_full => user_w_data1_w_full,
                user_w_data1_w_data => user_w_data1_w_data,
                
--              user_r_data2_r_rden  => user_r_data2_r_rden,
--              user_r_data2_r_empty => user_r_data2_r_empty,
--              user_r_data2_r_data  => user_r_data2_r_data,
                
--              user_w_data2_w_wren => user_w_data2_w_wren,
--              user_w_data2_w_full => user_w_data2_w_full,
--              user_w_data2_w_data => user_w_data2_w_data
                
                user_r_data2_r_rden  => open,
                user_r_data2_r_empty => open,
                user_r_data2_r_data  => open,
                
                user_w_data2_w_wren => open,
                user_w_data2_w_full => open,
                user_w_data2_w_data => open
                
        );







        -- tie outputs:
        
        --OUT_TEST1 <= '0';
        
        
        
                
        GPIO_LED2(0) <= s_bitslip_done;
        GPIO_LED2(1) <= s_bitslip_failed;
        GPIO_LED2(2) <= s_bitslip_drop_byte_regged;
        GPIO_LED2(3) <= s_bitslip_regged;

        ddd : process( s_iserdes_clk_div )
        begin
                if( rising_edge( s_iserdes_clk_div ) ) then
                        if( s_reset = '1' ) then
                                s_bitslip_regged <= '0';
                                s_bitslip_drop_byte_regged <= '0';
                        else
                                s_bitslip_regged <= s_bitslip_regged or s_bitslip;
                                s_bitslip_drop_byte_regged <= s_bitslip_drop_byte_regged or s_bitslip_drop_byte;
                        end if;
                end if;
        end process;

        -----------------------------------------------------------------------------------------------
        -- DATA PROCESSING:
                        
        -- Clock generator:
        iserdes_clock_generator_inst : iserdes_clock_generator
        port map (
                CLK_IN_P => IN_CLK_FOR_DATA_P, CLK_IN_N => IN_CLK_FOR_DATA_N,
                CLK_OUT => s_iserdes_clk, CLK_DIV_OUT => s_iserdes_clk_div, CLK_RESET => '0' );
                
        -- FRAME signal processing block:
        frame_processing_block_inst : entity work.processing_block
        port map (
                clk_iserdes_in => s_iserdes_clk, clk_iserdes_in_div => s_iserdes_clk_div, clk_global => bus_clk,
                rst => s_reset, 
                bitslip => s_bitslip, bitslip_done => s_bitslip_done, bitslip_drop_byte => s_bitslip_drop_byte,
                in_data_p => IN_FRAME_FOR_DATA_P, in_data_n => IN_FRAME_FOR_DATA_N,
                in_data_swap_pn => C_FRAME_WIRES_SWAPPED_PN,
                in_output_counting => s_gpio_dip_sw(1),
                
                o_iserdes_output => s_data16_to_bitslip,
                o_iserdes_output_valid => s_data16_to_bitslip_valid,
                
                o_data => s_from_processing_blocks_data( 31 downto 0 ),
                o_valid => s_from_processing_blocks_valid( 0 ),
                i_rden => s_from_processing_blocks_rden( 0 )
        );      
        
        -- bitslip processing:
        s_valid_for_bitslip_processing <= s_data16_to_bitslip_valid and s_spi_done;
        
        bitslip_compensation_inst : bitslip_compensation
        port map (
                clk => s_iserdes_clk_div, rst => s_reset,
                i_data => s_data16_to_bitslip, i_valid => s_valid_for_bitslip_processing,
                o_bitslip => s_bitslip, o_bitslip_done => s_bitslip_done, o_bitslip_drop_byte => s_bitslip_drop_byte, o_bitslip_failed => s_bitslip_failed );
        
        -- ADCs signal processing blocks:
        adc_proc_block_gen : for i in 0 to C_NUM_INPUT_ADC_DATA_PORTS - 1 generate
                adc_processing_block_inst : entity work.processing_block
                port map (
                        clk_iserdes_in => s_iserdes_clk, clk_iserdes_in_div => s_iserdes_clk_div, clk_global => bus_clk,
                        rst => s_reset, 
                        bitslip => s_bitslip, bitslip_done => s_bitslip_done, bitslip_drop_byte => s_bitslip_drop_byte,
                        in_data_p => IN_DATA_ADC_P(i), in_data_n => IN_DATA_ADC_N(i),
                        in_data_swap_pn => C_DATA_WIRES_SWAPPED_PN(i),
                        in_output_counting => '0',
                        
                        o_iserdes_output => open, o_iserdes_output_valid => open,
                        
                        o_data => s_from_processing_blocks_data( 32*(i+1+1) - 1 downto 32*(i+1) ), -- i+1, because 31 downto 0 is used by the FRAME result
                        o_valid => s_from_processing_blocks_valid( i + 1 ),
                        i_rden => s_from_processing_blocks_rden( i + 1 )
                );
        end generate;
        
        -- multiplexer:
        multiplexer_from_fifos_inst : multiplexer_from_fifos
        generic map (
                G_NUM_CHANNELS => C_NUM_INPUT_ADC_DATA_PORTS + 1,
                G_DATA_WIDTH => 32 )
        port map (
                clk => bus_clk, rst => s_reset,
                
                i_data => s_from_processing_blocks_data, i_valid => s_from_processing_blocks_valid,
                o_rden => s_from_processing_blocks_rden,
                
                o_data => s_from_multiplexer_data, o_valid => s_from_multiplexer_valid, i_full => s_from_multiplexer_full
        );
        
        -- interface to xillybus:
        -- FIFO_OUT instantiation:
        data2_frame_fifo_out_inst : fifo_32x512_walmostfull
                port map (
                        clk => bus_clk, srst => s_reset,
                        din => s_from_multiplexer_data, wr_en => s_from_multiplexer_valid, full => open, prog_full => s_from_multiplexer_full,
                        dout => user_r_data2_r_data, rd_en => user_r_data2_r_rden, empty => user_r_data2_r_empty, valid => open );
        
        -----------------------------------------------------------------------------------------------
        -- LO - Local Oscillator division module:
        
        -- TODO: not tested: addition of the CE input. Will the ADCs configure themselves without CLOCK?
        lo_divider_wrapper_inst : entity work.lo_divider_wrapper
        generic map ( G_DIVISOR => 30 )
  port map (
                IN_CLK_LO_N => IN_CLK_LO_N, IN_CLK_LO_P => IN_CLK_LO_P, in_clk_enable => '1',
                OUT_CLK_LO_DIVIDED_N => OUT_CLK_LO_DIVIDED_N, OUT_CLK_LO_DIVIDED_P => OUT_CLK_LO_DIVIDED_P );
        
        -----------------------------------------------------------------------------------------------
        -- SPI MASTER COMMUNICATION MODULE
        
        spi_transmitter_wrapper_inst : entity work.spi_transmitter_wrapper
        generic map(
                G_DATA1 => C_SPI_ADC_DATA1,
                G_DATA2 => C_SPI_ADC_DATA2,
                G_NUM_BITS_PACKET => C_SPI_ADC_LENGTH,
                G_NUM_PACKETS => C_SPI_ADC_PACKETS,
                G_NUM_BITS_PAUSE => C_SPI_ADC_PAUSE )
        port map(
                i_clk125 => bus_clk, i_reset => s_reset, i_data_selector => s_gpio_dip_sw(2),
                o_done => s_spi_done,
                OUT_SPI_N_CE => OUT_SPI_N_CE, OUT_SPI_DOUT => OUT_SPI_DOUT, OUT_SPI_CLK => OUT_SPI_CLK  );
                                                                                                              
end architecture;