----------------------------------------- I2S interface module ----------------------------- -- Author: Petter Källström -- Date: 2022-03-17 -- -- Description: -- * Send sound to the PMOD_I2S via the I2S interface -- * This module handles the timing and bit serialization of the protocol -- * App interface: -- - This module outputs = '1' twice per stereo sample (every 2048'th clock cycle). -- is one pulsed. -- - This module outputs = '1'/'0' for left/right, whatever it will be read next time. -- - One clock cycle after the pulse, lrsel will flip, and the application should -- prepare a new sample to provide on the bus. -- * The "application" can include a sample memory with control logic. Then it is this -- control logic that should consider the and signals. -- App interface timing: -- req: ______-_______________________-_______________________-____ -- lrsel: _______------------------------________________________---- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity i2s is port(clk,rst : in std_logic; -- 100 MHz, sync active high reset -- app interface: lrsel : out std_logic; -- 1=left, 0=right req : out std_logic; -- 1=I'll read new data on sample input sample : in signed(15 downto 0); -- I2S interface: mclk : out std_logic; -- master clock: e.g. 12.5 MHz lrck : out std_logic; -- left/right clock: 48.828 kHz (mclk/256) sclk : out std_logic; -- bit clock: 1.56 MHz (mclk/8, or lrclk*32) sdat : out std_logic); -- serial data. Connect to pmod_i2s.sdin end entity; architecture behav of i2s is signal counter : unsigned(10 downto 0) := (others=>'0'); -- divide 100 MHz clk down to mclk (clk/8), sck (clk/64), lrck (clk/2048) signal cntr_m : unsigned(2 downto 0); -- <= counter(2 downto 0); one rotation per mclk signal cntr_b : unsigned(2 downto 0); -- <= counter(5 downto 3); one rotation per bclk signal state_op : std_logic; -- one-pulse just before each state update. signal state : unsigned(4 downto 0); -- <= counter(10 downto 6); one rotation per sample signal shiftreg : signed(15 downto 0); begin -- Internal control signals: process(clk) begin if rising_edge(clk) then if rst = '1' then counter <= (others=>'0'); else counter <= counter + 1; end if; end if; end process; cntr_m <= counter(2 downto 0); cntr_b <= counter(5 downto 3); state <= counter(10 downto 6); state_op <= '1' when counter(5 downto 0) = "111111" else '0'; -- App interface: req <= '1' when state(3 downto 0) = "0000" and state_op = '1' else '0'; process(clk) begin if rising_edge(clk) then if rst = '1' then shiftreg <= X"0000"; lrsel <= '1'; elsif state_op = '1' then -- only update with the state change. if state(3 downto 0) = "0000" then shiftreg <= sample; else shiftreg <= shiftreg(14 downto 0) & '0'; end if; lrsel <= state(4); end if; end if; end process; -- I2S interface: lrck <= state(4); sclk <= cntr_b(2); mclk <= cntr_m(2); sdat <= shiftreg(15); end architecture; -- Timing diagram: -- lrck: ----_______(left)___________-------(right)----------________ -- sclk: __--__--__--__--__--__--__--__--__--__--__--__--__--__--__-- -- sdat: < 1>< 0><15><14><..>< 2>< 1>< 0><15><14><..>< 2>< 1>< 0><15> -- -- state:<31>< 0>< 1>< 2><..><14><15><16><17><18><..><30><31>< 0>< 1> -- lrsel: _______------------------------________________________---- -- req: ______-_______________________-_______________________-____ -- -- Note: The PMOD CS4344 sound chip actually want lrck to toggle one bclk cycle before changing sample.