-- PROJECT FOFB
-- COMBPM ELECTRON TOP LEVEL
-- RBR

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

library xpm;
use xpm.vcomponents.all;

library desyrdl;
use desyrdl.common.all;
use desyrdl.pkg_combpm.all;

use work.pkg_bpmpacket_stream.all;
use work.pkg_combpm_version.all;
use work.pkg_combpm.all;

entity top_combpm_electron is
    port(

        rst_n                : in std_logic;    -- Asynchronous reset
        free_100_clk         : in std_logic;    -- Freerunning clock for GT
        pps                  : in std_logic;    -- A pulse per second signal, sync to clk domain
        mc_time              : in std_logic_vector(39 downto 0);

        -- Transceiver QPLL interface
        qpll_out_clk         : in std_logic;    -- QPLL clock for transceivers
        qpll_ref_clk         : in std_logic;    -- QPLL ref clock
        qpll_reset           : out std_logic;   -- QPLL reset
        qpll_lock            : in std_logic;    -- QPLL is locked

        -- Debug output
        debug_datarx         : out std_logic_vector(15 downto 0);
        debug_status         : out std_logic_vector(6 downto 0);
        error_detect         : out std_logic;

        -- SFP interfaces
        sfp_txp              : out std_logic;
        sfp_txn              : out std_logic;
        sfp_rxp              : in std_logic;
        sfp_rxn              : in std_logic;
        sfp_rx_los           : in std_logic;
        sfp_mod_abs          : in std_logic;
        sfp_tx_disable       : out std_logic;
        sfp_tx_fault         : in std_logic;

        -- AXIS interface
        m_axis_aclk          : out std_logic;
        m_axis_tdata         : out std_logic_vector(C_TDATA_W-1 downto 0);
        m_axis_tdest         : out std_logic_vector(C_TDEST_W-1 downto 0);
        m_axis_tvalid        : out std_logic;

        -- AXI bus interface
        s_axi_aclk  : in std_logic;
        s_axi_m2s : in  t_combpm_m2s;
        s_axi_s2m : out t_combpm_s2m
    );
end top_combpm_electron;

architecture struct of top_combpm_electron is

    ------------------------
    -- SIGNAL DECLARATION --
    ------------------------
    signal sync_resetn        : std_logic;   -- This is async reset with sync deassertion
    signal sync_reset         : std_logic;   -- This is async reset with sync deassertion
    signal usrclk             : std_logic;
    signal rst                : std_logic;

    signal frame_seq_cnt      : std_logic_vector(15 downto 0);
    signal frame_valid_cnt    : std_logic_vector(31 downto 0);
    signal frame_invalid_cnt  : std_logic_vector(31 downto 0);
    signal frame_valid_rate   : std_logic_vector(31 downto 0);
    signal frame_invalid_rate : std_logic_vector(31 downto 0);
    signal cnt_seq_mismatch   : std_logic;
    signal seq_discontinuity  : std_logic;
    signal frame_error        : std_logic;

    signal gt_datarx          : std_logic_vector(15 downto 0);
    signal gt_powergood       : std_logic;
    signal gt_rxcdrlock       : std_logic;
    signal gt_rxresetdone     : std_logic;
    signal gt_rxbyteisaligned : std_logic;
    signal gt_rxbyterealign   : std_logic;
    signal gt_rxcommadet      : std_logic;

    signal addrmap_w          : t_addrmap_combpm_in;
    signal addrmap_r          : t_addrmap_combpm_out;

    signal cdc_status_array_axi      : std_logic_vector(155 downto 0);  -- CDC, clock axi side
    signal cdc_status_array_bpm      : std_logic_vector(155 downto 0);  -- CDC, clock bpm side
    signal cdc_control_array_axi     : std_logic_vector(2 downto 0);  -- CDC, clock axi side
    signal cdc_control_array_bpm     : std_logic_vector(2 downto 0);  -- CDC, clock bpm side

    signal m_axis_decoded_m2s    : t_bpmpacket_axis_m2s;

