-------------------------------------------------------------------------------
--
-- Project name: TRANSFORM_ADC_STREAM
--
-- (C) Copyright 2020 DFC Design, s.r.o., Brno, Czech Republic
-- Author: Jakub Hadamek (j.hadamek@dfcdesign.cz)
--
-------------------------------------------------------------------------------
--
-- 
-------------------------------------------------------------------------------


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity transform_adc_stream is
  port (
      -- Input clock and reset
      S_CLK                : in  std_logic;
      S_RESET              : in  std_logic;
      -- input stream
      S_AXIS_TVALID        : in  std_logic;
      S_AXIS_TDATA         : in  std_logic_vector(255 downto 0);
      -- Output clock and reset
      M_CLK                : in  std_logic;
      M_RESET              : in  std_logic;
      -- output stream channel 0
      M_AXIS_CH0_TVALID    : out std_logic;
      M_AXIS_CH0_TREADY    : in  std_logic;
      M_AXIS_CH0_TDATA     : out std_logic_vector(511 downto 0);
      -- output stream channel 1
      M_AXIS_CH1_TVALID    : out std_logic;
      M_AXIS_CH1_TREADY    : in  std_logic;
      M_AXIS_CH1_TDATA     : out std_logic_vector(511 downto 0)
   );
end transform_adc_stream;

architecture Behavioral of transform_adc_stream is

   -- ADC clock domain
   signal s_axis_tdata_d         : std_logic_vector(S_AXIS_TDATA'range);
   signal s_valid                : std_logic_vector(1 downto 0) := "00";
   signal s_axis_tdata_combined  : std_logic_vector(S_AXIS_TDATA'length*2-1 downto 0);
   signal s_axis_tready_combined : std_logic;
   signal s_axis_tvalid_combined : std_logic;
   
   -- CDC
   signal m_rstn                 : std_logic;
   signal s_rstn                 : std_logic;
   
   -- Another clock domain
   signal m_valid                : std_logic_vector(1 downto 0) := "00";
   signal m_read0                : std_logic;
   signal m_read1                : std_logic;
   signal m_axis_tdata_combined  : std_logic_vector(s_axis_tdata_combined'range);
   signal m_axis_tvalid_combined : std_logic;
   -- channel 0
   signal m_axis_ch0_tdata_i     : std_logic_vector(255 downto 0);
   signal m_axis_ch0_tdata_id    : std_logic_vector(255 downto 0);
   signal m_axis_ch0_tvalid_i    : std_logic;
   -- channel 1
   signal m_axis_ch1_tdata_i     : std_logic_vector(255 downto 0);   
   signal m_axis_ch1_tdata_id    : std_logic_vector(255 downto 0);    
   signal m_axis_ch1_tvalid_i    : std_logic;

begin

   m_rstn <= not M_RESET;
   s_rstn <= not S_RESET;

   -- valid output
   s_axis_tvalid_combined <= s_valid(s_valid'left);

   delay_stream_proc: process(S_CLK)
      begin 
         if rising_edge(S_CLK) then 
            if (S_RESET or not S_AXIS_TVALID or not s_axis_tready_combined) = '1' then
               s_valid <= "00";
               s_axis_tdata_d <= (others => '0');
               s_axis_tdata_combined <= (others => '0');
            else
               -- valid shift
               if s_valid = "00"then
                  s_valid <= "01";
               else
                  s_valid(1) <= s_valid(0);
                  s_valid(0) <= s_valid(1);
               end if;
               -- delay data
               s_axis_tdata_d <= S_AXIS_TDATA;
               -- output streams
               s_axis_tdata_combined <= S_AXIS_TDATA & s_axis_tdata_d;
            end if;
         end if;
      end process;

   axis_cdc_inst: entity work.axis_clock_converter_0 
      port map(
         s_axis_aresetn => s_rstn,
         m_axis_aresetn => m_rstn,
         s_axis_aclk    => S_CLK,
         s_axis_tvalid  => s_axis_tvalid_combined,
         s_axis_tready  => s_axis_tready_combined,
         s_axis_tdata   => s_axis_tdata_combined,
         m_axis_aclk    => M_CLK,
         m_axis_tvalid  => m_axis_tvalid_combined,
         m_axis_tready  => '1',
         m_axis_tdata   => m_axis_tdata_combined
      );

   -- adc data channels
   m_axis_ch0_tdata_i <= m_axis_tdata_combined(383 downto 256) & m_axis_tdata_combined(127 downto 0);
   m_axis_ch1_tdata_i <= m_axis_tdata_combined(511 downto 384) & m_axis_tdata_combined(255 downto 128);


   separate_channels_proc: process(M_CLK)
      begin 
         if rising_edge(M_CLK) then 
            if M_RESET = '1' then
               m_valid <= "00";
               m_read0 <= '0';
               m_read1 <= '0';
               m_axis_ch0_tdata_id <= (others => '0');
               m_axis_ch1_tdata_id <= (others => '0');
               M_AXIS_CH0_TDATA <= (others => '0');
               M_AXIS_CH1_TDATA <= (others => '0');
            else
               if (m_valid(m_valid'left) and M_AXIS_CH0_TREADY) = '1' then
                  m_read0 <= '1';
               elsif m_valid(m_valid'left) = '0' then
                  m_read0 <= '0';
               end if;
               
               if (m_valid(m_valid'left) and M_AXIS_CH1_TREADY) = '1' then
                  m_read1 <= '1';
               elsif m_valid(m_valid'left) = '0' then
                  m_read1 <= '0';
               end if;
               
               if m_axis_tvalid_combined = '1' then
                  -- valid shift
                  if m_valid = "00"then
                     m_valid <= "01";
                  else
                     m_valid(1) <= m_valid(0);
                     m_valid(0) <= m_valid(1);
                  end if;
                  -- channel 0
                  m_axis_ch0_tdata_id <= m_axis_ch0_tdata_i;
                  M_AXIS_CH0_TDATA    <= m_axis_ch0_tdata_i & m_axis_ch0_tdata_id;
                  -- channel 1
                  m_axis_ch1_tdata_id <= m_axis_ch1_tdata_i;
                  M_AXIS_CH1_TDATA    <= m_axis_ch1_tdata_i & m_axis_ch1_tdata_id;
               end if;
            end if;
         end if;
      end process;

      -- output stream channel 0
      M_AXIS_CH0_TVALID    <= m_valid(m_valid'left) and not m_read0;
      -- output stream channel 1
      M_AXIS_CH1_TVALID    <= m_valid(m_valid'left) and not m_read1;

end Behavioral;
