Introduction: 4 Step Digital Sequencer

CPE 133, Cal Poly San Luis Obispo

Project Creators: Jayson Johnston and Bjorn Nelson

In today’s music industry, one of the most commonly used “instruments” is the digital synthesizer. Every genre of music, from hip-hop to pop and even country, uses a digital synthesizer in the studio to create the beats and sounds they need to bring their music to life. In this tutorial, we will be creating a very simple synthesizer with the Basys 3 FPGA board.

The synthesizer will be able to play four selected quarter notes at a constant number of beats per minute. Users will use the switches to assign each quarter note to a musical pitch. For this project we are using a 4-bit digital to analog converter (DAC) to take the output from the board and convert it into an analog signal. The output from the DAC will then be fed to a standard computer speaker, creating our music. Sixteen discrete pitches are possible. We will restrict our synthesizer to a single octave of 12 notes, which fall between middle C (261.6 Hz) and B4 (493.9 Hz). The user will also have the option of assigning multiple notes at the same time, as well as assigning a rest by hitting assign while having none of the pitch switches shifted upward. As each note is selected and being played, the letter note is shown on the 7-segment display. We will also be using three of the buttons on the board, one for playing and pausing the music, one for resetting the synthesizer and putting it into “selection” mode, and the third for assigning each note a pitch while in selection mode.

Once the user is satisfied with their choice of notes, and after pressing the play button, the synthesizer will play each note in succession repeatedly until the user either presses pause or select.

Here is a list of the required equipment:

  • Vivado (or any VHDL work space)
  • Basys 3 or similar FPGA board
  • Digital to analog converter (min. 4-bits)
  • Speaker with headphone jack
  • Wire leads

Step 1: User Operation of Digital Sequencer

The following steps are to operate the digital sequencer. The digital sequencer supports the playback of 12 distinct pitches (C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B), which range from 261.6 Hz to 493.9 Hz.

1. Press the left button to put the board into selection mode. When in this mode, the leftmost 4 switches (switches 13 to 16) will each be used to store a distinct pitch value.

2. To make a selection, flip one of the left switches on, and then use the rightmost 4 switches (switches 1 to 4) to choose a desired pitch. The pitch associated with a specific combination of right switches will be shown on the seven segment display, and the display will update to the new associated pitch whenever the right switches are shifted to a new combination. A rest can be assigned by never assigning a pitch to one of the left switches, or by assigning a pitch shown as 0 on the display to the note. Once the desired pitch has been found and is shown on the display, press the bottom assign button to assign that specific pitch to the note.

3. Repeat step 2 for the three remaining notes, by flipping each of the remaining left switches on individually, choosing the respective pitch with the right switches, and pressing the bottom button to assign the pitch to the note. Multiple notes can be assigned the same pitch by shifting more than one of the left switches upward at the same time.

4. Now that all of the note pitches have been assigned, the digital sequencer is ready to play. To play the notes on the speaker, simply press the right play/pause button to begin playing the music. The order of the playback sequence mirrors the pitches associated with the left switches, from left to right. The notes will be played at a set number of beats per minute, in the order 1, 2, 3, 4, 1, 2.... The display will show the note that is currently playing as the speakers play the music. To pause the music playback, simply press the right button, and then the music will stop playing and a pause symbol will be shown on the display. Pressing the right button again will resume playback.

Step 2: Technical Details

Our synthesizer makes use of many different digital components. Included are finite state machines, registers, multiplexers, clock dividers and more. To build our synthesizer, we used 10 unique modular files. Rather than making each module a component, we broke the modular files down by function. Most modules, as a result, are more than one component. Note the image above shows every block tied together in our top design.

We will discuss each module by describing the inputs and outputs, breaking down its components, and explaining its purpose in the overall design. A ZIP file is included at the bottom of the instructable, which contains every VHDL code file used in the project.

