FIFO Queue Reader

With data now being received and made available, all that remains is to take the lower 16 bits that represent the actual data sample and output this to the left audio channel and then the next 16 bits of data to the right for as long as there is valid data available.

This can be done using the following FileIO ports

o_fifo_to_core_data   => fileio_data,
i_fifo_to_core_taken  => fileio_taken,
o_fifo_to_core_valid  => fileio_valid,
o_fifo_to_core_level  => fileio_rx_level,

Anytime fileio_valid is high, data is available to shift over to the DAC from the FIFO. Once the DAC has confirmed it has the data, fileio_taken can be set high and the FIFO queue should make the next word of data available. Rinse and repeat.

The DAC gets access to the audio data through three ports tied to the Example_Audio_Top.

-- Audio (clk_aud)
o_audio_l             : out word(23 downto 0); -- left  sample
o_audio_r             : out word(23 downto 0); -- right sample
i_audio_taken         : in  bit1;  -- sample ack

Each audio clock tick the framework will take the left and right audio data and signal when it has done so by setting i_audio_taken high. Between then and the next audio clock tick (48KHz) new left and right sample data should be made available.

A process is required to take the next word of data from the FIFO and output it to each audio channel. The following one has a (perhaps subtly) flaw or two.

-- Transfer available sample data from FIFO out to audio subsystem	
p_fileio_reader : process(i_clk_sys, i_rst_sys)
begin
  if (i_rst_sys = '1') then
      fileio_sample_cnt <= "00";
      fileio_taken      <= '0';
      o_audio_l   <= (others => '0');
      o_audio_r   <= (others => '0');
  elsif rising_edge(i_clk_sys) then
    if (i_ena_sys = '1') then
      fileio_taken <= '0';
      case fileio_sample_cnt is
        when "00" =>
          if (fileio_valid = '1') then
            fileio_taken <= '1';
            o_audio_l   <= fileio_data( 7 downto 0) & fileio_data(15 downto 8) & x"00";
            fileio_sample_cnt <= "01";
          end if;
        when "01" => -- wait for taken to update valid
            fileio_sample_cnt <= "10";

        when "10" =>
          if (fileio_valid = '1') then
            fileio_taken <= '1';
            o_audio_r   <= fileio_data( 7 downto 0) & fileio_data(15 downto 8) & x"00";
            fileio_sample_cnt <= "11";
          end if;
        when "11" => -- ready
          if (i_audio_taken = '1') then
            fileio_sample_cnt <= "00";
          end if;
        when others => null;
      end case;
    end if;
  end if;
end process;

As with the request process, this one starts off by handling the reset state and clearing out the state counter, the taken flag and both channel outputs.

The meat of the process is a four state counter. States “00” and “10” both wait until the FIFO signals a sample is available for reading. They signal taking of the sample and output it to the left or right channel accordingly and move onto the next state.

The next state after “00” is “01”, which is a time-waster to eat a system clock tick and allow time for the FIFO queue to realise that fileio_taken is high and that it should reset fileio_valid until it has the next sample ready for access.

State “11” if empty would also accomplishes the same before returning to “00” however this state has another purpose. It waits until the audio framework has signalled that the left and right samples made available on o_audio_l and r have been taken before looping to make the next two samples available.

This process runs off of the system clock to ensure it can make new sampled data available between each audio clock tick, which it can easily do as the enabled system clock (1 in 4) is running at over 7MHz whilst the audio system requests new samples at a more sedate 48kHz.

The audio clock itself is running at 49.152MHz and down sampled to 12.288MHz by the 1 in 4 clock enable then within the framework the Replay_Audio only samples the data once every 256 audio clocks, resulting in the final sample request frequency of 48kHz.

As before, make sure the default “0” output assigned to i_audio_l and r are removed.

Samples and Endians

Note how the returned sample data is output, bits 7 to 0 first, then 15 to 8 and finally eight “0” bits. This looks odd at first, however the PCM data is exported with little-endian byte ordering with the least significant byte first then the most significant. On the FPGA side the Audio hardware/framework also expect the data to be in little endian, so why the apparent change to big endian?

The key to this is deeper within the Replay Framework and relates to the data transfer from the ARM firmware via SPI. replay_fileio_sync.vhd which handles receiving data via SPI for disk transfers notes:

-- FIFO DATA, 16 bit streaming. The "x" sets fifo_ch (0-3 one hot).
-- fifo_ch is set according to "x".
-- first byte on spi is sent 15..8, second 7..0

During 16bit SPI transfers the byte order is switched to big endian. This is apparently for disk transfers with the Amiga core. For this reason when the data comes off the FIFO it needs to be switched back to little endian. Thanks to MikeJ for pointing this out.

As for the extra zero bits, the DAC (a WM8729) is used in 24bit mode. As the audio PCM is 16 bit, the data should be 0 extended when output the o_audio_l and r channels.

All make sense? Notice the flaw, I didn’t.

Build and run the core and notice how the audio plays slower than the loader version plays the same file. Comparing the source of the two there’s obvious differences but why?

Read on.

Next section Bullet Time

Back to index