VHDL: A First Look

Introduction

VHDL is a large and complex language. Because it was initially created for highly accurate modeling of circuits for timing simulation, VHDL includes many features related to modeling (such as timing specifications) that are of only passing interest to users of simulation and synthesis tools. To help put the language into a proper context and to emphasize its use as a design entry language, we are going to look at a few very simple circuits and show how they can be described for synthesis and testing.

In addition to the quick introduction to VHDL presented in this chapter, there are some very important concepts that will be introduced. Perhaps the most important concepts to understand in VHDL are the concepts of concurrency and hierarchy. Since these concepts are so important (and may be new to you) we'll be introducing both concurrency and hierarchy in these first simple examples. First, though, I'll present a very simple circuit so you can see what constitutes the minimum VHDL source file.

As you examine these examples and read the information in this chapter, you'll begin to understand some of the most important concepts of VHDL, and will have a better understanding of how many of the more detailed features we'll cover later can be used.

Simple Example: A Comparator

We'll start this section by looking at a very simple combinational circuit: an 8-bit comparator. The circuit is shown below in block diagram form:

This comparator accepts two 8-bit inputs, compares them, and produces a 1-bit result (either 1, indicating a match, or 0, indicating a difference between the two input values).

A comparator such as this is a combinational function constructed in circuitry from an arrangement of exclusive-OR gates or from some other lower-level structure depending on the capabilities of the target technology. (It is the job of logic synthesis to determine what exact arrangement of logic gates should be used.) When described in VHDL, however, a comparator can be a simple language statement the makes use of VHDL's built-in relational operators.

Comparator Source File

VHDL includes many high-level language features that allow you to describe combinational logic functions such as comparators. The following VHDL source code uses a single concurrent assignment to describe the operation of the comparator:
-------------------------------------------------------
-- Eight-bit comparator
--
entity compare is
    port( A, B: in bit_vector(0 to 7);
             EQ: out bit);
end compare;

architecture compare1 of compare is
begin

    EQ <= '1' when (A = B) else '0';

end compare1;
Let's look more closely at this source file; reading from the top, we see the following elements:

Entities and Architectures

Every VHDL design description consists of at least one entity/architecture pair. In VHDL, an entity declaration describes the circuit as it appears from the "outside", from the perspective of its input and output interfaces. If you are familiar with schematics, you might think of the entity declaration as being analogous to a block symbol on a schematic.

The second part of a minimal VHDL design description is the architecture declaration. Every entity in a VHDL design description must be bound with a corresponding architecture. The architecture describes the actual function of the entity to which it is bound. Using the schematic as a metaphor, you can think of the architecture as being roughly analogous to a lower-level schematic pointed to by the higher-level functional block symbol.

Entity Declaration

An entity declaration in VHDL provides the complete interface for a circuit. Using the information provided in an entity declaration (the port names and the data type and direction of each port), you have all the information you need to connect that portion of a circuit into other, higher-level circuits, or to design input stimulus for testing purposes. The actual operation of the circuit, however, is not included in the entity declaration.

Let's take a closer look at the entity declaration for this simple design description:

entity compare is
    port( A, B: in bit_vector(0 to 7);
             EQ: out bit);
end compare;
The entity declaration includes a name, compare, and a port statement defining all the inputs and outputs of the entity. The port list includes definitions of three ports: A, B, and EQ. Each of these three ports is given a direction (either in, out or inout), and a type (in this case either bit_vector(0 to 7), which specifies an 8-bit array, or bit, which represents a single-bit value).

There are many differents data types available in VHDL; we'll be covering these a bit later in this section. To simplify things in this introductory circuit, we're going to stick with the simplest data types in VHDL, which are bit and bit_vector.

Architecture Declaration And Body

