library ieee;
use ieee.std_logic_1164.all;
--use ieee.numeric_std.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity qsfp_capture_intfc_128 is
  port (
    qsfp_capture_aclk_in                               : in  std_logic;
    qsfp_capture_aresetn_in                            : in  std_logic;
    qsfp_capture_tdata_240b_in                         : in  std_logic_vector(239 downto 0);
    qsfp_capture_tvalid_240b_in                        : in  std_logic;
    qsfp_capture_tvalid_240b_cnt_out                   : out std_logic_vector( 31 downto 0);
    qsfp_capture_fifo_aempty_512b_cnt_out              : out std_logic_vector( 31 downto 0);
    qsfp_capture_iq_240b_to_512b_overflow_cnt_out      : out std_logic_vector( 31 downto 0);

    qsfp_capture_rx_data_ready_out                     : out std_logic;
    qsfp_capture_rx_data_ready_cnt_out                 : out std_logic_vector( 31 downto 0);

    tx_device_clk_in                                    : in  std_logic;
    tx_device_clk_aresetn_in                            : in  std_logic;
    tx_tdata_128b_out                                   : out std_logic_vector(127 downto 0);
    tx_tvalid_128b_out                                  : out std_logic;
    tx_tready_128b_in                                   : in  std_logic;
    tx_tvalid_128b_cnt_out                              : out std_logic_vector( 31 downto 0);

    chan1to4_mode_sel_in                                : in  std_logic;   
    cnt_reset_in                                        : in  std_logic 
  );
end entity qsfp_capture_intfc_128;

architecture arch_imp of qsfp_capture_intfc_128 is

  signal qsfp_rx_data_ready                             : std_logic;
  signal qsfp_tvalid_240b_cnt_r                         : std_logic_vector(31 downto 0) := (others => '0'); 

  signal qsfp_prog_full_240b                            : std_logic;
  signal qsfp_afull_240b                                : std_logic;
  signal qsfp_aempty_240b                               : std_logic;

  signal qsfp_rx_data_ready_cnt_r                       : std_logic_vector(31 downto 0) := (others => '0');      

  signal qsfp_iq_240b_to_512b_overflow                  : std_logic; 
  signal qsfp_iq_240b_to_512b_overflow_cnt_r            : std_logic_vector(31 downto 0) := (others => '0');  

  signal qsfp_tdata_512b                                : std_logic_vector(511 downto 0);  
  signal qsfp_tvalid_512b                               : std_logic;
  signal qsfp_tready_512b                               : std_logic;

  signal qsfp_tdata_512b_pipe                           : std_logic_vector(511 downto 0);  
  signal qsfp_tvalid_512b_pipe                          : std_logic;
  signal qsfp_tready_512b_pipe                          : std_logic;

  signal qsfp_almost_empty_512b_pipe                    : std_logic; 
  signal qsfp_almost_empty_512b_pipe_r                  : std_logic := '1'; 
  signal qsfp_fifo_aempty_512b_cnt_r                    : std_logic_vector(31 downto 0) := (others => '0');  

  signal axis_dwidth_converter_512b_to_128b_tdata_128   : std_logic_vector(127 downto 0);   
  signal axis_dwidth_converter_512b_to_128b_tvalid_128  : std_logic;
  signal axis_dwidth_converter_512b_to_128b_tready      : std_logic;

  signal axis_dwidth_converter_512b_to_32b_tdata_32b    : std_logic_vector(31 downto 0);   
  signal axis_dwidth_converter_512b_to_32b_tvalid_32b   : std_logic;
  signal axis_dwidth_converter_512b_to_32b_tready       : std_logic;

  signal qsfp_tdata_128b                                : std_logic_vector(127 downto 0);   
  signal qsfp_tvalid_128b                               : std_logic;
  signal qsfp_tready_128b                               : std_logic;

  signal tx_tvalid_128b                                 : std_logic;              
  signal tx_fifo_aempty_128b                            : std_logic;
  signal tx_fifo_aempty_128b_r                          : std_logic := '1'; 
  signal tx_fifo_prog_aempty_128                        : std_logic;

  signal tx_tvalid_128b_ena                             : std_logic;
  signal tx_tready_128b_ena                             : std_logic;

  signal tx_pre_buff_rdy_r                              : std_logic := '0'; 

  signal tx_tvalid_128b_cnt_r                           : std_logic_vector(31 downto 0) := (others => '0');  

