We don’t specifically discuss the details of any of the languages used to program an FPGA in this post. Instead, the information here is intended to provide important background knowledge which will help when learning how to program an FPGA.
What is an FPGA
We can think of an FPGA as if it were a massive collection of unconnected digital components. This includes basic components such as multiplexors and logic gates as well as more complex components like DSP cores in modern FPGAs.
When we write our code, we are actually creating connections between these different components to create a more complex system.
All of this means that we are fundamentally designing hardware when we create an FPGA based design.
As a result of this, we can design a number of circuits which run in parallel to each other. This means that FPGAs are capable of performing a large number of different operations at the same time. This is a major advantage over software approaches, which must be run sequentially by a CPU.
In addition, we also have much more control over the timing of our design in an FPGA. We can estimate to within a few nanoseconds how long operations will take to complete in an FPGA. Again, we could not do this if we used a CPU to implement our design.
As a result of these features, FPGA designs can be much quicker than the equivalent implementation in a microcontroller.
The drawback is that they tend to be more difficult to work with.
As we will come to see, this is not because designing FPGAs is inherently more difficult. The major difference is that there is a much smaller community of people who develop FPGAs. As a result, we have much less libraries and open source code available to us.
Structure of an FPGA
Although it is simple to think of an FPGA as an array of unconnected digital components, in reality FPGAs still have a fixed structure.
A basic FPGA consists of a number of configurable logic blocks (CLB), input/output (IO) blocks and a network which provides interconnection between them.
We also normally find embedded RAM memory and specialized components, such as DSP cores, in modern FPGAs.
Configurable Logic Blocks
The configurable logic blocks are the main building block of an FPGA. We can think of CLBs as if they were a reconfigurable logic gate which we can configure to perform different logical functions in our design.
A typical CLB consist of a few inputs, look-up tables (LUT), multiplexors and some RAM. LUTs are simply memory which is programmed to implement a given logical function.
The exact structure of a CLB depends on the actual chip vendor. More information about the structure of the CLB in a Xilinx FPGA is available here while the structure for an Intel FPGA is available here.
The IO blocks in FPGAs also vary slightly between different vendors. However, they typically always include simple registers, such as D type flip flops, and tri-state buffers. We can also select different logic standards to apply to our IO, such as 3V3 or 1V8 LVCMOS or LVDS. The exact standards we can use will depend on the FPGA we are using. In modern FPGAs it is also common for us to have dedicated pins for special functions, such as high speed data transfers.
FPGA Program Memory
Once we have finished designing our FPGA, we create a file which tells FPGA how it should be configured. The FPGA uses this to configure the internal LUTs and interconnections, as well as other internal components. Most modern devices use SRAM type memory to store this information due to its speed. One downside of this is that we need an external memory chip to store our program. This is because SRAM is volatile memory, meaning it can’t retain its state when it is powered off. Some vendors offer FPGAs which use flash memory instead of SRAM, as this memory type is non-volatile. However, SRAM based FPGAs can typically run with higher clock speeds which is why they are more popular.
FPGA Development Life Cycle
We can broadly divide the FPGA design process into three different stages – design, verification and implementation. In the design phase, we focus on turning our initial concept or idea into an actual FPGA device. This normally involves architecting the chip, or breaking it down into smaller blocks to form a complete design. We then implement each of these smaller blocks using a HDL language, or some other approach. The implementation stage takes our HDL design and converts this into a programming file for our FPGA. The verification process feeds into both of these processes. This involves testing and analysing our design and implementation to ensure that it functions correctly. The image below shows the stages in the development life cycle and how they are interlinked.
The first stage in the development of an FPGA is the design. We normally start this by architecting the chip in some way. This involves breaking the design into a number of smaller blocks to simplify the coding. This may be a formal process involving block diagrams and discussions with other engineers. This is especially likely if we are working on a complex design in a professional capacity. Similarly, we may just use pseudo code to create a basic idea of how our design will look if we are working on a small project.
The next stage is the creation of a model of our design. We normally use one of the major HDL languages for this purpose. There are two main styles of modelling which we use for this process. These are commonly referred to as Register Transfer Level (RTL) and Gate Level. When we use the RTL approach we are describing how data flows between FPGA registers. This means that we are writing code to explicitly describe the behaviour of our FPGA in terms of logic, RAM and state machines. We use gate level modelling to define the interconnection of different pre-existing components. These components are instances of integrated FPGA elements, such as PLLs, LUTs or register cells. Although we will use RTL most of the time, most designs we create will feature a mixture of both styles.
After writing a model of our design, we then need to prove that it works. The process which we use for this is known as verification. The first stage of this simulation of our design. For this purpose, we create a test bench which generates a number of inputs to our design. We then check that the FPGA outputs are what we expect them to be, either through manual inspection or through self checking code. We can repeat this process on our functional code and on our post place and route netlist. This is a model of the FPGA which is created by our software tools when we implement the FPGA. This model includes information about the internal timing of our FPGA and so is more representative of the final implementation.
Typically, simulation is the only process that is involved the verification of our design. However, as FPGA designs have become more complex, other techniques have become popular. More modern verification activities include hardware in the loop (HiL) and emulation. In both cases, this involves running code on our target device and feeding back data to simulation software. This allows us to run specific, structured tests on our device in near real time. This is an advantage as post place and route netlists are extremely slow to simulate in comparison to function code. A simulation which takes an hour to run with functional code can easily take a day or more to run with a post place and route netlist.
Once we have written our code and proved that it works, we then need to implement the code on our FPGA. There are actually three stages involved in this process – synthesis, place and route and programming file generation. The synthesis process transforms our functional code into a number of interconnected gate level macros. These are models of the internal FPGA cells. This process creates a netlist which describes what the contents of the programmed FPGA should be. We can think of this as being equivalent to a circuit diagram in traditional circuit design.
After we synthesise our design, we then map the netlist to the actual FPGA resources. The first part of this is mapping the macros to the physical cells in the FPGA using a process known as placement. We can think of this as being equivalent to placing components on a circuit board when we design PCBs in traditional electronic design. The second stage involves routing the interconnections between the different cells in a process known as placement. This process is equivalent to routing traces in a PCB. It is normally necessary to run the place and route process several times in order to meet the timer requirements of our design. The place and route tool is responsible for scheduling these multiple runs based on our configuration.
There are a number of different software tools which we can use to implement our design. Both of the major FPGA vendors (Xilnix and Altera) offer free synthesis tools which are suitable for most projects. We can also use paid tools such as Synplify Pro which typically deliver more optimised netlists. There are no third party place and route tools, meaning we must use the vendor specific tools. These are freely available to download, although paid versions are also available.
This post has given a brief introduction to the topic of FPGAs and the design process.
Although we can start designing FPGAs without any additional knowledge of these processes, it is important to have a good understanding of the fundamentals of FPGA design.
Additionally, there are detailed VHDL tutorials and verilog tutorials available elsewhere on this site which are a resource for getting started with FPGA design. These posts introduce all of the main feature of these languages and include a number exercises.