Block Requester

As previously mentioned, the Generic FileIO needs a process to drive it, requesting a block transfer then waiting until the transfer is acknowledge and complete before incrementing the address of the next request and repeating.

In addition, the requesting process needs to monitor the size of the FIFO queue and pause requesting data until there is sufficient space in the queue.

The ports from Fch_Generic relevant to doing this are

i_req                 => fileio_req,
o_ack_req             => fileio_ack_req,
o_ack_trans           => fileio_ack_trans,
o_trans_err           => fileio_trans_err,

i_addr                => fileio_addr,
i_size                => fileio_size,

As before, the “loader” provides a good example of how to do this using a simple state machine. This is one of those bits of code that initially looks complex but once broken down, is actually quite manageable.

p_fileio_req : process(i_clk_sys, i_rst_sys)
begin
  if (i_rst_sys = '1') then
    fileio_addr <= (others => '0');
    fileio_req  <= '0';
    fileio_req_state <= S_IDLE;
  elsif rising_edge(i_clk_sys) then
    if (i_ena_sys = '1') then
      fileio_req  <= '0';

      if (i_fcha_cfg.inserted(0) = '0') then
        fileio_addr <= (others => '0');
        fileio_req_state <= S_IDLE;
      else
        case fileio_req_state is
          when S_IDLE =>
            if (fileio_rx_level(9) = '0') then -- <hf
              fileio_req  <= '1'; -- note, request only sent when ack received
            end if;
            if (fileio_ack_req = '1') then
              fileio_req_state <= S_WAIT;
            end if;

          when S_WAIT =>
            if (fileio_ack_trans = '1') then
              if (red_or(fileio_trans_err) = '1') then
                fileio_addr <= (others => '0');
              else
                fileio_addr <= fileio_addr + fileio_size;
              end if;
              fileio_req_state <= S_IDLE;
            end if;

          when others => null;
        end case;
      end if;
    end if;
  end if;
end process;

The first if section just resets everything in the case of a system reset. The interesting part is what happens on the rising edge of the enabled system clock.

if (i_fcha_cfg.inserted(0) = '0') then
  fileio_addr <= (others => '0');
  fileio_req_state <= S_IDLE;
else

As long as channel A slot 0 has no file inserted, the address is kept at 0 and the request state at IDLE. Once a file is inserted however the IO requesting can begin

when S_IDLE =>
  if (fileio_rx_level(9) = '0') then -- <hf
    fileio_req  <= '1'; -- note, request only sent when ack received
  end if;
  if (fileio_ack_req = '1') then
    fileio_req_state <= S_WAIT;
  end if;

The first if checks the current size of the FIFO queue which has a 1024 word capacity. \( 2^{9}=512 \) therefore if bit 10 is set (zero indexed) the buffer contains at least 512 words already and another 512 word request risks overfilling it. Conversely if bit 10 is not set, there’s at least 512 bytes available and a new request may proceed. A fileio_req is thus only made when the buffer is at least half empty.

After making a request by continually taking fileio_req high. The process will remain in the idle state attempting to request data until the FileIO acknowledges the request has been initiated at which point it switches to the WAIT state to wait for the data transfer to complete.

when S_WAIT =>
  if (fileio_ack_trans = '1') then
    if (red_or(fileio_trans_err) = '1') then
      fileio_addr <= (others => '0');
    else
      fileio_addr <= fileio_addr + fileio_size;
    end if;
    fileio_req_state <= S_IDLE;
  end if;

This state checks if a transfer complete (fileio_ack_trans) has been signalled. If so, in the case of an error (such as reaching the end of file) the next transfer will occur from address 0x0. In the case of a successful transfer, the address is incremented ready to fetch the next 512 byte block of data.

The state is now set back to the IDLE state to wait for sufficient space in the queue to request the next block be transfered.

In order to implement the above, a couple of additional local signals are needed to track the current state.

type t_fileio_req_state is (S_IDLE, S_WAIT);
signal fileio_req_state       : t_fileio_req_state;

This could have also been done as a bit1 where 0 represents IDLE and 1 WAIT, but using a type makes the code a little more readable, especially as the number of states grow.

There is now enough in place to check that valid data is being read, although unless you have a 16 channel logic analyser, only a rough sanity test will be possible. With a four channel oscilloscope, fileio_data cannot be monitored directly however if you make a small 1024 16bit data file of all 0’s and a second of all 1’s (or alternating 0,1,2 etc) at least the first four bits can be verified and if that works it’s likely the rest will.

In order to do this test, fileio_audio_taken will need to be held high as the reader is not yet in place. Otherwise the fileio_data would never be changed and the FIFO queue will rapidly fill to capacity and transfer stall.

Next section FIFO Reader

Back to index