FPGA based music synthesizer with joystick control for pitch and volume.
This project implements a digital music synthesizer (Basically an audio tone generator) on an FPGA. It uses the Pmod JSTK2 (joystick) to control the pitch and volume of the generated sound in real-time. Pressing the joystick button changes the waveform type (Sine/Square/Triangular/Sawtooth).
- Joystick Control:
- X-axis: Controls Pitch
- Y-axis: Controls Volume
- Waveform Generation: Sine wave synthesis using lookup tables, square wave, sawtooth wave and triangular wave.
The system consists of the following main modules:
- Joystick Interface: Interfacing with the Pmod JSTK2 using SPI.
- Waveform Generator: Generating sine/square/sawtooth/triangular waveforms using a phase accumulator with variable phase increment for frequency control and scaled output for volume control.
- Audio Output: Interfacing with the Pmod DA2 using an SPI-like interface.
synth.srcs/: Vivado project source files (Verilog, Constraints, Simulation, Memory initialization files).scripts/: Python helper scripts.sine_wave_data_gen.py: Generates sine wave lookup table data.wavegen_analyze.py: Waveform visualizer using matplotlib.test_phase_Accumulation.py: A scratch script for testing how the phase accumulator works.
synth.xpr: Xilinx Vivado project file.
topmod
├── jstk : jstk2_interface - Interface for Pmod JSTK2 (Joystick) provides latest x and y values
│ └── sm : spi_master - SPI Master for joystick communication
├── wg : wavegen - Main waveform generation wrapper
│ ├── sc : scaler - Scales raw x and y values to feed into frequency generator as phase increment and volume.
│ ├── ws : wave_selector - Changes waveform type on joystick button press (Sine/Square/Triangular/Sawtooth)
│ └── fg : freq_generator - Generates phase_addr based on phase accumulation which is then used to index into the sine wave lookup table or for square/sawtooth/triangular wave generation.
│ └── sw : sine_waver - Sine wave lookup table to provide latest sine wave value based on phase_addr.
└── dac : da2_interface - Interface for Pmod DA2 (DAC) to send out wave amplitudes serially.
- Hardware: Hardware used was a zedboard (Xilinx Zynq-7000 series FPGA).
- Software: Xilinx Vivado Design Suite 2021.2
- Python: Python 3.13.9
-
Clone the Repository
git clone https://github.com/ayshmnmm/synth.git cd synth -
Open in Vivado
- Launch Vivado.
- Open project
synth.xpr.
-
Generate Bitstream
- Click "Generate Bitstream" in the Flow Navigator.
- Wait for synthesis and implementation to complete.
Testbenches:
wavegen_tb.v: Tests the wavegen module to see the waveform generated. Also used for analyzing DA2 interface timing.wavegen_analyze_tb.v: Tests the wavegen module and exports the waveform(for analysis using wavegen_analyze.py).jstk_interface_tb.v: Tests the correct retrieval of x and y values from the joystick.spi_master_tb.v: Tests the SPI signals and timing of the SPI master used for joystick interfacing.
To run the testbenches:
- Open the "Simulation" view in Vivado.
- Set the desired testbench as top module in
synth.srcs/sim_1. - Run behavioral simulation.
- Connect the Joystick module to the FPGA Pmod header (refer to
constraintsfile or block diagram for pinout). - Connect DAC module to the FPGA Pmod header (refer to
constraintsfile or block diagram for pinout). - Connect Amplifier module to the DAC module and the FPGA Pmod header.
- Connect a speaker and/or an oscilloscope to the Amplifier module output.
- Program the FPGA with the generated bitstream.
- Enjoy the music (if you can call it that)
The scripts/ directory contains tools to generate waveform data.
To regenerate the sine wave LUT:
python scripts/sine_wave_data_gen.py
Fig 1: RTL Schematic of the Top Module
Fig 2: Timing Report showing positive slack (100Mhz clock / 10ns clock period)