The MCU guy's introduction to FPGAs: The Software
This is a follow-on to my earlier column: The MCU guy's introduction to FPGAs -- Hardware. As I noted in that column, a lot of my friends are highly experienced embedded design engineers, but many of them come from a microcontroller (MCU) background, in which case they often have only a vague idea as to what an FPGA is and what it does. When pressed, they might say something like "You can configure an FPGA to do different things," but they really have no clue as to what's inside an FPGA or how one might be used in a design.
Similarly, MCU-based designers have typically heard about hardware description languages (HDLs) like Verilog and VHDL; they understand that hardware design engineers use these languages to capture the design intent; but... that's typically about the extent of their knowledge. Thus, in this column we are going to consider the FPGA equivalent to MCU software.
The FPGA design flow
Before we start, we first need to remind ourselves that FPGAs contain special configuration cells. Some of these configuration cells will be used to define the functionality of the programmable elements, while others will be used to make or break connections between different sections of interconnect, thereby connecting the various entities within the device. So, the question is, once we have decided what tasks we wish our FPGA to perform, how to we (a) capture our design's intent, (b) convert this representation into something the FPGA can use, and (c) load the design into the FPGA?
On the one hand, this is really quite simple, conceptually speaking. On the other hand, all of this stuff can be a little tricky to wrap one's brain around the first time one is exposed to it, so let's take things step-by-step. Speaking of which, let's start by taking a step back, as it were. Let's suppose we wish to create a program to run on a traditional microcontroller as depicted in the left-hand-side of the diagram below. In this case, we could capture our intent in some programming language like C or C++, and then we would compile this into a machine-code (executable) file to be run on the microcontroller. The final step would be to load the machine code file into the microcontroller's memory and then instruct the microcontroller to start executing the program. Note that our program might accept input from the outside world -- perhaps reading a data file or monitoring whatever is entered on a keyboard. The program will also generate some form of output -- perhaps writing to a log file or displaying graphics on a screen.
Comparison of MCU program creation and FPGA design creation flows.
Now let's consider the equivalent FPGA flow. For the purposes of these discussions, let's assume that we are working with a very simple FPGA that contains only fundamental programmable fabric in the form of programmable blocks containing lookup tables (LUTs) and registers, along with programmable interconnect. We start by capturing our design intent using a special hardware description language (HDL), such as Verilog or VHDL. Next, we run the synthesis tool, which takes our high-level HDL description and translates it into the resources that will be used in the FPGA (that is, the LUTs, registers, and so forth). You can think of the synthesis tool as being the hardware designer's version of a compiler -- it transforms the high-level input into a low-level equivalent. The output is a configuration file that defines the values that need to be loaded into the FPGAs configuration cells. Finally, we load the configuration file into the FPGA, thereby "programming" the FPGA. When the FPGA is running, it may accept external control and data signals presented to its input pins and it may generate values that it presents to the outside world on its output pins.
Of course, the above offers an extremely simplified view of things. In the case of the microcontroller flow, for example, the compiler would either be one that was specifically intended for use with a single MCU, or the user would have to specify the target MCU so as to receive the appropriate machine code instructions. Furthermore, there are usually some intermediate steps that are "hidden under the hood." Without the user knowing it, for example, a compiler typically generates an intermediate file in assembly language or pseudo-code, and it is this intermediate file that is subsequently assembled into the machine code output file.
Similarly, in the case of the FPGA flow, we would have to inform the synthesis tool as to the target FPGA (vendor, device family, specific member of that device family, etc.). Also, the synthesis tool outputs an intermediate file that allocates resources like "this function goes into a LUT," but it doesn’t actually specify which LUT to use in the FPGA. A variety of techniques, including place-and-route algorithms, are subsequently employed in order to generate the final configuration file.
- The FPGA design flow
- HDLs versus programming languages
- Simulation and synthesis
- Software functions/procedures versus hardware blocks/modules