Ok, that's one way to go about it.
Your:
I2S_DATA <= DAC_Buffer[ bitcounter ] ;
Is valid, but the extra addressing and position counter wont hamper anything at this rate, but the clues of a serial shift register. For now, just a simple logic error on your part here:
// Strobe sample_pulse once every 64 cycles & sample appropriate L/R input
if ( I2S_counter == 0 ) begin
DAC_Buffer <= DAC_Left ;
sample_pulse <= 1'b1 ;
bitcounter <= ~0 ; // set bitcounter to all 1's
I2S_WCLK <= LEFT ;
end else if ( I2S_counter == 32 ) begin
DAC_Buffer <= DAC_Right ; <<<<<<
sample_pulse <= 1'b1 ; <<<<<< REMEMBER, we want to do 1 stereo sample every 64 clocks.
bitcounter <= ~0 ; // set bitcounter to all 1's
I2S_WCLK <= RIGHT ;
end else begin
Ok, let's take a look at a simpler serial shifter.
Some other changes is that everything except the I2S_BCLK is now inside the 'if ( clk_i2s_pulse ) begin':
// Initial I2S_transmitter code, untested, by nockieboy August 2022.
module I2S_transmitter #(
parameter int BITS = 16, // Audio width, can be anything from 8 to 24
parameter bit INV_BCLK = 0 // When set, make I2S_BCLK fall during valid data
)(
//Inputs
input logic clk_in, // High speed clock
input logic clk_i2s, // 50/50 duty cycle serial audio clock to feed through
input logic clk_i2s_pulse, // Should strobe for one clk_in cycle at the beginning of each new clk_i2s
input logic sample_in, // Optional input to reset the sample position. This should either be tied to GND or only pulse once every 64 'clk_i2s_pulse's
input logic [BITS-1:0] DAC_Left, // Left channel digital audio sampled once every 'sample_pulse' output
input logic [BITS-1:0] DAC_Right, // Right channel digital audio sampled once every 'sample_pulse' output
//Outputs
output logic sample_pulse, // Pulses once when a new stereo sample is taken from the DAC_Left/Right inputs. Hint: once every 64 clk_i2s_pulse's
output logic I2S_BCLK, // I2S serial bit clock output (SCLK), basically the clk_i2s input in the correct phase
output logic I2S_WCLK, // I2S !left / right output (LRCLK)
output logic I2S_DATA // Serial data output
);
logic [5:0] I2S_counter <= 0 ;
logic [BITS-1:0] DAC_right_buffer <= 0 ;
logic [BITS-1:0] DAC_serial_buffer <= 0 ;
// Since I2S uses 64 clock pulses, we need one 6-bit reg counter.
// I expect a serial shift out register plus a second register to buffer the right DAC input.
// The code should be a few embedded if(), 1 for the main counter, one for the serial load/shifter,
// and a last reg buffering the output regs.
// We will copy/rename the PSG and make a new module testing the serializer with our fp_divider driving
// the serializer to show our I2S output in the waveform view. The left and right inputs should be
// created in the _tb module.
assign I2S_WCLK = I2S_counter[5] ; // Hard wire the I2S Word Clock to the I2S_counter MSB.
assign I2S_DATA = DAC_serial_buffer [BITS-1] ; // Hard wire the I2S serial data bits to the MSB of the serial data buffer.
always_ff @( posedge clk_in ) begin
// Optional Invert clk_i2s for correct I2S_BCLK phase
I2S_BCLK <= clk_i2s ^ !INV_BCLK ;
// Manage I2S pulse counter and serialisation
if ( clk_i2s_pulse ) begin
if (sample_in) I2S_counter <= 0 ; // Optional clear to I2S counter position '0'
else I2S_counter <= I2S_counter + 1 ;
sample_pulse <= ( I2S_counter == 0 ) ; // Pulse the output reference sample clock.
// Strobe sample_pulse once every 64 cycles & sample appropriate L/R input
if ( I2S_counter == 0 ) begin
DAC_right_buffer <= DAC_Right ; // Keep a copy of the Right channel data to be transmitted during the second half.
// This was done to make sure both left and right channel data are captured in parallel on the same clock.
DAC_serial_buffer <= DAC_LEFT ; // Transfer the left channel for immediate transmission.
end else if ( I2S_counter == 32 ) begin
DAC_serial_buffer <= DAC_right_buffer ; // Transfer the right channel's sample for immediate transmission.
end else begin
DAC_serial_buffer <= DAC_serial_buffer << 1 ; // Left shift the serial out data.
end
end // if ( clk_i2s_pulse ) begin
end // always
endmodule
This code should do it. If it simulates ok, we will test wire in this sine-table with counter to verify that you still hear a clean sine tone once wired into the HDMI output project.