In this lab you will design a peripheral device that plays musical notes. This device will have a memory mapped register that contains the currently playing music note. The processor will write to this register a number between 0 and 35 and the device will play the notes A, B, C, ..., G#, for 3 octaves, accordingly. Writing any other value will disable sound. The application running on the 8031 will use this peripheral device to play songs.
The goal of this lab is to play a song. A song is basically a list of
notes and how long each of those notes should be played.
Above is the keyboard we are trying to mimic. There are
12 basic notes: A, A#, B, C, C#, D, D#, E, F, F#, G, and, G#.
These 12 notes compose an octave. As you can see above the
pattern repeats. The second set of notes comprises another octave.
In this lab we want to mimic 3 octaves or a total of (3x12) 36 notes.
The first step is to model these notes, but how do we model
these? Notes are just frequencies. An 'A' above middle C, is
represented by 440 Hz. If we can generate a 440 Hz signal then
we can output a sound equivalent to A. How do we generate this
signal? On the XS40 board we are using there is an 8031, we know
it's clock speed is 12 MHz. We then build a clock divider to
slow the 12 MHz to 440Hz, output the results of our clock divider
to a pin on the XS40 board and we have our signal/note.
The first step is to model these notes, but how do we model these? Notes are just frequencies. An 'A' above middle C, is represented by 440 Hz. If we can generate a 440 Hz signal then we can output a sound equivalent to A. How do we generate this signal? On the XS40 board we are using there is an 8031, we know it's clock speed is 12 MHz. We then build a clock divider to slow the 12 MHz to 440Hz, output the results of our clock divider to a pin on the XS40 board and we have our signal/note.
The second concern in playing a song is controlling the duration for which a given note is played. We can tell how long to play a note by looking at it's type. Let's say that we are given some base value X. A whole note would play for the entire base value duration. So, if X=8, then the note would play for 8 units. A half note would play half of the base value, this means that a half note would play for X/2 or 4 units. A quarter note plays for a fourth of the base value, X/4 or 2 uints. Between a whole note and a half note, exists a halfdot. This note's duration is in the middle of a whole note duration and a half note duartion, (X + 0.5X)/2 or in the case of X=8, 6 units. The quarterdot is in between a half note and a quarter note and plays for 2 units. Each of the durations we will be dealing with is seen above along with it's corresponding duartion and symbol(s). By chaning the base value X, we can speed up a song or make it slower. However, the ratio of how long a note will play compared to other is still the same. By varying the base value X, we vary the tempo.
We know that in order to play a song we need to be able to specify which note to play and for how long. This is were the memory mapped register comes in. We won't get into the details of this (it's another lab...).
The basic idea is as follows: If you look at your XS40 board you have an 8031 and a FPGA connected to it. Connecting these entities you have a bus. Using the 8031 we can store the song (i.e. which notes to play and it's corresponding duration) in an array. We then indicate the note we want to play on the bus for the duration we want to play it. Therefore, if our song has a whole note corresponding to an A we know that we would send A to the FPGA using the bus. Then we would wait for 8 counts. The FPGA reads A from the bus into its internal register and plays that song until another note is writen to it.
However, we don't actually put the note value on the bus. Instead, both the 8031 and FPGA know code words which indicates which note is to be played. For example, the 8031 wants to play an A in octave 1. It has a look up table and sees that the code word corresponding to A in octive 1 is "00000000". The 8031 puts "00000000" on the bus. The FPGA sees "00000000" on the bus and using it's translation table, it know that it's supposed to play an A in octave 1, so it needs to generate a square wave of 440Hz. We do this for two reasons. Fist, the bus is 8-bits wide. The clock division values are 16-bits. We can however overcome this by designating two registers in the FPGA and send the higher 8 bits then the lower 8 bits. The second reason is that the 8031 should not have to keep the frequency division values. These values are specific to the FPGA. If we want to use another FPGA then instead of having to change the 8031 code, we still send the code word and the FPGA will translate the frequency division corresponding to it's speed.
All that is left is translating your song into notes and duration. You will translate a given song off of sheet music. You will see something similiar to above. The placement of the note inidicates which note it is. (Note: C appears twice in the figure above. The first C is located on octave 1, the second C is located at octive 2 and so on...). The duration of the note is indicated by what symbol is present. The symbols corresponding to the duration is provided above.
The connections between the 8051 and the FPGA are already done on the XS40 board. All you need to do is to connect the speaker to the XS40 board.
Below are all the files you need to create the music generator.
Do not change any of the ports, values, constants, or connections
provided in the code.
Generating the Bitstream: