Essential VHDL Amit Raj

This page describes some essential VHDL needed for EE 475. We use VHDL for building hardware hence we concentrate on the synthesis subset of the language. VHDL can also be used to accurately model digital systems. These simulation models can be extremely complex and are beyond the scope of this course.

Library Declarations
Every VHDL file (actually, every ENTITY, see below) should begin with:
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all;

The standard_logic_1164 library defines the variable types (std_logic and std_logic_vector)which we will use. The standard_logic_unsigned library overloads certain operators, such as '+', '<' and '-' for the types std_logic and std_logic_vector. Include the library declarations before each ENTITY!

Entity Declarations
Every digital circuit has inputs and outputs. VHDL uses an ENTITY declaration to describe the circuit's ports.

In EE 475 we will use the TYPE std_logic and std_logic_vector to the exclusion of all other types, with the possible exception of enumerated types later in the course. Std_logic is a scaler (one bit) and std_logic_vector is composite, an array, or bus in digital hardware terms. Our std_logic_vectors will always have a descending range - that is (N downto 0). And the LSB (the least significant bit corresponding to the 2 0 bit) will be X(0) and the MSB would be X(N). Ports, the inputs and outputs of a circuit have a mode. Modes used in EE 475 are IN, OUT, INOUT, and BUFFER. Ports of IN can only be read, trying to write, or assign a value will result in a error. Ports of mode OUT can only be written, attempting to read the value will result in an error. (Ports of mode OUT can only appear on the LHS of an assignment statement. Ports of mode IN can only appear on the RHS of an assignment statement.) A port of INOUT can be both read and written and is usually used for bi-directional ports. BUFFER ports can be written, and read, but only the value written by the entity can be read - in other words a BUFFER is like an OUT tied to an IN. In our system, the mode BUFFER may synthesis incorrectly with NO warning. Don't use it.

Port declarations will take the form SIGNAL_NAME: MODE TYPE;

SIGNAL_NAME is a identifier. The only thing to remember here is not to use a VHDL keyword (like "entity") or start the identifier with a number. Popular signal_names might be AIN, Data_out, Clock, reset, etc. VHDL literals are not case sensitive. In other words AIN, ain, Ain, aIn, etc all refer to the same identifier. Let's try to put this all together. Here's a typical entity declaration for this course:
entity design is port( a, b: in std_logic_vector(7 downto 0); dout: out std_logic_vector(7 downto 0); reset: in std_logic; clk: in std_logic ); end design;

This entity, named DESIGN, has two input buses A and B, one output bus DOUT, and two more inputs RESET and CLK. All but purely combinational circuits will have a clock and (usually) a reset. Most circuits (at least in this course) will be synchronous sequential circuits.

Architecture Declarations
The entity declaration is used to describe the ports of the digital system, the architecture is used to describe the behavior of the digital system. Our architectures should adhere to the following style guidelines even if they appear to be simpler than the syntax explained in the VHDL manual. A simple style is easier to maintain and sometimes produces smaller circuits when compiled to the FPGA. An architecture declaration for this course will look like the following:

Every architecture has a NAME (ARCH_NAME) and is a associated with some ENTITY (ENTITY_NAME). There is a declarative region in which we will declare signals (circuit nodes) internal to the digital system. (Ports are visible to other entities, internal signals are not.) Then after a BEGIN is where the hard stuff is - where we will describe the behavior of the digital system. A typical architecture (minus the hard stuff) should look like the following.
architecture synthesis1 of design is

signal x, y, z: std_logic; signal tmp: std_logic_vector(7 downto 0); begin THE_HARD_STUFF_MISSING; end synthesis1;

Use the architecture declarative region to declare signals internal to the design!

The hard stuff
At this point we have defined a minimal VHDL framework for our use in EE 475. We now need to define the behavior of a digital system in VHDL. It is important to remember that VHDL is a large language, used for many different applications and we will use a tiny subset of it for this class. The 'hard stuff' is the statement declarative region which will contain concurrent statements that describe the behavior of the entity. Unlike other programming languages (VHDL is really a hardware description language, not a programming language), statements in an architecture execute concurrently, not in sequential order.
architecture synthesis of example is begin A <= X AND Y; B <= X OR Y ; end synthesis; --THESE TWO STATEMENT EXECUTE SIMULTANEOUSLY --WHENEVER X OR Y CHANGES

Concurrent Signal Assignment statements will always generate combinational logic. In the above example an AND gate, and a OR gate. Operators available for our use include AND, OR, NOT, NAND, NOR, XOR, + (binary addition), - (binary subtraction), and & (concatentation). Some legal concurrent signal assignment statements include:
A <= X + Y; A <= Z OR (X AND Y); A <= X & Y;