begin

  qsfp_capture_rx_data_ready_out                 <= qsfp_rx_data_ready;
  qsfp_capture_rx_data_ready_cnt_out             <= qsfp_rx_data_ready_cnt_r;

  qsfp_capture_tvalid_240b_cnt_out               <= qsfp_tvalid_240b_cnt_r;
  qsfp_capture_iq_240b_to_512b_overflow_cnt_out  <= qsfp_iq_240b_to_512b_overflow_cnt_r;
  qsfp_capture_fifo_aempty_512b_cnt_out          <= qsfp_fifo_aempty_512b_cnt_r;  
  tx_tvalid_128b_cnt_out                          <= tx_tvalid_128b_cnt_r;

 -- ***240 to 128 converter 
  i_iq_240b_to_512b : entity work.iq_240b_to_512b  
    port map (
    aclk                             => qsfp_capture_aclk_in,           -- in
    aresetn                          => qsfp_capture_aresetn_in,        -- in
    s_axis_tdata                     => qsfp_capture_tdata_240b_in,   -- in
    s_axis_tvalid                    => qsfp_capture_tvalid_240b_in,  -- in
    prog_full                        => qsfp_prog_full_240b,  -- out
    almost_full                      => qsfp_afull_240b,      --out

    m_axis_tdata                     => qsfp_tdata_512b,      -- out
    m_axis_tvalid                    => qsfp_tvalid_512b,     -- out
    m_axis_tready                    => qsfp_tready_512b,     -- in
    almost_empty                     => qsfp_aempty_240b,     -- out
    overflow                         => qsfp_iq_240b_to_512b_overflow,     -- out
    sel_12b_16bn                     => '0'      -- 16-bit samples    -- in
  );
  
  qsfp_rx_data_ready   <= not qsfp_prog_full_240b;

  process(qsfp_capture_aclk_in)  
  begin
    if (rising_edge(qsfp_capture_aclk_in)) then

      if (qsfp_capture_aresetn_in = '0' or cnt_reset_in = '1') then
        qsfp_tvalid_240b_cnt_r   <= (others => '0');
      elsif (qsfp_capture_tvalid_240b_in = '1') then
        qsfp_tvalid_240b_cnt_r   <= qsfp_tvalid_240b_cnt_r + 1;         
      end if;

      if (qsfp_capture_aresetn_in = '0' or cnt_reset_in = '1') then
        qsfp_rx_data_ready_cnt_r   <= (others => '0');
      elsif (qsfp_rx_data_ready = '0') then
        qsfp_rx_data_ready_cnt_r   <= qsfp_rx_data_ready_cnt_r + 1;         
      end if;

      if (qsfp_capture_aresetn_in = '0' or cnt_reset_in = '1')  then    
        qsfp_iq_240b_to_512b_overflow_cnt_r   <= (others => '0');
      elsif (qsfp_iq_240b_to_512b_overflow = '1') then
        qsfp_iq_240b_to_512b_overflow_cnt_r  <= qsfp_iq_240b_to_512b_overflow_cnt_r + 1;
      end if;

    end if;
  end process;
    
--  i_qsfp_reg_slice_512 : entity work.axis_register_slice_512
--    port map (
--      aclk                              => qsfp_capture_aclk_in,              -- in  
--      aresetn                           => qsfp_capture_aresetn_in,      -- in  
--      s_axis_tdata                      => qsfp_tdata_512b,          -- in
--      s_axis_tvalid                     => qsfp_tvalid_512b,          -- in
--      s_axis_tready                     => qsfp_tready_512b,          -- out
--      m_axis_tdata                      => qsfp_tdata_512b_pipe,       -- out
--      m_axis_tvalid                     => qsfp_tvalid_512b_pipe,      -- out
--      m_axis_tready                     => qsfp_tready_512b_pipe      -- in
--    );

-- this fifo is actually 64 words deep
  i_axis_data_fifo_32x512 : entity work.axis_data_fifo_32x512  
  port map (     
    s_axis_aclk                   => qsfp_capture_aclk_in,
    s_axis_aresetn                => qsfp_capture_aresetn_in,
    s_axis_tdata                  => qsfp_tdata_512b,
    s_axis_tvalid                 => qsfp_tvalid_512b,    
    s_axis_tready                 => qsfp_tready_512b, 
    
    m_axis_aclk                   => tx_device_clk_in,         
    m_axis_tdata                  => qsfp_tdata_512b_pipe,          -- out                  
    m_axis_tvalid                 => qsfp_tvalid_512b_pipe,         -- out                   
    m_axis_tready                 => qsfp_tready_512b_pipe,         -- in
    almost_empty                  => qsfp_almost_empty_512b_pipe                         
    );
  
  process(tx_device_clk_in)  
  begin
    if (rising_edge(tx_device_clk_in)) then
      qsfp_almost_empty_512b_pipe_r  <= qsfp_almost_empty_512b_pipe;

      if (tx_device_clk_aresetn_in = '0' or cnt_reset_in = '1') then
        qsfp_fifo_aempty_512b_cnt_r   <= (others => '0');
      elsif (qsfp_almost_empty_512b_pipe = '1' and qsfp_almost_empty_512b_pipe_r = '0') then
        qsfp_fifo_aempty_512b_cnt_r   <= qsfp_fifo_aempty_512b_cnt_r + 1;
      end if;     
    end if;     
  end process;

      
