I've hauled my PMOD I2S out of the cupboard, and got it going on my Basys3, under Vivado
First, the I2S interface
----------------------------------------------------------------------------------
-- Engineer: Mike Field <hamster@snap.net.nz>
--
-- Description: Generate I2S audio stream
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
Library UNISIM;
use UNISIM.vcomponents.all;
entity i2s_output is
Port ( clk : in STD_LOGIC;
-- Interface to the source of the audio
data_l : in STD_LOGIC_VECTOR (15 downto 0);
data_r : in STD_LOGIC_VECTOR (15 downto 0);
data_accepted : out STD_LOGIC;
-- Interface out to the DAC
i2s_sd : out STD_LOGIC;
i2s_lrclk : out STD_LOGIC;
i2s_sclk : out STD_LOGIC;
i2s_mclk : out STD_LOGIC);
end i2s_output;
architecture Behavioral of i2s_output is
signal step : unsigned(8 downto 0) := (others => '0');
signal shift_out : std_logic_vector(16 downto 0) := (others => '0');
signal hold_r : std_logic_vector(15 downto 0) := (others => '0');
begin
-- Set the output signals
i2s_lrclk <= std_logic(step(8));
i2s_sclk <= std_logic(step(3));
i2s_sd <= shift_out(shift_out'high);
mclk_ODDR: ODDR
generic map(
DDR_CLK_EDGE => "OPPOSITE_EDGE", -- "OPPOSITE_EDGE" or "SAME_EDGE"
INIT => '0', -- Initial value for Q port ('1' or '0')
SRTYPE => "SYNC") -- Reset Type ("ASYNC" or "SYNC")
port map (
Q => i2s_mclk, -- 1-bit DDR output
C => clk, -- 1-bit clock input
CE => '1', -- 1-bit clock enable input
D1 => '1', -- 1-bit data input (positive edge)
D2 => '0', -- 1-bit data input (negative edge)
R => '0', -- 1-bit reset input
S => '0' -- 1-bit set input
);
process(clk)
begin
if rising_edge(clk) then
-- Default to telling the source to wait
data_accepted <= '0';
if step = "111111111" then
-- start sending the left sample, and signal the source
-- that we have started sending both channels
shift_out(15 downto 0) <= data_l;
hold_r <= data_r;
data_accepted <= '1';
elsif step = "011111111" then
-- switch to sending the right sample
shift_out(15 downto 0) <= hold_r;
elsif step(3 downto 0) = "1111" then
-- Just send the next bit.
shift_out <= shift_out(shift_out'high-1 downto 0) & '1';
end if;
step <= step + 1;
end if;
end process;
end Behavioral;
Second, the top level source
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity i2s_test_top is
Port ( clk100 : in STD_LOGIC;
-- Interface out to the DAC
JA : out std_logic_vector(3 downto 0));
end i2s_test_top;
architecture Behavioral of i2s_test_top is
component i2s_output is
Port ( clk : in STD_LOGIC;
-- Interface to the source of the audio
data_l : in STD_LOGIC_VECTOR (15 downto 0);
data_r : in STD_LOGIC_VECTOR (15 downto 0);
data_accepted : out STD_LOGIC;
-- Interface out to the DAC
i2s_sd : out STD_LOGIC;
i2s_lrclk : out STD_LOGIC;
i2s_sclk : out STD_LOGIC;
i2s_mclk : out STD_LOGIC);
end component;
signal clk : std_logic := '0';
signal toggle : std_logic := '0';
signal accepted : std_logic;
signal data : STD_LOGIC_VECTOR (15 downto 0) := x"C000";
signal count : unsigned(7 downto 0) := (others => '0');
begin
i_output: i2s_output Port map (
clk => clk,
-- Interface to the source of the audio
data_l => data,
data_r => data,
data_accepted => accepted,
i2s_mclk => ja(0),
i2s_lrclk => ja(1),
i2s_sclk => ja(2),
i2s_sd => ja(3)
);
p_clk: process(clk)
begin
if rising_edge(clk) then
if accepted = '1' then
if count = 99 then
count <= (others => '0');
data(15) <= not data(15);
else
count <= count + 1;
end if;
end if;
end if;
end process;
p_clk100: process(clk100)
begin
if rising_edge(clk100) then
if toggle = '1' then
clk <= not clk;
end if;
toggle <= not toggle;
end if;
end process;
end Behavioral;
Third, the constraints:
## Clock signal
set_property PACKAGE_PIN W5 [get_ports clk100]
set_property IOSTANDARD LVCMOS33 [get_ports clk100]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk100]
##Pmod Header JA
#Sch name = JA1
set_property PACKAGE_PIN J1 [get_ports {JA[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {JA[0]}]
set_property PACKAGE_PIN L2 [get_ports {JA[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {JA[1]}]
set_property PACKAGE_PIN J2 [get_ports {JA[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {JA[2]}]
set_property PACKAGE_PIN G2 [get_ports {JA[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {JA[3]}]
As it is running at 25Mhz, and using a 512 clocks per frame the audio rate will be 48,828 samples per second.
As the top level module drives it with a square wave taking 200 sample, it should be a buzz of 244.14Hz:
It looks quite noise, but that is actually because of the filtering on the edges - they contain no frequencies over 24kHz or so and so they 'ring' a little: