Difference between revisions of "VHDL"
(6 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
[[Category:FPGAs]][[Category:Software]] | [[Category:FPGAs]][[Category:Software]] | ||
− | VHDL stands for VHSIC-HDL, which in turn stands for Very High Speed Integrated Circuit Hardware Description Language. It is one way in which hardware can be described. The guide below was adapted by notes originally written by Samuel Ginsberg, which can be downloaded as a PDF here. [[File:VHDL Notes 2016 updated SamuelGinsberg.pdf]] | + | VHDL stands for VHSIC-HDL, which in turn stands for Very High Speed Integrated Circuit Hardware Description Language. It is one way in which hardware can be described. |
+ | The guide below was adapted by notes originally written by Samuel Ginsberg, which can be downloaded as a PDF here. [[File:VHDL Notes 2016 updated SamuelGinsberg.pdf]] | ||
Line 119: | Line 120: | ||
= Variables = | = Variables = | ||
+ | <span class="mw-customtoggle-Variables" style="font-size:small; display:inline-block; "><span class="mw-customtoggletext" data-expandtext="Illuminate" data-collapsetext="Deluminate">[Show/hide]</span></span> | ||
+ | <div id="mw-customcollapsible-Variables" class="mw-collapsible mw-collapsed"> | ||
It is possible to have variables in an architecture. Variables are used to store data within a process. Signals differ from variables in that variables cannot trigger events, nor can a change in a variable trigger a process. | It is possible to have variables in an architecture. Variables are used to store data within a process. Signals differ from variables in that variables cannot trigger events, nor can a change in a variable trigger a process. | ||
Line 128: | Line 131: | ||
one_variable := another_variable; | one_variable := another_variable; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | </div> | ||
+ | |||
+ | = VHDL Syntax = | ||
+ | <span class="mw-customtoggle-Syntax" style="font-size:small; display:inline-block; "><span class="mw-customtoggletext" data-expandtext="Illuminate" data-collapsetext="Deluminate">[Show/hide]</span></span> | ||
+ | <div id="mw-customcollapsible-Syntax" class="mw-collapsible mw-collapsed"> | ||
+ | == Comments == | ||
+ | Comments are preceded by "--" (two dashes) and extend to the end of the line. Multiline comments are not available, but some code editors provide a multiline comment feature. | ||
+ | |||
+ | == Identifiers == | ||
+ | Identifiers are names of signals, variables, components etc. etc. The names must all begin with a letter. After the initial letter they may have any combination of letters, numbers and underscores. VHDL is not case sensitive. | ||
+ | |||
+ | == Numbers == | ||
+ | VHDL has support for a very wide range of number representations. | ||
+ | |||
+ | Numbers may be in any base between 2 and 16. The hash (#) separator is used to separate the base from the rest of the number, by surrounding the number. e.g. 2#10101010# =10101010 base 2. Note that the base is an integer expressed in base 10. | ||
+ | |||
+ | Real literals may have decimal points. These numbers are real numbers with a fixed fractional part. The VHDL standard supports floating point numbers although some VHDL compilers do not. Floating point operations are extremely expensive to implement because of the inefficiency incurred in synthesizing the floating point operations. e.g. 3.1415 is a fixed point real number. | ||
+ | |||
+ | |||
+ | Remember that VHDL only looks like a programming language. Bear in mind that when you use a floating point number the required operation has to be constructed out of gates when the design is put onto a chip. Numbers may have exponents. The exponents are expressed in decimal and may only be integers. Exponents may be positive or negative. The exponent is signified by an 'E'. The exponent is raised to the base. For example, a number expressed in base 2 will be multiplied by 2 to the power of the exponent. | ||
+ | |||
+ | Example: 2#1010#E1=1010*2^1=10100=20 decimal. | ||
+ | |||
+ | Here is an example of VHDL using integer types: | ||
+ | <syntaxhighlight lang="vhdl" line='line'> | ||
+ | entity Code_Example is | ||
+ | port (clock : In std_logic; | ||
+ | sel : In std_logic; | ||
+ | output: Out integer range 0 to 255); | ||
+ | end Code_Example; | ||
+ | |||
+ | architecture archy of Code_Example is | ||
+ | begin | ||
+ | process (clock) | ||
+ | begin | ||
+ | if clock='1' then | ||
+ | if sel='1' then | ||
+ | output<=16#55#; | ||
+ | else | ||
+ | output<=2#1010_1010#; | ||
+ | end if; | ||
+ | end if; | ||
+ | end process; | ||
+ | end archy; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Integer types are used for representing numbers with no fractional part. VHDL guarantees that integers will be represented by at least 32 bits. Integers are signed. We can limit the range of integers by using the range keyword. This is useful for limiting the size of the representation and for making your code more compiler independent. In general limit your range to the size that you need. When we limit the range of a type we obtain a <i>Subtype</i>. | ||
+ | |||
+ | When limiting the range of an integer only the size of the representation is limited, not the actual range of the number. As an example if we said <code>dout: OUT integer range 0 to 2</code> in the example above we would still have had a 2 bit counter. The code that could be represented on those 2 bits would still include “11”, even though it is outside of our defined range. Some simulation packages would flag a warning if this occurs, but many would not. | ||
+ | |||
+ | When using an integer as a port, the port will have as many physical lines as are needed to represent the integer using standard binary coding. Thus, in the example above, 'dout' will be represented by two physical port lines. | ||
+ | |||
+ | == Characters and Strings == | ||
+ | Characters are enclosed in single quotation marks and are equivalent to their ASCII number. e.g. '0'=48 | ||
+ | |||
+ | Strings are enclosed in double quotation marks. Example "Samuel" To include a double quotation mark inside quotation marks use it twice "Fred said""hello"". | ||
+ | |||
+ | Strings may be formed in other number bases. These bases are binary, octal and hex. | ||
+ | |||
+ | <syntaxhighlight lang="vhdl" line='line'> | ||
+ | |||
+ | entity Code_Example is | ||
+ | port (clock : In std_logic; | ||
+ | sel : In Std_logic; | ||
+ | output: Out std_logic_vector (7 downto 0)); | ||
+ | end Code_Example; | ||
+ | architecture archy of Code_Example is | ||
+ | begin | ||
+ | process (clock) | ||
+ | begin | ||
+ | if clock='1' then | ||
+ | if sel='1' then | ||
+ | output<="01010101"; --binary string. Use a string to fill vector | ||
+ | else | ||
+ | output<=X"FF"; --Hexadecimal string | ||
+ | end if; | ||
+ | end if; | ||
+ | end process; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Enumerated Types == | ||
+ | VHDL provides enumerated types for convenience. We have already come across one of these, std_logic, in which the logic states are represented as an enumerated type having nine different states which represent different electrical possibilities available on logic signals. | ||
+ | |||
+ | <syntaxhighlight lang="vhdl" line='line'> | ||
+ | entity Code_Example is | ||
+ | port (clock : In std_logic; | ||
+ | output: Out integer range 0 to 1); | ||
+ | end Code_Example; | ||
+ | architecture archy of Code_Example is | ||
+ | type colour is (green, blue); | ||
+ | --declare types in architecture declarative section | ||
+ | begin | ||
+ | process (clock) | ||
+ | variable status: colour; | ||
+ | --declare variables in process declarative section | ||
+ | begin | ||
+ | if clock='1' then | ||
+ | if status =green then | ||
+ | output<=1; | ||
+ | status:=blue; | ||
+ | else | ||
+ | output<=0; | ||
+ | status:=green; | ||
+ | end if; | ||
+ | end if; | ||
+ | end process; | ||
+ | end archy; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | == Arrays == | ||
+ | These have the same meaning that you are used to. Arrays may have any number of dimensions and may be formed from any supported type, or previously defined type. | ||
+ | |||
+ | There are predefined types, such as strings and bit vectors. | ||
+ | |||
+ | The definition for the string type is given as follows: | ||
+ | <syntaxhighlight lang="vhdl"> | ||
+ | type string is array (positive range <>) of character; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | The definition for bit vectors is as follows: | ||
+ | <syntaxhighlight lang="vhdl"> | ||
+ | type bit_vector is array (natural range <>) of bit; | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | The "<>" angle brackets are called a box. They are placeholders which indicate that the range will be filled in when the types are used. | ||
+ | |||
+ | Bit vectors are essentially an array of bits used for binary numbers. Here is an example of the usage of a bit | ||
+ | vector: | ||
+ | <syntaxhighlight lang="vhdl"> | ||
+ | din: in bit_vector (2 downto 0); | ||
+ | </syntaxhighlight> | ||
+ | In this case the box will be filled in with the range 2 downto 0. | ||
+ | |||
+ | Arrays may be indexed to access individual elements. If we wish to access the first bit in din then we can say: | ||
+ | <code>if din(2)='1' then....</code> | ||
+ | |||
+ | Note that in the case of bit vectors when we specify the range we are specifying an array index. As a result, we are not specifying the numerical range, but rather the actual number of bits in the array. | ||
+ | |||
+ | If we use the “downto” keyword then the most significant bit will occupy the highest numbered array position. The opposite will apply if we use the “to” keyword. | ||
+ | |||
+ | We will not use bit vectors much, rather we will use 'standard logic vectors' which are more widely supported. The reason for this is that most of the libraries that are available for our VHDL system are defined in terms of standard logic vectors, rather than bit vectors. The behavior of std_logic_vectors is however very similar to the bit vector, except that, as noted above, std_logic has more possible states. | ||
+ | |||
+ | See above for examples of std_logic_vector usage. | ||
+ | |||
+ | |||
+ | </div> | ||
+ | |||
+ | = VHDL Operators = | ||
+ | Like any language VHDL has a set of operators. These operators may be divided into three types: Logical, arithmetic and comparison. | ||
+ | |||
+ | <span class="mw-customtoggle-Operators" style="font-size:small; display:inline-block; "><span class="mw-customtoggletext" data-expandtext="Illuminate" data-collapsetext="Deluminate">[Show/hide]</span></span> | ||
+ | <div id="mw-customcollapsible-Operators" class="mw-collapsible mw-collapsed"> | ||
+ | |||
+ | == Logical Operators == | ||
+ | Logical operators are used to derive logical results from bits and bit_vectors. When used with bit_vectors the operators act in a bitwise manner. When used with bit_vectors, those vectors must be of the same length. | ||
+ | The logical operators are as follows: | ||
+ | * NOT | ||
+ | * AND | ||
+ | * OR | ||
+ | * NAND | ||
+ | * NOR | ||
+ | * XOR | ||
+ | These should require no further explanation. | ||
+ | In addition there is a concatenation operator. This is the ampersand "&". This concatenates two bit vectors. | ||
+ | Example: | ||
+ | <syntaxhighlight lang="vhdl" line='line'> | ||
+ | signal a: bit_vector (1 to 3); | ||
+ | signal b: bit_vector (1 to 5); | ||
+ | signal c: bit_vector (1 to 8); | ||
+ | c <= a & b; --legal concatenation operation. | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Arithmetic Operators == | ||
+ | These operators perform basic arithmetic operations on numbers. Normally these operations are only intended for use on integers. Some VHDL suites come with libraries which contain arithmetic functions for use with bit_vectors and std_logic vectors. | ||
+ | |||
+ | The operators are: | ||
+ | * addition (+) | ||
+ | * subtraction (-) | ||
+ | * multiplication (*) | ||
+ | * division (/) | ||
+ | |||
+ | The only caution that applies here is that some packages only support multiplication and division by integer powers of two. This in effect reduces multiplication and division to the status of arithmetic shift operations. | ||
+ | |||
+ | == Comparison Operators == | ||
+ | These operators are used to compare two scalar or bit_vector arguments and they return 'true ' or 'false'. If bit_vectors are compared then the vectors must be of the same length. | ||
+ | {| | ||
+ | | < | ||
+ | | smaller than | ||
+ | |- | ||
+ | | <= | ||
+ | | smaller than or equal | ||
+ | |- | ||
+ | | > | ||
+ | | greater than | ||
+ | |- | ||
+ | | >= | ||
+ | | greater than or equal | ||
+ | |- | ||
+ | | = | ||
+ | | equal | ||
+ | |- | ||
+ | | /= | ||
+ | |not equal | ||
+ | |} | ||
+ | |||
+ | </div> | ||
+ | |||
+ | = Attributes of Objects = | ||
+ | Attributes are properties of VHDL types that can be very useful. We use attributes to extract further information about a VHDL object. | ||
+ | |||
+ | <span class="mw-customtoggle-AoO" style="font-size:small; display:inline-block; "><span class="mw-customtoggletext" data-expandtext="Illuminate" data-collapsetext="Deluminate">[Show/hide]</span></span> | ||
+ | <div id="mw-customcollapsible-AoO" class="mw-collapsible mw-collapsed"> | ||
+ | |||
+ | All attributes are accessed by using the apostrophe ' operator, sometimes pronounced “tick”. In theory attributes are classified by what sort of thing they return, but in authoritative sources (compare Ashenden 2008 to Perry 2002) they are dealt with in quite different ways. For purposes of this course we will be quite loose in our handling of attributes, aiming for utility rather than formal correctness. | ||
+ | |||
+ | == Attributes of Signals == | ||
+ | These attributes either derive information about signals or define new signals based on other signals. We have seen one of these already, the 'event attribute. This attribute creates a signal that is true when its base signal has changed in the last cycle. Examples have been shown above. There is also a 'last_value attribute which returns the value of a signal prior to its last transition. | ||
+ | |||
+ | |||
+ | There are many more of these attributes, however many of them are for simulation purposes only and cannot be directly synthesized to hardware. These would typically be used for generating testbenches. | ||
+ | |||
+ | |||
+ | == Attributes of Scalar Types == | ||
+ | |||
+ | 'pos – str'pos ("fred") gives the position of fred in the string str. | ||
+ | |||
+ | 'val – Var'val(x) gives the value at position x of variable var. | ||
+ | |||
+ | 'leftof – value in a variable to the left of a specified position. | ||
+ | |||
+ | 'rightof – value in a variable to the right of a specified position. | ||
+ | |||
+ | 'pred – value of a preceding position. Depends on range direction. | ||
+ | |||
+ | 'succ – value of a succeeding position. Depends on range direction. | ||
+ | |||
+ | |||
+ | Note that not all of these attributes apply to all types. Most of the above are useful with enumerated types. If an array is declared with a range of indices from 10 to 30 then Array'left will return 10. | ||
+ | |||
+ | Example: | ||
+ | Suppose we have the following declarations: | ||
+ | <syntaxhighlight lang="vhdl"> | ||
+ | lsb_output: OUT std_logic --declare a bit-wide port | ||
+ | signal count_signal_name : STD_LOGIC_VECTOR (3 downto 0); --declare a 4 bit wide signal | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Then the following line extracts the least significant bit from the signal and assigns it to the port: | ||
+ | <syntaxhighlight lang="vhdl"> | ||
+ | lsb_output <= count_signal_name (count_signal_name'right); | ||
+ | </syntaxhighlight> | ||
+ | This is useful for making procedures which handle arrays independently of the array width. | ||
+ | |||
+ | </div> | ||
− | |||
− | |||
− | |||
= More on Processes = | = More on Processes = | ||
= Changing Execution Flow in VHDL = | = Changing Execution Flow in VHDL = |
Latest revision as of 11:00, 28 January 2021
VHDL stands for VHSIC-HDL, which in turn stands for Very High Speed Integrated Circuit Hardware Description Language. It is one way in which hardware can be described. The guide below was adapted by notes originally written by Samuel Ginsberg, which can be downloaded as a PDF here. File:VHDL Notes 2016 updated SamuelGinsberg.pdf
Contents
Structure and Behaviour[edit]
There are two fundamentally different ways of specifying logic. The first method is to specify the structure of the logic and the other method is to specify the behaviour of the system.
[Show/hide]
Structure[edit]
[Show/hide]
The structure of a system describes that system in terms of "what is connected to what". The system is thus broken down into smaller units which are connected together to form a whole. The whole unit is called an entity. Inputs and outputs to/from the entity are called ports.
TODO: IMAGE
In this illustration the overall entity is called Z. The input ports are called A, B and C. The output ports are called D and E. The interconnections inside the entity Z are called signals. You will observe that X and Y are themselves entities. The entities X and Y would in turn have to be specified in terms of either their behaviour or their structure. This nested definition system implies that by using structural descriptions very big and complex blocks may be built up out of a number of simple blocks. This is fundamental to problem solving of almost any type.
Behaviour[edit]
[Show/hide]
You will see that structural descriptions are extremely useful for building systems from smaller components. Ultimately we need to specify what each component actually does. In this case structural descriptions are of limited use. We need some way of describing the behaviour of the entities that we use. At the very minimum we need to specify the behaviour of the entities at the bottom of the hierarchical structure. VHDL thus provides a means of specifying the behaviour of entities by means of a behavioural description. Behavioural descriptions resemble a programming language. We will discuss syntax as we need it to implement the concepts that follow. Please bear in mind as we go that, despite the similarities, a VHDL description is a hardware description, and that the analogy to a programming language is actually fairly weak.
Entities[edit]
[Show/hide]
As we have mentioned an entity is a "box" with inputs and outputs called ports. We need to give the ports names and associate them with an entity. Here is an example of this:
1 entity hadder is
2 port
3 (inA, inB : in std_logic;
4 sum,carry : out std_logic); --note the semicolon
5 end hadder;
This entity declaration states that we are creating an entity called hadder. This entity has two inputs called inA and inB and two outputs called sum and carry. Further we have declared that the ports are all 'std_logic' values. A std_logic value is basically a binary bit, which can take on the value 0 and 1. Std_logic bits can also take on a few other states, such as high impedance and undefined, in order to closely model the behaviour of real logic bits. The entity is shown graphically here:
TODO: IMAGE
Architectures[edit]
[Show/hide]
Once we have declared the entity we need to describe how it works. We use an architecture to do this. We could use either a structural description or a behavioural one.
Suppose for the sake of the illustration that the above entity implements a half adder. Our adder's circuit diagram is as follows:
TODO: IMAGE
Here is an example of structural description:
1 architecture structure of hadder is
2 --We now need to describe the interface to the components that we are
3 --using. These components are described in the ALTERA MAXPLUS2 library.
4 component a_7408 --AND gate
5 port (a_2: in STD_LOGIC;
6 a_3: in STD_LOGIC;
7 a_1: out STD_LOGIC);
8 end component;
9 component a_7486 --XOR gate
10 port (a_2: in STD_LOGIC;
11 a_3: in STD_LOGIC;
12 a_1: out STD_LOGIC);
13 end component;
14 --Signals are like wires used to connect components
15 signal inputA,inputB : std_logic;
16 begin
17 --see equivalent diagram in notes to understand this.
18 inputA<=inA;
19 inputB<=inB;
20 myAND: a_7408 port map (a_2=>inputA,a_3=>inputB,a_1=>carry);
21 myXOR: a_7486 port map (a_2=>inputA,a_3=>inputB,a_1=>sum);
22 end structure;
The first thing that we do is state which components are contained in our box. These are an AND gate and an XOR gate. We also define the ports of these components. Note that the components were created previously and we are only using them here. We then define two signals. These signals are the internal "wires" which connect components together. The signals are inputA and inputB.
After we have defined all of the types of things that go into our description we need to instantiate them. We do that in the next section of code. We make an instance of the AND gate called myAND and an instance of the XOR gate called myXOR. When we instantiate the components we list how the inputs are connected to the signals. This effectively creates a netlist. A netlist is a list of how components connect together. The first two statements connect the internal signals inputA and inputB to the input ports, as shown in the circuit diagram.
Processes[edit]
[Show/hide]
We sometimes want a set of VHDL statements to execute sequentially, one after the other. This allows us to build up a set of instructions that are 'executed' one after the other. The execution of this list is triggered by some event happening, very often on a port of the entity.
As an example consider this simple flip flop design:
1 LIBRARY ieee;
2 USE ieee.std_logic_1164.all;
3 ENTITY test_proc1 IS
4 PORT
5 (D_input : IN STD_LOGIC;
6 clk_input : IN STD_LOGIC;
7 Q_output : OUT STD_LOGIC);
8 END test_proc1;
9 ARCHITECTURE a OF test_proc1 IS
10 BEGIN
11 PROCESS (clk_input) --trigger this when clk_input changes
12 BEGIN
13 if (clk_input='1') then
14 Q_output <= D_input;
15 end if;
16 END
In this example we are describing the behavior of a D type flip flop. The flip flop's D input is transferred to the Q output and held there on the rising edge of the clock signal. We declare an entity as before.
The architecture begins with the keyword 'Process' followed by a list of port names, in this case clk_input. This list is called a 'sensitivity list'. Whenever one (or more) of the items in the sensitivity list changes the code that is contained in the process is executed. In this example when clk_input changes, the code within the process is executed. This code examines the state of the clk_input bit and if it is high the Q output is updated to be equal to the D input. We achieve the rising edge triggered action by looking for a change in clk_input and then examining to see if the change was a rising or a falling edge.
Variables[edit]
[Show/hide]
It is possible to have variables in an architecture. Variables are used to store data within a process. Signals differ from variables in that variables cannot trigger events, nor can a change in a variable trigger a process.
Furthermore, signal values within a process change only when the process is completed, whereas variables are evaluated immediately.
Variables are modified with the variable assignment operator which is :=, thus assigning one variable to another is done as follows:
1 one_variable := another_variable;
VHDL Syntax[edit]
[Show/hide]
Comments[edit]
Comments are preceded by "--" (two dashes) and extend to the end of the line. Multiline comments are not available, but some code editors provide a multiline comment feature.
Identifiers[edit]
Identifiers are names of signals, variables, components etc. etc. The names must all begin with a letter. After the initial letter they may have any combination of letters, numbers and underscores. VHDL is not case sensitive.
Numbers[edit]
VHDL has support for a very wide range of number representations.
Numbers may be in any base between 2 and 16. The hash (#) separator is used to separate the base from the rest of the number, by surrounding the number. e.g. 2#10101010# =10101010 base 2. Note that the base is an integer expressed in base 10.
Real literals may have decimal points. These numbers are real numbers with a fixed fractional part. The VHDL standard supports floating point numbers although some VHDL compilers do not. Floating point operations are extremely expensive to implement because of the inefficiency incurred in synthesizing the floating point operations. e.g. 3.1415 is a fixed point real number.
Remember that VHDL only looks like a programming language. Bear in mind that when you use a floating point number the required operation has to be constructed out of gates when the design is put onto a chip. Numbers may have exponents. The exponents are expressed in decimal and may only be integers. Exponents may be positive or negative. The exponent is signified by an 'E'. The exponent is raised to the base. For example, a number expressed in base 2 will be multiplied by 2 to the power of the exponent.
Example: 2#1010#E1=1010*2^1=10100=20 decimal.
Here is an example of VHDL using integer types:
1 entity Code_Example is
2 port (clock : In std_logic;
3 sel : In std_logic;
4 output: Out integer range 0 to 255);
5 end Code_Example;
6
7 architecture archy of Code_Example is
8 begin
9 process (clock)
10 begin
11 if clock='1' then
12 if sel='1' then
13 output<=16#55#;
14 else
15 output<=2#1010_1010#;
16 end if;
17 end if;
18 end process;
19 end archy;
Integer types are used for representing numbers with no fractional part. VHDL guarantees that integers will be represented by at least 32 bits. Integers are signed. We can limit the range of integers by using the range keyword. This is useful for limiting the size of the representation and for making your code more compiler independent. In general limit your range to the size that you need. When we limit the range of a type we obtain a Subtype.
When limiting the range of an integer only the size of the representation is limited, not the actual range of the number. As an example if we said dout: OUT integer range 0 to 2
in the example above we would still have had a 2 bit counter. The code that could be represented on those 2 bits would still include “11”, even though it is outside of our defined range. Some simulation packages would flag a warning if this occurs, but many would not.
When using an integer as a port, the port will have as many physical lines as are needed to represent the integer using standard binary coding. Thus, in the example above, 'dout' will be represented by two physical port lines.
Characters and Strings[edit]
Characters are enclosed in single quotation marks and are equivalent to their ASCII number. e.g. '0'=48
Strings are enclosed in double quotation marks. Example "Samuel" To include a double quotation mark inside quotation marks use it twice "Fred said""hello"".
Strings may be formed in other number bases. These bases are binary, octal and hex.
1 entity Code_Example is
2 port (clock : In std_logic;
3 sel : In Std_logic;
4 output: Out std_logic_vector (7 downto 0));
5 end Code_Example;
6 architecture archy of Code_Example is
7 begin
8 process (clock)
9 begin
10 if clock='1' then
11 if sel='1' then
12 output<="01010101"; --binary string. Use a string to fill vector
13 else
14 output<=X"FF"; --Hexadecimal string
15 end if;
16 end if;
17 end process;
Enumerated Types[edit]
VHDL provides enumerated types for convenience. We have already come across one of these, std_logic, in which the logic states are represented as an enumerated type having nine different states which represent different electrical possibilities available on logic signals.
1 entity Code_Example is
2 port (clock : In std_logic;
3 output: Out integer range 0 to 1);
4 end Code_Example;
5 architecture archy of Code_Example is
6 type colour is (green, blue);
7 --declare types in architecture declarative section
8 begin
9 process (clock)
10 variable status: colour;
11 --declare variables in process declarative section
12 begin
13 if clock='1' then
14 if status =green then
15 output<=1;
16 status:=blue;
17 else
18 output<=0;
19 status:=green;
20 end if;
21 end if;
22 end process;
23 end archy;
Arrays[edit]
These have the same meaning that you are used to. Arrays may have any number of dimensions and may be formed from any supported type, or previously defined type.
There are predefined types, such as strings and bit vectors.
The definition for the string type is given as follows:
type string is array (positive range <>) of character;
The definition for bit vectors is as follows:
type bit_vector is array (natural range <>) of bit;
The "<>" angle brackets are called a box. They are placeholders which indicate that the range will be filled in when the types are used.
Bit vectors are essentially an array of bits used for binary numbers. Here is an example of the usage of a bit vector:
din: in bit_vector (2 downto 0);
In this case the box will be filled in with the range 2 downto 0.
Arrays may be indexed to access individual elements. If we wish to access the first bit in din then we can say:
if din(2)='1' then....
Note that in the case of bit vectors when we specify the range we are specifying an array index. As a result, we are not specifying the numerical range, but rather the actual number of bits in the array.
If we use the “downto” keyword then the most significant bit will occupy the highest numbered array position. The opposite will apply if we use the “to” keyword.
We will not use bit vectors much, rather we will use 'standard logic vectors' which are more widely supported. The reason for this is that most of the libraries that are available for our VHDL system are defined in terms of standard logic vectors, rather than bit vectors. The behavior of std_logic_vectors is however very similar to the bit vector, except that, as noted above, std_logic has more possible states.
See above for examples of std_logic_vector usage.
VHDL Operators[edit]
Like any language VHDL has a set of operators. These operators may be divided into three types: Logical, arithmetic and comparison.
[Show/hide]
Logical Operators[edit]
Logical operators are used to derive logical results from bits and bit_vectors. When used with bit_vectors the operators act in a bitwise manner. When used with bit_vectors, those vectors must be of the same length. The logical operators are as follows:
- NOT
- AND
- OR
- NAND
- NOR
- XOR
These should require no further explanation. In addition there is a concatenation operator. This is the ampersand "&". This concatenates two bit vectors. Example:
1 signal a: bit_vector (1 to 3);
2 signal b: bit_vector (1 to 5);
3 signal c: bit_vector (1 to 8);
4 c <= a & b; --legal concatenation operation.
Arithmetic Operators[edit]
These operators perform basic arithmetic operations on numbers. Normally these operations are only intended for use on integers. Some VHDL suites come with libraries which contain arithmetic functions for use with bit_vectors and std_logic vectors.
The operators are:
- addition (+)
- subtraction (-)
- multiplication (*)
- division (/)
The only caution that applies here is that some packages only support multiplication and division by integer powers of two. This in effect reduces multiplication and division to the status of arithmetic shift operations.
Comparison Operators[edit]
These operators are used to compare two scalar or bit_vector arguments and they return 'true ' or 'false'. If bit_vectors are compared then the vectors must be of the same length.
< | smaller than |
<= | smaller than or equal |
> | greater than |
>= | greater than or equal |
= | equal |
/= | not equal |
Attributes of Objects[edit]
Attributes are properties of VHDL types that can be very useful. We use attributes to extract further information about a VHDL object.
[Show/hide]
All attributes are accessed by using the apostrophe ' operator, sometimes pronounced “tick”. In theory attributes are classified by what sort of thing they return, but in authoritative sources (compare Ashenden 2008 to Perry 2002) they are dealt with in quite different ways. For purposes of this course we will be quite loose in our handling of attributes, aiming for utility rather than formal correctness.
Attributes of Signals[edit]
These attributes either derive information about signals or define new signals based on other signals. We have seen one of these already, the 'event attribute. This attribute creates a signal that is true when its base signal has changed in the last cycle. Examples have been shown above. There is also a 'last_value attribute which returns the value of a signal prior to its last transition.
There are many more of these attributes, however many of them are for simulation purposes only and cannot be directly synthesized to hardware. These would typically be used for generating testbenches.
Attributes of Scalar Types[edit]
'pos – str'pos ("fred") gives the position of fred in the string str.
'val – Var'val(x) gives the value at position x of variable var.
'leftof – value in a variable to the left of a specified position.
'rightof – value in a variable to the right of a specified position.
'pred – value of a preceding position. Depends on range direction.
'succ – value of a succeeding position. Depends on range direction.
Note that not all of these attributes apply to all types. Most of the above are useful with enumerated types. If an array is declared with a range of indices from 10 to 30 then Array'left will return 10.
Example: Suppose we have the following declarations:
lsb_output: OUT std_logic --declare a bit-wide port
signal count_signal_name : STD_LOGIC_VECTOR (3 downto 0); --declare a 4 bit wide signal
Then the following line extracts the least significant bit from the signal and assigns it to the port:
lsb_output <= count_signal_name (count_signal_name'right);
This is useful for making procedures which handle arrays independently of the array width.