Dealing with digital audio is a nice thing. Here is my implementation of a 2- and 8-channel I2S-Receiver:

VHDL
-- 2-channel I2S-Receiver
-- Christian Noeding, christian@noeding-online.de
-- https://chrisdevblog.com | https://github.com/xn--nding-jua
--
-- Released under GNU General Public License v3


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 i2s_2ch_rx is
	port (
		bclk 				: in std_logic; -- I2S Bitclock (typical 1.536 MHz)
		lrclk				: in std_logic; -- I2S Wordclock (typical 48 kHz)
		sdata  			: in std_logic; -- I2S Serial-data
		
		left_out		: out std_logic_vector(23 downto 0) := (others=>'0');
		right_out		: out std_logic_vector(23 downto 0) := (others=>'0');
		sync_out		: out std_logic -- new data received successfully
	);
end i2s_2ch_rx;

architecture rtl of i2s_2ch_rx is
	signal signal_shift  	: std_logic_vector(31 downto 0);
	signal signal_left		: std_logic_vector(23 downto 0);
	signal zlrclk			  	: std_logic;
begin
	process(bclk)
	begin
		if rising_edge(bclk) then
			zlrclk <= lrclk;

			if lrclk = '1' and zlrclk = '0' then
				-- positive LR-edge -> we received data for right channel -> output data
				left_out <= signal_left;
				right_out <= signal_shift(31 downto 8);
				sync_out <= '1'; -- set output flag
			elsif lrclk = '0' and zlrclk = '1' then
				-- negative LR-edge

				-- we received data for left channel -> store it
				signal_left <= signal_shift(31 downto 8);
				sync_out <= '0';
			end if;
		end if;
	end process;

	get_data : process(bclk)
	begin
		if rising_edge(bclk) then
			-- fill shift-register with new data
			signal_shift <= signal_shift(30 downto 0) & sdata;
		end if;
	end process;
end rtl;

If you want to deal with multiple I2S ADCs that are connected to the same bit-clock and frame-sync, we can receive multiple channels at once:

VHDL
-- 8-channel I2S-Receiver
-- Christian Noeding, christian@noeding-online.de
-- https://chrisdevblog.com | https://github.com/xn--nding-jua
--
-- Released under GNU General Public License v3

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 i2s_8ch_rx is
	port (
		bclk 				: in std_logic; -- I2S Bitclock
		lrclk				: in std_logic; -- I2S Wordclock
		sdata_12		: in std_logic; -- I2S Serial-data
		sdata_34		: in std_logic; -- I2S Serial-data
		sdata_56		: in std_logic; -- I2S Serial-data
		sdata_78		: in std_logic; -- I2S Serial-data
		
		ch1_out			: out std_logic_vector(23 downto 0) := (others=>'0');
		ch2_out			: out std_logic_vector(23 downto 0) := (others=>'0');
		ch3_out			: out std_logic_vector(23 downto 0) := (others=>'0');
		ch4_out			: out std_logic_vector(23 downto 0) := (others=>'0');
		ch5_out			: out std_logic_vector(23 downto 0) := (others=>'0');
		ch6_out			: out std_logic_vector(23 downto 0) := (others=>'0');
		ch7_out			: out std_logic_vector(23 downto 0) := (others=>'0');
		ch8_out			: out std_logic_vector(23 downto 0) := (others=>'0');
		sync_out		: out std_logic -- new data received successfully
	);
end i2s_8ch_rx;

architecture rtl of i2s_8ch_rx is
	signal signal_shift_12	: std_logic_vector(31 downto 0);
	signal signal_shift_34	: std_logic_vector(31 downto 0);
	signal signal_shift_56	: std_logic_vector(31 downto 0);
	signal signal_shift_78	: std_logic_vector(31 downto 0);
	signal signal_1 	  		: std_logic_vector(23 downto 0);
	signal signal_3 		  	: std_logic_vector(23 downto 0);
	signal signal_5 	  		: std_logic_vector(23 downto 0);
	signal signal_7 	   		: std_logic_vector(23 downto 0);
	signal zlrclk				    : std_logic;
begin
	process(bclk)
	begin
		if rising_edge(bclk) then
			zlrclk <= lrclk;

			if lrclk = '1' and zlrclk = '0' then
				-- positive LR-edge -> we received data for right channel -> output data
				ch1_out <= signal_1;
				ch3_out <= signal_3;
				ch5_out <= signal_5;
				ch7_out <= signal_7;
				
				ch2_out <= signal_shift_12(31 downto 8);
				ch4_out <= signal_shift_34(31 downto 8);
				ch6_out <= signal_shift_56(31 downto 8);
				ch8_out <= signal_shift_78(31 downto 8);
				sync_out <= '1'; -- set output flag
			elsif lrclk = '0' and zlrclk = '1' then
				-- negative LR-edge

				-- we received data for left channel -> store it
				signal_1 <= signal_shift_12(31 downto 8);
				signal_3 <= signal_shift_34(31 downto 8);
				signal_5 <= signal_shift_56(31 downto 8);
				signal_7 <= signal_shift_78(31 downto 8);
				sync_out <= '0';
			end if;
		end if;
	end process;

	get_data : process(bclk)
	begin
		if rising_edge(bclk) then
			-- fill shift-register with new data
			signal_shift_12 <= signal_shift_12(30 downto 0) & sdata_12;
			signal_shift_34 <= signal_shift_34(30 downto 0) & sdata_34;
			signal_shift_56 <= signal_shift_56(30 downto 0) & sdata_56;
			signal_shift_78 <= signal_shift_78(30 downto 0) & sdata_78;
		end if;
	end process;
end rtl;

Leave a comment

Your email address will not be published. Required fields are marked *