The second part of a minimal VHDL source file is the architecture declaration. Every entity declaration you write must be accompanied by at least one corresponding architecture (we'll discuss why you might have more than one architecture in a moment).

Here's the architecture declaration for the comparator circuit:

architecture compare1 of compare is
begin

    EQ <= '1' when (A = B) else '0';

end compare1;
The architecture declaration begins with a unique name, compare1, followed by the name of the entity to which the architecture is bound, in this case compare. Within the architecture declaration (between the begin and end keywords) is found the actual functional description of our comparator. There are many ways to describe combinational logic functions in VHDL; the method used in this simple design description is a type of concurrent statement known as a conditional assignment. This assignment specifies that the value of the output (EQ) will be assigned a value of '1' when A and B are equal, and a value of '0' when they differ.

This single concurrent assignment demonstrates the simplest form of a VHDL architecture. As you'll see, there are many different types of concurrent statements available in VHDL, allowing you to describe very complex architectures. Hierarchy and subprogram features of the language allow you to include lower-level components, subroutines and functions in your architectures, and a powerful statement known as a process allows you to describe complex sequential logic as well.

Data Types

Like a high-level software programming language, VHDL allows data to be represented in terms of high-level data types. These data types can represent individual wires in a circuit, or can represent collections of wires using a concept called an array.

The preceding description of the comparator circuit used the data types bit and bit_vector for its inputs and outputs. The bit data type (bit_vector is simply an array of bits) values of '1' and '0' are the only possible values for the bit data type. Every data type in VHDL has a defined set of values, and a defined set of valid operations. Type checking is strict, so it is not possible, for example, to directly assign the value of an integer data type to a bit_vector data type. (There are ways to get around this restriction, using what are called type conversion functions.) VHDL is rich language with many different data types. The most common data types are listed below:

Design Units

One concept unique to VHDL (when compared to software programming languages and to its main rival, Verilog) is the concept of a design unit. Design units in VHDL (which may also be refered to as library units) are segments of VHDL code that can be compiled seperately and stored in a library. You have been introduced to two design units already: the entity and the architecture. There are actually five types of design units in VHDL; entities, architectures, packages, package bodies, and configurations.

Entities

A VHDL entity is a statement (indicated by the entity keyword) that defines the external specification of a circuit or sub-circuit. The minimum VHDL design description must include at least one entity, and one corresponding architecture.

When you write an entity declaration, you must provide a unique name for that entity, and provide a port list defining the input and output ports of the circuit. Each port in the port list must be given a name, direction (or mode, in VHDL jargon) and a type. Optionally, you may also include a special type of parameter list (called a generic list) that allows you to pass additional information into an entity. (Generics are discussed in Chapter NN, Design Partitioning).

Architectures

A VHDL architecture declaration is a statement (beginning with the architecture keyword) that describes the underlying function and/or structure of a circuit. Each architecture in your design must be associated (or bound) by name with one entity in the design.

VHDL allows you to create more than one alternate architecture for each entity. This feature is particularly useful for simulation, and for project team environments in which the design of the system interfaces (expressed as entities) is done by a difference engineer than the lower- level architectural description of each component circuit.

An architecture declaration consists of zero or more declarations (of items such as intermediate signals, components that will be referenced in the architecture, local functions and procedures, and constants) followed by a begin statement and a series of concurrent statements.

Packages and Package Bodies

A VHDL package declaration is identified by the package keyword, and is used to collect commonly-used declarations for use globally among different design units. You can think of a package as being a common storage area, one used to store such things as type declarations, constants, and global subprograms. Items defined within a package can be made visible to any other design unit in the complete VHDL design, and can be compiled into libraries for later re-use.

A package can consist of two basic parts: a package declaration and an optional package body. Package declarations can contain the following types of statements:

Items appearing within a package declaration can be made visible to other design units through the use of a use statement, as we will see.

If the package contains declarations of subprograms (functions or procedures) or defines one or more defered constants (constants whose value is not given), then a package body is required in addition to the package declaration. A package body (which is specified using the package body keyword combination) must have the same name as its corresponding package declaration, but can be located anywhere in the design (it does not have to be located immediately after the package declaration).

The relationship between a package and package body is somewhat akin to the relationship between an entity and its corresponding architecture (there may be only one package body written for each package declaration, however). While the package declaration provides the information needed to use the items defined within it (the parameter list for a global procedure, or the name of a defined type or subtype), the actual behavior of such things as procedures and functions must be specified within package bodies.

Configurations

The final type of design unit available in VHDL is called a configuration declaration. You can think of a configuration declaration as being roughly analogous to a parts list for your design. A configuration declaration (identified with the configuration keyword) specifies which architectures are to be bound to which entities, and allows you to change how components are connected in your design description at the time of simulation or synthesis.

Configuration declarations are always optional, no matter how complex a design description you create. In the absence of a configuration declaration, the VHDL standard specifies a set of rules that provide you with a default configuration; for example, in the case where you have provided more than one architecture for an entity, the last architecture compiled will take precedence, and will be bound to the entity.

Structure Of A Typical Small Design

[Figure missing]

For your first design efforts using VHDL, you will probably create design descriptions of similar complexity. To start with, you will create a single VHDL source file (using the text editor of your choice) and will write a top-level entity declaration defining the I/O of your circuit. You will then write an architecture corresponding to that entity and describing the internal function or structure of the circuit. If your circuit is a little more complex, you may write additional entity/architecture pairs, and connect these lower-level design units into your top-level architecture using VHDLs component instantiation features. Finally, you may choose to write one or more packages containing commonly-used items such as subprograms, constants or type declarations.

In addition to the information that you supply in your primary source file(s), you will probably also make use of standard libraries (specifically the built-in library defined in IEEE standard 1076 and the IEEE 1164 standard library). These libraries will have been provided to you by your simulation and/or synthesis tool vendors, and will be referenced from within your VHDL source file as needed.

More Complex Design

[Figure missing]

When creating a more complex design description, perhaps one intended for use in a team environment, you may choose to make use of VHDL's many features for design management, and will take a more structure approach to your design.

In such a design description, the project will be split into multiple VHDL source files. Commonly-used packages will be placed into one or more user-defined libraries, and a configuration declaration may be used to tie the whole project together.

VHDL designs such as this are most prevalent in top-down design environments, in which a system designer or project leader defines the basic interfaces of the system (perhaps in parallel with defining the functional specificaion of the circuit, in the form of one or more test benches) and delegates lower-level parts of the circuit to other team members.

The use of project- or company-wide libraries of functions or lower-level components can dramatically improve design re-use, which can in turn lead to increased design productivity.

Levels of Abstraction (Styles)

VHDL supports many possible styles of design description. These styles differ primarily in how closely they relate to the underlying hardware. When we speak of the different styles of VHDL, then, we are really talking about the differing levels of abstraction possible using the language. To give an example, it is possible to describe a counter circuit in a number of ways. At the lowest level of abstraction (the structural level), you could use VHDL's hierarchy features to connect a sequence of predefined logic gates and flip-flips to form a counter circuit.

The highest level of abstraction supported in VHDL is called the behavioral level of abstraction. When creating a behavioral description of a circuit, you will describe your circuit in terms of its operation over time. The concept of time is the critical distinction between behavioral descriptions of circuits and lower-level descriptions (specifically descriptions created at the dataflow level of abstraction).

In a behavioral description, the concept of time may be expressed precisely, with actual delays between related events (such as the propogation delays within gates and on wires), or may simply be an ordering of operations that are expressed sequentially (such as in a functional description of a flip-flop). When you are writing VHDL for input to synthesis tools, you may use behavioral statements in VHDL to imply that there are registers in your circuit. It is unlikely, however, that your synthesis tool will be capable of creating precisely the same behavior in actual circuitry as you have defined in the language. (Synthesis tools today ignore detailed timing specifications, leaving the actual timing results at the mercy of the target device technology.)

If you are familiar with event-driven software programming languages (such as Visual Basic) then writing behavior level VHDL will not seem like anything new. Just like a programming language, you will be writing one or more small programs that operate sequentially and communicate with one another through their interfaces. The only difference between behavior-level VHDL and a software programming language such as Visual Basic is the underlying execution platform: in the case of Visual Basic, it is the Windows operating system; in the case of VHDL, it is a simulator.

An alternate design method, in which a circuit design problem is segmented into registers and combinational input logic, is what is often called the dataflow level of abstraction. Dataflow is an intermediate level of abstraction that allows the drudgery of combinational logic to be hidden (and, presumably, taken care of by logic synthesis tools) while the more important parts of the circuit, the registers, are more completely specified.

There are some drawbacks to using a purely dataflow method of design in VHDL. First, there are no built-in registers in VHDL; the language was designed to be general-purpose, and the emphasis was placed by VHDL's designers on its behavioral aspects. If you are going to write VHDL at the dataflow level of abstraction, then you must first create (or obtain) behavioral descriptions of the register elements that you will be using in your design. These elements must be provided in the form of components (using VHDL's hierarchy features) or in the form of subprograms (functions or procedures).

