In the previous post in this series, we saw an overview of the typical FPGA development life cycle. In this post, we talk about the design process in more detail. The main task in this process is coding the design, normally using either VHDL or Verilog. However, we also consider other important elements of this process in this post.
Architecting the Design
The first stage of the design process is architecting the chip. This involves breaking the design into a number of smaller blocks in order to simplify the VHDL coding process. For large designs, this is especially beneficial as it allows engineers to work in parallel. In this case, we should consider this an integral part of the process. For smaller projects, we don’t need to formally carry out this step. However, it is always worth considering the architecture closely before starting detailed design work.
The next stage in the process is the creation of a model of the design. We typically use one of the two major Hardware Description Languages (HDL) – Verilog or VHDL – 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.
RTL Coding Style
Designs which use the RTL approach consist of code which describes the flow of data between registers within the FPGA. This means that we write code which explicitly describes the behaviour of the FPGA in terms of registers, logic, state machines and RAM. Let’s consider the example circuit below to demonstrate this concept.
n an RTL model of this circuit, we write code which describes the behaviour of the two flip-flops and the NOT gate. The code shown below gives the VHDL implementation of this circuit. Don’t worry if the syntax is unfamiliar to you at this point.
dff1_d <= not dff2_q; dff1: process (clock) is begin if rising_edge(clock) then dff1_q <= dff1_d; end if; end process dff1; dff2: process (clock) is begin if rising_edge(clock) then dff2_q <= dff1_q; end ill end process dff2;
In this code, the first line models the behaviour of the NOT gate. The flip flops, labelled as dff1 and dff2, are modelled inside of processes. These processes latch the value of the input whenever there is a rising edge on the clock. This matches the expected behaviour of a D type flip flop.
Gate Level Modelling
Gate level modelling consists of code which defines the interconnection of different pre-existing components. These components are instances of integrated FPGA elements, such as PLLs, LUTs or register cells. The code shown below is an example of gate level modelling. This implements the same double flip flop circuit we previously considered.
inverter : component not_gate port map ( inv_in => dff2_q, inv_out => dff1_d ); dff1 : component dff port map ( clock => clock, D => dff1_d, Q => dff1_q, Q_bar => open ); dff2 : component dff port map ( clock => clock, D => dff1_q, Q => dff2_q, Q_bar => open );
We can see that there are three separate components in this example model. There are two instances of the dff component, which is an implementation of a D type flip flop. In addition, there is a single instance of a NOT gate component. We use signals to wire the IO of these components one another. This results in a physical implementation of the circuit we saw above.
It is rare that we will ever want to create a gate level model. However, synthesis or place and route tools often create such models. We discuss both of these processes in more detail in the next post in this series. However, it is common to use RTL models which we have previously written as components in a similar manner. The syntax for instantiating the components and wiring them up is the same as that used in gate level modelling. Although this is similar to gate level modelling, the difference is that we don’t directly use cells in the FPGA. This approach is known as structural RTL modelling.
Although HDL languages are commonly referred to as programming languages, they have little in common with traditional languages. When working with languages such as C or Java, we are creating an abstract algorithm or describing program behaviour. The way the CPU actually implement this program is unlikely to be of any real interest. In contrast, when we design HDL code we are describing the behaviour of a digital circuit. It is important to remember this different approach when designing FPGAs. We have already seen this process in the simple example we used for RTL modelling. We started with a basic circuit and then wrote some VHDL which describes the behaviour of the circuit.
As this is an introductory post, we don’t fully consider the details of Verilog and VHDL in this post. There are a separate series of posts which act as tutorials for these languages. After reading these introductory posts, learning one of these languages should be your next step towards learning FPGA design.
VHDL vs Verilog
One of the first questions many people have when learning FPGA design is whether they should learn VHDL or Verilog. For anyone with ambitions of becoming a professional FPGA designer, it will be beneficial to learn both. So a more important question in this case is which language to learn first. Whilst the two languages do have a number of differences, these are probably too subtle for beginners to fully appreciate. In fact, even for professional engineers, a lot of the time it will come to either personal preference or a pre-existing policy within a company.
When it comes to RTL modelling, geography is usually the main deciding factor in the choice of language to learn. Verilog is the most used language by the big tech companies in California. Therefore, it makes sense to learn Verilog first for anyone hoping to work there. In Europe, VHDL is more widely used and it makes sense to start with this language. For hobbyists, Verilog is also a good choice as it is generally less verbose.
For gate level modelling, Verilog is more popular than VHDL. This is mainly due to the fact that it has an inherent ability to define the behavior of primitives. This means that we can define the behaviour of the cells within the FPGA using a language feature known as user defined primitives. This is particularly useful for chip vendors who must develop libraries which define the behavior of cells in their chips. Simulations which use verilog based models also tend to have faster execution times than VHDL equivalents.
Testing the Design
After architecting and coding our FPGA design, we then need to test our model. This is essential for identifying bugs and proving that our model functions as expected. Although we often have a separate verification teams for larger designs, some tests should be conducted by the designer. This will eliminate the most obvious and easy to find bugs before verification begins. As a result, the verification team can focus on more thoroughly testing the design, identifying more subtle bugs.
As a first step in simulating a design, we must compile the code. This process turns the functional RTL code into a model or executable file. This file is then used by the simulation tool to emulate the behaviour of the design. During this process, the compilation tool will create a number of warnings. These are issues with the code which won’t prevent compilation but may lead to simulation errors. Some examples of this include deprecated code constructs, non-synthesisable code or errors in vectors sizing. One example common example of a warning is attempting to assign an 8 bit signal with 9 bits of data.
It is normal for the designer to correct these warning before running any simulations. For large designs, this can be a time consuming task and it is likely that not all warnings are fixable. It is also common for the solution to the warnings to alter the behaviour of our model. If using a formal process, we should at least fix the warnings before beginning the verification process. When a separate verification process isn’t required, we should fix these warnings before beginning the implementation stage.
Even when working with a formal verification process, the FPGA designer is normally expected to carry out basic functional testing. This is where the lines between verification and design can start to become quite blurred, particularly for small chips. Functional simulations in this context involve creating tests which emulate the normal operating conditions of the circuit. This allows the designer to fix the most obvious functional bugs before verification begins. This means the verification team can utilise their time developing more sophisticated tests. This is likely to lead to the verification process discovering more subtle bugs.
For hobbyists, there is no need to go any further than this level of simulation. There is also no need to do any more testing than this for small projects. The exception to this is for projects where we are following a formal process such as DO-254. It is good practise to do so but if we find a bug after programming the FPGA it’s not a major issue. When we are working on large projects then verification is going to be much more comprehensive than this. This is also the case when we are following a formal design process. In these cases, we should treat the verification of our FPGA as a separate, formal process.