--------------------------------------------
-- Multiplexer from FIFOs
--
-- Waits until all i_valid signals are asserted. Then, if i_full == '0', cycles through all inputs and puts them to output.
-- If at that time i_full == '1', all the inputs are discarded.

library ieee;
use ieee.std_logic_1164.all;


entity multiplexer_from_fifos is
generic
        ( G_NUM_CHANNELS : natural :=  2;  -- number of channels
                G_DATA_WIDTH   : natural := 32   -- data width of individual packets
        );
port (
        
        clk : in std_logic;
        rst : in std_logic;
        
        -- input side
        i_data  : in std_logic_vector( G_DATA_WIDTH*G_NUM_CHANNELS - 1 downto 0 );
        i_valid : in std_logic_vector( G_NUM_CHANNELS - 1 downto 0 );
        o_rden  : out std_logic_vector( G_NUM_CHANNELS - 1 downto 0 );
        
        -- output side
        o_data  : out std_logic_vector( G_DATA_WIDTH - 1 downto 0 );
        o_valid : out std_logic;
        i_full  : in std_logic

);
end multiplexer_from_fifos;

architecture behavioral of multiplexer_from_fifos is

        subtype t_counter is natural range 0 to G_NUM_CHANNELS;
        signal s_counter : t_counter := 0;

        signal s_all_i_valid : std_logic;
        signal s_drop_data : std_logic;

begin

        assert( G_NUM_CHANNELS > 1 ) report "The number of channels must be higher than 1." severity failure;

        s_all_i_valid <= '1' when i_valid = ( i_valid'range => '1' ) else
                                                                         '0';
        
        
        counter_process : process( clk )
        begin
                if( rising_edge( clk ) ) then
                        s_drop_data <= '0';
                        if( rst = '1' ) then
                                -- reset
                                s_counter <= 0;
                        elsif( s_counter = 0 and s_all_i_valid = '1' and i_full = '0' and s_drop_data = '0' ) then
                                -- counter is stopped, i_data have new data and the following FIFO is ready to receive.
                                -- start the counter.
                                s_counter <= 1;
                        elsif( s_counter = 0 and s_all_i_valid = '1' and i_full = '1' ) then
                                -- discard the complete set of data because the following FIFO is full.
                                s_drop_data <= '1';
                        elsif( s_counter > 0 and s_counter < t_counter'high ) then
                                -- the counter is running and is somewhere in between, just increase the value.
                                s_counter <= s_counter + 1;
                        elsif( s_counter = t_counter'high and s_all_i_valid = '1' and i_full = '0' ) then
                          -- the counter has reached maximum value and there are new data waiting
                          -- start the counter right away
                          s_counter <= 1;
                        else
                                -- stop the counter
                                s_counter <= 0;
                        end if;
                end if;
        end process;                                                             
        
        ----------------------------------------------
        -- OUTPUT SIGNALS:
        o_data <= i_data( G_DATA_WIDTH*s_counter - 1 downto G_DATA_WIDTH*s_counter - G_DATA_WIDTH ) when s_counter > 0 and rst = '0' else
                                          ( others => '0' );
        o_valid <= i_valid( s_counter - 1 ) when s_counter > 0 and rst = '0' else
                                                 '0';
        o_rden_gen : for i in 0 to G_NUM_CHANNELS - 1 generate
                o_rden(i) <= '1' when ( s_counter = i + 1 or s_drop_data = '1' ) and i_valid(i) = '1' else
                                                                 '0';
        end generate;
        ----------------------------------------------
                                
        
end architecture;