But for hardware designers, for whom it can be difficult to relate the sequential descriptions and operation of behavioral VHDL with the hardware that is being described (or modeled), using the dataflow level of abstraction can make quite a lot of sense. Using dataflow, it can be easier to relate a design description to actual hardware devices (such as logic gates and flip-flops).

Structural VHDL

The dataflow and behavior levels of abstraction are used to describe circuits in terms of their logical function. There is a third style of VHDL that is used to combine such descriptions together into a larger, hierarchical circuit description.

Structural VHDL allows you to encapsulate one part of a design description (such as a simple logic gate of flip-flop, or a much larger subcircuit) as a re-usable component. Structural VHDL can be thought of as being analogous to a textual schematic (or netlist), or as a textual block diagram for higher-level design.

Sample Circuit

To help demonstrate some of the important concepts that we have touched on, we are going to look at a very simple circuit and show how the function of this circuit can be described in VHDL. The design descriptions that we will see have been written for synthesis, and therefore do not include timing specifications or other information not directly applicable to today's synthesis tools.

The circuit we'll be describing in VHDL combines the comparator circuit presented earlier with a simple 8-bit loadable shift register. The shift register will allow us to examine in detail how behavior-level VHDL can be written for synthesis.

The two sub-circuits (the shifter and comparator) will be connected using VHDL's hierarchy features to demonstrate the third level of abstraction: structure. The complete circuit is shown below:

