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.
We can think of an FPGA as if it were a massive collection of unconnected digital components.
When we program an FPGA, we are actually creating connections between these different components to create a 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.
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.
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 small blocks of memory which are 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.
In an FPGA, the IO blocks provide an connection to circuits which are external to the FPGA. They are connected directly to the physical pins of an FPGA.
We can program the IO blocks in an FPGA so that they function as inputs, outputs or a mixture of both. We can also select different logic standards to apply to our IO, such as 3V3 or 1V8 LVCMOS or LVDS.
In modern FPGAs it is also common for us to have dedicated pins for special functions, such as high speed data transfers.
The exact structure of an IO block in an FPGA varies slightly between different vendors.
However, they typically always include simple registers, such as D type flip flops, and tri-state buffers.
Once we have finished with our FPGA design, we create a programming file which tells the FPGA how it should be configured.
The FPGA uses this to configure the internal LUTs and interconnections, as well as other internal components such as PLLs and DSP cores.
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.
We can broadly divide the FPGA development process into three different stages – design, verification and implementation.
In the design phase, we focus on transferring 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 function 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 flip flops in the FPGA.
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 use RTL for the majority of our FPGA design work, most projects feature a mixture of both approaches.
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 netlists. 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 main process that is involved the verification of our design. We also normally complement this with hardware testing to ensure that the FPGA interfaces as expected with all external circuity.
However, as FPGA designs have become more complex, other techniques have become popular.
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 program this design 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 routing. 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 timing requirements of our design. The place and route tool is responsible for scheduling these multiple runs based on our configuration.
We also perform a static timing analysis (STA) as part of the place and route process. This analysis calculates the delays for all of the timing paths in our FPGA and ensures that our design meets with our timing requirements.
If our design fails the STA then we can't guarantee that our FPGA will work reliably. When this happens we either have to run the implementation process again with different settings or we must change our design.
The final stage in implementation process is generating the programming file which configures the FPGA.
The block diagram below gives an overview of the entire FPGA implementation flow.
There are a number of different software tools which we can use to implement our design.
We can also use paid tools such as Synplify Pro which typically deliver more optimized netlists.
There are no third party place and route tools for Xilinx or Intel parts, meaning we must use the vendor specific tools. These are freely available for download, although paid versions are also available.
For Lattice FPGAs, the open source nextpnr software is a popular place and route tool.
This post has given a brief introduction to the topic of FPGAs and the FPGA development process.
Although we can start designing FPGAs without any additional knowledge of these processes, it is important to have a good understanding of these fundamental stages in 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 features of these languages and include a number exercises and examples.