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