]> git.the-white-hart.net Git - vhdl/commitdiff
Add I2S blocks and tests
authorrs <>
Mon, 15 Dec 2025 18:50:00 +0000 (12:50 -0600)
committerrs <>
Mon, 15 Dec 2025 18:50:00 +0000 (12:50 -0600)
libraries/dsp/i2s_input.vhd [new file with mode: 0644]
libraries/dsp/i2s_output.vhd [new file with mode: 0644]
libraries/dsp/tests/test_i2s_ctrl.vhd [new file with mode: 0644]
libraries/dsp/tests/test_i2s_input.vhd [new file with mode: 0644]
libraries/dsp/tests/test_i2s_output.vhd [new file with mode: 0644]
libraries/simulated/proto_i2s_tx.vhd [new file with mode: 0644]

diff --git a/libraries/dsp/i2s_input.vhd b/libraries/dsp/i2s_input.vhd
new file mode 100644 (file)
index 0000000..60c53af
--- /dev/null
@@ -0,0 +1,82 @@
+--------------------------------------------------------------------------------
+-- 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;
diff --git a/libraries/dsp/i2s_output.vhd b/libraries/dsp/i2s_output.vhd
new file mode 100644 (file)
index 0000000..7d1703f
--- /dev/null
@@ -0,0 +1,64 @@
+--------------------------------------------------------------------------------
+-- 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;
diff --git a/libraries/dsp/tests/test_i2s_ctrl.vhd b/libraries/dsp/tests/test_i2s_ctrl.vhd
new file mode 100644 (file)
index 0000000..755af5a
--- /dev/null
@@ -0,0 +1,51 @@
+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;
diff --git a/libraries/dsp/tests/test_i2s_input.vhd b/libraries/dsp/tests/test_i2s_input.vhd
new file mode 100644 (file)
index 0000000..8268ef7
--- /dev/null
@@ -0,0 +1,82 @@
+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;
diff --git a/libraries/dsp/tests/test_i2s_output.vhd b/libraries/dsp/tests/test_i2s_output.vhd
new file mode 100644 (file)
index 0000000..1ef06a1
--- /dev/null
@@ -0,0 +1,100 @@
+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;
diff --git a/libraries/simulated/proto_i2s_tx.vhd b/libraries/simulated/proto_i2s_tx.vhd
new file mode 100644 (file)
index 0000000..83c1e86
--- /dev/null
@@ -0,0 +1,58 @@
+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;