So far, our embedded systems tutorials and projects have featured microcontroller boards such as different models from Arduino. To use the microcontroller, the user connects the digital and analog interface pins on the microcontroller to the peripheral electronics being controlled. Then, the user uploads software that defines a series of instructions the microcontroller will execute in a loop. While running, the processor at the core of the microcontroller reads and executes these instructions in order at a rate specified by the clock of the microcontroller module. We can, therefore, see that the microcontroller is usually only ever running one instruction at any time instant.
For many embedded systems projects, especially those implemented on introductory microcontroller modules such as an Arduino board, this individual instruction execution does not affect performance. The clock is fast enough that subsequent operations are indistinguishable by human senses. However, in larger applications, many of which may require different operations to be running in parallel, microcontroller implementations begin to become very complicated or downright impossible. Field-Programmable Gate Arrays (FPGAs for short), a different type of computing platform, often fill roles that microcontrollers cannot because they inherently function differently.
The main difference between FPGAs and microcontrollers is the method of configuration. Instead of uploading a series of instructions to be interpreted by an onboard chip as in a microcontroller, users configure FPGAs’ hardware directly. This means that the programs users write for FPGAs are not series of instructions to be repeatedly executed but rather an outline for how internal hardware should be configured to perform different tasks. This hardware comes in the form of internal logic gates and memory units that at the most fundamental level are combinations of multi-transistor circuits.
Because FPGA implementations of projects are actually just hardware connections, FPGAs can run multiple operations in parallel! Imagine, for example, that the system was tasked with reading the voltage drop across a light-dependent resistor, a hall-effect sensor input, and communicating with another device via a UART connection. The block diagram for a microcontroller implementation might look something like this:
Notice that the different tasks are done in series. An FPGA implementation may decide to dedicate different circuits to perform each of the three tasks, thereby allowing the system to perform all three operations in parallel. The block diagram for the FPGA implementation would therefore simply look something like this:
It is important to note that the above example is a very simple explanation of the differences between microcontroller and FPGA implementations and operation. In actuality, the internal implementations of projects on FPGAs depend on the project itself, and microcontrollers may parallelize instruction execution to improve performance. The key point to take away is the difference in how these two architectures implement the same tasks: microcontrollers execute instructions in series, while FPGAs implement behavior by routing internal hardware.
There are many FPGA development boards available that are designed as introductory modules. Digilent and Xilinx sell the Arty and Basys boards, for example. In this tutorial, we will be using the Mojo V3 from Embedded Micro, a fairly low-cost board also designed as an introductory module. I personally like the Mojo over boards such as the Basys 3 because it comes with a large number of header pins which can be used to interface it with outside circuitry! In addition, Embedded Micro also has some great tutorials to help developers get started (link provided in the appendix!)
In this tutorial, we’ll start of with a basic input/output example to introduce you to programming the Mojo. In part 2, we’ll look at a more complex project: a hardware PWM implementation!
This tutorial demonstrates configuring the Mojo using a hardware-description language called Verilog (we’ll talk more about it in a bit!) Verilog and its variants are frequently used in industry, so you’ll be able to apply what you learn through your projects on the Mojo to projects on other more advanced boards as well!
Here’s the hardware and software you’ll need to follow along:
Let’s say we want to light up an external LED only when two external buttons are both pressed down. We first note that this is a digital system, because the button input voltages and the LED output voltage only ever take one of two values: 0 volts or 3.3 volts. We can represent 0 volts with a logical 0 and 3.3 volts with a logical 1, thereby encoding all our possible voltage values on the inputs and output into a binary numbering system. In essence, the 0 and 1 are another way of saying “high” and “low” voltage for the digital signals.
We can summarize all the values of the button inputs and the corresponding values of the LED output in the following table:
|Input 1 (Button 1)||Input 2 (Button 2)||Output (LED)|
Individuals familiar with boolean algebra, the study of different operations on and with binary values, will recognize this two-input one-output system as an AND gate. The operating principle of an AND gate is that the output is only 1 when both inputs are 1, which is precisely what we would like for our button and LED setup! There are many logic gates that define output values as some function of input values. To check them all out, click the link in the appendix below!
Let’s first connect buttons and an LED to the Mojo, following the diagram below:
We can now begin programming the Mojo. Start ISE and open the base project that you downloaded. The screen should look something like this:
On the left portion of the window, you should see a hierarchy of different items. Double-click the one that says mojo_top.v . The Verilog code in this file should pop up on the right-hand side of the screen.
Now, a little bit about Verilog and the file structure. The first aspect of Verilog to note is that it is not a programming language in the conventional sense. As mentioned before, FPGAs are designed such that their internal hardware is configurable. Verilog serves as the interface between the designer and the FPGA implementation software (in our case, the ISE Design Suite). More specifically, Verilog code is read by the implementation software and translated through internal algorithms into the series of logic gates that can carry out the functionality specified in the code. Thus, Verilog is called a hardware description language, or HDL for short.
Verilog code is divided into different modules that interact with each other — one can think of these modules like functions in other programming languages. These modules can have multiple inputs and outputs, and the code inside the module body specifies how the outputs are driven based on different input values. As you may notice, different modules can be distributed over a number of Verilog .v files. In the case of the Mojo base project, there are several .v files for AVR, SPI, and serial functionality. The mojo_top.v file is the central Verilog module in which the main behavior of the FPGA is specified. ISE is smart enough to recognize that this is the central (“main”) module of our program, and in the file hierarchy on the left-hand side of the screen, you’ll see that the mojo_top.v file thus appears at the top of the list.
In addition to the Verilog .v files, you will also see a file with the extension .ucf, which stands for User Constraints File. In this file, the user specifies names for the input and output (I/O) connections in his or her project, and identifies the pins and I/O standard associated with these signals. We’ll start off by editing this file. Double-click the file name in the left-hand side browser, and it should open up in a new tab on the right-hand side.
You’ll see that there are already several entries in this file. These entries specify names for connections that are already on the board such as the connections for the onboard LEDs and different communication buses. Scroll down to the end of the file and hit the “enter” key on your keyboard to create a new line. We want to make three entries, one for each of the button input pin connections and one for the LED output pin connection. All we have to do is follow the format of the other entries in the file!
You can copy any of the other lines in the file beginning with “NET” and paste it three times at the bottom of the file, changing the string in quotes at the beginning of the line and the number that follows the letter “P” shortly after. In this tutorial, we’re going to use pins 14 and 21 as the button inputs and pin 26 as the LED output, so we’ll add the following entries at the bottom of the file:
NET “button_a” LOC = P14 | IOSTANDARD = LVTTL;
NET “button_b” LOC = P21 | IOSTANDARD = LVTTL;
NET “led_external” LOC = P26 | IOSTANDARD = LVTTL;
Just make sure that none of the pins that you select conflict with a pin that is already specified in the UCF file.
Your UCF file should now look something like this:
Now that we’ve specified the signal names specific to our project and their associated connections, we can write the code that describes the LED output as a function of the button inputs. As you might have guessed, this involves modifying the mojo_top module.
At the top of the module declaration is the word “module” followed by the module name (in our case, mojo_top) and a list of input and output signals that the module will work with. We’ll need to add our two button signal names and our LED signal name to this connection list, qualifying the signals as inputs and outputs. We can do this by appending the following lines at the end of the signal list:
The module header should now look like this:
So far, we’ve declared signal names for our buttons and external LED, assigned these signals to I/O pins on the Mojo, and specified in Verilog that these signals are inputs and outputs respectively. Now, all we have to do is define the conditions under which the LED will light up! More specifically, we need to specify the cases in which the LED is on (binary 1) and off (binary 0).
If you recall from earlier, I mentioned that the LEDs operating behavior was exactly that of the boolean AND function. In Verilog, we can specify boolean AND through the symbol &&. We want to assign to the LED the value of the AND function applied to the button inputs. The Verilog code that will do this is:
assign led_external = button_a && button_b;
Appending this line to the end of the module will give us the behavior we want. The lines above this line which were already there when we opened the base project set up different signals such as the SPI and onboard LED signals on the board that we will not be using in this project.
The completed module should now look like this:
To program the FPGA to implement the behavior we have specified, we have to generate a programming file, which comes in the form of a .bin file. Upon command, ISE synthesizes our design (checks it for errors and runs tests on it), implements it (defines the internal gate architecture required to implement the specified behavior), and generates this programming file. To run this process, double-click the “Generate Programming File” button on the left-bottom window.
Over the course of the programming file generation, ISE will issue several warnings about missing and/or unrouted nets. These warnings are not critical to this implementation and will not affect our programming of the FPGA. ISE only issues these warnings because we did not make use of all the signals that are specified in the UCF.
When the programming file generation is completed, ISE will place a green check-mark next to the button that we just double-clicked.
Now, all we have to do is upload the programming file to the Mojo board. First, make sure to connect the buttons and LED to the Mojos pins as in the diagram above.
Next, open the Mojo Loader software and select the USB port corresponding to the Mojo. Then, select the .bin file that was generated by ISE. You will have to navigate to the /syn/ folder in your project directory. When the .bin file has been opened within Mojo Loader, go ahead and click the “Load” button in the bottom right of the window. This will begin the process of uploading the programming file to the Mojo.
When the process is done, you should be able to test your implementation! The LED should only light up when both buttons are pressed down. Congratulations, you have finished your first FPGA project! Check out part 2 of this tutorial for a more complex hardware PWM project!
Embedded Micro Tutorials: https://embeddedmicro.com/tutorials/mojo
Logic Gates: https://en.wikipedia.org/wiki/Logic_gate