interfaces component
I2S Audio Transmitter
A stereo I2S transmitter that streams two 16-bit samples per frame over the classic three-wire audio interface: bit clock sck, word select ws, and serial data sd. This is the format audio DACs and codecs expect on their digital input.
The bit clock runs at a quarter of clk, from a two-bit divider. A five-bit slot counter walks the 32 bit slots of a frame. Its top bit is ws directly: low while the left word streams, high for the right. Data leaves a 16-bit shift register MSB first, and every output edge lands on a falling edge of sck, so a receiver samples safely on the rising edge. The standard I2S one-bit delay falls out of the load timing. Each sample loads one slot after ws flips, so a word's last bit crosses into the next channel's first slot.
In the waveform: watch the first rise of ws while sd still carries the left word. sample_l is 0xA5C3, so its final 1 lands one slot after the flip. That one-slot overlap is the I2S signature.
In the netlist: the whole machine is one 16-bit shift chain, a two-bit divider, and a five-bit slot counter. Nothing more is needed to speak I2S.
The course builds serial interfaces from the wire up, from UART framing to bus arbitration, in Communication Interfaces.
-- I2S stereo transmitter: sck = clk/4, 32 bit slots per frame, data MSB
-- first on sck falling edges with the standard one-bit delay after ws
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity i2s_transmitter is
port (
clk : in std_logic;
rst : in std_logic;
sample_l : in std_logic_vector(15 downto 0);
sample_r : in std_logic_vector(15 downto 0);
sck : out std_logic;
ws : out std_logic;
sd : out std_logic
);
end entity i2s_transmitter;
architecture rtl of i2s_transmitter is
signal div_r : unsigned(1 downto 0);
signal bit_r : unsigned(4 downto 0);
signal bit_next : unsigned(4 downto 0);
signal shreg_r : std_logic_vector(15 downto 0);
begin
bit_next <= bit_r + 1;
-- Everything advances on the clk edge that ends div_r = 3: that is the
-- falling edge of sck, so sd and ws only ever move there.
seq_p : process(clk)
begin
if rising_edge(clk) then
if rst = '1' then
div_r <= (others => '0');
bit_r <= (others => '0');
shreg_r <= (others => '0');
else
div_r <= div_r + 1;
if div_r = 3 then
bit_r <= bit_next;
if bit_next = 1 then
shreg_r <= sample_l; -- one slot after ws falls
elsif bit_next = 17 then
shreg_r <= sample_r; -- one slot after ws rises
else
shreg_r <= shreg_r(14 downto 0) & '0';
end if;
end if;
end if;
end if;
end process seq_p;
sck <= div_r(1);
ws <= bit_r(4);
sd <= shreg_r(15);
end architecture rtl;