begin


    -- This CDC is used to deassert async reset in sync with the transceiver clock.
    xpm_cdc_async_rst_inst : xpm_cdc_async_rst
    generic map (
        DEST_SYNC_FF => 4,
        INIT_SYNC_FF => 0,
        RST_ACTIVE_HIGH => 0  -- DECIMAL; 0=active low reset, 1=active high reset
    )
    port map (
        dest_arst => sync_resetn,
        dest_clk => usrclk,
        src_arst => rst_n
    );


    -- Reset invert polarity
    rst <= not rst_n;
    sync_reset <= not sync_resetn;

    -- Debug
    debug_datarx        <= gt_datarx;
    debug_status(0)     <= gt_rxcommadet;
    debug_status(1)     <= gt_rxcdrlock;
    debug_status(2)     <= gt_rxbyterealign;
    debug_status(3)     <= gt_rxbyteisaligned;
    debug_status(4)     <= frame_error;
    debug_status(5)     <= cnt_seq_mismatch;
    debug_status(6)     <= seq_discontinuity;
    error_detect        <= frame_error or cnt_seq_mismatch or seq_discontinuity;

    -- Output clock
    m_axis_aclk <= usrclk;

    -- SFP direct connexion
    sfp_tx_disable  <= '1';

    ----------------------
    -- AXI-MM INTERFACE --
    ----------------------
    blk_desyrdl: block
    begin
        inst_aximm: entity desyrdl.combpm
        port map(
            pi_clock    => s_axi_aclk,
            pi_reset    => sync_reset,
            pi_s_top    => s_axi_m2s,
            po_s_top    => s_axi_s2m,
            pi_addrmap  => addrmap_w,
            po_addrmap  => addrmap_r
        );

        addrmap_w.VERSION.data.data                 <= C_VERSION;

        cdc_status_array_bpm(0)              <= sfp_rx_los;
        cdc_status_array_bpm(1)              <= sfp_mod_abs;
        cdc_status_array_bpm(2)              <= gt_powergood;
        cdc_status_array_bpm(3)              <= qpll_lock;
        cdc_status_array_bpm(4)              <= gt_rxcdrlock;
        cdc_status_array_bpm(5)              <= gt_rxresetdone;
        cdc_status_array_bpm(6)              <= gt_rxbyteisaligned;
        cdc_status_array_bpm(7)              <= gt_rxbyterealign;
        cdc_status_array_bpm(8)              <= gt_rxcommadet;
        cdc_status_array_bpm(9)              <= frame_error;
        cdc_status_array_bpm(10)             <= cnt_seq_mismatch;
        cdc_status_array_bpm(11)             <= seq_discontinuity;

        cdc_status_array_bpm(43 downto 12)   <= frame_valid_cnt;
        cdc_status_array_bpm(75 downto 44)   <= frame_invalid_cnt;
        cdc_status_array_bpm(107 downto 76)  <= frame_valid_rate;
        cdc_status_array_bpm(139 downto 108) <= frame_invalid_rate;
        cdc_status_array_bpm(155 downto 140) <= frame_seq_cnt;

        inst_xpm_cdc_status_array_single: xpm_cdc_array_single
        generic map (
            DEST_SYNC_FF => 4,
            INIT_SYNC_FF => 0,
            SIM_ASSERT_CHK => 0,
            SRC_INPUT_REG => 1,
            WIDTH => cdc_status_array_axi'length
        )
        port map (
            dest_out => cdc_status_array_axi,
            dest_clk => s_axi_aclk,
            src_clk => usrclk,
            src_in => cdc_status_array_bpm
        );

        inst_xpm_cdc_control_array_single: xpm_cdc_array_single
        generic map (
            DEST_SYNC_FF => 4,
            INIT_SYNC_FF => 0,
            SIM_ASSERT_CHK => 0,
            SRC_INPUT_REG => 1,
            WIDTH => cdc_control_array_axi'length
        )
        port map (
            dest_out => cdc_control_array_bpm,
            dest_clk => usrclk,
            src_clk => s_axi_aclk,
            src_in => cdc_control_array_axi
        );

        addrmap_w.SFP.RXLOS.data(0)                 <= cdc_status_array_axi(0);
        addrmap_w.SFP.MODABS.data(0)                <= cdc_status_array_axi(1);
        addrmap_w.GT.POWERGOOD.data(0)              <= cdc_status_array_axi(2);
        addrmap_w.GT.QPLLLOCK.data(0)               <= cdc_status_array_axi(3);
        addrmap_w.GT.RXCDRLOCK.data(0)              <= cdc_status_array_axi(4);
        addrmap_w.GT.RXRESETDONE.data(0)            <= cdc_status_array_axi(5);
        addrmap_w.GT.RXBYTEISALIGNED.data(0)        <= cdc_status_array_axi(6);
        addrmap_w.GT.RXBYTEREALIGN.data(0)          <= cdc_status_array_axi(7);
        addrmap_w.GT.RXCOMMADET.data(0)             <= cdc_status_array_axi(8);
        addrmap_w.PROTOCOL.FRAMEERROR.data(0)       <= cdc_status_array_axi(9);
        addrmap_w.PROTOCOL.SEQFRAMECNTERROR.data(0) <= cdc_status_array_axi(10);
        addrmap_w.PROTOCOL.SEQFRAMEDISCONT.data(0)  <= cdc_status_array_axi(11);

        addrmap_w.VALIDFRAMECNT.data.data           <= cdc_status_array_axi(43 downto 12);
        addrmap_w.INVALIDFRAMECNT.data.data         <= cdc_status_array_axi(75 downto 44);
        addrmap_w.VALIDFRAMERATE.data.data          <= cdc_status_array_axi(107 downto 76);
        addrmap_w.INVALIDFRAMERATE.data.data        <= cdc_status_array_axi(139 downto 108);
        addrmap_w.FRAMESEQ.data.data                <= cdc_status_array_axi(155 downto 140);

        cdc_control_array_axi(0)                    <= addrmap_r.GT.RXRSTDATAPATH.data(0);
        cdc_control_array_axi(1)                    <= addrmap_r.GT.RXRSTPLLDATAPATH.data(0);
        cdc_control_array_axi(2)                    <= addrmap_r.GT.RXCOMMADETEN.data(0);

    end block blk_desyrdl;

    --------------------------------------
    -- LIBERA ELECTRON PROCOTOL DECODER --
    --------------------------------------
    protocol_inst: entity work.combpm_protocol_electron
    port map(
        rst_n              => sync_resetn,
        clk                => usrclk,
        pps                => pps,
        gt_datarx          => gt_datarx,

        m_axis_m2s         => m_axis_decoded_m2s,

        mc_time            => mc_time,
        soft_reset         => addrmap_w.PROTOCOL.SOFTRESET.data(0),
        frame_seq_cnt      => frame_seq_cnt,
        frame_valid_cnt    => frame_valid_cnt,
        frame_invalid_cnt  => frame_invalid_cnt,
        frame_valid_rate   => frame_valid_rate,
        frame_invalid_rate => frame_invalid_rate,
        cnt_seq_mismatch   => cnt_seq_mismatch,
        seq_discontinuity  => seq_discontinuity,
        frame_error        => frame_error
    );

    ---------------
    -- GT WIZARD --
    ---------------
    gtwizard_inst : combpm_gtwizard
    PORT MAP (
        -- Async reset
        gtwiz_reset_all_in(0)                 => rst,
        gtwiz_userclk_tx_reset_in(0)          => rst,
        gtwiz_userclk_rx_reset_in(0)          => rst,

        -- Free run clock
        gtwiz_reset_clk_freerun_in(0)         => free_100_clk,

        -- Clock and data
        gtwiz_userclk_rx_usrclk2_out(0)       => usrclk,
        gtwiz_userdata_tx_in                  => (others => '0'),
        gtwiz_userdata_rx_out                 => gt_datarx,

        -- QPLL COMMON
        gtwiz_reset_qpll1lock_in(0)           => qpll_lock,
        gtwiz_reset_qpll1reset_out(0)         => qpll_reset,
        qpll1clk_in(0)                        => qpll_out_clk,
        qpll1refclk_in(0)                     => qpll_ref_clk,

        -- Control
        gtwiz_reset_rx_datapath_in(0)          => cdc_control_array_bpm(0),
        gtwiz_reset_rx_pll_and_datapath_in(0) => cdc_control_array_bpm(1),
        rxbufreset_in                         => "0",
        rxcommadeten_in(0)                    => cdc_control_array_bpm(2),
        rx8b10ben_in                          => "1",
        rxmcommaalignen_in                    => "1",
        rxpcommaalignen_in                    => "1",
        tx8b10ben_in                          => "1",
        gtwiz_reset_tx_pll_and_datapath_in    => "0",
        gtwiz_reset_tx_datapath_in            => "0",

        -- Status
        gtwiz_userclk_tx_active_out           => open,
        gtwiz_userclk_rx_active_out           => addrmap_w.GT.RXCLKACTIVE.data,
        gtwiz_reset_tx_done_out               => open,
        gtwiz_reset_rx_done_out(0)            => gt_rxresetdone,
        gtpowergood_out(0)                    => gt_powergood,
        rxbyteisaligned_out(0)                => gt_rxbyteisaligned,
        rxbyterealign_out(0)                  => gt_rxbyterealign,
        rxcdrlock_out(0)                      => gt_rxcdrlock,
        rxcommadet_out(0)                     => gt_rxcommadet,
        rxbufstatus_out                       => open,
        rxclkcorcnt_out                       => open,
        rxpmaresetdone_out                    => open,
        txpmaresetdone_out                    => open,

        -- SFP
        gthrxn_in(0)                          => sfp_rxn,
        gthrxp_in(0)                          => sfp_rxp,
        gthtxn_out(0)                         => sfp_txn,
        gthtxp_out(0)                         => sfp_txp,

        -- Not used
        qpll0clk_in                           => "0",     -- not used
        qpll0refclk_in                        => "0",  -- not used
        gtwiz_reset_rx_cdr_stable_out         => open, -- Do not use
        gtwiz_userclk_rx_srcclk_out           => open,
        gtwiz_userclk_rx_usrclk_out           => open,
        rxctrl0_out                           => open,
        rxctrl1_out                           => open,
        rxctrl2_out                           => open,
        rxctrl3_out                           => open,
        txctrl0_in                            => (others => '0'),
        txctrl1_in                            => (others => '0'),
        txctrl2_in                            => (others => '0'),
        gtwiz_userclk_tx_srcclk_out           => open,
        gtwiz_userclk_tx_usrclk_out           => open,
        gtwiz_userclk_tx_usrclk2_out          => open
    );


    -------------------
    -- PACKET FILTER --
    -------------------
    inst_filter:entity work.combpm_packet_filter
    generic map(
        G_W_ADDR_TABLE   => C_W_ADDR_TABLE
    )
    port map(
        axis_clk     => usrclk,
        axi_clk      => s_axi_aclk,
        axis_rst_n   => sync_resetn,

        -- AXIS SLAVE INTERFACE
        s_axis_tdest  => m_axis_decoded_m2s.tdest,
        s_axis_tdata  => m_axis_decoded_m2s.tdata,
        s_axis_tvalid => m_axis_decoded_m2s.tvalid,

        -- AXIS SLAVE INTERFACE
        m_axis_tdest  => m_axis_tdest,
        m_axis_tdata  => m_axis_tdata,
        m_axis_tvalid => m_axis_tvalid,

        -- Table configuration interface
        pi_table_en     => addrmap_r.filtertable.en,
        pi_table_we     => addrmap_r.filtertable.we,
        pi_table_addr   => addrmap_r.filtertable.addr(C_W_ADDR_TABLE-1 downto 0),
        pi_table_data   => addrmap_r.filtertable.data(7 downto 0),
        po_table_data   => addrmap_w.filtertable.data(7 downto 0)
   );
   addrmap_w.filtertable.data(31 downto 8) <= (others => '0');

end architecture struct;