VHDL is a strongly typed language. The types on the RHS and the LHS must be the same, and if std_logic_vectors must be of the same length. This makes sense! Can't tie a 4 bit bus to an 8 bit bus unless one resolves the dangling 4 bits. For instance if you had a 4 bit vector and an 8 bit vector, you could zero-extend the shorter one, as follows:
signal X: std_logic_vector(3 downto 0); signal Y: std_logic_vector(7 downto 0); begin Y <= "0000" & X;

Concatention allows signals to be combined into larger composites (bigger buses). The opposite, slicing apart a bus, is implied by indexing.
architecture synthesis of example is

signal X, Y: std_logic_vector(7 downto 0); signal Z: std_logic_vector(3 downto 0); begin X <= Z <= Z & Z; --COMBINE TWO 4 BIT BUSES INTO ONE 8 BIT BUS Y(3 downto 0); --TAKE A 4 BIT SLICE OF Y AND ASSIGN IT TO Z

end synthesis;

At this point we can write boolean equations in VHDL, and even arithmetic equations using the overloaded "+" and "-" operators, but we are still forced to derive the boolean equations manuallly. The advantage of VHDL comes from using complex VHDL concurrent signal assignment statements and letting the synthesis tool derive the implied boolean (logical) equations. In addition to assignment statments, there is a when-else conditional signal assignment statement that allows the expression of conditionals in concurrent signal assignment statements:
A <= B when X='1' else C when Y="00" else D;

There is also a selected signal assignment statement which corresponds to a case statement. In the code fragment below, SELECTOR is a signal which is used to specify which alternative is assigned to A. Since all cases must be covered in the statment, the OTHERS keyword must be included.
with SELECTOR select A <= B when "00", C when "01", D when OTHERS;

Combinational circuits should use concurrent statements!

Sequential Statements
At this point we can write an entity, create an architecture, and even describe simple combinational circuits in VHDL. But this does not allow us to describe more complex behaviors associated with sequential circuits, nor to take full advantage of the synthesis system's ability to generate the digital circuit implementation from the VHDL description. If we want to introduce clocked behavior we need one more, very general concurrent statement, the PROCESS statement. The process statement may contain other statements which appear to execute in sequential order, and which appear more similar to familiar C-like languages. Do not be deceived. Each process executes at the same time as every other process statement and at the same time as every other concurrent statement. Further, within a single process there are quite limiting rules to how you can use a signal. The process statement has the general form:
process --variables begin --Sequential Statements

end process;

Of all the VHDL statements we will use in this course, the process statement will be the most simplified. In general we will use it for modeling clock circuits, register updates, and occasionally complex combinational circuits. The following impliments a synchronous reset of a register and a specification to update it when the clock rises.
process begin wait until clk'event AND clk='1'; if reset = '0' then A <= "0000"; else A <= B; end if; end process;

