Building upon the audio playback core discussed in the Audio Guide I decided to move onto video output. Rather than recreating the same functionality as the loader by displaying a background, I thought a basic audio visualiser would make for a simple yet interesting alternative.
Whilst the visualiser is quite basic, taking an 8bit sample from each channel and displaying it on a 256 pixel tall screen area (480p resolution, so the two channels overlap slightly), it proved a useful exercise to learn how to use the Replay Frameworks BRAM components.
The visualiser runs with a resolution of 720x480p @ 60Hz. Two 1024 word buffers are used to store the 720 audio samples needed to produce a display frame. One buffer holds data needed for the current display frame whilst the other write buffer is being populated with 720 new audio samples. The read/write buffers are swapped during vsync anytime the write buffer is full.
Due to the timing and limited number of samples, it’s possible to output 720 new samples each frame which combined with the lack of display persistence can result in some messy looking frames. A crude frame_delay was added to avoid populating the new write buffer with data until X frames have been output. This allows the same read buffer to be output over several consecutive frames. The youtube video above shows the visualiser running @60Hz without the frame_delay in use.
One initial stumbling block with this example is that the FPGA side of the block ram is running off of the video clock. Yet the samples that need storing are obtained in the audio clock domain. To resolve this the FIFO_ASync_D16 component was used. This is a pipelined FIFO buffer that can be written to using one clock domain and read from a different one. In this case, writes were done using the audio clock and reads the video clock.
A process running off of the video clock can then read from the FIFO and write this to memory until a full frames worth of samples are ready for display. The memory is thus read from and written to using the same video clock.
The next issue is that during display with the memory accessed using the video clock, both the left and right audio sample need to be obtained on the same clock tick but only one read can occur per tick.
I created a new ram_1024_W18 component based on the Replay’s 2048_W8 to allow storing 1024 samples of audio (only 720 are needed) with the left and right channels held in the upper 8 and lower 8 bits respectively. A single read can now provide both channels of data.
I believe the ARM side will see this memory as a 2048_W8 block, although I did not test that, nor confirm the byte ordering for when two bytes of data are written on the ARM side then read as one 16bit word by the FPGA.
NOTE: Had DDR memory being used instead of BRAM, access times may have been trickier. From what I’ve read DRAM can have 32bits read out per system clock tick. In addition read and writes need to share the same bus, unlike with the BRAM where two independent modules can be used to allow writing to one whilst reads occur on the other.
Converting this project over to using DRAM for storage may be a useful way to become more familiar with that side of the framework and might require a rethink of how everything is done.
I’ve added additional comments in the source code which is available in my fpga-replay repo on github.