0. General Information/Introduction 0.1 The Group - Why and What The newsgroup comp.lang.vhdl was created in January 1991. It's an international forum to discuss ALL topics related to the language VHDL which is currently defined by the IEEE Standard 1076/2002. Included are language problems, tools that only support subsets etc. but NOT other languages such as Verilog HDL. This is not strict - if there is the need to discuss information exchange from EDIF to VHDL for example, this is a topic of the group. The group is unmoderated. Please think carefully before posting - it costs a lot of money! (Take a look into your LRM for example or try to search Google Groups - if you still cannot find the answer, post your question, but make sure, that other readers will get the point). 0.2 What Is VHDL VHDL-1076 (VHSIC (Very High Speed Integrated Circuits) Hardware Description Language) is an IEEE Standard since 1987. It is "a formal notation intended for use in all phases of the creation of electronic systems. ... it supports the development, verification, synthesis, and testing of hardware designs, the communication of hardware design data ..." [Preface to the IEEE Standard VHDL Language Reference Manual] and especially simulation of hardware descriptions. Additionally, VHDL-models are a DoD requirement for vendors. Today many simulation systems and other tools (synthesis, verification and others) based on VHDL are available. The VHDL users community is growing fast. Several international conferences organized by the VHDL Users Groups(s) have been held with relevant interest. Other international conferences address the topic as well. Chapter 1 - An Introduction and Background VHDL is an acronym which stands for VHSIC Hardware Description Language. VHSIC is yet another achronym which stands for Very High Speed Integrated Circuits. If you can remember that, then you're off to a good start. The language has been known to be somewhat complicated, as its title (as titles go). The acronym does have a purpose, though; it is supposed to capture the entire theme of the language, that is to describe hardware much the same way we use schematics. VHDL can wear many hats. It is being used for documentation, verification, and synthesis of large digital designs. This is actually one of the key features of VHDL, since the same VHDL code can theoretically achieve all three of these goals, thus saving a lot of effort. In addition to being used for each of these purposes, VHDL can be used to take three different approaches to describing hardware. These three different approaches are the structural, data flow, and behavioral methods of hardware description. Most of the time a mixture of the three methods are employed. The following sections introduce you to the language by examining its use for each of these three methodologies. There are also certain guidelines that form an approach to using VHDL for synthesis, which is not addressed by this tutorial. VHDL is a standard (VHDL-1076) developed by IEEE (Institute of Electrical and Electronics Engineers). The language has been through a few revisions, and you will come across this in the VHDL community. Currently, the most widely used version is the 1987 (std 1076-1987) version, sometimes referred to as VHDL'87, but also just VHDL. However, there is a newer revision of the language referred to as VHDL'93. VHDL'93 (adopted in 1994 of course) is fairly new and is still in the process of replacing VHDL'87. Chapter 2 - Structural Descriptions This chapter discusses the first of the three approaches to design with VHDL, the structural description. Section 1 - Building Blocks To make designs more understandable and maintainable, a design is typically decomposed into several blocks. These blocks are then connected together to form a complete design. Using the schematic capture approach to design, this might be done with a block diagram editor. Every portion of a VHDL design is considered a block. A VHDL design may be completely described in a single block, or it may be decomposed in several blocks. Each block in VHDL is analogous to an off-the-shelf part and is called an entity. The entity describes the interface to that block and a separate part associated with the entity describes how that block operates. The interface description is like a pin description in a data book, specifying the inputs and outputs to the block. The description of the operation of the part is like a schematic for the block. For the remainder of the tutorial we will refer to a block as a design, even though a complete design may be a collection of many blocks interconnected. The following is an example of an entity declaration in VHDL. entity latch is port (s,r: in bit; q,nq: out bit); end latch; The first line indicates a definition of a new entity, whose name is latch. The last line marks the end of the definition. The lines in between, called the port clause, describe the interface to the design. The port clause contains a list of interface declarations. Each interface declaration defines one or more signals that are inputs or outputs to the design. Each interface declaration contains a list of names, a mode, and a type. In the first interface declaration of the example, two input signals are defined, s and r. The list to the left of the colon contains the names of the signals, and to the right of the colon is the mode and type of the signals. The mode specifies whether this is an input (in), output (out), or both (inout). The type specifies what kind of values the signal can have. The signals s and rare of mode in (inputs) and type bit. Next the signals q and nq are defined to be of the mode out (outputs) and of the type bit (binary). Notice the particular use of the semicolon in the port clause. Each interface declaration is followed by a semicolon, except the last one, and the entire port clause has a semicolon at the end. All of the signals in the example are defined to be of the type bit. The type bit is a predefined type that can have two values represented by '0' and '1'. This type is used to represent two level logic signals. The second part of the description of the latch design is a description of how the design operates. This is defined by the architecture declaration. The following is an example of an architecture declaration for the latch entity. architecture dataflow of latch is signal q0 : bit := '0'; signal nq0 : bit := '1'; begin q0<=r nor nq0; nq0<=s nor q0; nq<=nq0; q<=q0; end dataflow; The first line of the declaration indicates that this is the definition of a new architecture called dataflow and it belongs to the entity named latch. So this architecture describes the operation of the latch entity. The lines in between the begin and end describe the latch's operation. This example uses the data flow approach which is discussed later, so we won't discuss the meaning of these lines here. The next section explains how to specify the latch's operation using the structural approach. Section 2 - Connecting Blocks Once we have defined the basic building blocks of our design using entities and their associated architectures, we can combine them together to form other designs. This section describes how to combine these blocks together in a structural description. Let's specify the operation of the latch entity used in the previous section by connecting some previously defined entities. The entity declaration for the latch was: entity latch is port (s,r: in bit; q,nq: out bit); end latch; We will declare an architecture different from the one in the last section that demonstrates the structural approach. To do so, we assume that an entity named nor_gate has been defined that will be used in the design. The schematic for the latch might be We can specify the same connections that occur in the schematic using VHDL with the following architecture declaration: architecture structure of latch is component nor_gate port (a,b: in bit; c: out bit); end component; begin n1: nor_gate port map (r,nq,q); n2: nor_gate port map (s,q,nq); end structure; The lines between the first and the keyword begin are a component declaration. It describes the interface of the entity nor_gate that we would like to use as a component in (or part of) this design. Between the begin andend keywords, the first two lines and second two lines define two component instances. There is an important distinction between an entity, a component, and a component instance in VHDL. The entity describes a design interface, the component describes the interface of an entity that will be used as an instance (or a sub-block), and the component instance is a distinct copy of the component that has been connected to other parts and signals. To compare these with the process of bread board design with off-the-self parts. The entity and architecture is like the data book describing the interface and schematics of how the part works. The component is like the short pin listing that comes with the part to describe how it should be connected. The component instance is the actual part itself, of which you may have many that each operate independently. In this example the component nor_gate has two inputs (a and b) and an output (c). There are two instances of the nor_gate component in this architecture corresponding to the two nor symbols in the schematic. The first instance represents the top nor gate in the schematic. The first line of the component instantiation statement gives this instance a name, n1, and specifies that it is an instance of the component nor_gate. The second line describes how the component is connected to the reset of the design using the port map clause. The port map clause specifies what signals of the design to connect to the interface of the component in the same order as they are listed in the component declaration. The interface is specified in order as a,b, and then c, so this instance connects r to a, nq to b, and q to c. This corresponds to the way the top gate in the schematic is connected. The second instance, named n2, connects s to a, q to b, and nq to c of a different instance of the same nor_gate component in the same manner as shown in the schematic. The structural description of a design is simply a textual description of a schematic. A list of components and there connections in any language is sometimes called a netlist. The structural description of a design in VHDL is one of many means of specifying netlists. Chapter 3 - Data Flow Descriptions The data flow description is the second of the three paradigms for describing hardware with VHDL. The following sections discuss this approach to VHDL design. Section 1 - A First Example In the data flow approach, circuits are described by indicating how the inputs and outputs of built-in primitive components (ex. an and gate) are connected together. In other words we describe how signals (data) flow through the circuit. Let's look at the first example. Suppose we were to describe the following SR latch using VHDL as in the following schematic. We might build an entity like the one that follows. entity latch is port (s,r : in bit; q,nq : out bit); end latch; architecture dataflow of latch is begin q<=r nor nq; nq<=s nor q; end dataflow; note: If you are familiar with programming languages, notice that the <= symbol was chosen carefully to avoid confusion with the variable assignment operator (usually = or :=) of typical programming languages. The signal assignment operator in VHDL specifies a relationship between signals, not a transfer of data as in programming langauges. As we saw in the last section, the entity describes the interface to the design. There are four signals s,r,q, and nq that are accessible externally to the design. Again we model the signals in our design with the VHDL data type bit, which can represent two level logic values. The architecture part describes the internal operation of the design. In the data flow approach we indicated how data flows from the inputs to the outputs. In VHDL this is accomplished with the signal assignment statement. The example architecture consists of two signal assignment statements. A signal assignment statement describes how data flows from the signals on the right side of the <= operator to the signal on the left side. The first signal assignment in the example tells us that the data coming from signals rand nq flow through a nor gate to determine the value of the signal q. The nor represents a built-in component called an operator, because it operates on some data to produce new data. The second signal assignment, similar to the first, indicates that the signal nq is produced from data (s and q) flowing through (or processed by) the nor operator. The right side of the <= operator is called an expression. The value of the expression is determined by evaluating the expression. Evaluating the expression is performed by substituting the values of the signals in the expression and computing the result of each operator in the expression. Section 2 - How it Works In the last section we saw an example of a data flow description and what it describes. In this section we will learn how a simulator uses that description to model the design. The VHDL standard not only describes how designs are specified, but also how they should be interpreted. This is the purpose of having standards, so that we can all agree on the meaning of a design. It is important to understand how a VHDL simulator interprets a design because that dictates what the "correct" interpretation is according to the standard (Hopefully, simulators are not all 100% correct). The scheme used to model a VHDL design is called discrete event time simulation. When the value of a signal changes, we say an event has occurred on that signal. If data flows from signal A to signal B, and an event has occurred on signal A (i.e. A's value changes), then we need to determine the possibly new value of B. This is the foundation of the discrete event time simulation. The values of signals are only updated when certain events occur and events occur at discrete instances of time. Since one event causes another, simulation proceeds in rounds. The simulator maintains a list of events that need to be processed. In each round, all events in a list are processed, any new events that are produced are placed in a separate list (and are said to be scheduled) for processing in a later round. Each signal assignment is evaluated once, when simulation begins to determine the initial value of each signal. Lets examine how the event time simulation proceeds for the previous example of an SR latch. The following is a schematic version of the SR latch. The internal operation of the latch was essentially captured using the following two statements. q<=r nor nq; nq<=s nor q; Since data flows from r and nq to q, we say that q depends on r and nq. In general, given any signal assignment statement, the signal on the left side of the <= operator depends on all the signals appearing on the right side. If a signal depends on another signal that an event has occurred on, then the expression in the signal assignment is re-evaluated. If the result of the evaluation is different than the current value of the signal, an event will be scheduled (added to the list of events to be processed) to update the signal with the new value. Thus, if an event occurs on r or nq, then the nor operator is evaluated, and if the result is different than the current value of q, then an event will be scheduled to update q. Suppose at a particular moment during a simulation of the SR latch example, the values of the signals are s='0',r='0', q='1', and nq='0'. Now suppose the value of the signal r changes (due to some event external to the design) to the value '1'. Since q depends on r, we must re-evaluate the expression r nor nq, which now evaluates to '0'. Since the value of q must be changed to '0', a new event will be scheduled on the signal q. During the next round the event scheduled for q is processed and q's value is updated to be '0'. Also, since nq depends on q, the expression s nor q must be re-evaluated. The result of the expression is '1', so an event is scheduled to update the value of nq. During the next round, when the event on nq is processed, the expression for q will be evaluated again because it depends on nq. However, the result of the expression will be '0' and no new event will be scheduled because q is already '0'. Since, no new events were scheduled, there are no more events that will occur internally to the latch. Now, suppose an external event causes r to return to the value '0'. Since q depends on r, r nor nq is evaluated again. The result of this expression is '0' and q is already '0', so no events are scheduled. As you can see, this correctly models the SR latch as we would expect. When the signal r became active ('1') the output of the latch was reset, and when r became inactive ('0') the output remained unchanged. The simulation rounds described in these last two paragraphs can be summarized as follows. start : r='0',s='0',q='1',nq='0' round 1: r='1',s='0',q='1',nq='0', The value '0' is scheduled on q. round 2: r='1',s='0',q='0',nq='0', The value '1' is scheduled on nq. round 3: r='1',s='0',q='0',nq='1', No new events are scheduled. round 4: r='0',s='0',q='0',nq='1', No new events are scheduled. Section 3 - The Delay Model The example from the last section shows how a functional simulation proceeds. It is called a functional simulation because it models only how the design functions without timing considerations. This is in contrast to atiming simulation, which models the internal delays that are present in real circuits. This section explains how VHDL can be used to model time delays to obtain a timing simulation. We will discuss two models of delay that are used in VHDL. The first is called the inertial delay model. The inertial delay model is specified by adding an after clause to the signal assignment statement. For example, suppose that a change on the input of a nor gate would cause the output to change after a delay of 1ns. To model this delay in the SR latch example, we could replace the two signal assignments with the following two statements. q<=r nor nq after 1ns; nq<=s nor q after 1ns; Now during simulation, say signal r changes and will cause the signal q to change, rather than schedule the event on q to occur during the next round, it is scheduled to occur 1ns form the current time. Thus the simulator must maintain a current time value. When no more events exist to be processed at the current time value, time is updated to the time of the next earliest event and all events scheduled for that time will be processed. A timing diagram for this modified SR latch produced by a simulator might be: Notice the change did not occur in q until 1ns after the change in r. Likewise the change in nq did not occur until 1ns after the change in q. Thus, the "after 1ns" models an internal delay of the nor gate. However, this is not the end of the story for the inertial delay model. Typically, when a component has some internal delay and an input changes for a time less than this delay, then no change in the output will occur. This is also the case for the inertial delay model. The following timing diagram would be produced using the inertial delay model, if the '1' pulse on the signal r was shortened (to anything less than 1ns) from the previous example. The value of q never changed because the change in r did not last long enough. Said another way, the change in r did not gain enough inertia. Although most often the inertial delay is desired, sometimes all changes on the input should have an effect on the output. For example, a bus experiences a time delay, but will not "absorb" short pulses as with the inertial delay model. As a result, VHDL provides the transport delay model. The transport delay model just delays the change in the output by the time specified in the after clause. You can elect to use the transport delay model instead of the inertial delay model by adding the keyword transport to the signal assignment statement. The SR latch example could be modified to use the transport delay model by replacing the signal assignments with the following two statements. q<=transport r nor nq after 1ns; nq<=transport s nor q after 1ns; If the transport delay model were used, the result of the same simulation shown in the last diagram would result in the following timing diagram. Section 4 - Other Types In the previous sections all of the signals in the examples have been of the type bit. VHDL provides several other types, some of which are described here. Often times we use several bit signals together to represent a binary number in a design. VHDL provides a mechanism for defining new types which represent a collection of several data items of the same type. These kinds of types are called arrays. There is a predefined array type calledbit_vector which represents a collection of bits. The following example demonstrates how the bit_vector type can be used to define a 1-to-4-line demultiplexer. entity demux is port (e: in bit_vector (3 downto 0); -- enables for each output s: in bit_vector (1 downto 0); -- select signals d: out bit_vector (3 downto 0)); -- four output signals end demux; architecture rtl of demux is signal t : bit_vector(3 downto 0); -- an internal signal begin t(3)<=s(1) and s(0); t(2)<=s(1) and not s(0); t(1)<=not s(1) and s(0); t(0)<=not s(1) and not s(0); d<=e and t; end rtl; Comments can be added at the end of a VHDL statement or on a line by itself preceeded by the -- symbol. First notice how the bit_vector is used in the definition of a signal. The definition of s indicates that s is a bit_vector and the (1 downto 0) part specifies that the signal s contains two bits numbered 1 down to 0. Similarly, d and e are arrays of 4 bits numbered from 3 down to 0. Second, notice that signals, such as t, can be declared within an architecture that are not visible from outside this entity. These internal signals are created with signal declarations as the one in the example. They contain the key word signal followed by a list of names of the signals to create, followed by the type of the signals. Third, the architecture refers to the individual bits in t and s by number. The two bits in s are number 1 and 0, so they are referred to as s(1) and s(0). Finally, notice that the last signal assignment demonstrates that operations can be performed on a whole array of data at once. This statement is equivalent to the four following statements. d(3)<=e(3) and t(3); d(2)<=e(2) and t(2); d(1)<=e(1) and t(1); d(0)<=e(0) and t(0); Each data item in an array is called an element. The number of elements in a signal of an array type is indicated by the range that follows the type name. The elements are numbered according to the range, and each element is referred to as an individual by number. Operations can be performed on an array as a whole (applying to every element in the array), or they can be performed using individual elements of the array, independent of the others. When operations are performed on whole vectors, the vectors must have the same number of elements. If they do not, the simulator will report an error and stop the simulation. In an operation between vectors, elements are matched as they are number from left to right. Thus, if a variable v1 has elements 0 to 1 and variable v2 has elements 1 downto 0. Then v1:=v2; would assign v2(1) to v1(0) and v2(0) to v1(1). Another predefined type is time. This type is used to represent values of time. We have already used constant values of this type in the after clause. Time is an example of a physical type. All values of a physical type have two parts, a number and a unit name. The type time includes the following predefined unit names sec (seconds), ms (milliseconds), us (microseconds), ns (nanoseconds), ps (picoseconds), and fs (femtoseconds). There are several other types predefined in VHDL including types for integers and real numbers. These are mentioned later in the sections related to behavioral descriptions. There are also many capabilities for defining your own types, which is beyond this tutorial but are described in standard VHDL texts. Section 5 - Other Operators The previous sectioned mentioned a few different types that are available in VHDL. There are also several built-in operators that can be used with those types. This section mentions some of these. The logical operators NOT, AND, OR, NAND, NOR, and XOR can be used with any bit type or bit_vector. When used as operators on bits they have their usual meaning. When used with bit_vectors, the bit_vectors must have the same number of elements, and the operation is performed bitwise. For example, "00101001" xor "11100101" results in "11001100". note: just as '0' and '1' represent constant bit values, constant bit_vectors can be written in VHDL as a list of bit values in double quotes. For example, if d is a bit_vector(1 to 4) the following statement gives d the permanent values d(1)='1', d(2)='1', d(3)='0', and d(4)='0'. d<="1100"; Hexadecimal can also be used as a shortcut as in the following example. d<=X"C"; Since C is the hexadecimal number 12, which in binary is 1100, this statement is equivalent to the one preceeding it. The X in the front indicates that the number is in hexadecimal instead of the normal binary. The typical algebraic operators are available for integers, such as +,-,* (multilication), and / (division). Although these operations are not built-in for bit_vectors, they are often provided in libraries that come with your VHDL software. They are used with bit_vectors by interpreting them as a binary representation of integers, which may be added, subtracted, multiplied, or divided. Also predefined are the normal relational operators. They are =, /=, <, <=, > and >= and have their usual meanings (/= denotes the not equal operator). The result of all these operators is a boolean value (TRUE or FALSE). The arguments to the = and /= operators may be of any type. The arguments of the <, <=, > and >= operators may be any scalar type (integer, real, and physical types) or the bit_vector type. If the arguments are bit_vectors, then the arguments must be the same length and the result is TRUE only if the relation is true for each corresponding element of the array arguments. The & operator is a built-in VHDL operator that performs the concatenation of bit_vectors. For example, with the following declarations: signal a: bit_vector (1 to 4); signal b: bit_vector (1 to 8); The following statement would connect a to the right half of b and make the left half of b constant '0'. b<="0000" & a; The & appends the a to the end of the "0000" to form a result that contains 8 bits. Chapter 4 - Behavioral Descriptions There are three different paradigms for describing digital components with VHDL, structural, data flow, and behavioral descriptions. This chapter dicusses the behavioral approach. Section 1 - The Process Statement The behavioral approach to modeling hardware components is different from the other two methods in that it does not necessarily in any way reflect how the design is implemented. It is basically the black box approach to modeling. It accurately models what happens on the inputs and outputs of the black box, but what is inside the box (how it works) is irrelevant. The behavioral description is usually used in two ways in VHDL. First, it can be used to model complex components that would be tedious to model using the other methods. This might be the case for example, if you wish to simulate the operation of your custom design connected to a commercial part like a microprocessor. In this case, the microprocessor is complex and its internal operation is irrelevant (only the external behavior is important) so it would probably be modeled using the behavioral style. Second, the behavioral capabilities of VHDL can be more powerful and is more convenient for some designs. In this case the behavioral description will likely imply some structure of the implementation. Behavioral descriptions are supported with the process statement. The process statement can appear in the body of an architecture declaration just as the signal assignment statement does. The contents of the process statement can include sequential statements like those found in software programming languages. These statements are used to compute the outputs of the process from its inputs. Sequential statements are often more powerful, but sometimes have no direct correspondence to a hardware implementation. The process statement can also contain signal assignments in order to specify the outputs of the process. Our first example of the process statement is trivial and would not normally be done in a process statement. However, it allows us to examine the process statement without learning any sequential statements first. compute_xor: process (b,c) begin a<=b xor c; end process; The first part compute_xor: is used to name the process. This part is optional. Next is the keyword process that starts the definition of a process. Following that is a list of signals in parenthesis, called the sensitivity list. Since the process statement's contents may not have indicated any structural characteristics, there is no way to know when the process should be re-evaluated to update the outputs. The signal sensitivity list is used to specify which signals should cause the process to be re- evaluated. Whenever any event occurs on one of the signals in the sensitivity list, the process is re-evaluated. A process is evaluated by performing each statement that it contains. These statements (the body of the process) appear between the begin and end keywords. This example process contains one statement, the signal assignment. Unlike signal assignments that appear outside the process statement, this signal assignment is only evaluated when events occur on the signals in the process' sensitivity list, regardless of which signals appear on the right side of the <= operators. This means it is critical to make sure the proper signals are in the sensitivity list. The statements in the body of the process are performed (or executed) in order from first to last. When the last statement has been executed the process is finished and is said to be suspended. When an event occurs on a signal in the sensitivity list, the process is said to be resumed and the statements will be executed from top to bottom again. Each process is executed once during the beginning of a simulation to determine the initial values of its outputs. Section 2 - Using Variables There are two major kinds of objects used to hold data. The first kind, used mostly in structural and data flow descriptions, is the signal. The second, which can only be used in processes is called a variable. A variable behaves like you would expect in a software programming language, which is much different than the behavior of a signal. Although variables represent data like the signal, they do not have or cause events and are modified differently. Variables are modified with the variable assignment. For example, a:=b; assigns the value of b to a. The value is simply copied to a immediately. Since variables may only be used in processes, the assignment statement may only appear in a process. The assignment is performed when the process is executed, as explained in the last section. The following example shows how a variable is used in a process. count: process (x) variable cnt : integer := -1; begin cnt:=cnt+1; end process; Variable declarations appear before the begin keyword of a process statement as in the example. The variable declaration is the same as the signal declaration except the key word variable is used instead of signal. The declaration in this example includes an optional part, which specifies the initial value of the variable, when a simulation begins. The initialization part is included by adding the := and some constant expression after the type part of the declaration. This initialization part may also be included in signal declarations. The variable cnt is declared to be of the type integer. The integer type represents negative and positive integer values. The process in the example contains one statement, the assignment statement. This assignment computes the value of cnt plus one and immediately stores that new value in the variable cnt. Thus, cnt will be incremented by one each time this process is executed. Remember, from the last section, that a process is executed once at the beginning of simulation and then each time an event occurs on any signal in its sensitivity list. Since the value is initialized to -1, and the process is executed once before beginning simulation, the value of cnt will be 0 when simulation begins. Once simulation begins, cnt will be incremented by one each time the signal x changes, since xis in the sensitivity list. If x is a bit signal, then this process will count the number of rising and falling edges that occur on the signal x. Section 3 - Sequential Statements There are several statements that may only be used in the body of a process. These statements are called sequential statements because they are executed sequentially. That is, one after the other as they appear in the design from the top of the process body to the bottom. In this section we will examine some of these statements. The first example illustrates the if statement and a common use of the VHDL attribute. count: process (x) variable cnt : integer :=0 ; begin if (x='1' and x'last_value='0') then cnt:=cnt+1; end if; end process; This if statement has two main parts, the condition and the statement body. A condition is any boolean expression (an expression that evaluates to TRUE and FALSE, such as expressions using relational operators). The condition in the example uses the attribute last_value, which is used to determine the last value that a signal had. Attributes can be used to obtain a lot of auxiliary information about signals. The value of an attribute for a particular signal is obtained by specifying the name of the signal, followed by a ' (called a tick) and the name of the attribute desired. Thus the condition in the example is true only if the current value of x is '1' and its previous value was '0'. Since this statement will only be executed when an event has occurred on x (i.e. x has just changed), this condition will be true when a rising edge occurs on x. This is because we know x just changed, we know it was a '0' and now it is a '1'. The statement body of the if statement is just a list of sequential statements that appear between the key words then and end if. The execution of the if statement begins by evaluating the condition. If the condition evaluates to the value TRUE then the statements in the statement body will be executed. Otherwise, execution will continue after the end ifand the statement body of the if statement is skipped. Thus, the assignment statement in this example is executed every time there is a rising edge on the signal x, counting the number of rising edges. An example of another common form of the if statement is ... if (inc='1') then cnt:=cnt+1; else cnt:=cnt-1; end if; ... This form has two statement bodies. If the condition is TRUE, the first list of statements is executed (between the then and the else) and the second list of statements (between the else and the end if) is not. Otherwise, the second statement list is executed and the first is not. Thus, this example will increment cnt if inc is '1' and decrement it otherwise. The last statement we will look at is the loop statement. We will explain just one form of the loop statement, often called a for statement. The for statement is used to execute a list of statements several times. The following example uses a loop statement to compute the even parity of a bit vector. signal x : bit_vector (7 downto 0); ... process (x) variable p : bit; begin p:='0' for i in 7 downto 0 loop p:=p xor x(i); end loop; end process; The signal x is an 8 bit signal representing a byte. The variable p is used to compute the parity of this byte. The first part of the for loop i in 7 downto 0 is called the parameter specification. It specifies how many times the loop body will be executed and creates a temporary variable. It begins with the name of the temporary variable that will be created, in this case it is i. This is followed by the key word in and then a range of values as we have seen before. The body of the loop is executed once for every value in the range specified. The value of the temporary variable is assigned one of the values in the range each time the loop body is executed. In this example, the assignment will be executed first with i=7 then again with i=6, and again with i=5, and so on down to 0. This loop statement behaves the same as the following statements. p:='0'; p:=p xor x(7); p:=p xor x(6); p:=p xor x(5); p:=p xor x(4); p:=p xor x(3); p:=p xor x(2); p:=p xor x(1); p:=p xor x(0); Notice how the temporary variable i was used in the statement body of the loop to operate on different elements of the vector x each time the body of the loop is executed. This is a very common use of the loop statement. Although this loop contains only one statement, there may be many statements in the loop body. Section 3 - Signals and Processes This section is short, but contains important information about the use of signals in the process statement. The issue of concern is to avoid confusion about the difference between how a signal assignment and variable assignment behave in the process statement. Remember a signal assignment, if anything, merely schedules an event to occur on a signal and does not have an immediate effect. When a process is resumed, it executes from top to bottom and no events are processed until after the process is complete. This means, if an event is scheduled on a signal during the execution of a process, that event can be processed after the process has completed at the earliest. Let's examine an example of this behavior. In the following process two events are scheduled on signals x and z. ... signal x,y,z : bit; ... process (y) begin x<=y; z<=not x; end process; If the signal y changes then an event will be scheduled on x to make it the same as y. Also, an event is scheduled on z to make it the opposite of x. The question is, will the value of z be the opposite of y? Of course, the answer is no, because when the second statement is executed, the event on x has not been processed yet, and the event scheduled on z will be the opposite of the value of x before the process begins. This is pointed out because this is not necessarily the intuitive behavior and because variables operate differently. For example, in process (y) variable x,z : bit; begin x:=y; z:=not x; end process; The value of the variable z would be the opposite of the value of y because the value of the variable x is changed immediately. Section 4 - Program Output In most programming languages there is a mechanism for printing text on the monitor and getting input from the user through the keyboard. Even though your simulator will let you monitor the value of signals and variables in your design, it is also nice to be able to output certain information during simulation. It is not provided as a language feature in VHDL, but rather as a standard library that comes with every VHDL language system. In VHDL, common code can be put in a separate file to be used by many designs. This common code is called a library. In order to use the library that provides input and output capabilities you must add the statement use textio.all; immediately before every architecture that uses input and output. The name of the library is textio and this statement indicates that you wish to use everything or all of the textio library. Once you have done that, you may use any of the features discussed in this section. Note that although it is not part of the language, the library is standard and will be the same regardless of the VHDL tools you are using. Text is input and output using textio via a variable of the type line. Since variables are used for textio, input and output is done in processes. The procedure for outputting information is to first place it in text form into the variable of type line and then to request that the line be output. This is shown in the following example. use textio.all; architecture behavior of check is begin process (x) variable s : line; variable cnt : integer:=0; begin if (x='1' and x'last_value='0') then cnt:=cnt+1; if (cnt>MAX_COUNT) then write(s,"Counter overflow - "); write(s,cnt); writeline(output,s); end if; end if; end process; end behavior; The write function is used to append text information at the end of a line variable which is empty when the simulator is initialized. The function takes two arguments, the first is the name of the line to append to, and the second is the information to be appended. In the example, s is set to "Counter overflow - ", and then the current value of cnt is converted to text and added to the end of that. The writeline function outputs the current value of a line to the monitor, and empties the line for re-use. The first argument of the writeline function just indicates that the text should be output to the screen. If MAX_COUNT were a constant equal to 15 and more than 15 rising edges occur on the signal x, then the message Counter overflow - 16 would be printed on the screen. The write statement can also be used to append constant values and the value of variables and signals of the types bit, bit_vector, time, integer, and real. Keyboard input is more complex than output, and is not discussed in this tutorial.