Inputs

  • Clk (native clock signal)
  • PP (play/pause)
  • Sel (put synthesizer in selection mode)
  • Assign (assign a step to a pitch)
  • Step (the positional notes)
  • Freq (the switches creating the desired pitch)

Outputs

  • Anode (7-segment anodes)
  • Cathode (7-segment cathodes)
  • DAC (4-bits driving the DAC)

Step 3: Technical Details

Step 4: 7-segment Clock Divider

Our synthesizer makes use of three clock dividers, all producing signals that serve a different purpose in our project. A clock divider takes a native clock signal and produces an altered signal which has a frequency that is less than the original clock signal. The native clock of the Basys 3 is 100 MHz. This is the frequency that our clock dividers utilize. If you are using a different FPGA board with a different native clock frequency, you may have to alter the code.

The 7-segment clock divider produces a signal which drives the seg_display file. We will explain how this file works in more detail when we get to its section. Essentially, this clock divider produces a 240 Hz signal that will be used to switch between anodes and cathodes on the display. The signal is 240 Hz because the frequency at which the human eye cannot recognize the absence of light is 60 Hz. We are using two digits, so by doubling this frequency, each digit will oscillate at 60 Hz. Then we double it get to get 240 Hz because the system is only changing when the signal goes high, not when it goes low.

To achieve this, the divider takes the native 100 MHz signal and counts up on every rising edge. When the counter reaches 416667, the output will go from low to high, or vice versa.

Inputs

  • Clk (native clock signal)

Outputs

  • Clk_7seg (to seg_display)

Components

  • D register
  • MUX
  • Inverter
  • Adder

Step 5: Beats Per Minute Clock Divider

The BPM clock divider works in a similar manner. This divider produces the clock frequency which drives the switching between the four steps when outputting tones in the play state. We decided on switching between notes at 100 BPM. At 100 BPM, each note will be played for 3/5 of a second. The resulting signal would have a frequency of 1.67 Hz.

To produce a signal of this frequency, we again used a counting system, but this time the count was 60 million. Every time the counter hit 60 million, the output signal would toggle high or low.

Inputs

  • Clk (native clock frequency)

Outputs

  • Clk_BPM (to output_FSM)

Components

  • D register
  • MUX
  • Inverter
  • Adder

Step 6: Pitches Clock Divider

The Pitches Clock Divider is the largest of our clock dividers. This divider outputs 12 different signals corresponding to the 12 different notes that our synthesizer can play. Using basic knowledge of music theory, we deduced that a bit or bus could oscillate at a rate which corresponds to the frequency of musical notes. To see the frequencies we used, look here. We used the fourth octave of pitches.

The same counting system is used here. For the specific values we counted to, see the file labeled Clk_div_pitches.

Inputs

  • Clk (native clock frequency)

Outputs

  • C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B (to output_select)

Components

  • D register
  • MUX
  • Inverter
  • Adder

Step 7: Play/Pause/Select State Machine

In our project there are two finite state machines (FSM). A FSM is a logic device that can exist in only one state out of a finite amount of states. Using a FSM, a digital circuit can move to a new state based on a combination of inputs. Using input logic, the state of an FSM will change when there is a rising edge of the clock. From the state and inputs into the circuit, you can create output logic that gives outputs that only exist if the FSM is in a certain state.

The PPS state machine is the first FSM in our circuit. There are three states in this FSM; Play, Pause, and Selection mode. To move through the different states, we used the PP and Selection buttons. See the state diagram above to see how transitions between states occur. We made this FSM transition on the rising edge of the native 100 MHz clock, so that it would be impossible for the machine to not transition when one of the buttons was pressed, even for a very short amount of time. The present state (P_state) is the only output from this module.

Inputs

  • Clk (native clock frequency)
  • Sel (left button)
  • PP (right button)

Outputs

  • P_state (present state, to output_FSM, note_assign, seg_dsiplay, final_select)

Components

  • MUX
  • D register

Step 8: Play/Pause/Select State Machine