This circuit has been intentionally drawn to look like a hierarchical schematic, with each of the lower-level circuits represented as blocks. In fact, many of the concepts of VHDL are concepts familiar to users of schematic hierarchy. These concepts include the ideas of component instantiation, mapping of ports, and design partitioning.

In a more structured project environment, you would probably enter a circuit such as by first defining the interface requirements of each block, then describe the overall design of the circuit as a collection of blocks connected together through hierarchy at the top-level. Later, after the system interfaces have been designed, you would proceed down the hierarchy (using a top-down approach to design) and fill in the details of each subcircuit.

In this example, however, we're going to begin with each of the lower-level blocks, then connect them to form the complete circuit.

Comparator (Dataflow)

The comparator portion of the design will be identical to the simple 8-bit comparator that we have already seen. The only difference is that we are going to be using the IEEE 1164 standard logic data types (std_ulogic and std_ulogic_vector), rather than the bit and bit_vector data types used previously. Using standard logic data types for all system interfaces is highly recommended, as it allows circuit elements from different sources to be easily combined, and provides you with the opportunity to perform more detailed and precise simulation that would otherwise be possible.

The updated comparator design, using the IEEE 1164 standard logic date types, is shown below:

---------------------------------
-- Eight-bit comparator
--
library ieee;
use ieee.std_logic_1164.all;
entity compare is
    port (A, B: in std_ulogic_vector(0 to 7);
              EQ: out std_ulogic);
end compare;

architecture compare1 of compare is
begin
    EQ <= '1' when (A = B) else '0';
end compare1;
Let's take a closer look at this simple VHDL design description. Reading from the top of the source file, we see

Barrel Shifter (Entity)

The second, and most complex, part of this design is the barrel shifter circuit. This circuit accepts 8-bit input data, loads this data into a register and, when the load input signal is low, rotates this data by one bit with each rising edge clock signal. The circuit is provided with an asynchronous reset, and the data stored in the register is accessible via the output signal Q.

They are many ways to describe a circuit such as this in VHDL. If you are going to be using synthesis tools to process the design description into an actual device technology, however, you must restrict yourself to well established synthesis conventions when entering the circuit. We'll examine two of these conventions when entering this design.

Using a Process

The first design description that we will look at for this shifter is a description that uses a VHDL process statement to describe the behavior of the entire circuit over time. This is the behavioral level of abstraction, and represents the highest level of abstraction practical (and synthesizable) for registered circuits such as this one. The VHDL source code for the barrel shifter is shown below:
-------------------------------------------------------
-- Eight-bit  barrel shifter
--
library ieee;
use ieee.std_logic_1164.all;
entity rotate is
    port( Clk, Rst, Load: in std_ulogic;
              Data: in std_ulogic_vector(0 to 7);
              Q: out std_ulogic_vector(0 to 7));
end rotate;

