Skip to main content

FPGA Audio Spectrum Analyzer

· 4 min read

Verik can be used for FPGA design with the Xilinx Vivado toolchain. Vivado WebPACK can be installed for free and has all the functionality required to simulate and synthesize FPGA designs. In this project we build an audio spectrum analyzer with an FFT on the Nexys 4 FPGA board and visualize the output on a VGA display.

The block diagram of the design is shown below. The design is separated into two clock domains, clk_100mhz and pixel_clk. clk_100mhz is the clock signal directly derived from the 100 MHz crystal oscillator on the Nexys 4 board. It is used to drive the ADC that takes in the analog audio input, perform the computations, and write the results into the block memory. pixel_clk is a 65 MHz clock derived from a clock generator configured with the Vivado Clocking Wizard. It is used to generate signals to drive the VGA display. The design computes the spectrum by performing an FFT and taking the absolute value of each frequency component. The modules in the pipeline communicate with the AXI4-Lite protocol. A FIFO is inserted between the square-and-sum step and the square root step to accommodate the latency of the square root.

fpga-block-diagram

Verik makes it easy to integrate Vivado IP into the design. We can do so by referencing the *.xci configuration files and *.v module stub files produced by the Xilinx IP Core Generator. The stub files can be imported and directly referenced from the Verik environment. We use the Xilinx IP Core Generator to generate IP for the ADC, FFT, FIFO, square root, and block memory. For example, we directly instantiate the FFT core as follows.

@Make
val fft_core = FftCore(
aclk = clk_100mhz,
s_axis_config_tdata = u0(),
s_axis_config_tvalid = false,
s_axis_config_tready = nc(),
s_axis_data_tdata = cat(fft_data, u0<`16`>()),
s_axis_data_tvalid = fft_valid,
s_axis_data_tlast = fft_last,
s_axis_data_tready = fft_ready,
m_axis_data_tdata = fft_out_data,
m_axis_data_tvalid = fft_out_valid,
m_axis_data_tready = fft_out_ready,
m_axis_data_tlast = fft_out_last,
event_frame_started = nc(),
event_tlast_unexpected = nc(),
event_tlast_missing = nc(),
event_status_channel_halt = nc(),
event_data_in_channel_halt = nc(),
event_data_out_channel_halt = nc()
)

After computing the spectrum, we display the results on a VGA monitor. We do this by generating the pixel values and sync signals as required by the VGA protocol. Pixel values are read out line-by-line, with hsync and vsync to synchronize each frame. An illustration of the VGA signals is shown below.

fpga-vga-timing

We can directly synthesize the design with Vivado from the makefile generated by the Verik toolchain. This runs the script synth.tcl that creates a Vivado project, imports the sources, constraints, and IPs, and runs the synthesis. The output of the build is the bit file out.bit that is ready to be programming onto the FPGA. The synth target of the makefile runs the following command.

$(VIVADO) -mode batch -source synth.tcl

We run the build on a Windows machine with an installation of Vivado WebPACK and write the bit file onto a Nexys 4 board. Here we see a picture of the system in action. The microphone is wired up to channel 3 of the JXADC port of the Nexys 4. The computed spectrum is displayed on the monitor. The switches on the Nexys 4 can be used to configure the gain of the display.

fpga-setup

The source code for this project can be found here.