The DE0 Development and Education board is designed in a compact size with all the essential tools for novice users to gain knowledge in areas of digital logic, computer organization and FPGAs. It is equipped with Altera Cyclone III 3C16 FPGA device, which offers 15,408 LEs(Logic Elements, which are basic units used to build up more advanced behaviors). The board provides 346 user I/O pins(An Arduino usually has about 20), and is loaded with a rich set of features that makes it suitable to be used for advanced university and college courses, as well as the development of sophisticated digital systems. The DE0 combines the Altera low-power, low-cost, and high performance Cyclone III FPGA to control the various features of the DE0 Board. The DE0 Development Board includes software, reference designs, and accessories required to ensure the user simple access in evaluating their DE0 Board.
Quite a number of things, but a good sampling of what an undergraduate can achieve in a month or two on and off can be found on Cornell's ECE 576 project page, below.
https://people.ece.cornell.edu/land/courses/ece5760/FinalProjects/
Some places they may be relevant to physics is in speeding up massively parallel operations, such as simulations of fluid dynamics. https://vanhunteradams.com/DE1/Lattice_Boltzmann/Lattice_Boltzmann.html A rather standard Cyclone V chip can achieve an order of magnitude speedup compared to C code running on a Mac M1, or over three orders of magnitude faster than a Python-based simulation, despite running at a slower clock speed!
Once booted, plug the DE0 into the computer using the USB cable. The 7 segment readouts should begin counting from 0 to F and the LEDs should scan back and forth. If this doesn’t happen, make sure the Run/Program switch is set to Run
and the red push-button switch is ON. Now start up the Quartus II software and start a new project using: FILE→ New Project Wizard
The New Project Wizard dialog will open, like so:
Click Next and pick directory and name for your project. It is a good idea to use a new subdirectory so that your files don’t mingle with the files of other projects.
Click Next here (creating the directory if needed), and again at the “Add Files” page.
You should now be at the Family and Device Setting dialog (page 3/5).
Once here, be sure that the Family field shows “Cyclone III”.
Select the Device named EP3C16F484C6
(this is the FPGA type installed on the DE0). You can copy and paste the name into the search box, but beware of leading or trailing spaces.
Now click Finish to complete the Project Wizard. Your screen should now look something like this:
Now you are going to do the hardware equivalent of a “Hello World” program – you are going to make an LED turn on. We’ll learn about how to use the Quartus II software as we go. Select File→New
. Under Design Files
, select Block Diagram/Schematic File
and then hit OK.
You should now be in schematic entry mode, which allows you to draw devices on the screen to build up your circuits.
Now - click on the Pin Tool and select an OUTPUT
. A little ghostly output pin will appear next to your cursor. Drag it to the middle of the screen and left-click to drop it.
Next, click on the Symbol tool. This will bring up a dialog like so. Navigate down to Primitives | Other
and select the VCC
element. You can also just type the exact name into the search box.
Drag the $V_{CC}$ element over to your schematic and place it to the left of your output pin. Now select the Orthogonal Node
tool and connect the $V_{CC}$ point to the output pin. Your schematic should look something like this:
At this point save your schematic file by hitting control-S
. Pick a filename and click “Save”.
Now we will synthesize our schematic using the Start Analysis and Synthesis
icon:
This tool does a preliminary analysis of the schematic and attempts to convert it into logic-gate level form for the final compilation. It should take a short time to run and will probably give you a few warnings. This is ok.
If you successfully synthesized, you will have a logical output pin that is defined, but not associated with any of the physical pins on the FPGA. To complete the design, you need to connect your logical pin to a physical pin using the Pin Planner Tool (under menu item Assignments, or via Control-Shift-N). Click on the Pin Planner. A dialog will open which looks like this, showing all the 484 pins on the FPGA.
To assign your pin properly, look at the bottom half of this dialog and click in the Location
field in the row corresponding to your pin (in this case pin_name1
), and type in the number J1
.
A list of pin numbers is included in the DE0 manual and also on the following wiki page. In the present case, the pin PIN_J1
corresponds to the 1st green LED (LED Green[0]) on the right of DE0 board. Now exit the Pin Planner (hit X button, upper right).
Always be sure to synthesize your design before trying to assign pins, as the synthesis process identifies open pins and places them in this dialog for you to assign.
Looking back at the schematic – there should now be a pin assignment tagged next to the output pin, like so:
There's one more step needed before compiling: We need to let Quartus know what file it's supposed to work with. To do this, go to the menu on the left, select the files tab, right-click on your file and click set as top-level entity
We are now ready to compile! Select the Compile button and wait with bated breath…
The compilation process can take a minute or more, depending on how complicated your design is. You will likely get 10-20 warnings during the compilation. This is ok.
If the compilation was successful, you now have a fully-defined design and you need to burn it into the FPGA. This is done using the Programmer function . Sadly, this does not produce bacon like the icon indicates.
Running the Programmer will bring up a dialog like so:
If USB-Blaster
does not show up next to the Hardware Setup
button, click that button and select it from the Currently Selected Hardware
drop-down. If USB-Blaster is not an option, then your USB driver is not installed properly, and you may need to get help from the TA.
If no file is automatically selected, click the Add File…
button, go to the output_files
folder, and select the .sof
file with your project name.
If all is well, then click the Start
button and the FPGA will be programmed. If the progress bar doesn’t show 100% (Successful)
within a few seconds, be sure that your device is in RUN mode (The switch on the bottom left should be toggled up). If all else fails, ask your TA.
After the device is programmed, you should notice that the counting and flashing on the DE0 have stopped and that the right-most green LED is lit. Congratulations! You just made your “Hello World” program and learned how to use an FPGA.
That was a little too easy, so now let’s add some user input. Go to File→Save As and save your schematic under another name. We are going to add a few components.
After doing this, go to the files
tab under the Project navigator
on the left-hand side of the screen, right-click the new schematic, and select Set as Top-Level Entity
. Otherwise, when you go to compile things, Quartus may be stuck on the other file.
First - add another pin. This time an INPUT pin. Position it to the left of the output pin.
Next, delete the $V_{CC}$ pin and the connecting wires. To do this, click on them to make the blue selection indicators appear. Then hit the delete key.
Now, using the Symbol Tool, add a NOT gate
(it is under Primitives |Logic
) and connect one end to the input and one end to the output.
Hit control-S to save. Now, synthesize and assign pins. You want to associate your INPUT pin to an input on the DE0 board. We will use PIN_F1
, which is push-button 2 (the left-most one).
Your schematic should now look something like this:
Now save (Control-S) and Compile and Program.
The right-most green LED should now be dark, until you push button 2, when it will light up (you may have to push it hard on some of the DE0s). Congratulations! You have now performed IO with your FPGA.
The reason that we had to put the inverter in there is that the default output signal associated with the three pushbutton inputs is HI
(3.3V). By pressing the button, you are actually asserting a LO
state. Remember this for later when you are assigning logic to the IO pins.
Last week you built a 4-bit up/down counter circuit. Now, we'll see how we can make arbitrarily large counters using Quartus.
Let’s start a new project for this circuit.
Follow the instructions from the Getting Started section.
Make a schematic file and then select the lpm_counter
module (Symbol Tool |megafunctions |arithmetic |lpm_counter
).
“LPM” stands for “Library of Parameterized Modules,” which basically means you can create a custom device (a counter in this case) with various properties you set. This can be much simpler than building what you need from scratch!
Click Next
on the first dialog window to get to page 3 of 7 of the plugin manager wizard.
To start, we'll select a 4-bit counter that only counts up. To do this, change q
to 4, press Finish
, and then press Finish
on the next screen. You should be left holding a counter, which will look like the following when placed in the block diagram:
You'll want to add an input pin and connect it to the clock
input of the counter. Now, there's something new we have to deal with for the output.
The output of this counter is actually 4 separate bits. However, Quartus defaults to bundling multiple bits together in a bus
connection. We'll need to assign a name to the bus, and then names to the individual bits we want to assign to be used later.
Use the orthogonal bus tool
(or click and drag from the thick purple line attached to q[3..0]
) to extend the connection out to the right some. Then click on the bus wire you just drew and start typing to name it. For now, use the name counter[3..0]
to let Quartus know that the entire bus is named counter
and that it contains 4 bits. Your schematic should look something like the following:
Now, to use those individual bits. Use the orthogonal node tool
to create a new wire that starts on the bus and ends off to the right. Then click on your new wire and name it counter[0]
. This splits off the lowest bit of the counter to be used by you elsewhere. It should look like the following:
Repeat the process 3 more times, changing the numbers to 1,2, and 3.
With your bits separated out, attach each one to an output pin. You may want to rename the output pins to remind you of what they're for.
Run the Analysis & Synthesis on your design, and then go to the pin planner tool. You'll want to assign the input to pin F1
again, and the outputs of the four counter bits to the four left-most LEDs. Those belong to pins B1
, B2
, C2
, and C1
respectively.
Compile your project, and use the programmer to write it to your FPGA. The result should be a that you have a 4-bit binary counter indicated by 4 LEDs that increments when you press button 2. Remember how much wiring it took to accomplish something similar in hardware last week? This is just the start.
I don't know about you, but I don't read binary numbers very naturally. Fortunately, the DE0 also has four 7-segment displays which can be used to show hexadecimal numbers. The connections on the rightmost 7-seg display are shown here:
With the appropriate conversion logic, we can use one of these to display the output of our counter. However, the process of converting binary numbers to a base 10 digit is not trivial. Here is what the gatelevel logic looks like:
(Placeholder: img)
Fortunately, there are ICs built to do the hard work for us and several of these ICs are modeled already in the Quartus software. This is one of the fabulous benefits of designing with FPGAs – the entire phase space of available logical devices is there for you to use with just a few clicks.
Go to the Symbol Tool and use the search field to locate a 7447 chip (Others |maxplus2 |7447
):
Insert this into your schematic, and assign the seven OA..OG
outputs to seven individual output pins. To enable the converter for your circuit, you will also need to tie the LTN
and RBIN
pins to $V_{CC}$ (Primitives |Other |VCC)
, as shown. Now hook up the 7447 inputs to your counter outputs. Your schematic should look something like this:
RBIN stands for “Reverse Blanking Input”. When low, it's used to let these modules communicate with one another to turn off segments that have leading zeros on them. We could use it if we wanted to get fancy here.
LTN stands for “Lantern”. When low, it turns on all of the lights in the display at once, suitable for testing if something's broken or not. Not very useful when counting though.
Analyze the design, and then assign the 7 output pins to the following FPGA pins, in order:
Compile it and test it. How does it work? Why does it fail for counts greater than 9?
Parsing binary numbers
To make sure that we're only inputting an acceptable value to the counter, we can use a division module, lpm_divide
(Megafunctions |Arithmetic | lpm_divide
). Click through until you get to the plugin manager screen.
Change the numerator and denominator bits to 4 and 4, as shown above, and then click Finish
. Your screen should look something like the following:
Now we need to tell the division module that we're dividing by 10. To do this, we'll need to generate a binary 10 using the lpm_constant
(Megafunctions |Gates | lpm_constant
) tool. Configure your constant to be 4 bits wide with a value of 10, as shown below:
We're nearly there! Now for the following:
numer
ator input of the division functiondenom
inator input of the division functionremain
der output and give it a name[3..0]
to denote 4 bitsThe result should look something like this. Test it.
Sure, it goes from 0 to 9 and then 0 to 5 before repeating, but it is giving us the 'ones' digit of our number successfully. We could take the numerator bits and connect them to another 7-segment display to get the tens digit of our counter, and we will in fact be doing that later.
Last week, we introduced a rotary encoder as a sort of digital analog to a potentiometer. However, you may have noticed that it did not cleanly transition between states, there could be quite a bit of noise in the transition.
Instead of rebuilding the circuit, we've created a breakout module that will plug directly into the FPGA board, shown below:
With that done, we can now access the two rotary encoder values as pins V7
and AB9
. Let's try this by going back to our FPGA program and re-configuring the button from pin F1
to V7
. After that, compile and test your program.
What do you observe?
You probably noticed the output sporadically changing by more than one increment at a time. To address the issue, we'll build something called a debouncer
. This sort of signal processing is often needed when working with mechanical inputs, as the contacts can literally vibrate between open and closed as they move. This in turn causes a quick burst of noise, that would be invisible in most analog circuits but wreaks havoc with digital devices.
Our debouncer will consist of 6 basic elements:
maximum output
of the counter to disable it until the input changes again.
In order to keep things clean, we'll make a sub-circuit here by creating a new block diagram file in our project.
Create a new block diagram file in your project. Start out by adding a pair of inputs and one output, named input
, clock
and output
respectively.
Save your file as debounce.bdf
. Make sure you navigate to the main directory of your project; it defaults to the output_files
subdirectory. Failure to do this will make it so that Quartus won't properly recognize that the block is part of your project
To start off, add in a pair of D Flip-Flops (dff
) and a XOR gate (xor
). Connect them together to make a change detection circuit, which will cause an output on the XOR for one clock cycle every time the input changes.
Next, we'll create a new counter with some features we haven't used yet. Go to the symbol tool and add a lpm_counter
module. On the first screen, change the name to something else such as DebounceCounter
If you don't do this, you may get some very strange errors later due to Quartus finding multiple counters with the same name.
Next, click through to the configuration screen. We'll want to change the following settings:
q
to Count enable
and Carry-out
checkboxesAsynchronous inputs
section check the clear
checkboxOnce your counter is finished, it should look something like the following:
Now, place another dff
in your circuit off to the side, and make the following connections:
clock
pin to the clock
input of the countercout
counter output to a not
gate cnt_en
input on the counter aclr
input of the counterQ
output on your second original flip-flop to the D
input of the new flip-flopcout
output on the counter to the clock >
pin of the new flip-flopAfter all of this, you should have something like the following:
Now, why did we do all of this? Well, the CountEnable (cnt_en
) pin only lets the counter tick upwards when it is held high. The cout
pin on the counter stands for CarryOUT, which goes high after the counter reaches its maximum value. Thus, connecting these makes the counter stop after it goes counts to $2^{16}-1 = 65535$. Since each clock cycle takes 20ns, this makes it so that our circuit takes ~1.3ms of the output being steady before it changes state.
If we were to use 12 bits, this would instead be around 80 $\mu$s, which is too quick for some of these encoders to stop being noisy. If you have a particularly angry encoder, you might need to increase the counter to 17,18, or even 19 bits. Each additional bit doubles the time that the output has to be stable for it to change in the FPGA.
The asynchronous clear aclr
will reset the counter to zero. By connecting it to the XOR gate, we'll reset the counter any time the button's state changes.
The last flip-flip will only change state to match the button after the timer has reached its maximum value because the only time its clock is enabled is when cout
is high.
All of this ensures that the output the code sees will only change after the input has settled down over a little bit of time.
Now to actually use our sub-circuit! Navigate the menus to File → Create/Update → Create Symbol Files for Current File
It will try to save the result in the output_files
directory again (ARGH), so navigate up to the main project directory before you save the symbol file.
With that done, switch back to your main project block diagram. Click on the Symbol Tool and scroll all the way to the top of the options, where you should find your new debouncer circuit waiting for you.
With that done, its time to add it into our circuit by doing the following:
sysclk
or clk
.The end result should look like the following:
After Analyzing the project, go to the pin planner and assign your sysclk
input to pin G21
. This connects to one of the internal 50 MHz clocks in the FPGA.
Compile and test your project.
You should now have fewer issues with your counter jumping by multiple values at once.
You should be in good shape if you finish here on day 1
The original plan to for this class was for you to do this last week with discrete chips, but the input noise from our encoders made that untenable. We'll go over the logic of how the counter works in an expandable segment, and jump to the final circuit for now.
To begin with, you'll want to add another input for the other pin of your rotary encoder V7
and insert another copy of your debouncing circuit to process that input.
Taking an exclusive OR (XOR) between an input and its previous state will detect if the state has changed within the last clock cycle only. This works because the XOR only goes high if the two inputs to it are different. By then copying this setup for the other channel and ORing the inputs together, we have a setup that will produce an output either time one of the encoder channels changes state.
Next, we compare the output of one encoder channel with the previous state of the other channel. Since the two encoder outputs are 90 degrees out of phase with one another, then one output's change will always precede the other rotating clockwise, whereas the other channel will change first when turning the other direction. The reason we compare with the previous state of one of the channels is to reduce any errors due to timing, as in some implementations of this circuit the changing state and logic operation may occur too close together and not produce a reliable output. With our debounced circuit this should no longer be an issue, but we left it in place as good practice.
Here's a brief video that may help illustrate what we're talking about in the first minute or so:
Next, you'll build the following bit of processing logic. It is designed such that it will output a pulse every time A or B changes, and such that it will output a 0 or 1 depending on which signal is leading (giving us left-right info)
Build the circuit as shown, hook the lower output (labeled change_detected
in the image) up to your counter, and verify that it still counts.
If you get an error reading Logic function of type *** and instance “***” is already defined as a signal name or another logic function
, then one of your symbols in has the same name as another one of the same type. Double-click on the error message to have Quartus jump to the offender and change its name.
Make sure you use underscores instead of spaces in variable names. Quartus gets cranky if you try to use spaces.
Now that we've got our inputs sorted out, its time to expand things out to use the entire display.
To do this, we'll need to make the following modifications:
up-down
setting is on the page where you change the number of bitsModulus
counter, and then has a dialog box letting you set the maximum count valueDigit | ||||
Display Segment | Ones | Tens | Hundreds | Thousands |
A | E11 | A13 | D15 | B18 |
B | F11 | B13 | A16 | F15 |
C | H12 | C13 | B16 | A19 |
D | H13 | A14 | E15 | B19 |
E | G12 | B14 | A17 | C19 |
F | F12 | E14 | B17 | D19 |
G | F13 | A15 | F14 | G15 |
If you want you can set up a custom block for the division by ten logic, or you can try to copy-paste or just build things multiple times.
Or, in general, here are some of the most relevant pins: FPGA Pins
If you want to keep playing around with the FPGA, the following projects might make for interesting starting places.
This project is a copy of the counter you just finished with a major addition. Instead of just letting you set a number with your rotary encoder, it uses that value to create a square wave of said frequency. The square wave is output on the top right pin in the GPIO 0 bank of pins AB16
; if you're not sure which then
ask a TA.
This makes use of an element called an accumulator
, which has an internal buffer as well as a multi-bit input. Every clock cycle, it adds the input to the buffer (accumulating a larger and larger number) until the buffer runs over and resets to zero. The overflow
output is set high when this happens.
By having an 8 bit buffer and a 8 bit counter input, the overflow will take several cycles before triggering the overflow the first time. As the counter value gets larger, it will overflow more and more frequently until the counter is at 2^8-1, at which case it will overflow on that same input. The result is that the overflow output is low most of the time, but spends more and more time high as the counter gets higher. Piping this signal to an output (say, and LED) results in what looks like a smooth brightness transition. This technique is known as Pulse Density Modulation
, and is a fundamental tool in Direct Digital Synthesis (DSS
).
The project also has some extra logic to make the clock count down instead of up, and some flip-flops used to toggle which LED is lit. Finally, it has a cascade of comparison circuits set up to stagger a trio of clocks to be 1/3 of a period apart.