architecture rotate1 of rotate is
begin
    reg: process(Rst,Clk)
        variable Qreg: std_ulogic_vector(0 to 7);
    begin
        if Rst = '1' then   -- Async reset
            Qreg := "00000000";
        elsif (Clk = '1' and Clk'event) then
            if (Load = '1') then
                Qreg := Data;
            else
                Qreg := Qreg(1 to 7) & Qreg(0);
            end if;
        end if;
        Q <= Qreg;
    end process;
end rotate1;
Let's look closely at this source file. Reading from the top, we see:

Process Statement

The process statement in VHDL is the primary means by which sequential operations (such as registered circuits) can be described. For use in describing register circuits, the most common form of a process statement is:
architecture  arch_name  of  ent_name  is
begin
    process_name: process(sensitivity_list)
        local_declaration;
        local_declaration;
        . . .
    begin
        sequential statement;
        sequential statement;
        sequential statement;
        .
        .
        .
    end process;
end arch_name;
A process statement consists of the following items: The easiest way to think of a VHDL process such as this is to relate it to event-driven software, as a program that executes (in simulation) any time there is an event on one of its inputs (as specified in the sensitity list). A process describes the sequential execution of statements that are dependent on one ore more events having occurred. A flip-flop is a perfect example of such a situation: it remains idle, not changing state, until there is a significant event (either a rising edge on the clock input or an asynchronous reset event) that causes it to operate and potentially change its state.

Although there is a definite order of operations within a process (from top to bottom), you can think of a process as executing in zero time. This means that a process can be used to describe circuits functionally, without regard to their actual timing, and that multiple processes can be "executed" in parallel with little or no concern for which processes complete their operations first.

Let's see how the process for our barrel shifter operates. For your reference, the process is shown below:

reg: process(Rst,Clk)
        variable Qreg: std_ulogic_vector(0 to 7);
    begin
        if Rst = '1' then   -- Async reset
            Qreg := "00000000";
        elsif (Clk = '1' and Clk'event) then
            if (Load = '1') then
                Qreg := Data;
            else
                Qreg := Qreg(1 to 7) & Qreg(0);
            end if;
        end if;
        Q <= Qreg;
    end process;
As written, the process is dependent on (or sensitive to) the asynchronous inputs Clk and Rst. These are the only signals that can have events directly affecting the operation of the circuit; in the absence of any event on either of these signals, the circuit described by the process will simply hold is current value (the process will remain suspended).

Now let's examine what happens when an event occurs on either one of these asynchronous inputs. First, consider what happens when the input Rst has an event in which it transitions to a high state (represented by the std_ulogic value of '1'). In this case, the process will begin execution, and the first if statement will be evaluated. Because the event was a transition to '1', the simulator will see that the specified condition (Rst = '1') is true and the assignment of variable Qreg to the reset value of "00000000" will be performed. The remaining statements of the if- then-elsif expression (those that are dependent on the elsif condition) will be ignored. The final statement in the process, the assignment of output signal Q to the value of Qreg, is not subject to the if-then-elsif expression, is therefore placed on the process' queue for execution (as I'll describe in a later chapter, signal assignments do not occur until the process actually suspends). Finally, the process suspends, all signals that were assigned values in the process (in this case Q) are updated, and the process waits for another event on Clk or Rst.

What about the case in which there was an event on Clk? In this case, the process will again execute, and the if-then-elsif expressions will be evaluated in turn until a valid condition is encountered. If the Rst input continues to have a high value (a value of '1'), then the simulator will evaluation the first if test as true, and the reset condition will take priority. If, however, the Rst input is not a value of '1', then the next expression (Clk = '1' and Clk'event) will be evaluated. This expression is the most commonly-used convention for detecting clock edges in VHDL. To detect a rising edge clock, we write the expression Clk = '1' in the conditional expression, just as we did when detecting a reset condition. For this circuit, however, the expression Clk = '1' would not be specific enough, since the process may have begun execution as the result of an event on Rst that did not result in Rst transitioning to a '1'. To ensure that the event we are responding to is in fact an event on Clk, we use the built-in VHDL attribute 'event to check if Clk was that signal triggering the process execution.

If the event that triggered the process execution was in fact a rising edge on Clk, then the simulator will go on to check the remaining if-then logic to determine which assignment statement is to be executed. If Load is determined to be '1', then the first assignment statement is executed and the data is loaded from inout D to the registers; if Load is not '1', then the data in the registers is shifted, as specified using the bit slice and concatenation operations available in the language.

Confusing? Perhaps; but if you simply use the style just presented as a template for describing registered logic, and don't worry too much about the details of how it gets executed during simulation, you won't have much trouble. Just keep in mind that every assignment to a variable or signal that you make dependent on a Clk = '1' and Clk'event expression will result in at least one register when synthesized.

Process Statements Without Sensitivity Lists

VHDL process have two primary forms. The first form uses the sensitivity list described in the previous section, and execute during simulation whenever there is an event on any signal in the sensitivity list. These first form of a process statement is the most common (and recommended) method for describing sequential logic for the purposes of synthesis.

There is another form of process statement that is useful for other applications, however. This form of a process statement does not include a sensitivity list, and instead includes one or more statements that suspend the execution of the process until some condition has been met. The best example of such as process is a test bench, in which a sequence of test inputs are to be applied over time, with a predefined time value (or external triggering event) being defined as the condition for re-activation of process. The general form of such as process is:

architecture  arch_name  of  ent_name  is
begin
    process_name: process
        local_declaration;
        local_declaration;
        . . .
    begin
        sequential statement;
        sequential statement;
        wait until (condition);
        sequential statement;
        . . .
        wait for (time);
        . . .
    end process;
end arch;
Examples of this form of process will be examined later in this chapter.

VHDL requires that all processes have either a sensitivity list, or include one or more wait statements to suspend the process. (It is not legal to have both a sensitivity list and a wait statement.)

Concurrent and Sequential VHDL

The fundamental difference between concurrent and sequential statements in VHDL is one of the most important concepts that you need to make effective use of the language. The following diagram illustrates the basic difference between these two types of statements:

The left-most diagram illustrates how concurrent statements are executed in VHDL. Concurrent statements are those statements that appear between the begin and end statements of a VHDL architecture. This area of your VHDL architecture is what is known as the concurrent area. In VHDL, all statements in the concurrent area are executed at the same time, and there is no signifigance to the order in which the statements are entered.

Using a Procedure

As we have seen from the first version of the barrel shifter, describing registered logic using processes requires that you follow some established conventions (if you intend to synthesize the design) and to consider the behavior of the entire circuit. In the barrel shifter design description previously shown, the registers were implied by the placement and use of statements such as if Clk = '1' and Clk'event: assignment statements subject to that clause resulted in D flip-flops being implied for the signals.

For smaller circuits, this mixing of combinational logic functions and registers is fine, and not difficult to understand. For larger circuits, however, the complexity of the system being described can make such descriptions hard to manage, and the results of synthesis can often be confusing. For these circuits, it often makes more sense to retreat to a dataflow level of abstraction, with the boundaries between registered and combinational logic clearly defined.

One easy way to do this is to remove the process from your design and replace it with a series of concurrent statements representing the combinational and registered portions of the circuit. The following VHDL design description uses this method to describe the same barrel shifter circuit previously described:

architecture rotate2 of rotate is
    signal D,Qreg: std_logic_vector(0 to 7);
begin

    D <= Data when (Load = '1') else
                  Qreg(1 to 7) & Qreg(0);

    dff(Rst, Clk, D, Qreg);

    Q <= Qreg;

end rotate3;
In this version of the design description, the behavior of the D flip-flop has been placed in an external procedure, dff(), and intermediate signals have been introduced to more clearly describe the separation between the combinational and registered parts of the circuit. The following diagram helps to illustrate this separation:

D Flip-Flip Procedure

Here's an example of a procedure being used to describe the behavior of a D-type flip-flop:
procedure dff (signal Rst, Clk: in std_ulogic;
                        signal D: in std_ulogic_vector(0 to 7);
                        signal Q: out std_ulogic_vector(0 to 7)) is
begin
    if Rst = '1' then
        Q <= "00000000";
    elsif Clk = '1' and Clk'event then
        Q <= D;
    end if;
end dff;

Structural VHDL

Structure VHDL provides us with the equivalent of a netlist, in which various discrete components are connected together to create a larger system. Design hierarchy is created when components are collected together using structural VHDL.

The following example show how the compator and barrel shifter already presented could be connected using structural VHDL:

library ieee;
use ieee.std_logic_1164.all;

entity rotcomp is port(Clk, Rst, Load: in std_ulogic;
                       Init: in std_ulogic_vector(0 to 7);
                       Test: in std_ulogic_vector(0 to 7);
                       Limit: out std_ulogic);
end rotcomp;

architecture structure of rotcomp is

    component compare
        port(A, B: in std_ulogic_vector(0 to 7);
             EQ: out std_ulogic);
    end component;

    component rotate
        port(Clk, Rst, Load: in std_ulogic;
             Data: in std_ulogic_vector(0 to 7);
             Q: out std_ulogic_vector(0 to 7));
    end component;

    signal Q: std_ulogic_vector(0 to 7);

begin

    COMP1: compare port map (Q, Test, Limit);
    ROT1: rotate port map (Clk, Rst, Load, Init, Q);

end structure;

Test Benches

Test benches are extremely important to a complete design description. It is not enough to simply describe the behavior of your circuit from the inside-out. The only way to verify that a design description written in VHDL (or any other form of representation) operates as expected is to simulate it. Test benches provide the framework in which to perform such a simulation.

The easiest way to understand the concept of a test bench is to think of it as a virtual tester circuit. This circuit, which you will describe in VHDL, applies stimulus to your design description and (optionally) verifies that the simulated circuit does what it is intended to to.

The following figure graphically illustrates the relationship between the test bench and your design description, which is called the unit (or device) under test.

To apply stimulus to your design, your test bench will probably be written using one or more sequential processes, and will use a series of signal assignments and wait statements to describe the actual stimulus. You will probably use VHDL's looping features to simplify the description of repetitive stimulus (such as the system clock), and you may also use VHDL's file and record features to apply stimulus in the form of test vectors.

To check the results of simulation, you will probably make use of VHDL's assert feature, and you may also use the file features to write the simulation results to a disk file for later analysis.

For complex design descriptions, developing a comprehensive test bench can be a large-scale project in itself. In fact, it is not unusual for the test bench to be larger and more complex than the underlying synthesizable design. For this reason, you should plan your project so that you have the time required to develop the functional tests in addition to developing the circuit being tested. You should also plan to create test benches that are re-usable, perhaps by developing a master test bench that reads test data from a file.

Sample Test Bench

The following test bench demonstrates a very simple set of stimulus for the rotate and compare design description. Note that this test bench does not include any output value checking.
library ieee;
use ieee.std_logic_1164.all;

entity testbnch is
end testbnch;

architecture behavior of  testbnch is
    component rotcomp is
        port(Clk, Rst, Load: std_ulogic;
                Init: std_ulogic_vector(0 to 7);
                Test: std_ulogic_vector(0 to 7);
                Limit: out std_ulogic;
    end component;
    signal Clk, Rst, Load: std_ulogic;
    signal Init: std_ulogic_vector(0 to 7);
    signal Test: std_ulogic_vector(0 to 7);
    signal Limit: std_ulogic;
begin
    begin
    DUT: rotcomp port map
        (Clk, Rst, Load, Init, Test, Limit);

    clock: process
        variable clktmp: std_ulogic := '0';
    begin
        clktmp := not clktmp;
        Clk <= clktmp;
        wait for 10 ns;
    end process;

    stimulus: process
    begin
        Rst <= '0';
        Load <= '1';
        Init <= "00001111";
        Test <= "11110000";
        wait for 20 ns;
        Load <= '0';
        wait for 120 ns;
    end process;

Reading Test Vectors

While the previous test bench is adequate for a simple circuit such as our rotate and compare circuit, a more typical design requires more complex test stimulus, and benefits from output checking of simulated values. One common approach to comprehensive stimulus is to use data files containing tabular stimulus and expected response values. These tables of values, which are called test vectors, can be used repeatedly to verify that a large circuit continues to operated as expected after modifications have been made, and to compare pre- and post-synthesis results.

What We’ve Learned So Far

In this introduction to VHDL, we have covered the basic features of the language, and have seen many concepts (such as concurrent and sequential statements, hierarchy and test benches) that are common to all HDLs, including VHDL and Verilog. We have seen some very simple examples of VHDL design descriptions, and we have seen that there are more than one way to describe a given circuit. We have covered combinational and registered circuits, and have seen how structural VHDL can be used to connect smaller circuits together into a larger circuit.

To more fully understand VHDL, we will need to cover more complex circuits and see how a large design description can be simplified using VHDL's advanced language features. These advanced discussions, and much more, are provided in VHDL Made Easy!, available from Accolade Design Automation. Click here for more details. 



Copyright 1997, Accolade Design Automation, Inc., All rights reserved.

For more information about this Web Page contact webmaster@acc-eda.com.