This chapter provides an introduction to the basic language constructs in VHDL; defining logic blocks, structural, dataflow and behavioral descriptions, concurrent and sequential functionality, design partitioning and more.
The following hypertext list is a table of contents for this document. The user can navigate to any section of this document by "Pointing and Clicking" on any of the blue highlighted text. This will automatically direct the user to that particular subsection.
All entities that can be simulated have an architectural description. The architecture describes the behavior of the entity. A single entity can have multiple architectures. One architecture might be behavioral, while another could be a structural description of the design. Jump to Examples of Architecture.
An attribute is data that is attached to VHDL objects or predefined data about VHDL objects. Examples are the current drive capability of a buffer, or the maximum operating temperature of a device. Jump to section on Attributes.
A bus is a group or signals or particular method of communication used in hardware design. In VHDL a bus is a special kind of signal that my have its drivers turned off or tri-stated. Go to VHDL Document #2, see section on Busses.
A definition of the interface to a sub-module, rather like a "socket", to which an entity may later be bound.
Statements within an architecture which execute concurrently in simulated time, independently of their order.
Defines the "binding" of each component instance to an entity, and each entity to an architecture. Can be defined using a configuration library unit or from within an architecture.
A text file containing source code for one or more design units.
A data structure containing analyzed design units (library units).
A VHDL module contained in a design file, consisting of the source code for a library unit preceded by any required Library or Use clauses. Analysis of a design unit defines the corresponding library unit in a design library.
This is a source of a signal. If a signal is driven by two tri-state buffers, if both buffers are active the signal has two drivers.Go to VHDL Document #2, see sections of Buffers.
All designs are expressed in terms of entities. An entity is the basic building block in a design. The uppermost level of the design is the top level entity. If the design is hierarchical, then the top-level description will have lower-level descriptions contained in it. These lower level descriptions will be lower level entities contained in the top level description of the design. A library unit which describes the external interface of a hardware module.Jump to Examples of Entities.
A group of sequential statements which can be "called" from different places in a model, reading one or more input parameters and returning a single value.
A generic is VHDL's term for a parameter that passes information to an entity. For instance, if an entity is a gate level model with a rise time and fall time delay, values for those delays could be passed into the entity with generics. Jump to section on Generics
An analyzed design unit. The five types of library unit are: entity, architecture, package, package body and configuration.
The definition of multiple functions or procedures with the same name, which operate on different parameter combinations or types.
A package is a collection of commonly used data types and subprograms used in a design. Think of a package as a toolbox that contains the tools used to build designs.Jump to Examples of Packages.
A secondary unit associated with a package, whose main purpose is to contain the full code for any functions or procedures which have been declared in the associated package.
A library unit which can exist in a design library. The primary design units are: entity, package and configuration.
A group of sequential statements which can be "called" from different places in a model. It may have parameters of modes in, out or inout.
A concurrent statement which contains a collection of sequential statements and which can interact concurrently with other concurrent statements. A process is the basic unit of execution in VHDL. All operations that are performed in the simulation of a VHDL description are broken down into single or multiple processes. Jump to section on Processes.
The determination of the value of a signal when it is simultaneously driven by more than one source.
The region of VHDL code within which a declared item (e.g. a constant) may be used.
A library unit which defines a body associated with a primary unit which has already been analyzed into the same design library. The secondary units are architecture (associated with an entity) and package body (associated with a package).
Statements which are executed in the order they are
written, as with "conventional" software.
Return to
top of document.
The basic building blocks in VHDL are Entities and Architectures. An Entity describes the boundaries of the logic block. Its ports and generics are declared here. An Architecture describes the contents of the logic block in structural, dataflow, and behavioral constructs.
entity small_block is port ( a ,b, c : in bit; o1 : out bit; o2 : out bit ); end small_block; architecture vhdl_example of small_block is signal s : bit; begin o1 <= s or c; s <= a and b; o2 <= s xor c; end vhdl_example;
This VHDL description shows the implementation small_block. The port list is given with a direction ( in this case in or out ), and a type (bit) for each port. The entity's name is small_block. There can be multiple architectures per entity, but always only one architecture is executed. By default, the last defined architecture is implemented.
The architecture describes the contents of small_block. The architecture starts with a declarative region; in this case, the internal signal s is declared. It also has the type ( bit ), just like the ports in the entity.
A signal is another form of an object in VHDL. All objects and expressions in VHDL are strongly typed. That means that all objects are of a defined type and the VHDL compiler issues an error message if there is a type mismatch. For example, you can not assign an integer signal to a bit.
The architecture contents starts after the begin statement. This is called the dataflow environment. Please refer to the previous example Go to Example # 1. All statements in the dataflow environment are executed concurrently, and thus the order of the statements is irrelevant. This is why it is valid to use s before s is assigned anything. Assignment of a value to a signal is done with the <= sign. In the first statement, o1 is assigned the result of s or c. or is a predefined operator.
Processes are sequentially executed statements, as opposed to dataflow environment, where all statements are executed concurrently. In a process, the order of the statement does matter. In fact processes resemble the sequential coding style of high level programming languages. Also, processes offer a variety of powerful statements and constructs that make them very suitable for high level behavioral descriptions.
A process can be called from the dataflow area. Each process is a sequentially executed program, but all processes run concurrently. In a sense, multiple processes resemble multiple programs that can run simultaneously. Processes communicate with each other via signals that are declared in the architecture. Also the port defined in the entity can be used in the processes.
entity experiment is port ( source : in bit_vector ( 0 to3) ; ce : in bit; wrclk : in bit; selector : in _vector( 0 to 1); result : out bit ); end experiment; architecture vhdl_example of experiment is signal int_reg : bit_vector( 0 to 3); begin -- dataflow environment writer : process -- process statement -- declarative region ( empty here ) begin -- sequential environment -- sequential clocked statements wait until wrclk'event and wrclk = '1' ; if (ce = '1') then int_reg <= source ; end if ; end process writer; reader : process ( int_reg, selector) -- process statements with sensitivity list. -- declarative region ( empty here) begin -- sequential ( not-clocked) statements. case selector is when "00" => result <= int_reg(0); when "01" => result <= int_reg(1); when "10" => result <= int_reg(2); when "11" => result <= int_reg(3); end case; end process reader ; end vhdl_example; This example describes a circuit that can load a source vector of 4 bits, on the edge of a write clock (wrclk ), store the value internally in the register ( int_reg ) if a chip enable ( ce ) is active while it produces on bit of the register constantly ( not synchronized ). The bit is selected by a selector signal of 2 bits. The description consists of two process, one to write the value into the internal register, and one to read from it. The two processes communicate via the register value int_reg. The first process ( writer) includes a wait statement. The wait statement causes the process to execute only if its condition is true. ( A further explanation is given later in the chapter.) In this case, the wait statement waits until a positive edge occurs on the signal wrclk (when "00" => result <= int_reg(0; expression wrclk'event and wrclk = '1' ). Each time the edge occurs, the statements below the wait statements are executed. In this case, the value of the input signal source is loaded into the internal signal int_reg only if ce is '1'. If ce is '0', int_reg retains it's value. In synthesis terms, this translates into a D-flipflop, clocked on wrclk, and enabled by ce. The second process (reader) does not have a waitstatement. Instead, it has a sensitivity list, with the signal int_reg and selector there. This construct defines that the whole process is executed each time either int_reg or selector changes. If the process is executed, the output signal result gets updated depending upon the values of int_regand selector. Note that this leads to combinatorial behavior, since result only depends on int_reg and selector, and each time either of these signals changes, result get updated. A process has an optional name ( in this case writer and reader ), as sensitivity list OR a wait statement, and a declarative region where signals, variable, functions, etc. can be declared which are used only within the process. The next section of the process is the sequential environment where all statements are made. Each statement is executed sequentially, as in a programming language. Not all constructs, or combinations of constructs, in a process lead to behavior that can be synthesized into logic. More information about synthesizable VHDL constructs is given later. Return to top of document.
Constant values in VHDL are given in literals. Literals are lexical elements. Below is an overview, with examples given for each type of literal, Character Literals: '0' 'X' 'a' '%' # String Literals: "1110100" "XXX" "words in quotes" Bit String Literals: B"0010_0001" X"5f" O"63_07" Decimal Literals: 27 4.25 Physical Literals 2 nS 5.0 V 15 pF Identifiers: Words that Identify Something. Literals are used to define types and as constant values in expressions. This list provides a brief description of their function in VHDL which will be more clear after the descriptions of types and expressions.
order your literal, but does not represent a value.
single quoted.
quoted.
Bit string literals are a special form of string literals. They contain an array of the characters '0' and '1', and are preceded by one of three representation forms. Bis the bit representation ( 0 or 1 allowed ), X is the hexadecimal representation ( 0 to F allowed ), and O is the octal representation ( 0 to 7 allowed). X"5F" is exactly the same as B"01011111" which is again the same as the string literal "01011111".
by the compiler and are only inserted for readability.
they are not used in the synthesis part of the design, there will
be no further mention of them.
Identifiers can be enumeration literals. They are case-insensitive, like all identifiers in VHDL. Their use becomes more clear with the discussion of "types". Return to top of document.
A type is a set of values. VHDL supports a large set of types, but we will only concentrate on those that are useful for synthesis. An example of a typeis the VHDL predefined type bit. It consists of only two values: the character literals '0' and '1'. In VHDL syntax, the definition of the bit type is: type bit is ('0' , '1'); This type indicates that all objects of type bit can only contain the values '0' or '1'. The VHDL compiler issues an error if you try to the value 'a' to an object of this type. In VHDL there are bound and unbound types. A bound type has a defined number of values, and unbound type does not. As an example of a bound type is the definition of an 8 bit array ( a vector) of bits: type eight_bit_vector is array ( 0 to7 ) of bit; All objects of type eight_bit_vector should have 8 bit values. On the other hand, we could define an unbound, general array of bit type: type bit_vector is array ( integer range <>)of bit; The syntax <> tells the compiler to reserve space for this array, but its range has not yet been defined. Its range could be dependent upon logical constructs created in another section of code. The type bit_vector is also a predefined type in VHDL, and it's definition is the same as the one shown above. Also see Predefined Types. The size of bit_vector is not yet defined, but it can be used to define other types that are bounded, or objects of this type can have "bound" on the type. A type with a bound is called a subtype. For example, we bound the bit_vectortype and create a 10 bit vector of bits with the following statement. subtype ten_bit_vector is bit_vector ( 0 to9); For logic synthesis all objects should be bounded to assure that their value can be implemented in a finite amount of space. Another predefined type is an "integer", which contains all integer values. Officially, VHDL defines that at least the range of -2147483647 to +2147483647 should be contained in an integer. Integers can also be bounded. subtype small_integer is integer range 0 to255; defines an integer that only allows the values 0 to 255. Return to top of document.
VHDL pre-defines a number of types. Listed below are the ones which are important for logic synthesis. type bit is ('0', '1'); type bit_vector is array (integer range<>) of bit; type integer is range MIN to MAX; subtypepositive is integer range1 to MAX; subtype natural is integer range 0 to MAX; type boolean is ( TRUE, FALSE ); Additionally the IEEE 1164 Standard for VHDL specifies a 9-valued logic. The meanings of these different types is given below: 'U' Uninitialized 'X' Unknown '0' Forced Low '1' Forced High 'Z' High Impedance 'W' Weak Unknown 'L' Weak Low 'H' Weak High '-' Don't Care The weak values on a node can always be overwritten by a forcing value. The high impedance state can always be overwritten by all other values.. Most of these values are meaningful for simulation purposes only. Some restrictions apply of you want to use these values for synthesis of logic. Only the values '0', '1', 'X', '-', and 'Z' have a useful meaning for synthesis. Some examples of IEEE 1164 statements are
type std_logic is ('U', 'X','0', '1','Z', 'W', 'L', 'H', '-');
type std_logic_vector is array (natural range <>) of std_logic;
subtype std_logic is resolution_func_std_logic;
type std_logic_vector is (natural range <>) of std_logic;
The identifier resolution_func is a function that defines which value should be generated in case multiple values are assigned to an object of the same type. This is called the resolution function of the type. NOTE: Resolution functions are ignored by logic synthesis, since simultaneous assignment of multiple values to one object are not synthesizeable. Return to top of document.
It is possible to define types yourself. VHDL supports enumerated types with character literals and with identifiers. As an example of enumerated type with character values is the std_logic type. Type declarations for synthesis are done to either define a type whose objects will represent a single bit value ( like bit, or std_logic ) or to define a type that will represent the states of a state machine. Return to top of document.
Objects in VHDL: signals, variable, constants, ports, loop variables, generics; can contain values. Values can be assigned to objects, and these values can be used elsewhere in the description by using the object in an expression. All objects except loop variables have to be declared before they are used. This section describes the various objects in VHDL and their semantics. Return to top of document.
Signals represent the wires in a logic circuit. Here are a few examples of signal declarations. signal binary_5 : bit_vector( 0 to 5 ) := B"00000" ; signal aux : bit ; signal max_value : integer ; Signals can be declared in all declarative regions in VHDL except for functions and procedures. The declaration assigns a name to the signal ( binary_5 ) ; a type, with or without a range restriction ( bit_vector ( 0 to 5 )); and optionally, an initial (constant) value. Initial values on signals are usually ignored by logic synthesis. But simulator will recognize it, for behavioral simulation. For further information on initial values of signals see section onPredefined Types. Also Jump to VHDL Document #3, section of General VHDL Language Restrictions. Signals can be assigned values using an assignment statement ( i.e. aux <= '0' ; ) . If the signal is an array type, elements of the signal's array can be accessed and assigned using indexing or slicing. Jump to Indexed Arrays. Assignments to signals are not immediate, but scheduled to be executed after a delay, (this is the essential difference between variables and signals). This is discussed in detail later. Jump to Assignment Statements. Return to top of document.
Constants cannot be assigned a value after their declaration. Their only value is the initial constantvalue. Initialization of a constant is required. An example of declaring a constant is: constant ZEEE_8 : std_logic_vector ( 0 to7 ) := "ZZZZZZZZ" ; This assigns a tri-state value to the vector named "ZEEE_8". Return to top of document.
Variables cannot be declared or used in the dataflow areas or in packages, but only in processes, functions and procedures. An example of declaring a variable is variable temp : integer range 0 to15 := 5 ; Assignment to a variable are immediate. This effect is an essential difference between variables and signals. This is discussed in Assignment Statements. The initial assignment to a variable is optional. The initial assignment to a variablein a process is usually ignored by logic synthesis. Jump to VHDL Document #3, section on General VHDL Language Restrictions. Return to top of document.
A port is an interface terminal of an entity. A port represents an ordinary port in a netlist description. Ports in VHDL are just like other objects; they are typed and can have an initial value. In addition, a port has a "direction". This is a property that indicates the possible information flow through the port. Possible directions are in, out, inout,and buffer,where inout and buffer indicate a bidirectional functionality. A buffer is the synthesis equivalent of tri-state driver (i.e. 74244), two signals paths may diverge from a pin in an FPGA, etc. the output path has a buffer attached to it and this buffer will be tri-stated or high impedance, while the input signal path is in use. An INOUT is analogous to a 74HC245, a bidirectional buffer driver chip. entity adder is port ( input_vector : in bit_vector ( 0 to 7 ); output_vector : out bit_vector (0 to 7 ) ); end adder; After declaration, a port can be used in the architecture of the entity as if it were a normal signal, Return to top of document.
A generic is a property of an entity. A generic is useful in coding because you can define the property of the generic in one place in your code. If you need to make a change later, i.e. change the width of the bus for instance, you would only have to change the size assignment for it. If did not use one, you would have find every single instance of the bus in the code and edit the change in every line. ( and there's no guarantee that you change them all correctly either.) A good example of a generic is the definition of the size of the interface of the entity. Generics are declared in a generic list. entity increment is generic (size : integer := 8 ); port ( ivec : in bit_vector ( 0 tosize-1 ); ovec : out bit_vector ( 0 to size-1) ) ; end increment ; The generic 'size' can be used inside the entity ( i.e. to define the size of the ports ) and in the architecture that matches the entity. In this example, the generic 'size' is defined as an integer with an initial value 8. The sizes of the input and output ports of the entity increment are set to be 8 bits unless the value of the generic is overwritten by a generic map statement in the component instantiation of the entity. inst-1 : increment generic map (SIZE =>16 ) port map ( ivec => invec, ovec => outvec); Here, a 16 bit incrementer is instantiated, and connected to the signal invec and outvec. The section on instantiation explains more about this. Jump to section on Component Instantiation. Return to top of document.
A loop variable is a special object in the sense that it does not have to be declared. The loop variable gets its type and value from the specified range in the iteration scheme. for I in 0 to 5 loop a( I ) <= b( I ) and ena ; end loop ; In this code fragment, I becomes an integer with the values 0,1,2,,,,5 respectively, when the loop statements are executed 6 times. A loop variable can only be used in an expression, and there can be no assignments to the loop variable. For logic synthesis, the range specified for the loop variable must be a compile-time constant, otherwise the construct is not synthesizeable; the warning message that the process runs on forever will be generated. Return to top of document.
Objects can be defined as arrays. An array is a set of elements. To access the individual elements, for instance to assign a value to the element, or if an element is used in an expression, VHDL supports the indexed array construct. The type of an index value of the array is the type of an element of the array. signal an_array : bit_vector ( 0 to 8 ) ; signal msb_bit : bit ; ....... an_array(5) := '0' ; msb_bit <= some_array (0) ; Also, parts of the array can be selected using the sliced array construct. For example: signal an_array : bit_vector ( 0 to 8 ) ; signal a_vector : bit_vector ( 3 downto 0) ; signal a_slice : bit_vector ( 3 to 0 ) ; ........ an_array(1 to 4 ) <= a_vector: an_array( 8 downto 5 ) <= "0000" ; --wrong direction for slice. In the last statement, the direction of the slice range is downto, while the index range of a_vector was defined to be ascending to. In this case, an_array( 8 downto 5 ) would cause the VHDL compiler to issue an error. Indexed arrays resemble a selection from the array. If the index is a constant, the VHDL compiler will connect a single "wire" to the right logic element. VHDL support variable indexing of arrays, where the index can be a non-constant expression. Indexes of arrays do not have to be integers. An index could also be an enumerated type. Return to top of document.
This section briefly discusses the basic statements that can be used in VHDL descriptions.
These are if - then ,elsif - then and else types of statements. signal a : integer ; signal output_signal, x, y, z : bit _vector( 0 to3 ) ; ....... output_signal <= x ; elsif a = 2 then output_signal <= y ; elsif a = 3 then output_signal <= z ; else output_signal <= "0000" ; end if ; This code fragment describes a multiplexer function, implemented with an if-then-else statement. This statement can only be used in a sequential environment, such as a process. procedure, or a function. See sections on Multiplexers in Zen & the Art of VHDL. and also Tips & Tricks documents The same functionality in a dataflow environment is accomplished with the use of the conditional signal assignment statement: signal a : integer ; signal output_signal x, y, z : bit_vector ( 0 to3 ); ..... output_signal <= x when a = 1 else y when a = 2 else z when a = 3 else "0000" ; Return to top of document.
If many conditional clauses have to be performed on the same selection signal a case statement is a better solution than the if-then-else construct. signal output_signal x, y, z : bit_vector ( 0 to3 ); ...... case selection is when "0010" => output_signal <= x; when "0100" => output_signal <= y; when "1000" => output_signal <= z; when "1010" | "1100" | "0110"=> output_signal <= x and y and z ; when others => output_signal <= "0000" end case ; The | ( logical OR ) sign indicates that this particular case has to be entered if any of the given choices is true Each case can contain a sequence of statements. The case statement can only be used in a sequential environment. In the dataflow environment, the selected signal assignment statement has the equivalent behavior: signal output_signal , my_selection, x, y, z : bit_vector( 0 to 3 ) ; ..... with my_selection select output_signal <= x when "0010" , y when "0100" , z when "1000" , x and y and z when "1010" | "1100"| "0110" "0000" when others ; Return to top of document.
In many cases, especially with operations on arrays, many statements look alike, but differ only on minor points. In that case, you might consider using a loop statement. signal result, input_signal : bit_vector ( 0 to5 ) ; signal ena : bit ; ..... for i in 0 to 5 loop result( i ) <= ena and input_signal( i ) ; end loop ; In this code fragment, each bit of an input signal is "anded" with a single bit enable signal, to produce an output array signal. The loop variable i does not have to be declared. It holds an integer value since the loop range is an integer range. For useful examples of iterative statements and "for loops" jump to Process Examples, in Zen & the Art of VHDL. The loopstatement can only be used inside sequential environments. It's equivalent statement in the dataflow environment is the "generate" statement: signal result, input_signal : bit_vector ( 0 to5 ) ; signal ena : bit ; ..... G1 : for i in 0 to 5 generate result( i ) <= ena and input_signal( i ) ; end generate ; Note that the generate statement is preceded by a label ( G1). A label is required in the generate statement but is optional in the loop statement. Return to top of document.
Assignments can be done to signals, port,and variables in VHDL. Assignments to signals, and ports are done with the <= operator. signal o, a, b : std_logic_vector ( 0 to 5) ; ..... o <= a xor b ; In this code fragment "o" gets assigned the value of the vector-XOR (bit by bit) of vectors "a" and "b". The type of the object on the left hand side of the assignment should always match the type of the value on the right hand side of the assignment.. Signal assignments can be used both in dataflow and sequential environments. For information on std_logic_vector jump to section on Packages Assignment to variablesare done with the := sign. variable o : std_logic_vector ( 0 to 5 ) ; signal a, b : std_logic_vector ( 0 to 5 ); ..... o := a and not b ; Variable assignments can only be used in sequential environments. Typeson left and right hand sides of the := sign must match. Otherwise will get misleading compiler results. There is one important difference between assignments to signals and assignments to variables: when the values are updated. The value of a variable in a variable assignment is updated immediately after the assignment. The value of a signal in a signal assignment is not updated immediately, but gets "scheduled" a until after a delta (delay) time. This delay time is not related to actual time, but is actually a characteristic of the simulator used. This behavior of the signal assignment does not have any effect for signal assignments in a dataflow environment, since assignments are done concurrently there. However, in a process, the actual value of the signal changes only after the complete execution of the process. The following example illustrates this effect. It shows the description of a multiplexer that can select one bit out of a four bit vector using two select signals. entity mux is port ( s1, s2 : in bit ; inputs : in bit_vector ( 0 to 3 ); result : out bit ) ; end mux ; architecture wrong_way of mux is begin process ( s1, s2, inputs ) signal muxval : integer range 0 to3 ; begin muxval <= 0 ; if ( s1 = '1' ) then muxval <= muxval +1; if ( s2 = '1' ) then muxval <= muxval +2; -- use muxval as index of array 'inputs' result <= inputs ( muxval) ; end if; end if; end process; end wrong_way ; This description does not behave as intended. The problem is because muxval is a signal; the value of muxval is not immediately set to the value defined by bit a and b. Instead, muxval still has the same value it had when the process started when the case statement is executed. All assignments to muxvalare scheduled until after the process finishes. This means that muxval still has the value it got from the last time the process was executed, and that value is used to select the bit from the input vector. The solution to this problem is to make muxval a variable. In that case, all assignments done to muxval are immediate, and the process works as intended. entity mux is port (s1, s2 : in bit : inputs : in bit_vector ( 0 to 3 ); result : out bit ) ; end mux ; architecture right_way of mux is begin process ( s1, s2, inputs ) variable muxval : integer range 0 to3 ; begin muxval := 0 ; if (s1 = '1' ) then muxval := muxval+1; if (s2 = '1' ) then muxval := muxval+2; -- use muxval as index of array 'inputs' result <= inputs ( muxval ) ; end process ; end right_way; As a general rule, if you use signal assignments in processes, do not use the value of the signal after the assignment, unless you explicitly need the previous value of the signal. Alternatively, you can use a variable instead. Return to top of document.
VHDL predefines a large number of operators for operations on objects of various types. The following is an overview: Relational operators an ALL types ( predefined or not): =, /=, <, <=, >, >= Logical operators on pre-defined types BIT and BOOLEAN: AND, OR, NAND, NOR, XOR, NOT Arithmetic operators on all integer types: +, -, *, /, **, mod, rem, abs Concatenation of elements into an array of elements: &, (,,,,) Relational operators operate on any type. The basis of comparing two values is derived from the order of definition. For example, in the std_logictype the value 'U' is smaller than the value '1' because 'U' is defined first in the order of values in the type. Jump to section on Predefined Types for more information on 'U', 'X', '1', '0', 'Z', etc. The comparison of two arrays is accomplished by comparing each element of the array. The left most element is the most significant one for comparisons. signal a : bit_vector ( 7 downto 0 ) ; signal b : bit_vector ( 5 to 9 ) ; In this example, a( 7 )is the most significant bit for comparisons with vector a, and b( 5 ) is the most significant bit for comparisons with vector b. Logical operators work in a straightfoward manner and do the appropriate operation of types BIT and BOOLEAN, and also for one-dimensional arrays of BIT and BOOLEAN. In the latter case, the logical operation is executed on each element of the array. The result is an array with the same size and type as the operands. Arithmetic operators work on integers and on all types derived from integers. Concatenation operators can group elements of the same type into an array of that type. Consider the following examples: signal a, b, c : bit ; signal x : bit_vector( 5 downto 0 ) ; signal y : bit_vector ( 0 to 3 ); ,,,,, -- using concatenation operator x <= a & b & c & B"00" & '0'; -- using an aggregate y <= ('1', '0', b, c ); This description is the same as the following one: signal a, b, c : bit ; signal x: bit_vector( 5 downto 0 ) ; signal y : bit_vector ( 0 to 3 ); ,,,,, x( 5 ) <= a ; x( 4 ) <= b ; x( 3 ) <= c ; x( 2 downto 1 ) <= "00" ; x(0) <= 0; y( 0 ) <= '1' ; y( 1 ) <= '0' ; y( 2 ) <= b ; y( 3 ) <= c ; The aggregate operator in VHDL is especially useful when assigning a vector of unknown or large size: signal o : bit_vector ( 0 to 255 ); ,,,, o <= (0 => '1', others => '0' ); In this example, o( 0 ) is assigned '1' and all other elements of o (independent of it's size) get the value '0'. Return to top of document.
In VHDL, attributes can be set on a variety of objects, such as signals and variables, and many other identifiers, like types, functions, labels etc. An attribute indicates a specific property of the signal, and is of a defined type. Using attributes at the right places creates a very flexible style of writing VHDL code. An example of this is given at the end of this section Jump to Attribute Example. Predefined VHDL Attributes. VHDL pre-defines a large set of attributes for signals. The following example shows the definition of two vectors and the values of the VHDL predefined attributes for them. signal vector_up : bit_vector ( 4 to 9 ) ; signal vector_down : bit_vector (25 downto0 ) ; ,,,,, vector_up'LEFT -- returns integer 4 vector_down'LEFT -- return integer 25 vector_up'RIGHT -- returns integer 9 vector_down'RIGHT -- returns integer 0 vector_up'HIGH -- returns integer 9 vector_down'HIGH -- returns integer 25 vector_up'LOW -- returns integer 4 vector_down'LOW -- returns integer 0 vector_up'LENGTH -- returns integer 6 vector_down'LENGTH -- returns integer 26 vector_up'RANGE -- returns range 4 to 9 vector_down'RANGE -- returns range 25 to 0 vector_up'REVERSE_RANGE -- returns range 9 to 4 vector_down'REVERSE_RANGE -- returns range 0 to 25 These attributes are the most important ones that reveal the properties of vectors. The attributes do not have to be written in capitals; VHDL is case-insensitive for identifiers. An important predefined attribute for synthesis is EVENT attribute. It's value reveals the edges or transitions of signals. Information about the EVENT attribute can be found at VHDL Document #2, Flip-Flops, and Event Attributes Return to top of document.
To indicate where attributes in a VHDL description would be useful, consider the following example: entity masked_parity is port ( source : in bit_ vector ( 0 to5 ); mask : in bit_vector ( 0 to 5 ); result : out bit ); end masked_parity ; architecture marginal_example of masked_parity is begin process (source, mask ) variable temp : bit ; variable masked_source : bit_vector ( 0 to5 ) ; begin masked_source := source and mask ; temp := masked_source( 0 ); for I in 1 to 5 loop temp := temp xor masked_source( I ) ; end loop ; result <= temp; end process ; end marginal_example; This example calculates the parity of the bits of a source vector, where each bit can be masked. This VHDL description is correct, but it is not very flexible. Suppose the application changes slightly and requires a different size input. Then the VHDL description has to be modified significantly, since the range of the vector affects many places in the description. The information is not concentrated, and there are many discrepancies. Attributes can resolve these dependencies. Here is an improved version of the same example, where attributes LEFT, RIGHT, and RANGE define the dependencies on the size of the vector. entity masked_parity is generic ( size : integer := 5 ); port ( source : in bit_ vector ( 0 to size ); mask : in bit_vector ( source'RANGE ); result : out bit; ); end masked_parity ; architecture better_example of masked_parity is begin process (source, mask ) variable temp : bit ; variable masked_source : bit_vector ( source'RANGE ) ; begin masked_source := source and mask ; temp = masked_source( source'LEFT ); for I in source'LEFT+1 to source'RIGHTloop temp := temp xor masked_source( I ) ; end loop ; result <= temp; end process ; end better_example ; If the application requires a different sized parity checker, then this time you only have to modify the source vector range, and the attributes ensure that the rest of the description gets adjusted accordingly. Now the information is concentrated. Return to top of document.
When using processes and dataflow statements it is possible to use VHDL as a high level hardware description language. However, as the descriptions get more and more complicated, some form of design partitioning or hierarchy is required or at least is desirable. VHDL offers a variety of methods for design partitioning. One form of partitioning is to divide a description into various processes. In the following section four more types f partitioning are discussed: blocks, subprograms (functions and procedures ), components, and packages. A block is a method to cluster a set of related dataflow statements. signals, subprograms, attributes, etc. that are local to that block can be defined in a block declarative region. All statements in a block are executed concurrently, and thus define a dataflow environment. architecture xxx of yyy is signal global_sig, g1, g2 ,c bit ; begin B1 : block -- block declarative region. signal local_sig : bit ; begin -- block concurrent statements -- Block in a block. B2 : block ( c = '1') -- Block has a "GUARD"expression port ( o1, o2 : out bit ) --block port declarations port map ( o1 => g1, o2 => g2 ) ; begin o1 <= guarded local_sig ; o2 <= global_sig ; end block ; endblock; end xxx ; Blocks can be nested as in the example above. Signals, ports, and generics declared outside the block can be used inside the block, either directly ( as global_sig used in block B2 ), or via a port map ( as g1 is connected to o1 in the block B2 ), or generic maps ( for generics ). There is no real difference between the two methods, except that the port (generic)map construct is a cleaner coding style which could reduce errors when using or assigning to global objects. A block can also have a GUARD expression ( c='1' in block B2 ). In that case, an assignment inside the block that contains the keyword GUARDEDwill only be executed when the GUARD expression is TRUE. In the example above, o1 only gets the value of local_sig when c = '1'. GUARDED blocks and assignments provide an interested alternative to construct latches or flip-flops in a synthesized logic circuit. See further examples of this at ...... VHDL Document #2, Flip-Flops, and Event Attributes Return to top of document.
Subprograms ( functions and procedures ) are powerful tools to implement functionality that is repeatedly used. Functions take a number of arguments that are all inputs to the function, and return a single value. Procedures take a number of arguments that can be inputs, outputs, or inouts depending on the direction of the flow of information through the argument. All statements in functions and procedures are executed sequentially, as in a process. Also, variables that are local to the subprogram can be declared in the subprogram. Local signals are not allowed. As an example, suppose that you would like to add two vectors. In this case, you could define a function that performs the additions. The following segment of code shows how an addition of two 6 bit vectors can be done. function vector_adder ( x : bit_vector ( 0 to 5 ); y : bit_vector ( 0 to 5 ) return bit_vector ( 0 to 5 ) is -- declarative region variable carry : bit ; variable result : bit _vector ( 0 to 5 ) ; begin -- sequential statements carry := '0' ; for I in 0 to 5 loop result( I ) := x( I ) xor y( I ) xor carry ; carry := carry and ( x( I ) or y ( I ) or x ( I ) and y( I ) ; end loop; return result ; end vector_adder ; An example of a procedure is shown below. The procedure increments a vector only if an enable signal is high. procedure increment ( vect : inout bit_vector ( 0 to 5 ) ; ena : in bit ;= '1' ) ; is begin if ( ena = '1') then vect := vector_adder ( vect, "000001") ; end if ; end increment ; This incrementer procedure shows the behavior of an inout port. The parameter vect is both set and used in this procedure. Also, the procedure statements use a call to the previously defined vector_adder function. In an input of a function or a procedure is connected when it is used, that input will get the initial value as declared in the interface list. For example, the input ena will get the ( initial ) value '1' if it is not connected in a procedure call to the procedure increment . It is an error if an input is not connected and also does not have an initial value specified. One important feature of subprograms in VHDL is that the argument can be unbound.. The given examples operate on vectors of 6 bits. If you want to use the subprograms for arbitrary length vectors, you could specify the length dependent attributes and not specify a range on the parameters ( leave them unbound ). Here is a redefinition of both the vector addition function and the incrementer procedure for arbitrary length vectors function vector_adder ( x : bit_vector ; y : bit_vector return bit_vector is variable carry : bit ; variable result : bit _vector ( x'RANGE ) ; begin -- sequential statements carry := '0' ; for I in x'RANGE loop result( I ) := x( I ) xor y( I ) xor carry ; carry := carry and ( x( I ) or y ( I ) or x ( I ) and y( I ) ; end loop; return result ; end vector_adder ; procedure increment ( vect : inout bit_vector; ena : in bit := '1' ) is begin if ( ena = '1') then vect := vector_adder ( vect, "000001") ; end if ; end increment ; In the procedure increment example, name association was added in the parameter list of the vector_adder call. The name association ( i.e. x => vect ) is an alternative way to connect a formal parameter ( x ) to its actual parameter ( vect ). Name associations ( as well as positional associations ) are helpful if the number of parameters is large. Subprograms can be called from the dataflow environment and from any sequential environment ( processes and other subprograms ). If a procedure output or inout is a signal, the corresponding parameter of the procedure should also be declared as a signal. Return to top of document.
Components are a method of introducing structure in a VHDL description. A component represents a structural module in the design. Using components, it is possible to describe a netlist in VHDL. Components are instantiated in the dataflow environment. Here is an example of a structural VHDL description where four one-bit RAMS and a counter module are instantiated. entity scanner is port ( reset : in bit ; stop : in bit ; load : in bit ; clk : in bit ; load_value : in bit_vector ( 0 to 3 ) ; data : out bit_vector ( 0 to 3 ) ; ) ; end scanner ; architecture in_vhdl of scanner is component RAM_32by1 port ( a0, a1, a2, a3, a4 : in bit ; we, d : in bit ; o : out bit ; end component ; component counter generic ( size : integer := 4 ) ; port(clk : in bit ; enable : in bit ; reset : in bit ; result : out bit_vector( 0 to 4 ) ; ) ; end component ; signal ena : bit ; signal addr : bit_vector( 0 to 4 ) ; begin for I in 0 to 3 generate ram : RAM_32 by port map ( a0=>addr( 0 ), a1=>addr( 1 ), a2=>addr( 2 ),a3=>addr( 3 ), a4=>addr( 4 ), d=>data( I ), we=>load, o=>data(I ) ) ; end generate ; ena => not stop ; count : counter generic map (size=>addr'length) port map (clk=>clk, enable=>ena, reset=>reset,result=>addr ); end in_vhdl ; The generate statement here is used to instantiate four RAMS. Components have to declared before they can be used. This is done in the declaration area of the architecture, or in a package Jump to Section on Packages. The declaration defines the interface of he component ports with their type and their direction. Actually this example is just a netlist of components. We added one dataflow statement (the assignment to ena ) to show that structure and behavior can be mixed in VHDL. The ports of the component are connected to actual values with the generic map construct. In this example the generic size is set to 4 with the attribute length on the array addr.If no generic value was set to size ( or if the generic map construct was completely absent ), size get the value 4, as indicated by the initial value on size in the generic list of the component. It is an error if a generic ( or input port ) is not connected in a generic map (or port map ) construct and there is no initial value given in the component generic ( or port ) list. The definitions of the components counter and RAM_32by1 have not been given yet. There are two ways to do his in VHDL:
1. Define an entity and architecture for the component in the same, or an included VHDL file
2. Use an input technology that contains a component with the same name and interface list.
The component counter is a good example of the first option: entity counter is generic ( size : integer ) ; port ( clk : in bit ; enable : in bit ; reset : in bit ; result : out bit_vector ( 0 to size-1 ) ); end counter; architecture in_vhdl of counter is begin process ( clk, reset ) begin if ( reset = '1' ) then result <= (others => '0' ) elsif (clk'event and clk = '1' ) then if (enable = '1' ) then result <= result + "1" ; end if ; end if ; end process end in_vhdl ; This description includes only behavior. There is no component instantiated, although it is possible, and it makes hierarchical design possible. Note that an asynchronous reset construction is used to reset the counter value. Details of various synthesizable forms of reset are given in VHDL Document #2, sections on Sync and Async Resets. For the second option, use a source technology in our design environment that includes the component RAM_32by1, (this is an example that exists in the Viewlogic VHDL library.) If the source technology is in a library (i.e. Viewlogic, Altera, Actel, etc. ) the VHDL compiler will recognize the component in the 'lib_name' library, and instantiate it in the design. Return to top of document.
A package is a cluster of declarations and definitions of objects, function, procedures, components, a tributes, etc. that can be used in VHDL descriptions. You cannot define an entity or an architecture in a package, so a package itself does not represent a circuit. A package consists of two parts. The package header, with declarations, and the package body, with definitions. An example of a package is std_logic_1164 the IEEE standard # 1164 logic types package. It defines types and operations on types for 9 valued logic. Nine valued logic refers to the types of output signals that a logic device can create (i.e. H, L, Z, X, and other permutation of hard / soft high and low logic levels). library ieee ; use std_logic_1164; entity xyz is port ( x : std_logic ; -- type std_logic is known since it is defined in -- Package std_logic_1164 ,,,,, This example shows how the IEEE 1164 standard logic types and functions become accessible to the description in entity xyz. This is the general form to include in a package in a VHDL description: library <lib>; use <lib>.<package>.<selection> ; The use clause is preceded by a library clause. The predefined libraries work and std do not have to be declared in a library clause before they are used in a use clause. All other libraries do need to be declared. The <selection > can consist of only one name of an object, component, type or subprogram that is present in the package, or the word all, in which case a;; functionality described in the package is loaded in to the VHDL compiler and can be used in the VHDL description. Return to top of document
An alias is an alternate name for an existing object. By using an alias of an object, you actually use the object to which it refers. By assigning an alias, you actually assign to the object to which the alias refers. signal vec : std_logic_vector ( 4 downto 0 ) ; alias mid_bit : std_logic is vec(2) ; -- Assignment mid_bit < ;= '0' ; -- this is the same as vec( 2 ) < ;= '0' ; Aliases are often useful in unbound function calls. For instance, if you want to make a function that takes the AND operation of the two left most bits of an arbitrary array parameter. If you want to make the function general enough to handle arbitrary sized arrays, this function could look like this; function left_AND (arr:std_logic_vector ) returnstd_logic is begin return arr(arr'left ) and arr( arr'left-1 ) ; end left_AND ; -- Note the function does not work for ascending -- index ranges of arr. This function will only work correctly if the index of arr is descending ( downto ). Otherwise, arr'left-1 is not a valid index number. VHDL does not have a simple attribute that will the one-but-left most bit out of an arbitrary vector, so it will be difficult to implement a function that works correctly both for ascending and descending index ranges. Instead, you could make an alias of arr, with a known index range, and operate on the alias: function left_AND (arr:std_logic_vector ) returnstd_logic is array aliased_arr : std_logic_vector ( 0 to arr'length-1) is arr ; begin return aliased_arr(0 ) and aliased_arr( 1) ; -- Function works for ascending and descending index ranges of arr. NOTE: Not all VHDL compilers fully support aliasing.
Return to top of document