← All projects & experiments

SMALL EXPERIMENT · FPGA & VHDL

Making an FPGA play music

4 min read · Built with Nishant Aggarwal

Nishant and I first got an FPGA to play notes selected with switches and buttons. We worked through the clock dividers, pitch selection, display, and simulations that made the tone player work. Then we gave it a memory for notes and timing so it could play “Ode to Joy” on its own.

Blue FPGA development board with the seven-segment note display illuminated and a speaker connected at the left
The music player running on the FPGA board. The display shows the current note while the sequencer advances through the tune.

Playing a note by hand

The first version was a digital piano. Each of seven slide switches selected a natural note, from C through B. Buttons could shift that selection one semitone flat or sharp, choose an octave, or reset the circuit. The controls produced a five-bit note code. A zero code meant silence.

Those bits were a compact way to describe a note, not an audio recording. The note decoder translated the code into a clock-divider value. The divider then toggled a digital output to make a square wave for the speaker. Changing the code changed the wave’s frequency, which is what changed the pitch.

Getting the pitch right

We worked from the board’s 100 MHz clock, dividing it down to a 1 MHz timing signal for the audio path. The output flips state after each programmed count, so a complete high-low cycle takes twice that count. That makes the tone frequency 1,000,000 / (2 × divider) Hz.

For C3, the divider value was 3,822. The calculation gives 130.82 Hz against a target of 130.81 Hz. In our simulation, the speaker output had a period of about 7.64 ms, or roughly 130.9 Hz. Checking a measured period was more convincing than just trusting the note name in the code.

Simulation showing switch input 80, divider 0EEE, and the speaker square wave for C3
Our C3 simulation: the selected divider produces a square wave with a period of about 7.64 ms.

Showing and testing the note

The board’s seven-segment display showed the selected letter and octave. It lights one digit at a time and scans them quickly enough to look steady; the sharp sign uses the decimal point. That display made the pitch selection visible while we checked the speaker output.

We also drove the controls through a VHDL testbench before running the design on the board. Stepping through C3, D3, E3, and G3 let us see the note code, divider value, and sound output change together. The simulated waveforms helped separate an input-mapping mistake from a timing mistake.

Simulation showing successive switch values 80, 40, 20, and 08 with corresponding divider and sound signals
Changing the switch selection in simulation changes the note code, divider, and speaker waveform.

Giving the piano a melody

Once the manual tone player worked, we replaced its switch-driven note selection with a sequencer. Our “Ode to Joy” arrangement contains 65 events, including rests. Two VHDL constant arrays hold matching note codes and durations; reading one position from each says what to play and how long to hold it.

Durations are counted in 50 ms steps. Ten ticks make a 500 ms note, while a zero note code mutes the speaker for a rest. The sequencer feeds the same note decoder, audio divider, and display used by the manual player. The song data changed how notes were selected, not how the circuit generated them.

Opening phrase: E4, E4, F4, G4, G4, F4, E4, D4, C4, C4, D4, E4, E4, D4, D4, followed by a rest; longer bars indicate longer durations
The opening phrase drawn from our stored notes and durations. Repeated notes are separate events.

Keeping pitch and tempo separate

The audio divider sets pitch; a second timing path decides when to move to the next event. We reused the divider to make a 20 Hz pulse from the 1 MHz signal, giving one tempo tick every 50 ms. A half-second note uses ten ticks, or about 120 beats per minute.

The sequencer tracks the current event and its elapsed ticks. When an event’s duration runs out, it clears the tick count and advances; after the final event, it returns to the start. A single-cycle tick pulse prevents the much faster main clock from advancing the melody more than once per beat. Adjacent events with the same pitch can still sound continuous without a rest between them, even though the stored notes are separate.

What I took from it

The switch-controlled piano made the relationship between a number and a pitch tangible. Adding the sequencer turned it into something that could perform on its own. There is no recording being played back: the FPGA generates each tone, counts its duration, and chooses the next note.

Hear it play

The finished player runs through the stored melody on the board. Open the video on YouTube ↗