-------------------------------------------------------------------------------
-- Quad output I2S transmitter
-- Based on 
--    - OpenCore's i2s_interface by Geir Drange (https://opencores.org/projects/i2s_interface)
-- Adapted for left-justified operation
-------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;

entity i2s_quad_transmitter is
	generic ( 
        DATA_WIDTH : integer range 16 to 32 := 32
    );
	port (
        -- Master clock & reset
		clk                 : in std_logic;
		reset_n             : in std_logic;
        
        -- Audio clocks
		bclk 			    : in std_logic;
		lrclk			    : in std_logic;
        
        -- Input L/R samples for each channel
		sample_ch_1_l 		: in std_logic_vector(DATA_WIDTH - 1 downto 0);
		sample_ch_1_r 		: in std_logic_vector(DATA_WIDTH - 1 downto 0);
		sample_ch_2_l 		: in std_logic_vector(DATA_WIDTH - 1 downto 0);
		sample_ch_2_r 		: in std_logic_vector(DATA_WIDTH - 1 downto 0);
		sample_ch_3_l 		: in std_logic_vector(DATA_WIDTH - 1 downto 0);
		sample_ch_3_r 		: in std_logic_vector(DATA_WIDTH - 1 downto 0);
		sample_ch_4_l 		: in std_logic_vector(DATA_WIDTH - 1 downto 0);
		sample_ch_4_r 		: in std_logic_vector(DATA_WIDTH - 1 downto 0);
           
        -- Output serial data
		sdout_1             : out std_logic;
		sdout_2             : out std_logic;
		sdout_3             : out std_logic;
		sdout_4             : out std_logic
    );
end i2s_quad_transmitter;

architecture rtl of i2s_quad_transmitter is
    -- Bit clock detector & signals
    signal zbclk, zzbclk, zzzbclk     : std_logic;
    signal bclk_neg_edge    : std_logic;
    signal bclk_pos_edge    : std_logic;

    -- Word clock detector & signal
	signal zlrclk, zzlrclk, zzzlrclk   : std_logic;
    signal lrclk_edge       : std_logic;
	 
    signal bit_counter		: integer range 0 to 31 := 0;

    -- Samples buffer for each channel
    signal sample_ch_1_l_buf		: std_logic_vector(DATA_WIDTH - 1 downto 0);
    signal sample_ch_1_r_buf		: std_logic_vector(DATA_WIDTH - 1 downto 0);
    signal sample_ch_2_l_buf		: std_logic_vector(DATA_WIDTH - 1 downto 0);
    signal sample_ch_2_r_buf		: std_logic_vector(DATA_WIDTH - 1 downto 0);
    signal sample_ch_3_l_buf		: std_logic_vector(DATA_WIDTH - 1 downto 0);
    signal sample_ch_3_r_buf		: std_logic_vector(DATA_WIDTH - 1 downto 0);
    signal sample_ch_4_l_buf		: std_logic_vector(DATA_WIDTH - 1 downto 0);
    signal sample_ch_4_r_buf		: std_logic_vector(DATA_WIDTH - 1 downto 0);
    
begin
	detect_bclk_edge : process(clk) begin
		if rising_edge(clk) then
			zbclk <= bclk;
			zzbclk <= zbclk;
			zzzbclk <= zzbclk;
			if zzbclk = '1' and zzzbclk = '0' then
				bclk_pos_edge <= '1';
			elsif zzbclk = '0' and zzzbclk = '1' then
				bclk_neg_edge <= '1';
			else
				bclk_pos_edge <= '0';
				bclk_neg_edge <= '0';
			end if;
		end if;
	end process;	
	

    detect_lrclk_edge : process(clk) begin
    	if rising_edge(clk) then
        	zlrclk <= lrclk;
        	zzlrclk <= zlrclk;
        	zzzlrclk <= zzlrclk;
			if zzlrclk /= zzzlrclk then
				lrclk_edge <= '1';
			else
				lrclk_edge <= '0';
			end if;
      end if;
   end process;
	
	detect_sample : process(clk) begin
		if rising_edge(clk) then
			if reset_n = '0' then 
				bit_counter <= 0;
			else
                -- If the left/right channel changes, reset the output bit counter
				if lrclk_edge = '1' then
					bit_counter <= 0;
				end if;
                
                -- Increment the bit counter a positive edge of the bit clock
				if bclk_pos_edge = '1' then
					bit_counter <= bit_counter + 1;
				end if;
                
                -- Update the sample buffers on the last positive bit clock edge 
                -- TODO: Shouldn't this be on the negative edge of LRCLK?
				if bclk_pos_edge = '1' then  	
					if bit_counter = DATA_WIDTH-1 then
                        sample_ch_1_l_buf <= sample_ch_1_l;
                        sample_ch_1_r_buf <= sample_ch_1_r;
                        sample_ch_2_l_buf <= sample_ch_2_l;
                        sample_ch_2_r_buf <= sample_ch_2_r;
                        sample_ch_3_l_buf <= sample_ch_3_l;
                        sample_ch_3_r_buf <= sample_ch_3_r;
                        sample_ch_4_l_buf <= sample_ch_4_l;
                        sample_ch_4_r_buf <= sample_ch_4_r;
					end if;
				end if;
			end if;
		end if;
	end process;	
	

	send_data : process(clk) begin
		if rising_edge(clk) then
			if reset_n = '0' then
                -- Reset asserted, send zeros to the output
				sdout_1 <= '0';
				sdout_2 <= '0';
				sdout_3 <= '0';
				sdout_4 <= '0';
			else
                -- Normal state, output the proper bit (left or right) for each channel on the negative bit clock edge
				if bclk_neg_edge = '1' then  	
                    if lrclk = '1' then
                        sdout_1 <= sample_ch_1_l_buf(DATA_WIDTH - 1 - bit_counter);
                        sdout_2 <= sample_ch_2_l_buf(DATA_WIDTH - 1 - bit_counter);
                        sdout_3 <= sample_ch_3_l_buf(DATA_WIDTH - 1 - bit_counter);
                        sdout_4 <= sample_ch_4_l_buf(DATA_WIDTH - 1 - bit_counter); 
                    else
                        sdout_1 <= sample_ch_1_r_buf(DATA_WIDTH - 1 - bit_counter);
                        sdout_2 <= sample_ch_2_r_buf(DATA_WIDTH - 1 - bit_counter);
                        sdout_3 <= sample_ch_3_r_buf(DATA_WIDTH - 1 - bit_counter);
                        sdout_4 <= sample_ch_4_r_buf(DATA_WIDTH - 1 - bit_counter);
                    end if;
				end if;

			end if;
		end if;
	end process;
end rtl;
        