--- /dev/null
+--------------------------------------------------------------------------------
+-- i2s_input - receives a stream of samples from an I2S interface
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library unisim;
+use unisim.vcomponents.all;
+
+
+entity i2s_input is
+ port (
+ sck: in std_logic;
+ ws: in std_logic;
+ sd: in std_logic;
+
+ stb_o: out std_logic;
+ rdy_i: in std_logic;
+ l_dat_o: out std_logic_vector(15 downto 0);
+ r_dat_o: out std_logic_vector(15 downto 0)
+ );
+end i2s_input;
+
+
+architecture behavioral of i2s_input is
+
+ signal shift_reg: std_logic_vector(15 downto 0);
+ signal ws_reg: std_logic;
+ signal ws2_reg: std_logic;
+
+ signal latch_l: std_logic;
+ signal latch_r: std_logic;
+
+ signal samp_l_reg: std_logic_vector(15 downto 0);
+ signal samp_r_reg: std_logic_vector(15 downto 0);
+
+ signal stb_reg: std_logic;
+
+begin
+
+ process (sck, ws)
+ begin
+ if rising_edge(sck) then
+ shift_reg <= shift_reg(14 downto 0) & sd;
+ ws2_reg <= ws_reg;
+ ws_reg <= ws;
+ end if;
+ end process;
+
+ latch_l <= '1' when ws_reg = '1' and ws2_reg = '0' else '0';
+ latch_r <= '1' when ws_reg = '0' and ws2_reg = '1' else '0';
+
+ process (sck, latch_l, latch_r, shift_reg)
+ begin
+ if rising_edge(sck) then
+ if latch_l = '1' then
+ samp_l_reg <= shift_reg;
+ end if;
+ if latch_r = '1' then
+ samp_r_reg <= shift_reg;
+ end if;
+ end if;
+ end process;
+
+ process (sck, latch_r, rdy_i)
+ begin
+ if rising_edge(sck) then
+ if latch_r = '1' then
+ stb_reg <= '1';
+ elsif rdy_i = '1' then
+ stb_reg <= '0';
+ end if;
+ end if;
+ end process;
+
+ stb_o <= stb_reg;
+ l_dat_o <= samp_l_reg;
+ r_dat_o <= samp_r_reg;
+
+end behavioral;
--- /dev/null
+--------------------------------------------------------------------------------
+-- i2s_output - transmits a stream of samples to an I2S interface
+--------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+library unisim;
+use unisim.vcomponents.all;
+
+
+entity i2s_output is
+ port (
+ sck: in std_logic;
+ ws: in std_logic;
+ sd: out std_logic;
+
+ stb_i: in std_logic;
+ rdy_o: out std_logic;
+ l_dat_i: in std_logic_vector(15 downto 0);
+ r_dat_i: in std_logic_vector(15 downto 0)
+ );
+end i2s_output;
+
+
+architecture behavioral of i2s_output is
+
+ signal ws_reg: std_logic;
+ signal shift_reg: std_logic_vector(15 downto 0);
+
+ signal latch_l: std_logic;
+ signal latch_r: std_logic;
+
+begin
+
+ sd <= shift_reg(15);
+
+ process (sck, ws)
+ begin
+ if falling_edge(sck) then
+ ws_reg <= ws;
+ end if;
+ end process;
+
+ latch_l <= '1' when ws = '0' and ws_reg = '1' else '0';
+ latch_r <= '1' when ws = '1' and ws_reg = '0' else '0';
+
+ process (sck, latch_l, latch_r, l_dat_i, r_dat_i, shift_reg)
+ begin
+ if falling_edge(sck) then
+ if latch_l = '1' then
+ shift_reg <= l_dat_i;
+ elsif latch_r = '1' then
+ shift_reg <= r_dat_i;
+ else
+ shift_reg <= shift_reg(14 downto 0) & '0';
+ end if;
+ end if;
+ end process;
+
+ rdy_o <= latch_r;
+
+end behavioral;
--- /dev/null
+library ieee;
+use ieee.std_logic_1164.all;
+
+library work;
+
+
+entity test_i2s_ctrl is
+end test_i2s_ctrl;
+
+
+architecture behavior of test_i2s_ctrl is
+
+ constant CLK_I_PERIOD: time := 20 ns;
+
+ signal rst_i: std_logic;
+ signal clk_i: std_logic;
+
+ signal ws: std_logic;
+ signal sck: std_logic;
+
+begin
+
+ p_test: process
+ begin
+ -- Reset
+ rst_i <= '1';
+ wait for CLK_I_PERIOD*4;
+ rst_i <= '0';
+
+ -- Done
+ wait;
+ end process;
+
+ e_uut: entity work.i2s_ctrl
+ port map (
+ rst_i => rst_i,
+ clk_50 => clk_i,
+
+ ws => ws,
+ sck => sck
+ );
+
+ p_clk: process
+ begin
+ clk_i <= '0';
+ wait for CLK_I_PERIOD/2;
+ clk_i <= '1';
+ wait for CLK_I_PERIOD/2;
+ end process;
+
+end;
--- /dev/null
+library ieee;
+use ieee.std_logic_1164.all;
+
+library simulated;
+
+library work;
+
+
+entity test_i2s_input is
+end test_i2s_input;
+
+
+architecture behavior of test_i2s_input is
+
+ constant CLK_50_PERIOD: time := 20 ns;
+
+ signal rst_i: std_logic;
+ signal clk_50: std_logic;
+
+ signal sck: std_logic;
+ signal ws: std_logic;
+ signal sd: std_logic;
+
+ signal stb: std_logic;
+ signal rdy: std_logic;
+ signal l_dat: std_logic_vector(15 downto 0);
+ signal r_dat: std_logic_vector(15 downto 0);
+
+begin
+
+ p_test: process
+ begin
+ -- Initial values
+ rdy <= '1';
+
+ -- Reset
+ rst_i <= '1';
+ wait for CLK_50_PERIOD*4;
+ rst_i <= '0';
+
+ -- Test
+
+ -- Done
+ wait;
+ end process;
+
+ e_uut: entity work.i2s_input
+ port map (
+ sck => sck,
+ ws => ws,
+ sd => sd,
+
+ stb_o => stb,
+ rdy_i => rdy,
+ l_dat_o => l_dat,
+ r_dat_o => r_dat
+ );
+
+ e_src: entity simulated.proto_i2s_tx
+ port map (
+ sck => sck,
+ ws => ws,
+ sd => sd
+ );
+
+ e_ctrl: entity work.i2s_ctrl
+ port map (
+ rst_i => rst_i,
+ clk_50 => clk_50,
+ ws => ws,
+ sck => sck
+ );
+
+ p_clk50: process
+ begin
+ clk_50 <= '0';
+ wait for CLK_50_PERIOD/2;
+ clk_50 <= '1';
+ wait for CLK_50_PERIOD/2;
+ end process;
+
+end;
--- /dev/null
+library ieee;
+use ieee.std_logic_1164.all;
+
+library work;
+
+
+entity test_i2s_output is
+end test_i2s_output;
+
+
+architecture behavior of test_i2s_output is
+
+ constant CLK_50_PERIOD: time := 20 ns;
+
+ signal rst_i: std_logic;
+ signal clk_50: std_logic;
+
+ signal sck: std_logic;
+ signal ws: std_logic;
+ signal sd: std_logic;
+
+ signal stb: std_logic;
+ signal rdy: std_logic;
+ signal l_dat: std_logic_vector(15 downto 0);
+ signal r_dat: std_logic_vector(15 downto 0);
+
+ signal rx_stb: std_logic;
+ signal rx_l_dat: std_logic_vector(15 downto 0);
+ signal rx_r_dat: std_logic_vector(15 downto 0);
+
+begin
+
+ e_test: process
+ begin
+ -- Initial values
+
+ -- Reset
+ rst_i <= '1';
+ wait for CLK_50_PERIOD*4;
+ rst_i <= '0';
+
+ -- Test
+
+ -- Done
+ wait;
+ end process;
+
+ e_ctrl: entity work.i2s_ctrl
+ port map (
+ rst_i => rst_i,
+ clk_50 => clk_50,
+ sck => sck,
+ ws => ws
+ );
+
+ e_src: entity work.src_counter
+ generic map (WIDTH => 16)
+ port map (
+ rst_i => rst_i,
+ clk_i => sck,
+ stb_o => stb,
+ rdy_i => rdy,
+ dat_o => l_dat
+ );
+ r_dat <= l_dat;
+
+ e_uut: entity work.i2s_output
+ port map (
+ sck => sck,
+ ws => ws,
+ sd => sd,
+ stb_i => stb,
+ rdy_o => rdy,
+ l_dat_i => l_dat,
+ r_dat_i => r_dat
+ );
+
+ -- FIXME: one treats the left sample as first, the other treats the right sample as first
+ -- so this RX block receives staggered samples
+ -- Not a big deal for streaming audio, so not worth fixing now.
+ e_rx: entity work.i2s_input
+ port map (
+ sck => sck,
+ ws => ws,
+ sd => sd,
+ stb_o => rx_stb,
+ rdy_i => '1',
+ l_dat_o => rx_l_dat,
+ r_dat_o => rx_r_dat
+ );
+
+ p_clk: process
+ begin
+ clk_50 <= '0';
+ wait for CLK_50_PERIOD/2;
+ clk_50 <= '1';
+ wait for CLK_50_PERIOD/2;
+ end process;
+
+end;
--- /dev/null
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+
+entity proto_i2s_tx is
+ port (
+ sck: in std_logic;
+ ws: in std_logic;
+ sd: out std_logic
+ );
+end proto_i2s_tx;
+
+
+architecture behavioral of proto_i2s_tx is
+
+ signal ws_reg: std_logic;
+
+begin
+
+ process (sck, ws)
+ begin
+ if rising_edge(sck) then
+ ws_reg <= ws;
+ end if;
+ end process;
+
+ process
+ variable i: integer := 0;
+ variable samp_l: std_logic_vector(15 downto 0);
+ variable samp_r: std_logic_vector(15 downto 0);
+ begin
+ wait until ws_reg = '0' or ws_reg = '1';
+ while true loop
+ -- Get next samples
+ samp_l := std_logic_vector(to_signed(i, 16));
+ samp_r := std_logic_vector(to_signed(i, 16));
+ i := i + 1;
+
+ -- Output left sample
+ while ws_reg = '0' loop
+ sd <= samp_l(15);
+ samp_l := samp_l(14 downto 0) & '0';
+ wait until rising_edge(sck);
+ wait until falling_edge(sck);
+ end loop;
+
+ -- Output right sample
+ while ws_reg = '1' loop
+ sd <= samp_r(15);
+ samp_r := samp_r(14 downto 0) & '0';
+ wait until rising_edge(sck);
+ wait until falling_edge(sck);
+ end loop;
+ end loop;
+ end process;
+
+end behavioral;