-------------------------------------------------------------------------------
-- Receiver for Behringer UltraNet
-- Based on 
--    - OpenCore's i2s_interface by Geir Drange (https://opencores.org/projects/i2s_interface)
--    - https://github.com/xn--nding-jua/UltranetReceiver
-- 
-- Deserialize the 24bit AES/EBU data from ultranet into useful data
--
-- UltraNet sends eight 24-bit audio-samples in AES/EBU-format.
-- Bsync (Z subframe in AES/EBU) will signal channel 1 followed by seven more samples identified by lrclk
------------------------------------------------------------------------------- 

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all; 
use ieee.numeric_std.all; -- lib for unsigned and signed

entity ultranet_rx_deserializer is
	generic ( 
        CHANNELS : integer range 2 to 8 := 8
    );
	port (
        -- Master clock
		clk 		: in std_logic;

        -- Block start (should be asserted when Z subframe is being transmitted)
		bsync		: in std_logic;
        
        -- Audio clocks
		bclk 		: in std_logic;
		lrclk		: in std_logic;

        -- Input serial data
		sdata		: in std_logic;

        -- Output sample and destination channel
		sample_out	: out std_logic_vector(23 downto 0);
		channel		: out unsigned(2 downto 0);

        -- New data received signal
		new_data	: out std_logic
    );
end entity;

architecture rtl of ultranet_rx_deserializer is
    -- Block sync detector & signal
	signal zbsync, zzbsync, zzzbsync	    : std_logic;
	signal bsync_pos_edge		            : std_logic;

    -- Bit clock detector & signals
	signal zbclk, zzbclk, zzzbclk 	        : std_logic;
	signal bclk_neg_edge, bclk_pos_edge     : std_logic;

    -- Word clock detector & signal
	signal zlrclk, zzlrclk, zzzlrclk	    : std_logic;
	signal lrclk_edge					    : std_logic;

	signal in_sound_data_region 	        : std_logic;

	signal bit_counter					    : integer range 0 to 31 := 0;
	signal channel_counter				    : integer range 0 to 7 := 0;

	signal sample_data			            : std_logic_vector(23 downto 0);
begin
	detect_bsync_pos_edge : process(clk) begin
		if rising_edge(clk) then
			zbsync <= bsync;
			zzbsync <= zbsync;
			zzzbsync <= zzbsync;
			if zzbsync = '1' and zzzbsync = '0' then
				bsync_pos_edge <= '1';
			else
				bsync_pos_edge <= '0';
			end if;
		end if;
	end process;

	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
            -- Check if we are starting a new block
			if bsync_pos_edge = '1' then 
                -- Yes, reset counters, the channels counter is not set to zero to compensate for processing delays to make sure the demux+transmitter output on the correct channel
				bit_counter <= 0;
				channel_counter <= 4;
                -- Also reset the new data signal
				new_data <= '0';
			else
                -- No, check if we are on the rising or flling edge of the word clock
				if lrclk_edge = '1' then
                    -- Yes, reset the bit counter
					bit_counter <= 0;
                    -- Increment the channel counter up to the defined number of channels then reset to 0
					if channel_counter < (CHANNELS-1) then
						channel_counter <= channel_counter + 1;
					else
						channel_counter <= 0;
					end if;
				end if;

                -- Increment the bit counter (up to 32 bits) on a rising edge of the bit clock
				if bclk_pos_edge = '1' then
					if bit_counter < 31 then
						bit_counter <= bit_counter + 1; -- increment bit_counter until bit 31
					end if;
				end if;
                
                -- Sample the data on the falling edge of the bit clock
				if bclk_neg_edge = '1' then  	
                    -- Set the audio data region signal if we are in the correct range [....AAAASSSSSSSSSSSSSSSSSSSSEUCP]
					if bit_counter = 4 then
						in_sound_data_region <= '1';
					elsif bit_counter >= 24+4 then
						in_sound_data_region <= '0';
					end if;
				end if;
                
                -- Assert the new data signal within the unused 4bits at the end of the frame
				if bit_counter = 30 and bclk_neg_edge = '1' then 
					new_data <= '1';
				else
					new_data <= '0';
				end if;
			end if;
		end if;
	end process;
    
    -- Send the data out
	sample_out <= sample_data;
	channel <= to_unsigned(channel_counter, channel'length);

	get_data : process(clk) begin
		if rising_edge(clk) then
			-- Receive individual bits for audio-data (24 bits) on the rising edge of the bit clock and only if in the correct region
			if bclk_pos_edge = '1' and in_sound_data_region = '1' then
                -- in AES3/EBU the first bit after preamble is LSB, so we have to shift from the left to the right
				sample_data <= sdata & sample_data(sample_data'high downto 1);
			end if;
		end if;
	end process;
end rtl;
        