----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date: 01/20/2020 12:03:14 PM
-- Design Name: 
-- Module Name: transform_adc_stream - Behavioral
-- Project Name: 
-- Target Devices: 
-- Tool Versions: 
-- Description: 
-- 
-- Dependencies: 
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
----------------------------------------------------------------------------------


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;

Library xpm;
use xpm.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

   function vec2bitor(vec   : std_logic_vector(1 downto 0)) return std_logic is
      variable ret         : std_logic := '0';
   begin 
      for i in 0 to vec'length-1 loop
         ret := ret or vec(i);      
      end loop;
      return ret;
   end function;

   -- ADC clock domain
   signal s_axis_tdata_d         : std_logic_vector(S_AXIS_TDATA'range) := (others => '0');
   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) := (others => '0');
   signal s_axis_tready_combined : std_logic := '0';
   signal s_axis_tvalid_combined : std_logic := '0';
   
   -- CDC
   signal m_rstn                 : std_logic := '0';
   signal s_rstn                 : std_logic := '0';
   
   -- Another clock domain
   signal m_valid                : std_logic_vector(1 downto 0) := "00";
   signal m_read0                : std_logic := '0';
   signal m_read1                : std_logic := '0';
   signal m_axis_tdata_combined  : std_logic_vector(s_axis_tdata_combined'range) := (others => '0');
   signal m_axis_tvalid_combined : std_logic := '0';
   signal m_axis_tready_combined : std_logic := '0';
   -- channel 0
   signal m_axis_ch0_tdata_i     : std_logic_vector(255 downto 0) := (others => '0');
   signal m_axis_ch0_tdata_id    : std_logic_vector(255 downto 0) := (others => '0');
   signal m_axis_ch0_tvalid_i    : std_logic := '0';
   -- channel 1
   signal m_axis_ch1_tdata_i     : std_logic_vector(255 downto 0) := (others => '0');   
   signal m_axis_ch1_tdata_id    : std_logic_vector(255 downto 0) := (others => '0');    
   signal m_axis_ch1_tvalid_i    : std_logic := '0';

   signal s_axis_tdata_switched     : std_logic_vector(S_AXIS_TDATA'range) := (others => '0');  
   signal s_axis_tdata_ch0          : std_logic_vector(127 downto 0) := (others => '0');   
   signal s_axis_tdata_ch1          : std_logic_vector(127 downto 0) := (others => '0');     
   signal s_axis_tdata_ch0_switched : std_logic_vector(127 downto 0) := (others => '0');   
   signal s_axis_tdata_ch1_switched : std_logic_vector(127 downto 0) := (others => '0');     
begin

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


   -- valid output
   s_axis_tvalid_combined <= s_valid(s_valid'left);
   
   -- Swith bytes to first 128 bits are data from channel 0 and other 128 bits data from channel 1
   s_axis_tdata_ch0 <= S_AXIS_TDATA(S_AXIS_TDATA'left downto 128);
   s_axis_tdata_ch1 <= S_AXIS_TDATA(127 downto 0);
   
   switch_bytes_0_gen: for i in 0 to 3 generate
      s_axis_tdata_ch0_switched(i*32+15 downto i*32) <= vec2bitor(s_axis_tdata_ch0(64+i*8+1 downto 64+i*8)) & "0" & 
         s_axis_tdata_ch0(96+(i+1)*8-1 downto 96+i*8) & s_axis_tdata_ch0(64+(i+1)*8-1 downto 64+i*8+2);
         
      s_axis_tdata_ch0_switched(i*32+31 downto i*32+16) <= vec2bitor(s_axis_tdata_ch0(i*8+1 downto i*8)) & "0" &
         s_axis_tdata_ch0(32+(i+1)*8-1 downto 32+i*8) & s_axis_tdata_ch0((i+1)*8-1 downto i*8+2);
         
      s_axis_tdata_ch1_switched(i*32+15 downto i*32) <= vec2bitor(s_axis_tdata_ch1(64+i*8+1 downto 64+i*8)) & "0" &
         s_axis_tdata_ch1(96+(i+1)*8-1 downto 96+i*8) & s_axis_tdata_ch1(64+(i+1)*8-1 downto 64+i*8+2);
         
      s_axis_tdata_ch1_switched(i*32+31 downto i*32+16) <= vec2bitor(s_axis_tdata_ch1(i*8+1 downto i*8)) & "0" &
         s_axis_tdata_ch1(32+(i+1)*8-1 downto 32+i*8) & s_axis_tdata_ch1((i+1)*8-1 downto i*8+2);
   end generate;

   s_axis_tdata_switched <= s_axis_tdata_ch1_switched & s_axis_tdata_ch0_switched;
   
   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_switched;
               -- output streams
               s_axis_tdata_combined <= s_axis_tdata_switched & s_axis_tdata_d;
            end if;
         end if;
      end process;

      axis_cdc_clk: xpm_fifo_axis
      generic map(
        CLOCKING_MODE         =>  "independent_clock",
        FIFO_DEPTH            =>  16,
        TDATA_WIDTH           =>  512,
        USE_ADV_FEATURES      => "1000"
      )
      port map(
        s_aresetn             => s_rstn,
        m_aclk                => M_CLK,
        s_aclk                => S_CLK,
        s_axis_tvalid         => s_axis_tvalid_combined,
        s_axis_tready         => s_axis_tready_combined,
        s_axis_tdata          => s_axis_tdata_combined,
        s_axis_tstrb          => x"ffffffffffffffff",
        s_axis_tkeep          => x"ffffffffffffffff",
        s_axis_tlast          => '0',
        s_axis_tid            => "0",
        s_axis_tdest          => "0",
        s_axis_tuser          => "0",
        m_axis_tvalid         => m_axis_tvalid_combined,
        m_axis_tready         => m_axis_tready_combined,
        m_axis_tdata          => m_axis_tdata_combined,
        injectsbiterr_axis    => '0',
        injectdbiterr_axis    => '0'
      );

   -- 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);

   m_axis_tready_combined <= '1';

   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
               -- value was already read and it shouldn't be valid anymore
               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;
               -- value was already read and it shouldn't be valid anymore
               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;
