This post is the first in a series which introduces the concepts and use of VHDL for FPGA design. We begin by looking at how we write VHDL components using the entity, architecture and library keywords.
These elements are fundamental to the way VHDL designs are structured and we will see how they relates to the FPGA hardware we are describing.
We don't need to discuss the entire history of VHDL in order to work with it. However, we must consider one important historical point – the version of the language we use.
The major standards of VHDL were released in 1987, 1993 and 2008. Another minor version was released in 2002 and a draft version of VHDL-2018 will also be released soon.
We should use the VHDL-2008 standard with new designs as it includes a number of useful improvements.
Although it may be tempting to begin using VHDL-2018 as soon as it is released, this should be avoided. This is because it often takes software vendors several years to support all the new features in a standard.
As a result of this slow up take, VHDL-93 and 2002 have remained popular up until the last few years. Therefore, we will look at the important differences between these standards as we encounter them in these posts.
When we design FPGAs, it is important to remember one fundamental principle – we are designing hardware and not writing a computer program.
Therefore, we must describe the behaviour of a number of different components which we then connect together. This expectation is reflected in the way VHDL files are defined.
Every component we design in VHDL requires two separate parts - an entity and an architecture.
The entity defines the external interface to the VHDL component we are designing, including a definition of the inputs and outputs.
We use the architecture to create either a functional or structural description of the component.
Together, the entity and architecture are equivalent to a verilog module.
We can also use the VHDL configuration keyword to tell our compiler which architecture belongs to which entity. However, we do not normally have to do this as modern tools can automatically link the correct entity and architecture files.
We use the entity to define the external interface to the VHDL component we are designing. This mainly involves defining the inputs and outputs, although we can provide further configuration details as well. The syntax below shows how we declare an entity in VHDL.
entity <entity_name> is generic ( <generic_name> : <type> ); port ( <input_name> : in <type>; <output_name> : out <type); <bidir_name> : inout <type> ); end entity <entity_name>;
In this construct, <entity_name> would be the name of the component which is being designed. It is good practise to keep the name of the file and the entity the same or at least very similar. This makes it simpler to manage large designs with many components.
We use ports in a VHDL entity declaration to define the inputs and output of the component we are designing. Therefore, the ports are equivalent to pins in a more traditional electronic component.
We can define ports as either in, out or inout, which correspond to inputs, outputs and bidirectional ports respectively. We commonly refer to these different types of port as modes.
In addition, we also declare which type of data the port uses in the <signal_type> field. We talk about the different data types in VHDL in more depth in the next post.
However, the most commonly used type is the std_logic type which is used to describe a single bit in VHDL.
We can also include generics in our VHDL entity declaration, although this is not required. Using generics allows us to pass constants into our component when we instantiate it. This means we can reconfigure the behaviour of our components so that we can reuse them in multiple scenarios. We discuss the use of VHDL generics in more detail in a later post.
We use the VHDL in keyword to define inputs to our VHDL designs. Inputs are the simplest of the three modes to understand and use within a component. The only rule associated with the use of inputs is that they must never be assigned a value. As a result of this, we can't use a construct such as the one shown below in our code.
<input_name> <= <some_value>;
We use the VHDL out keyword to define outputs from our VHDL designs. Outputs are a little more complex than inputs to use, depending on the standard of VHDL we use.
In the VHDL-2008 standard the out mode was revised so that we can both read and write them. However, prior to VHDL-2008 outputs could be assigned a value but not read. Whilst this doesn't often create a problem, there are instances when the output value has an effect on the actual logic of the circuit.
To better understand how we work with outputs, let's consider the very basic circuit shown below.
In this case, the output of DFF2 is not only the module output but also an input to DFF1.
The simplest solution for modelling this circuit would be to use a fourth mode which is known as a buffer. This mode has the same functionality as the out mode but we can also read its value. However, we don't often use this as it is historically known to cause synthesis problems with Xilinx parts.
A more common solution to this problem is the use of an internal signal which duplicates the value of the output. Although this approach is cumbersome, it is the preferred method when using VHDL-93 or 2002.
The code snippet below gives an example of this approach.
-- Declare a signal in the architecture body (discussed below) signal out_buffered : std_logic; -- Example logic circuit which relies on output value out_buffered <= out_buffered and in_a; -- Assign the output port outa <= out_buffered
In VHDL we use the -- characters to denote that we are writing a comment.
We use comments to include important information about our code which others may find useful. The VHDL compiler ignores any thing which we write in our comments.
In the code snippet above, we can see this in action as comments are used to describe each line of the code.
The final mode is the bidirectional port which we declare using the VHDL keyword inout.
We should only ever use this mode for communication with components which are external to the FPGA. This is because FPGAs don't have internal tri-state buffers which implement the high impedance state which bidirectional buses require. These buffers are important as they prevent collisions on the bus.
Attempting to use internal bi-directional ports can lead to simulation vs synthesis mismatches. This means that the functional code will simulate without an issue and the design will appear to function as intended. However, after we synthesise our design it will no longer function correctly. This happens because the synthesis tool is fundamentally incapable of implementing the code.
We commonly use the inout mode to interface with memory devices, such as flash memory chips. This is because these chips often have large data buses which are driven by both the FPGA and the external device. As a result of this, we must ensure that only one devices drives the bus at a time. Therefore, our code must set the inout pins to high impedance when we are not directly driving them.
The architecture is the second part of a VHDL component and is used to describe the behaviour or structure of our design. We must associate an architecture with a previously defined VHDL entity. The code snippet below shows how to declare a VHDL architecture.
architecture <architecture_name> of <entity_name> is -- signal declarations begin -- functional RTl (or structural) code end architecture <architecture_name>;
We can associate an entity with more than one architecture if required. However, we should try to avoid doing this as it is difficult to manage.
We use the <architecture_name> field to give our architecture a name. We can give the architecture any name we choose but it is common to name VHDL architectures after the type of code we are describing. This normally means we use rtl or behavorial for rtl type code and struct for structural or gate level type code.
An architecture consists of two parts, a section for signal declarations and a section for the functional code. The signal declarations go before the begin keyword and define connections, or wires, which are internal to the component.
The second section of the architecture comes after the begin keyword and defines the functionality of the component. There are two mains methods for doing this – rtl code and structural code.
In the case of rtl code, this is a VHDL based description of how data flows through the component.
When we write structural code, we determine the behaviour of the block by connecting a number of previously defined components together.
In VHDL, we use signals to define connections, or wires, which are internal to our entity architecture pair.
We can also use variables to define these connections. However, we can only use variables within a VHDL process and they are used less frequently as a result. We discuss VHDL variables in more detail in a future post.
To demonstrate what VHDL signals are, let's consider a basic example. If we reconsider the counter example we previously saw, we should declare the green wires in the diagram below as signals.
The code snippet below shows the syntax we use to declare a signal in VHDL.
signal <signal_name> : <signal_type>;
In this construct, the <signal_name> field denotes the actual name of the signal.
We should use meaningful names when declaring signals. This makes it easier for anyone who reads the code in the future to understand its purpose.
When we name signals there are a number of rules which we must follow. We discuss these rules in more detail later in this post.
As we saw when talking about VHDL ports, the <signal_type> field is used to indicate the type of signal being used. This is important in determining the behaviour of the signal. We talk about the different data types in VHDL in more depth in the next post.
In this post we have talked about the concept of naming different elements, such as entities, ports and signals. Whilst there is great deal of freedom in naming these elements, there are two rules which we must obey.
Firstly, the name must begin with a letter rather than a number or any other special character (such as _ , *+).
Secondly, a signal can't have the same name as a VHDL keyword. These are words such as “entity”, “signal” or “port” which make up a part of the core VHDL syntax. We can use these words to form part of a name as long as they aren’t identical. For example, signal1 would be a valid name to use.
Unlike a lot of programming languages, VHDL is not case sensitive. As a result of this, the name ExamPle1 is considered to be the same as example1.
In VHDL, we use a container known as a library to store our all of our entities, architectures and packages.
We can create as many libraries as we want and use them to store common design units. However, we may also simply use a single library to contain all of our unique design files for a project.
Whenever we want to use a design unit in another piece of code, we must include the relevant library. If we omit this step, our toolset will be unable to link the design files together.
The code snippet below shows the syntax we use to include libraries in our design.
In addition to entity and architectures, we can also store VHDL packages in a library.
We don't directly use packages as a design element. Instead, we use them to define types or functions which we need in other design files. It’s not important to understand this in detail now as we discuss the use of VHDL packages more in a future post.
When using a package, we must explicitly include it in the design using the VHDL use keyword. The code snippet below shows the syntax for doing this.
We have to include one important package in every VHDL file - the std_logic_1164 package. This package is a part of the IEEE library which defines the std_logic type (amongst others).
The code snippet below shows the method we use to include the std_logic_1164 package in our design.
library ieee; use ieee.std_logic_1164.all;
When compiling a VHDL design, we must nominate a working library. This refers to the library where the current design will be stored.
We can use any named library as the working library but there is a special VHDL . This is a VHDL keyword which refers to the current default library rather than an actual library name. Despite this, we can use it in exactly the same way as a normal library.
The code snippet below demonstrates the use of the work keyword.
library work; use work.example_package.all;
In this blog post, we have seen that there are three important steps to structuring a VHDL design.
Firstly, we must include all relevant libraries. We then declare an entity and finally an architecture which describes the functionality. It is important that we carry out these steps in this order.
The code snippet below shows the method for declaring a basic component, although it has no defined functionality.
library ieee; use ieee.std_logic_1164.all; entity example is port ( clock : in std_logic; in_a : in std_logic; out_a : out std_logic ); end entity example; architecture rtl of example is -- Define functionality end architecture rtl;
It is good practise to use a separate file for every component we design. If this is not possible then the scope of the library is very important.
When we invoke a library with the VHDL use keyword, it only affects the entity and architecture directly beneath it. If we have two entities in a file which both use the IEEE library then we must include the library twice, once above each entity declaration.
What do we use entities, architectures and libraries for in VHDL designs?show answer
Entities are used to define the inputs and outputs of a VHDL component.
Architectures are used to describe the functionality of a VHDL component.
We compile all of our design files into a library.
What are the three types of mode that a port can have?show answer
Input (VHDL keyword in), output (VHDL keyword out) and bidirectional (VHDL keyword inout)hide answer
List the limitations of each of the different types of mode.show answer
Inputs can be read but can’t be assigned a value.
Outputs can be assigned a value but can’t be read unless using VHDL-2008
Bidirectional ports can only be used for external connections.
Write an entity declaration for a three input AND gate component.show answer
entity and_example is port ( in_a : in std_logic; in_b : in std_logic; in_c : in std_logic; and_out : out std_logic; ); end entity and_example;
What do we use signals for in VHDL designs?show answer
Signals are used to define connections which are internal to the VHDL component. They can be thought of as wires in our designhide answer
Write an entity and architecture for the circuit shown below:show answer
entity example is port ( clock : in std_logic; a : in std_logic; b : in std_logic; addr : in std_logic; q : out std_logic ); end entity example; architecture rtl of example is -- Signal declarations begin -- Functional VHDL code end architecture rtl;