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 qsfp1_capture_intfc is
  port (
    qsfp1_capture_aclk_in                               : in  std_logic;
    qsfp1_capture_aresetn_in                            : in  std_logic;
    qsfp1_capture_tdata_240b_in                         : in  std_logic_vector(239 downto 0);
    qsfp1_capture_tvalid_240b_in                        : in  std_logic;
    qsfp1_capture_tvalid_240b_cnt_out                   : out std_logic_vector( 31 downto 0);
    qsfp1_capture_fifo_aempty_512b_cnt_out              : out std_logic_vector( 31 downto 0);
    qsfp1_capture_iq_240b_to_512b_overflow_cnt_out      : out std_logic_vector( 31 downto 0);

    qsfp1_capture_rx_data_ready_out                     : out std_logic;
    qsfp1_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_fifo_tdata_256b_out                              : out std_logic_vector(255 downto 0);
    tx_fifo_tvalid_256b_out                             : out std_logic;
    tx_fifo_tready_256b_in                              : in  std_logic;
    tx_fifo_tvalid_256b_cnt_out                         : out std_logic_vector( 31 downto 0);

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

architecture arch_imp of qsfp1_capture_intfc is

  signal qsfp1_rx_data_ready                            : std_logic;
  signal qsfp1_tvalid_240b_cnt_r                        : std_logic_vector(31 downto 0) := (others => '0'); 

  signal qsfp1_prog_full_240b                           : std_logic;
  signal qsfp1_afull_240b                               : std_logic;
  signal qsfp1_aempty_240b                              : std_logic;

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

  signal qsfp1_iq_240b_to_512b_overflow                 : std_logic; 
  signal qsfp1_iq_240b_to_512b_overflow_cnt_r           : std_logic_vector(31 downto 0) := (others => '0');  

  signal qsfp1_tdata_512b                               : std_logic_vector(511 downto 0);  
  signal qsfp1_tvalid_512b                              : std_logic;
  signal qsfp1_tready_512b                              : std_logic;

  signal qsfp1_tdata_512b_pipe                          : std_logic_vector(511 downto 0);  
  signal qsfp1_tvalid_512b_pipe                         : std_logic;
  signal qsfp1_tready_512b_pipe                         : std_logic;

  signal qsfp1_almost_empty_512b_pipe                   : std_logic; 
  signal qsfp1_almost_empty_512b_pipe_r                 : std_logic := '1'; 
  signal qsfp1_fifo_aempty_512b_cnt_r                   : std_logic_vector(31 downto 0) := (others => '0');  

  signal axis_dwidth_converter_tdata_512b_to_256b       : std_logic_vector(255 downto 0);   
  signal axis_dwidth_converter_tvalid_512b_to_256b      : std_logic;

  signal tx_fifo_tready_256b                            : std_logic;

  signal tx_fifo_tvalid_256b                            : std_logic;              
  signal tx_fifo_aempty_256b                            : std_logic;
  signal tx_fifo_aempty_256b_r                          : std_logic := '1'; 
  signal tx_fifo_prog_aempty_256b                       : std_logic;

  signal tx_fifo_tvalid_256b_ena                        : std_logic;
  signal tx_fifo_tready_256b_ena                        : std_logic;

  signal tx_pre_buff_rdy_r                              : std_logic := '0'; 

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

begin

  qsfp1_capture_rx_data_ready_out                 <= qsfp1_rx_data_ready;
  qsfp1_capture_rx_data_ready_cnt_out             <= qsfp1_rx_data_ready_cnt_r;

  qsfp1_capture_tvalid_240b_cnt_out               <= qsfp1_tvalid_240b_cnt_r;
  qsfp1_capture_iq_240b_to_512b_overflow_cnt_out  <= qsfp1_iq_240b_to_512b_overflow_cnt_r;
  qsfp1_capture_fifo_aempty_512b_cnt_out          <= qsfp1_fifo_aempty_512b_cnt_r;  
  tx_fifo_tvalid_256b_cnt_out                     <= tx_fifo_tvalid_256b_cnt_r;

 -- ***240 to 512 converter 
  i_iq_240b_to_512b : entity work.iq_240b_to_512b  
    port map (
    aclk                             => qsfp1_capture_aclk_in,          -- in
    aresetn                          => qsfp1_capture_aresetn_in,       -- in
    s_axis_tdata                     => qsfp1_capture_tdata_240b_in,    -- in
    s_axis_tvalid                    => qsfp1_capture_tvalid_240b_in,   -- in
    prog_full                        => qsfp1_prog_full_240b,           -- out
    almost_full                      => qsfp1_afull_240b,               -- out

    m_axis_tdata                     => qsfp1_tdata_512b,               -- out
    m_axis_tvalid                    => qsfp1_tvalid_512b,              -- out
    m_axis_tready                    => qsfp1_tready_512b,              -- in
    almost_empty                     => qsfp1_aempty_240b,              -- out
    overflow                         => qsfp1_iq_240b_to_512b_overflow, -- out
    sel_12b_16bn                     => '0'                             -- 16-bit samples    -- in
  );
  
  qsfp1_rx_data_ready   <= not qsfp1_prog_full_240b;

  process(qsfp1_capture_aclk_in)  
  begin
    if (rising_edge(qsfp1_capture_aclk_in)) then

      if (qsfp1_capture_aresetn_in = '0' or cnt_reset_in = '1') then
        qsfp1_tvalid_240b_cnt_r   <= (others => '0');
      elsif (qsfp1_capture_tvalid_240b_in = '1') then
        qsfp1_tvalid_240b_cnt_r   <= qsfp1_tvalid_240b_cnt_r + 1;         
      end if;

      if (qsfp1_capture_aresetn_in = '0' or cnt_reset_in = '1') then
        qsfp1_rx_data_ready_cnt_r   <= (others => '0');
      elsif (qsfp1_rx_data_ready = '0') then
        qsfp1_rx_data_ready_cnt_r   <= qsfp1_rx_data_ready_cnt_r + 1;         
      end if;

      if (qsfp1_capture_aresetn_in = '0' or cnt_reset_in = '1')  then    
        qsfp1_iq_240b_to_512b_overflow_cnt_r   <= (others => '0');
      elsif (qsfp1_iq_240b_to_512b_overflow = '1') then
        qsfp1_iq_240b_to_512b_overflow_cnt_r  <= qsfp1_iq_240b_to_512b_overflow_cnt_r + 1;
      end if;

    end if;
  end process;
    
-- this fifo is actually 64 words deep
  i_axis_data_fifo_32x512 : entity work.axis_data_fifo_32x512  
  port map (     
    s_axis_aclk                   => qsfp1_capture_aclk_in,
    s_axis_aresetn                => qsfp1_capture_aresetn_in,
    s_axis_tdata                  => qsfp1_tdata_512b,               -- in
    s_axis_tvalid                 => qsfp1_tvalid_512b,              -- in
    s_axis_tready                 => qsfp1_tready_512b,              -- out
    
    m_axis_aclk                   => tx_device_clk_in,         
    m_axis_tdata                  => qsfp1_tdata_512b_pipe,          -- out                  
    m_axis_tvalid                 => qsfp1_tvalid_512b_pipe,         -- out                   
    m_axis_tready                 => qsfp1_tready_512b_pipe,         -- in
    almost_empty                  => qsfp1_almost_empty_512b_pipe                         
    );
  
  process(tx_device_clk_in)  
  begin
    if (rising_edge(tx_device_clk_in)) then
      qsfp1_almost_empty_512b_pipe_r  <= qsfp1_almost_empty_512b_pipe;

      if (tx_device_clk_aresetn_in = '0' or cnt_reset_in = '1') then
        qsfp1_fifo_aempty_512b_cnt_r   <= (others => '0');
      elsif (qsfp1_almost_empty_512b_pipe = '1' and qsfp1_almost_empty_512b_pipe_r = '0') then
        qsfp1_fifo_aempty_512b_cnt_r   <= qsfp1_fifo_aempty_512b_cnt_r + 1;
      end if;     
    end if;     
  end process;

      
--    i_ila_256b_4_qsfp1 : ila_256b_4  
--      port map ( 
--      clk    => qsfp1_capture_aclk_in,
--      probe0 => qsfp1_tvalid_240b, 
--      probe1 => qsfp1_aempty_240b, 
--      probe2 => qsfp1_afull_240b, 
--      probe3 => qsfp1_rx_data_ready,
--      probe4 => qsfp1_tvalid_512b,  
--      probe5 => qsfp1_tready_512b,
--      probe6 => qsfp1_afull_512b,
--      probe7 => qsfp1_prog_full_512b,
--      probe8 => qsfp1_almost_empty_512b_pipe)
--    );
  
  i_axis_dwidth_converter_512b_to_256b : entity work.axis_dwidth_converter_512b_to_256b  
    port map (
    aclk                             => tx_device_clk_in,                               -- in
    aresetn                          => tx_device_clk_aresetn_in,                       -- in
    s_axis_tdata                     => qsfp1_tdata_512b_pipe,                          -- in
    s_axis_tvalid                    => qsfp1_tvalid_512b_pipe,                         -- in
    s_axis_tready                    => qsfp1_tready_512b_pipe,                         -- out

    m_axis_tdata                     => axis_dwidth_converter_tdata_512b_to_256b,       -- out
    m_axis_tvalid                    => axis_dwidth_converter_tvalid_512b_to_256b,      -- out
    m_axis_tready                    => tx_fifo_tready_256b                             -- in
  );
    
  -- big FIFO before JESD TX PORT - actually 64 words deep
  i_tx_fifo_32kx256 : entity work.axis_data_fifo_32kx256  
    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_tdata_512b_to_256b,       -- in
    s_axis_tvalid                    => axis_dwidth_converter_tvalid_512b_to_256b,      -- in
    s_axis_tready                    => tx_fifo_tready_256b,                            -- out

    m_axis_tdata                     => tx_fifo_tdata_256b_out,                         -- out                       
    m_axis_tvalid                    => tx_fifo_tvalid_256b,                            -- out                       
    m_axis_tready                    => tx_fifo_tready_256b_ena,                        -- in                      
    almost_empty                     => tx_fifo_aempty_256b,                            -- out   
    prog_empty                       => tx_fifo_prog_aempty_256b                        -- out                     
    );

  tx_fifo_tvalid_256b_ena <= tx_pre_buff_rdy_r and tx_fifo_tvalid_256b;
  tx_fifo_tready_256b_ena <= tx_pre_buff_rdy_r and tx_fifo_tready_256b_in;
  
  process(tx_device_clk_in)  
  begin
    if (rising_edge(tx_device_clk_in)) then
      tx_fifo_aempty_256b_r  <= tx_fifo_aempty_256b;
      
      if ((tx_fifo_aempty_256b = '1') and (tx_fifo_aempty_256b_r = '1')) then
        tx_pre_buff_rdy_r  <= '0';
      elsif (tx_fifo_prog_aempty_256b = '0') then
        tx_pre_buff_rdy_r  <= '1';
      end if;     

      if (tx_device_clk_aresetn_in = '0' or cnt_reset_in = '1') then    
        tx_fifo_tvalid_256b_cnt_r   <= (others => '0');
      elsif (tx_fifo_tvalid_256b_ena = '1' and tx_fifo_tready_256b_ena = '1') then
        tx_fifo_tvalid_256b_cnt_r   <= tx_fifo_tvalid_256b_cnt_r + 1;
      end if;

    end if;     
  end process;

  tx_fifo_tvalid_256b_out <= tx_fifo_tvalid_256b_ena;
  
end architecture arch_imp;