Step 9: Output FSM

This is the second FSM referenced in the previous section. This FSM serves a different function than the other, but the basis for this one is essentially the same.

The output FSM only operates if the present state from the first FSM is "01" (the play state). Essentially, this is the enable in for the module. If the state is "01", then the FSM is going to switch between states on the rising edge of the BPM clock signal. We do this because the output_FSM is controlling which binary number for the selected pitch gets sent to the output_select and seg_display modules. The FSM has a 16-bit input coming from the note assign module, which will be covered next. In the "00" state for the output_FSM, the module will output "xxxx" for the first note assigned. Then in "01", it will output "yyyy" for the second note and so on for each note before rolling back over to the first note. See the state diagram above.

This FSM differs from the first because there is no input logic to control switching between states. Instead, the FSM is only going to operate when the state from the first FSM is "01", and then this FSM will transition between states only on the rising edge of the clock signal. Another difference is that this module has output logic, meaning it doesn't output the present state, it outputs the binary number for the pitch at that state.

Inputs

  • Clk_BPM (BPM clock signal from clock divider)
  • FSM1_state (PS from PPS FSM)
  • Pitch_in (pitches from note_assign)

Outputs

  • Pitch_out (one pitch at a time, to output_select and seg_display)

Components

  • MUX
  • D register

Step 10: Output FSM

Step 11: Note Assign

The note assign module is responsible for actually assigning a pitch to the positional note, or step. This module is actually quite simple. It first checks if the circuit is in the "selection" state and if a step switch (far left) is high. If this is true and the assignment button is pressed, the output of the module will be equal to the binary number represented by the frequency switches (far right).

Originally, we had attempted to make a module that would actually save one of the pitch clock signals to the output, but we experienced issues with the output changing to follow the input clock signals. This is the only module used more than once in the final design. Every step has a note_assign module associated with it, and because of that, each instance of the module gets one bit of the Step bus.

Inputs

  • P_state (present state from PPS FSM)
  • Sel (left button)
  • Switch (one step switch)
  • Freq (far right switches for pitch)
  • Assign (bottom button, assigns a note)

Outputs

  • Pitch (binary number, to output_FSM)

Components

  • MUX
  • D resgister

Step 12: Output Select

Output select is responsible for taking the binary number for a pitch and connecting that to its respective clock signal. Despite its size, this is also a relatively simple module. Output_select is essentially a binary decoder, decoding the binary number for a pitch to a specific clock signal. Actually assigning the output to a clock frequency worked better here compared to the note_assign module, because all this module had to do was MUX the clock signals with the binary number representing the control input.

We apologize for the strange routing, Vivado organized the pitch signals alphabetically for the clk_div_pitches file, but for this file it organized them by ascending binary number, causing the pitches to be in a different order. Also note that if the binary number from the output_FSM was "0000" or anything greater than "1100", then the MUX sent through a flat '0' signal.

Input

  • Pitch (from output_FSM);
  • C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B (pitch clock signals)

Output

  • Tone (a single bit which matches the selected clock signal, to square_wave)

Components

  • MUX

Step 13: Square Wave Gen

Module square_wave is the generator for the square wave that is outputted from the board to the DAC. Using the tone signal from the previous file, this square_wave inverts the 4 bit number between "0000" and "1111" on the rising edge of Tone. Tone is a specific pitch frequency, so square_wave produces a wave with a different frequency when output_FSM transitions to another state. The 4-bit output from this module goes to the fin_sel module, where logic dictates whether this bus will be outputted based on the state from PPS FSM.

An alternative to this square wave generator is producing a sine wave. While this would most likely produce a better final tone, it is considerably more difficult to implement, so we opted to just generate a square wave.

Inputs

  • Tone (oscillating bit from output_select)

Outputs

  • DAC_input (oscillating 4-bit bus that changes at same frequency of tone)

Components

  • Inverter
  • D register

Step 14: 7-segment Display

