Skip to content
agg23 edited this page Sep 23, 2022 · 2 revisions

The sound system in APF is very simple and the HDL to interact with it is fairly portable between cores. APF recieves a i2s signed 16-bit stereo signal at 48kHz.

Audio IP

Since the audio requirements of each core are basically identical, I created sound_i2s.sv, which can be found in this utils repo. It handles all of the I2S protocol, clock generation, and synchronization (via FIFO) of the audio data to the main clock.

Simply set the CHANNEL_WIDTH parameter to the width of your channel data (generally 15 for unsigned, and 16 for signed). Note that you cannot set 16 bits if you have an unsigned signal, so you probably want to drop the lowest bit of your net. Make sure to also set whether or not the audio is signed with SIGNED_INPUT.

Standard Implementation

The standard audio implementation shared between most cores so far utilizes a shift register to shift individual bits of the audio signal into the i2s bus. APF expects 16 active bits and 16 spacer bits. The standard implementation shifts the signal left 16 times, then continues sending the last bit of the audio signal.

// output the next bit
audgen_dac <= audgen_sampshift[31];

...

audgen_lrck_cnt <= audgen_lrck_cnt + 1;

if(audgen_lrck_cnt < 16)
begin
  // only shift for 16 clocks per channel
  audgen_sampshift <= {audgen_sampshift[30:0], 1'b0};
end

Since the signal is stereo, you then repeat this process for the right channel.

Mono

For mono signals, choose what volume level you will play at, and duplicate the signal for both channels (remember that it's signed, so we leave the first bit empty).

wire [31:0] audgen_sampdata = mono_sound ? 32'h70007000 : 32'b0;

Stereo

For stereo signals, you need to separate the left and right channels as part of the 32 bit word going into the shifter.

wire [31:0] audgen_sampdata = {1'b0, channel_right, {14{1'b0}}, 1'b0, channel_left, {14{1'b0}}};
Clone this wiki locally