--    i_ila_128_4_qsfp1 : ila_128_4  
--      port map ( 
--      clk    => qsfp_capture_aclk_in,
--      probe0 => qsfp_tvalid_240b, 
--      probe1 => qsfp_aempty_240b, 
--      probe2 => qsfp_afull_240b, 
--      probe3 => qsfp_rx_data_ready,
--      probe4 => qsfp_tvalid_512b,  
--      probe5 => qsfp_tready_512b,
--      probe6 => qsfp_afull_512b,
--      probe7 => qsfp_prog_full_512b,
--      probe8 => qsfp_almost_empty_512b_pipe)
--    );

  qsfp_tready_512b_pipe <= axis_dwidth_converter_512b_to_32b_tready when chan1to4_mode_sel_in = '1' else axis_dwidth_converter_512b_to_128b_tready; 
  
  i_axis_dwidth_converter_512b_to_128b : entity work.axis_dwidth_converter_512b_to_128b  
    port map (
    aclk                             => tx_device_clk_in,                              -- in
    aresetn                          => tx_device_clk_aresetn_in,                   -- in
    s_axis_tdata                     => qsfp_tdata_512b_pipe,                          -- in
    s_axis_tvalid                    => qsfp_tvalid_512b_pipe,                         -- in
    s_axis_tready                    => axis_dwidth_converter_512b_to_128b_tready,      -- out
    m_axis_tdata                     => axis_dwidth_converter_512b_to_128b_tdata_128,   -- out
    m_axis_tvalid                    => axis_dwidth_converter_512b_to_128b_tvalid_128,  -- out
    m_axis_tready                    => qsfp_tready_128b                                -- in
  );


--  i_axis_dwidth_converter_512b_to_32b : entity work.axis_dwidth_converter_512b_to_32b  
--    port map (
--    aclk                             => tx_device_clk_in,                              -- in
--    aresetn                          => tx_device_clk_aresetn_in,                             -- in
--    s_axis_tdata                     => qsfp_tdata_512b_pipe,                      -- in
--    s_axis_tvalid                    => qsfp_tvalid_512b_pipe,                     -- in
--    s_axis_tready                    => axis_dwidth_converter_512b_to_32b_tready,          -- out
--    m_axis_tdata                     => axis_dwidth_converter_512b_to_32b_tdata_32b,       -- out
--    m_axis_tvalid                    => axis_dwidth_converter_512b_to_32b_tvalid_32b,      -- out
--    m_axis_tready                    => qsfp_tready_128b                           -- in
--  );

--  qsfp_tdata_128b    <= axis_dwidth_converter_512b_to_32b_tdata_32b & axis_dwidth_converter_512b_to_32b_tdata_32b & axis_dwidth_converter_512b_to_32b_tdata_32b & axis_dwidth_converter_512b_to_32b_tdata_32b when chan1to4_mode_sel_in = '1' else  axis_dwidth_converter_512b_to_128b_tdata_128;
--  qsfp_tvalid_128b   <= axis_dwidth_converter_512b_to_32b_tvalid_32b when chan1to4_mode_sel_in = '1' else  axis_dwidth_converter_512b_to_128b_tvalid_128;
    
  -- big FIFO before JESD TX PORT - actually 64 words deep
  i_tx_fifo_32kx128 : entity work.axis_data_fifo_32kx128  
    port map (
    s_axis_aclk                      => tx_device_clk_in,                        -- in
    s_axis_aresetn                   => tx_device_clk_aresetn_in,                  -- in
    s_axis_tdata                     => axis_dwidth_converter_512b_to_128b_tdata_128,   ---qsfp_tdata_128b,          -- in
    s_axis_tvalid                    => axis_dwidth_converter_512b_to_128b_tvalid_128,   --qsfp_tvalid_128b,         -- in
    s_axis_tready                    => qsfp_tready_128b,    -- out

--    m_axis_aclk                      => tx_device_clk_in,                  -- in
    m_axis_tdata                     => tx_tdata_128b_out,     -- out                       
    m_axis_tvalid                    => tx_tvalid_128b,    -- out                       
    m_axis_tready                    => tx_tready_128b_ena,        -- in                      
    almost_empty                     => tx_fifo_aempty_128b,-- out   
    prog_empty                       => tx_fifo_prog_aempty_128   -- out                     
    );

  tx_tvalid_128b_ena <= tx_pre_buff_rdy_r and tx_tvalid_128b;
  tx_tready_128b_ena <= tx_pre_buff_rdy_r and tx_tready_128b_in;
  
  process(tx_device_clk_in)  
  begin
    if (rising_edge(tx_device_clk_in)) then
      tx_fifo_aempty_128b_r  <= tx_fifo_aempty_128b;
      
      if ((tx_fifo_aempty_128b = '1') and (tx_fifo_aempty_128b_r = '1')) then
        tx_pre_buff_rdy_r  <= '0';
      elsif (tx_fifo_prog_aempty_128 = '0') then
        tx_pre_buff_rdy_r  <= '1';
      end if;     

      if (tx_device_clk_aresetn_in = '0' or cnt_reset_in = '1') then    
        tx_tvalid_128b_cnt_r   <= (others => '0');
      elsif (tx_tvalid_128b_ena = '1' and tx_tready_128b_ena = '1') then
        tx_tvalid_128b_cnt_r   <= tx_tvalid_128b_cnt_r + 1;
      end if;

    end if;     
  end process;

  tx_tvalid_128b_out <= tx_tvalid_128b_ena;
  
end architecture arch_imp;