The seg_display module controls the 7-segment display on our basys board. Within the module, two processes occur. The first process decodes Freq when in the "selection" state or Pitch when in "play" mode. In "pause" mode, the module decodes to show the pause symbol. Looking at the VHDL code, you can see that the binary decoder actually decodes the input into two different signals, cathode1 and cathode2. Cathode1 represents the letter corresponding to the pitch to be displayed, and cathode2 represents the flat symbol (b) if there is one. The reason for this relates to the second process done by the seg_display module.

On a basys3 board, the segment display has common cathodes. While the anodes control which digit is turned on, the cathodes control which segments are on. Since the display has common cathodes, that means you can only display one set of segments at a time. That poses an issue for this project because we want to display a letter at the first digit and the flat symbol, if necessary, at the same time. Now remember the 7seg clock signal? To get around this issue, we change the anodes and cathodes back and forth on the 7seg clock signal. Because the clock signal is 240 Hz and we're using two digits, each digit will oscillate at 60 Hz. To the human eye, it will look like the digits aren't oscillating at all.

Also note that the basys3 board display uses negative logic. This means if an anode or cathode is set to '0', that digit or segment will be on, and vice versa.

Inputs

  • Pitch (binary number for a note, used in play state)
  • Freq (frequency switches, used when in selection state)
  • P_state (present state from PPS FSM)
  • Clk_240Hz (clock signal from Clk_div_7seg, double 120 because we are only using the rising edge)

Outputs

  • Cathode (bus which controls segments on the display, final output)
  • Anode (bus which controls digits on the display, final output)

Components

  • Latch
  • MUX
  • D register

Step 15: Final Select

Final select is the last module used in this project. Another simple module, this module controls the final output that will be going to the DAC. When in the "selection" or "pause" state, the module is going to output a static "0000" so that no music will be played from the speakers. In the "play" state, the module will output the oscillating 4-bits as determined by square_wave.

Inputs

  • P_state (present state from PPS FSM)
  • DAC_input (the oscillating 4-bits from square_wave)

Outputs

  • DAC (equals DAC_input in play state, final output)

Components

  • MUX

Step 16: External Devices: DAC

A digital to analog converter (DAC) takes a discrete signal and converts it into a continuous signal. Our DAC has four bits and is made from a summing amplifier. By using a ratio of resistors in the supply and feedback loop, we were able to create a system that outputs at 16 different levels creating by the "summing" of each branch. Bit0, the top branch, carries the least weight and contributes the smallest potential when high because of that branches higher resistance. The weight increases as you go down the branches. If you were to count in binary up and then back down using the bit inputs, the output voltages would look like a step wise sine wave. The input to the DAC was connected to the one of the PMODs on the board to transfer the 4-bit signal.

The DAC was originally assembled for an Electrical Engineering class and was designed and soldered by us, not bought from a store. Above is an image of the design file for creating the printed circuit board.

Step 17: External Devices: Speaker

For this project, you aren't going to want to buy a super nice pair of speakers. As you can tell, the sound is pretty basic. We went and bought a $8 set of computer speakers from Best Buy. Anything with a headphone jack works fine. Monotone works fine as well. You can even use headphones, but you might blow them out!

To connect the output of the DAC to the speakers, we used jumper cables and then held the output cable to the tip of the headphone jack and the cable for ground to the base. We tried using electrical tape to hold the cables in place, but it caused a lot of interference. Trying a different style of tape could solve this issue.

For our speakers, we turned them to the highest setting and got a decently loud noise.

And that is the last step for creating a digital sequencer from a FPGA board! Go to the next two sections to download all our VHDL code and see the sequencer in action.

Step 18: Video Demo

This video shows the final version of the working project, including the process of assigning the switches to 4 distinct pitches, and the speakers playing the respective notes.

Step 19: VHDL Code

Here is the code for the entire project, including the constraint and sim files used while building the sequencer. Note that unused design files say so in the architecture.