This process waits until the clk goes from 0 to '1' then executes the sequential statements. The VHDL (or circuit) tests the value of reset, and if it is '0' A is cleared, else B is transfered to A. Use a process for clocked circuits! Skahill and other texts suggest various forms for process statement, but for this course this form will work for most situations for clocked processes. If you want an asynch reset you can use a form like:
process (reset, clk) begin if reset='0' then -- Initial conditions for registers elsif (clk'event and clk='1') then -- Behavior of the circuit when not in reset. end if; end process;

Some Details
At this point you probably saying "Ok here is the syntax - but what about the semantics!". You have seen the form of the VHDL but you are not too sure about the meaning of the statements and how the are interpreted by either a simulator or a synthesis tool. There is one specific point which causes lots of bugs. Signals do not behave like C-language variables. In a programming language like C, or C++, or ADA there are variables that hold values. If we wrote the following variable assignment statements:
A := X + Y;

B := A + X;

The value of B would be? X + Y + X. And B is updated immediately. Meaning for the following snippet of code we would expect that "DO THIS" would occur, not "DO THAT" - if coniditional would be true, not the else.
A := X + Y; B := A + X; if B = ((2*X)+Y) then --DO THIS else --DO THAT end if;

This is also true in VHDL, if you use 'variables' but not if you use signals. VHDL has VARIABLES (note we have been discussing mostly signals). In general we will discourage the use of variables.
process variable A, B: integer; begin A := X + Y; --assume X and Y are signals defined else where B := A + X; if B = ((2*X)+Y) then X <= 0; Y <= 0; else X <= X + 1; Y <= Y + 1; end if; end process;

VHDL variables can be defined inside processes and behave like variables. They get updated immediately and the new value is available for immediate use. So for the above contrived example B will be equal to 2X+Y when the if conditional is evaluated. Signals don't behave that way!
process begin wait until clk='1'; if reset = '0' then A <= "0000"; else A <= A + 1; end if; end process;

The above clocked process can be read to say "when the clock ticks do what follows". And that is to update A. Now A must hold its value between clock ticks so A is in fact the output of a register. And it makes sense that A only changes when the clock ticks. But signals never can change immediately they always have some finite delay associated with them. If we change the earlier process not to use variables for A and B, but instead use signals for A and B.
process begin

A <= X + Y; --assume X and Y are signals defined else where B <= A + X; --assume A and B are signals defined else where if B = ((2*X)+Y) then X <= 0; Y <= 0; else X <= X + 1; Y <= Y + 1; end if; end process;

In this process the signal assigment statements don't change A and B immediately - there is a delta delay - so the conditional uses the old value of B what ever that might be, not the new value assigned previously. One way to think about this is to consider that all signals change value ONLY at the end of the process, after all statments in the process of executed. And after all, they are really not sequential at all but concurrent. These semantics, with signals, allows processes to run in any order and still have the same result. the following code fragment behaves in the expected way from the 'sequential frame of mind', since we test CNT before we set it.
process begin wait until clk='1'; if CNT = 5 then CNT <= 0; else CNT <= CNT + 1; end if; end process;

However if you set the variable before you test it, you will actually be testing the value before it was set.
process --DON'T DO THIS unless you want the previous value begin wait until clk='1'; CNT <= CNT + 1; if CNT = 5 then CNT <= 0; end if; end process;

A useful stylistic rule is: For any possible execution of the process (execution of conditionals) the process should only assign a value to a particular signal once. Assigning a value more than once is syntactically correct, but only the last assigned value will have any effect.

Sequential Statements
VHDL has the full range of sequential statements found in any language - IF, CASE, and various forms of LOOP. We will not use the LOOP constructs. The IF has the general form:
if conditional then --sequential statements elsif conditional then

--sequential statements else --sequential statements end if;

You can use an if statement freely in a clocked process.
process begin wait until clk = '1' if A = "00" then X <= X + 1; end if; end process;

You will get what you expected. A register to hold X that is only updated when A = 00. However, if you use an if statment in an unclocked process
process (A) begin if A = "00" then X <= X + 1; end if; end process;

What happens is that it is possible when this process runs that X does not get a value assigned (A is not equal to "00".) What is the value of X then? It must be the OLD value. Hence while this process does not have a clock it still imples a register - the synthesis tool builds a latch controlled by the value of A. It will latch X on A = 00. Conditionals in unclocked processes must cover every case, or they will imply (perhaps) unwanted registers.
process begin if A = "00" then X <= X + 1; elsif A = "00" then X <= X + 2; elsif A = "00" then X <= X + 3; else X <= X + 4; end if; end process;

The above unclocked process will generate a purely combinational circuit - notice the "else". Except in very comlex cases it is safer to use a concurrent statement outside of a process for combinatorial logic. In genera, IF statements should be used with care when generating combinational logic - first they add logic for enforcing the implied priority of an IF statement and they can lead to unintended latches. Instead use a CASE statement - it will enforce that every input is covered and has a more truth table like form.
case X is when "00" when "01" when "10" when others end case; => => => => Z Z Z Z <= <= <= <= "001"; "111"; "011"; "101";

The above case statement implements a truth table.

Common Mistakes
  

     

Getting fancy - keep your VHDL simple. If you are lost - draw a block diagram and then write the VHDL for each block as a process in an entity architecture pair. Forgetting your boolean algebra - A NAND B NAND C is not equal to NOT (A AND B AND C). Precedence - VHDL does not assign precedence to the AND and OR operators. Hence given the equation A AND B OR C AND D you will get an error. You must supply parenthesis. You will likely want (A AND B) OR (C AND D). A NAND B NAND C also has problems since A NAND B is really NOT (A AND B) you must supply parenthesis! But remember ((A NAND B) NAND C) is not equal to NOT (A AND B AND C). Duplicate assignment - In this course we can only have one CONCURRENT assignment to a signal or port. If we write
architecture example of simple is begin A <= X + Y; A <= X - Y; end example;

or the identical process form
architecture example of simple is begin process begin A <= X + Y; end process; process begin A <= X - Y; end process; end example;

we will get an error. Why? We are effectively connecting two outputs to a single point in the circuit. This is a very common error. How to make this work this example work? We must add a multiplexer or a tri-state bus.
A <= X+Y when sel='0' else X-Y;

or for a tri-state bus
A <= X+Y when sel='0' else "ZZZZ"; A <= X-Y when sel='1' else "ZZZZ";

Sign up to vote on this title
UsefulNot useful