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;
