In this tutorial, we are going to make an 8 step synthesizer for our FPGA board. We will be working with the Digilient Nexys 2 board but our code could be modified for other boards. This project was built by Lincoln Tran and Joseph Coplon for our final project of our CPE-133 class at California Polytechnic State University, taught by Dr. Andrew Danowitz.
An 8 step sequencer is a device that plays a sound track based on the position of the switches. On our board, there are 8 switches. The sequencer is a time line that goes from left to right on the row of switches. If the switch is up, a specific note will be played when the time passes it. Pressing a button would change the frequency for the switches. The end result would be a repeating track of 8 notes with frequencies of the users choice. We based a lot of our game off of the Tone Matrix, an online step sequencer. Its hard to understand our game without playing with a full scale one first so we recommend messing around with the Tone Matrix. Its pretty easy to lose a lot of time in such a simple game. We didn't want this whole project to be too complicated so we decided to stick with the inputs found on the board, but expanding it to have more notes with external buttons or more LED's isn't overly difficult.
In this Instructable, we will first go over the components, the logic design, and how everything is connected together. Then we'll talk a little about connecting the external device (speaker) and how to play the game.
Step 1: Inputs and Outputs
Here is a list of inputs and outputs that we have to incorporate into our VHDL. Having an understanding what we are trying to connect together will help significantly.
Step 2: Components of the circuit
Designing the Components:
In order to implement a synthesizer on the Nexys 2 FPGA board, several components need to be designed. Starting with the outputs, modules will be required to handle turning on LED's and outputting square wave functions to the speaker. Modules will also be needed to debounce button inputs and generate a single clock pulse from a button press. Finally, modules like counters, flip-flops, and multiplexers will be needed to handle the remaining logic of storing notes and playing back sequences correctly.
The following list contains a brief description of what each component in this project does, how each component is being used, and how to implement these components in VHDL:
Button Toggle: In the case of the play/pause button, we want to toggle between two states exactly one time when the button is pressed. In order to do this, we use a finite state machine (FSM). This way, after transitioning between states once, the button toggle circuit will not allow another state transition to occur until the button has been released and then pressed again. A diagram representing this FSM is shown above.
The finite state machine contains two states for each possible output. Beginning at the top of the FSM diagram, the circuit is in the pause state. As soon as the button is pressed, the circuit will go to the play state. It will remain in this first play state until the button is released. Now, the circuit is still in the play state, but it is ready for the button to be pressed again to transition to the pause state. Similarly, the circuit will once again wait for the button to be released before it becomes ready to switch to the play state.
Clock Divider: The clock divider circuit simply converts from the 50 MHz frequency of the Nexys 2 to a much smaller frequency. This is used to set the BPM (beats per minute) of the synthesizer. In this project we are using a VHDL module written by Bryan Mealy of California Polytechnic State University, San Luis Obispo.
Counter: A counter simply outputs a binary number. When its input is high, it will increase this number by 1 on the rising edge of clock. The counters used in this circuit uses T Flip-Flops to store a 3 bit number. Each flip-flop represents one bit being stored. The flip-flop representing the least significant bit will toggle each time the counter is supposed to increment. The other flip-flops will toggle whenever all of bits below them are equal to one. This behavior will also make it so when the counter reaches the highest possible number, 111(binary), it will go back to zero when incremented next.
In this project, a counter is used to keep track of the current beat so that the speaker knows which tone to play, and so the correct LED can be turned on. A counter is also used to keep track of the current tone which can be inputted by the user.
Debouncer: The push buttons on the Nexys 2 FPGA board are not perfect. Ideally, when pressed the contacts would touch together exactly once, and stay together until the button is released. In reality, the contacts have a tendency to "bounce" multiple times, in that when the button is pressed the contacts will come in and out of contact several times before sticking. In terms of our synthesizer, this could lead to a play/pause button which switches between the two states multiple times when the button is pressed, and is not guaranteed to land on the correct one. To counteract this, a debounce circuit will wait a certain amount of time for the input to "settle down".
In this project we used a VHDL module written by Scott Larson of Digi-key, originally published on the Digi-key EE wiki.
Pulse Generator: The counters used in this project will increment along with a certain clock frequency as long as they are enabled. For some functions, such as incrementing the frequency when pressing the button to change the current tone, we might want to increment a counter only once with each button press. This requires taking an input and converting it to a pulse with a duration of exactly one clock cycle. The pulse generator circuit will do this. When its input goes to high, its output goes high for exactly one clock cycle. This is accomplished by keeping track of the current input for a given rising edge of clock, as well as the previous input for the last rising edge of clock. In the case where the current input is high and the previous input is low, the output is high. Otherwise, the output is low. In VHDL, this is accomplished using a behavioral model. A process block checks the values of the input value incr and the previous input incr_prev on the rising edge of the clock. If incr is one and incr_prev is zero, the output q is set to one. Otherwise, q is set to zero. Finally, incr_prev gets the value of incr.
sseg_dec: This circuit takes a binary input with up to eight bits and displays it as a decimal number on the Nexys2 seven segment display. In this project, we are using a VHDL module written by Bryan Mealy of California Polytechnic State University, San Luis Obispo.
Square Wave Generator: This component will generate a square wave based on a 3-bit input. It does so by dividing the input clock signal by a specific number to achieve the desired frequency. The output is then toggled on and off according to this frequency. There are seven predetermined frequencies, based on the pentatonic scale, which represents the notes from A4 to C6. The frequencies for these tones range from 440 Hz to 1046.5 Hz. If the input is 000(binary), then no square wave is played.
Step 3: Tying the components together
After designing all the necessary VHDL modules, these components must be connected together. The block diagram above demonstrates how all these components are connected.
Now we will discuss why these components are connected in the way that they are.
Part 1: Incrementing Frequency
Lets begin with Button 1, which increments the current tone frequency. First this input has to go through a debouncer, so the signal is only toggled once for each button press. Next, the debounced signal is sent to the pulse generator module. This ensures that the current tone will be incremented once and only once with each button press. The output of the pulse generator goes to the counter, which stores and increments the current tone. The output of the counter goes to the sseg_dec module, so that the current frequency is displayed on the Nexys2 board’s seven segment display. The output is also sent to the input of each D Flip-Flop, ready to be "saved" to the relevant beat.
Part 2: Toggling Play and Pause
Next we can examine how Button 3, which toggles between play and pause, connects to the circuit. First the signal goes through a debouncer to ensure the state only toggles once for each button press. The output of the debouncer connects to the button toggle circuit, which transitions between high and low each time the input goes from low to high (i.e. each time the button is pressed down). The 3-bit counter takes uses the output of the button toggle as an enable signal, so whenever the toggle is high, the counter will increment continuously. In other words, the output of the toggle represents whether the circuit is in the play or pause state. The counter keeps track of the current beat being played by the synthesizer.
The counter is connected to a frequency divider so it increments at a reasonable speed. The speed at which the counter increments is also representative of the BPM (beats per minute) of the synthesizer. The output of the counter connects to the LED's over the switches on the Nexys2 board, giving an easy indication of which beat is currently being played. The output also goes towards the select signal of the multiplexer. The purpose of the multiplexer will be explained in Part 4.
Part 3: D Flip-Flops
The D flip-flops represent a critical part of the synthesizer. Each flip-flop represents one of the eight different beats. Each flip-flops stores three bits which represent the tone played on that beat. Whenever the enable signal is high, the flip-flop will update its output Q to whatever is connected to its input D. In this circuit, the D is connected to whatever frequency is currently stored by the counter in Part 1. The enable signal is connected to and AND gate, meaning the enable signal will be high when both of the relevant inputs are true. The first input is the switch corresponding to that beat. The second input is button 2, the "Save" button. In other words, to input a new frequency to a certain beat, the user must turn on the correct switch, and then press the save button. Note that button 2 is not connected to a debouncer, since it does not matter if the D flip-flop updates multiple times when the user presses a button.
The flip-flops have another input, which is the clear or reset. This input is connected to Button 4, so whenever that button is pressed the flip-flops will revert to an output of 000(binary). Note that this button also does not require a debouncer, as resetting the circuit multiple times per button press gives the same result as resetting it once. Finally, the outputs of all the flip-flops go towards the multiplexer, whose functionality will be explained in the next section.
Part 4: The Multiplexer and the Speaker
The final part of this circuit is the multiplexer and speaker output. The purpose of a multiplexer is to choose between several different inputs to give one output. In this case, one of eight possible tones stored by each of the eight flip-flops needs to be selected to be outputted to the speaker. The correct tone is determined by the select signal, which is connected to the counter that keeps track of the beat. In other words, as long as the circuit is in the play state, the select signal will increment from 000(binary) to 111(binary), and the output of the multiplexer will cycle through each of the flip-flop outputs.
The output of the multiplexer is connected to a square wave generator. Based on the 3-bit input, the square wave generator will toggle its output signal to produce a square wave with a certain frequency. This wave is then sent to the speaker, which will play the correct tone.
Step 4: Creating a UCF file
After writing all the VHDL modules for this project, it is important to write a User Constraints File (UCF) which describes which pins on the FPGA board correspond to the inputs and outputs of the main VHDL module. The relevant pin numbers can be found using the manual for your FPGA board. In the file StepSynthesizer.ucf you can see which pins we used in our project.
Step 5: Connecting external devices
The physical aspect of this project was pretty simple since we only had one external component. The board has most of inputs and outputs that are vital to the game such as the seven segment display, buttons, and switches. The only thing we had to add in was the speaker.
The Nexys board has several outputs pins for Pmods so you can pick any. Looking at connector circuit diagram, we can see that pins 5 and 11 are ground and pins 6 and 12 are power and the rest of the pins are data. We connected the ground of our speaker (solid black line) to pin 11 and the positive (black with white strip line) to pin 7 of the connector. From our user constraint file found in the last step, we outputted the square wave to pin 7.
Step 6: Playing the game
Here is a short video showing how to play the game. Enjoy!