FAQ comp.lang.vhdl (part 1): General

comp.lang.vhdl

Frequently Asked Questions And Answers (Part 1): General


Preliminary Remarks

This is a monthly posting to comp.lang.vhdl containing general information. Please send additional information directly to the editor:

edwin@ds.e-technik.uni-dortmund.de (Edwin Naroska)

Corrections and suggestions are appreciated. Thanks for all corrections.

There are three other regular postings: part 2 lists books on VHDL, part 3 lists products and services (PD+commercial), part 4 contains descriptions for a number of terms and phrases used to define VHDL.



Table of Contents


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 (Conference on Hardware Description Languages -CHDL-, Design Automation Conference -DAC- ...).

0.3 Before Posting

0.4 Major Contributors to this FAQ

The basic version of this FAQ was created by Tom Dettmer. Georg Staebner converted Part 4 to HTML. Special thanks to Paul Menchini for contributing/revising major parts of Section 4.2 as well as Section 4.3!

0.5 Disclaimer

These articles (FAQ Part 1 to 4) are provided as is without any express or implied warranties. While every effort has been taken to ensure the accuracy of the information contained in this article, the author/contributors assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein.


1. Abbreviations

AHDL:
Analog Hardware Description Language
BBS:
Bulletin Board System
DoD:
USA Department of Defense
FAQ:
Frequently Asked Questions
IEEE:
The Institute of Electrical and Electronics Engineers. In case of VHDL, they defined the standard 1076
LRM:
Language Reference Manual
TISSS:
Tester Independent Support Software System
VASG:
VHDL Analysis and Standardization Group
VFE:
VHDL Forum Europe
VHDL:
VHSIC Hardware Description Language
VHSIC:
Very High Speed Integrated Circuits - A program of the DoD
VI:
VHDL International
VIUF:
VHDL International Users Forum
VUG:
VHDL Users Group, see below
WAVES:
Waveform Vector and Exchange Specification, proposed IEEE Standard

2. Contacts and Archives

2.1 Official Contacts

Accellera

Accellera's (http://www.accellera.org/) goal is to improve designers' productivity, the electronic design industry needs a methodology based on both worldwide standards and open interfaces. Accellera was formed in 2000 through the unification of Open Verilog International and VHDL International to focus on identifying new standards, development of standards and formats, and to foster the adoption of new methodologies.

Accellera's mission is to drive worldwide development and use of standards required by systems, semiconductor and design tools companies, which enhance a language-based design automation process. Its Board of Directors guides all the operations and activities of the organization and is comprised of representatives from ASIC manufacturers, systems companies and design tool vendors.

The contact address is

Accellera
15466 Los Gatos Boulevard
PMB 071, Suite 109
Los Gatos, CA 95032
Phone: (408) 358-9510
Fax: (408) 358-3910

VHDL International

VHDL International and Open Verilog International have merged into a new organization, ACCELLERA (see above).

VHDL-AMS, 1076.1 Working Group

The purpose of 1076.1 Working Group (WG) is to develop analog extensions to VHDL, i.e. to enhance VHDL such that it can support the description and simulation of circuits that exhibit continuous behavior over time and over amplitude. As of summer 1993 the IEEE Computer Society, through its Standards Activity Board (SAB), has approved the 1076.1 WG under PAR1076.1.
     1076.1 Executive Committee
        Working Group Chair & Secretary: 
                Alain Vachoux
                Swiss Federal Institute of Technology
                Integrated Systems Center
                CH-1015 Lausanne, Switzerland
                Phone: +41 21 693 6984
                Fax: +41 21 693 4663
                Email: alain.vachoux@epfl.ch
        Working Group Vice-Chair: 
                Ernst Christen
                Analogy Inc.
                P.O. Box 1669
                9205 SW Gemini Drive
                Beaverton, OR 97075-1669, USA
                Phone: (503) 520-2720
                Fax: (503) 643-3361
                Email: christen@analogy.com
     1076.1 Mailing List
        Reflectors (information to all members of the mailing list):
                1076-1@epfl.ch                  European address
                ahdl1076@cadence.com            US address
        Submit new names to be put on the mailing list to
                1076-1-request@epfl.ch
        Submit to 1076.1 Executive Committee only:
                1076-1-exec@epfl.ch
     1076.1 Repositories:
        ftp://vhdl.org/pub/analog/ftp_files/

See Section 4.6 and http://www.vhdl.org/analog/ for further information.

WAVES/TISSS

Standard for VHDL Waveform and Vector Exchange (WAVES). See http://www.vhdl.org/waves/ for further information.

2.2 VHDL User Groups

VHDL International Users Forum

The first (VUG) was transformed into the second (VIUF) in 1991. VIUF is Chaired by Praveen Chawla and is a chapter and thus sponsored by VHDL International (VI). They organize conferences and have some other activities.

WWW: http://server.vhdl.org/viuf/
Email: viuf-info@vhdl.org


2.3 Archives

Archives:

3. VHDL on the Web

Here are some useful links on the web related to VHDL. If you discover an interesting server not mentioned in this list or a link is broken please send a note to the editor.

3.1 Tutorials

3.2 VHDL Models

The following links point to some servers containing "non commercial" VHDL models. Note, there may be some limitations and restrictions concerning the use of this software.

Here are some links to commercial model sites:

A list of cores provided by various vendors is available from http://www.isdmag.com/eedesign/softcoretables.html. For other commercial model vendors see FAQ part 3 products & services.

3.3 Magazines

3.4 VHDL Sites

See also FAQ part 3 products & services for other VHDL vendor sites.


4. Frequently Asked Questions

There's not much until today - but I included those questions I've often heard from beginners. If someone feels, that a point should be included, please let me know.

4.1 About Changes to the VHDL Standard

According to IEEE rules every five years a standard has to be reproposed and accepted. This can include changes. Because VHDL is relatively young, the restandardization in 1992 included changes as well as the revision from 2000 and 2002 (actually, no new VHDL standard was produced in 1997).

Changes in VHDL93 include: groups, shared variables, inclusion of foreign models in a VHDL description, operators, support of pulse rejection by a modified delay model, signatures, report statement, basic and extended identifiers, more syntactic consistency.

A summary of the changes made in VHDL-2000 were presented by Paul Menchini at a recent IHDL conference on "Whats New: VHDL-2000" (coauthored by J. Bhasker). The presentation slides are available from http://users.aol.com/hdlfaq/vhdl2001-foils.pdf.


4.2 Language Related Questions

This chapter tries to answer some questions about language details which appear more or less regular on the net.

Paul Menchini contributed/revised major parts of this section. Special thanks to Paul for spending a lot of time and effort on this task!

4.2.1 USE of Library Elements?

Often users believe, they can use names of libraries /= work by simply inserting a use clause in the source. The analyzer responds with error messages. Insert a library clause before the use clause and all should work fine (see your LRM or FAQ Part 4 - B.74 for details).  

4.2.2 Component Instantiation and Default Component Binding

VHDL provides three methods for instantiating a component into an architecture:  

4.2.3 GENERATE Usage and Configuration

The generate statement (FAQ Part 4 - B.105) is a concurrent statement (FAQ Part 4 - B.44) that contains other concurrent statements. Two forms exist: for and if generate. An example which uses both is:
        First: if i=0 generate
          Q: adder port map (A(0), B(0), Cin, Sum(0), C(0));
        end generate;
        Second: for i in 1 to 3 generate
            Q: adder port map (A(i), B(i), C(i-1), Sum(i), C(i));
        end generate;
The components are addressed (e.g., for specification): First.Q, Second(1).Q, Second(2).Q, and Second(3).Q An external configuration specification might look like:
        for First -- First.Q
          for Q: adder use entity work.adder(architectureX);
          end for;
        end for;

        for Second(1) -- Second(1).Q
          for Q: adder use entity work.adder(architectureX);
          end for;
        end for;
        for Second(2) -- Second(2).Q
          for Q: adder use entity work.adder(architectureY);
          end for;
        end for;
        for Second(3) -- Second(3).Q
          for Q: adder use entity work.adder(architectureZ);
          end for;
        end for;
Note: that form is used in an external configuration. If you need it inside the architecture you have to insert a block (FAQ Part 4 - B.136) in the generate statement and place your configuration specification within that block (see attribute application).

If you have a VHDL'93 compliant tool, things are easier. The generate statement has a declarative part in which you can directly place any needed configuration specifications. An example:

        First: if i=0 generate
          for all: adder use entity work.adder(architectureX);
        begin
          Q: adder port map (A(0), B(0), Cin, Sum(0), C(0));
        end generate;
        Second: for i in 1 to 3 generate
          for all: adder use entity work.adder(architectureY);
        begin
            Q: adder port map (A(i), B(i), C(i-1), Sum(i), C(i));
        end generate;
 

4.2.4 Aggregates/Arrays Containing a Single Element

The question is often, whether
        -- array type "one_element" contains a single element
        subtype one_element IS bit_vector(1 TO 1); 

        type single IS record
                a : integer; 
             end record;

        signal o: one_element;
        signal s: single;
        ...
        s <= 1; -- first illegal try to assign a value to 
		-- the record
        s <= (1); -- second try, also not legal in VHDL
  
        o <= '1'; -- first illegal try to assign a value to 
	 	  -- the array
        o <= ('1'); -- second try, also illegal
is valid VHDL? It isn't. "Aggregates containing a single element association must always be specified using named association in order to distinguish them from parenthesized expressions." says the LRM. Therefore, "(1)" is simply a parenthesized expression (equal to "(((1)))").
        s <= (a => 1); -- ok
        o <= (1 => '1'); -- ok;
        o <= (others => '1'); -- also ok, because the compiler 
                              -- can derive from the context that 
                              -- there is only a single element
is valid. See FAQ Part 4 - B.7 for more information on aggregates.  

4.2.5 Operations With Array Aggregates

Often operations between vectors (e.g., bit_vector, std_logic_vector, unsigned, signed) are required where at least one of the arguments is a constant vector. An example is:
        signal Sig : bit_vector(7 downto 0);
        ...
        process
           if Sig = "00000000" then -- ok
           ...
        end process;
The compare operation will return true if each of the bits of "Sig" is equal to '0'.

While in this example the constant vector is defined as a bit string literal (see also FAQ Part 4 - B.33), it would be more convenient to define the zero vector using array aggregates (FAQ Part 4 - B.7), as shown in the next example:

        if Sig = (others => '0') then -- illegal!!!
        ...
The code given above fails to compile as the arguments for vector operators are usually defined as unconstrained arrays and VHDL does not require the size of the left and right operator argument to be equal. Hence, it is impossible for the compiler to determine the bounds of the constant value.

There are several solutions to this problem:

Note that the assignment symbol (signal or variable assignment) is not a VHDL operator. Hence, it is not possible to overload it (FAQ Part 4 - B.174). Furthermore, the size of the right-hand side of an assignment operation must match the size of its target. If the right-hand side is unconstrained, this requirement allows the constraint to "cross over" from the target and determine the bounds and direction of the right-hand side. Consequently, the following signal assignment statement is legal:
        Sig <= (others => '0'); -- ok
Another important issue when building aggregates is that a non locally static expression for a choice is allowed for an aggregate with a single choice only. I.e., the following code is illegal as "num" is not locally static:
	variable num : integer;
	...
	Sig <= (num => '1', others => '0'); -- illegal!!!
The compiler cannot determine which bit of the aggregate is set to '1' as "num" may vary during runtime. To achieve the desired behavior a process may be used:
	variable num : integer;
	...
	p: process (num)
	begin
	   Sig <= (others => '0');
	   Sig(num) <= '1';
  	end process;
 

4.2.6 How to Attach Attributes Inside of Generate

The '87 LRM is a bit confused as to whether the generate statement forms a declarative region, so it does not make provision for a declarative part in the syntax. However, it was decided that it indeed does form a declarative region (FAQ Part 4 - B.56), so in the '93 LRM we also included the syntax extension necessary for a declarative part. For '87, however, there is no such declarative part. The canonical method is to use an embedded block statement:
        G1:for i in DataIn'Low to DataIn'High generate  
           b: block                                 -- PJM addition
                attribute foo of RECV: label is 42; -- PJM addition
              begin                                 -- PJM addition
                RECV:CHVQA; [simplified-PJM]
              end block b;                          -- PJM addition
        end generate ;
cited from an article of Paul Mechini.  

4.2.7 Notes on Range Directions

Consider the following code
        signal r3, r4: Bit_Vector(3 downto 0);
        ...
        r3(0 to 1) <= r4(0 to 1);
Note the inverse range directions (FAQ Part 4 - B.193) of the slices (FAQ Part 4 - B.222) and the arrays "r3" and "r4". The 87 LRM does not clarify, if this is legal, but the official interpretation of the 87 language (VASG) says that the directions of slices must match the direction of the array being sliced. So it is not legal code. This decision is reflected in the VHDL'93 LRM.

Another related problem is

        signal r3, r4: Bit_Vector(3 downto 0);
        ...
        r3(0 downto 3) <= r4(0 downto 3);
As these slices are null slices (FAQ Part 4 - B.167) the code is legal. An array value of no elements (i.e., having a 'LENGTH of 0, FAQ Part 4 - B.165) can be assigned to an array having no elements, as long as their types are the same.  

4.2.8 Integer - Time Conversion

The following example converts integer (FAQ Part 4 - B.134) to time (FAQ Part 4 - B.182) and time to integer.
        architecture convert of test is
           signal a, c : integer := 20;
           signal b : time := 1 ns;
        begin
           process
           begin
              wait for 1 fs;
              a <= a + 1;
              b <= a * 1 fs;
              wait for 1 fs;
              c <= b / 1 fs;
           end process;
        end;
Please note that, depending on the actual value of signal b and the internal representation of time and integer values, the divide operation may overflow. E.g., some simulators use 32 bits to represent integers and 64 bits to store time values. In this case, the division operation may produce results that cannot be represented within 32 bits. E.g., 1 ms / 1 fs equals to 1E12, which cannot be stored as a 32-bit integer. Hence, choose the divisor carefully, based on the expected time values and the required resolution, in order to prevent overflows (e.g., 1 ms / 1 ps = 1E9 does not overflow 32 bits).  

4.2.9 "Don't Cares" in VHDL

The names given to the states (be it X, Z, don't care, even 0 and 1) of an enumeration type (FAQ Part 4 - B.85) have only the meaning brought by the subprograms of the package that defines the type. For example, 0 is 0 only because of the way "and", "or", etc., are written. Z is 'high impedance' and X is 'conflict' only because of the way the package, and particularly the resolution function is written. As for the "don't care", it has no particular meaning for simulation, and the STD_LOGIC package does not provide any semantics for it: it's a normal state.

For example in a case statement like

        variable address : std_logic_vector(5 downto 0);
        ...
        case address is 
           when "-11---" => ...
           when "-01---" => ...
           when others => ...
        end case;
an 'address' value of "111000" or "101000" will match the 'others' clause! Here is a solution to this problem:
        if (address(4 downto 3)="11") then ...
        elsif (address(4 downto 3)="01") then ...
        else ...
        end if;
Another solution is to use the "std_match" functions defined in the numeric_std package (see Section 4.8):
        if (std_match(address, "-11---") then ...
        elsif (std_match(address, "-01---") then ...
        else ...
        end if;
Partially extracted from an article by Jacques Rouillard.  

4.2.10 How to Open and Close Files

The answer depends on which version of the language you're using. In VHDL'87, files cannot be opened and closed under model control. Instead the file object must be re-elaborated (see FAQ Part 4 - B.81). The following example shows how access a file via a procedure:
        -- VHDL'87 example!
        -- Note the file is opend when get_file_data is 
	-- called and closed when it returns!
        procedure get_file_data(file_name : in string) is
           type int_file_type is file of integer; 
				-- see FAQ Part 4 - B.99
           file file_identifier : int_file_type is in file_name;
				 -- open file
        begin
           ... -- read in the file
           return; -- note, the file is closed now!
        end get_file;
        ...

        get_file_data("file1"); -- reads in the file named "file1"

Additionally, in VHDL'93 it is possible to open and close files under model control via file_open(...) and file_close(...).

Note that the syntax rule for file declaration in VHDL'87 and VHDL'93 are different. Moreover, the VHDL'87 syntax rule is not a subset of the corresponding VHDL'93 syntax rule.

File declartion in VHDL'87:

        type integer_file is file of integer;

        -- The following two declarations open a file for reading
        file file_ident1 : integer_file is "a_file_name1"; 
        file file_ident2 : integer_file is in "a_file_name2";

        -- The next declaration opens a file for writing
        file file_ident3 : integer_file is out "a_file_name3";
File declaration in VHDL'93:
        type integer_file is file of integer;

        -- The following two declarations open a file for reading
        file file_ident1 : integer_file is "a_file_name1";
        file file_ident2 : integer_file open read_mode is 
                                           "a_file_name2";

        -- The next declaration opens a file for writing
        file file_ident3 : integer_file open write_mode is 
                                            "a_file_name3";

        -- The next declaration opens a file for appending
        file file_ident4 : integer_file open append_mode is 
                                             "a_file_name4";

        -- Finally, in VHDL'93 it is possible to declare a file 
        -- identifier without associating it with a file name 
        -- directly. Use the (implicitly declared) procedures 
        -- file_open(...) and file_close(...) to open/close 
        -- the file.
        file file_ident5 : integer_file;
        ...
        file_open(file_ident5, "a_file_name5", read_mode); -- opens
                                -- "a_file_name5" for reading
        file_close(file_ident5); -- closes file

 

4.2.11 How to Read/Write Binary Files

The file formats for binary files read or written by VHDL are not standardized. However, each simulator should be able to read its own generated binary files. This gives you two choices: Partially extracted from an article posted by Wolfgang Ecker.

To read raw binary files (e.g. bitmaps) a solution was suggested by Edward Moore which should work at least with Modelsim: Open a file of type 'character', which in Modelsim translates to one byte of i/o. Use the READ procedure to read each character and the 'VAL and 'POS attributes to convert from character to integer. Note, this technique does not work with simulators that use only 7 bits to represent characters.  

4.2.12 How to Use Package Textio for Accessing Text Files

While a simulator should always be able to read back its own generated binary files it is often not possible to share binary files between different simulators. A portable way to access data from files is provided by the package IEEE.TextIO, which implements mechanisms to read and write data values in ASCII format. In detail, it defines subprograms to perform formatted I/O operations for the data types bit, bit_vector, boolean, character, integer, real, string and time.

The package TextIO introduces two new types: "line" and "text":

        type line is access string; -- line is a pointer to "string"
        type text is file of string;
Further, two procedures, readline and writeline, are declared that respectively read and write an entire line from or to a file. Finally, TextIO provides a set of overloaded procedures named "read" and "write" to respectively read or write data values of a specific data type from or to a line. In detail, separate read and write functions are defined for each of the data types bit, bit_vector, boolean, character, integer, real, string and time. The following code shows the declaration of procedures readline and writeline as well as the corresponding read and write procedures for type bit:
        procedure readline(file F : text; L : out line);
        procedure writeline(file F : text; L : inout line);

        procedure read(L : inout line; value: out bit;
                       good : out boolean);
        procedure read(L : inout line; value: out bit);

        procedure write(L : inout line; value : in bit;
                        justified : in side := right;
                        field : in width := 0); 
The parameter "good" of the first read procedure returns true if the operation was successful (and false otherwise) while the second read procedure generates a run-time error in case of an error occuring while reading. (Usually, an appropriate message is printed to the screen in this event.) The optional parameters "justified" and "field" of the write procedure controls the alignment (left or right) and (minimum) number of characters which are used to print the corresponding data value (i.e., remaining characters not required to print the data value are filled with blanks). The write procedure for type real has an additional parameter named "digits", which specifies the number of digits following the decimal point.

As mentioned before the subprograms read and write do not directly access files but instead read and write data from or to a line. This approach allows the same file to be accessed simultaneously from several processes. Hence, in order to write data to a file, a line is first created and preloaded with the corresponding text. Then, it is atomically written to the file via writeline. Similarly, reading data from a file is performed as a sequence of readline and read operations. Note that a single line may contain several data values, as shown in the following example:

        use std.TextIO.all;

        entity top is
        end top;
        architecture arch of top is

           type data_set is record
              bvec : bit_vector(0 to 7);
              int : integer;
           end record;

           type data_set_vec is array (natural range <>) of data_set;

           procedure read_from_file(file_name : string;
                                    dr : out data_set_vec) is
              file data_file : text open read_mode is file_name;
              variable L : line;
              variable i : integer := dr'low;
           begin
              while not endfile(data_file) loop
                 -- note that bvec and int are read from the
                 -- SAME line
                 readline(data_file, L);
                 read(L, dr(i).bvec);
                 read(L, dr(i).int);
                 deallocate(L); -- just to make sure that no memory
                                -- is lost
                 i := i + 1;
              end loop;
           end read_from_file;

           procedure write_to_file(file_name : string;
                                   dw : data_set_vec) is
              file data_file : text open write_mode is file_name;
              variable L : line;
           begin
              for i in dw'range loop
                 -- note that bvec and int are written to the
                 -- SAME line
                 write(L, dw(i).bvec);
                 write(L, dw(i).int);
                 writeline(data_file, L);
              end loop;
           end write_to_file;

           signal data : data_set_vec(0 to 10) :=
                 ( 1 => ((others => '1'), 0),
                   3 => ((others => '1'), -1),
                   others => ((others => '0'), 2));
        begin

         p: process
            variable data_read : data_set_vec(0 to 10);
         begin
            -- write data to file
            write_to_file("testfile.txt", data);
            -- then, read it back from file
            read_from_file("testfile.txt", data_read);
            -- compare read with written data
            assert data_read /= data
               report "Read data is ok!"
               severity note;
            wait; -- end process
         end process;

        end arch; 
Note that all procedures modify their parameter L (and hence modify the string referred to by L): Note that directly assigning to variables of type line may introduce memory leaks. E.g., the following procedure will leak memory each time it is called:
        use std.TextIO.all;
        ...
        procedure memory_leak is
           variable L : line;
        begin
	   L := new string(1 to 16);
	   L.all := "this will create";
           -- the next assignment creates a memory leak as
           -- L is not deallocated!
	   L := new string(1 to 12); 
	   L.all := "memory leaks";
           -- as assigning null to L does not automatically 
           -- deallocates memory the following line also 
           -- leaks memory
           L := null;
	   L := new string(1 to 1); 
           -- further, returning without deallocating L will 
           -- most probably create another memory leak!
        end procedure;
In addition, TextIO contains a function Endfile, which is a predicate that indicates whether the next call to Readline will fail. (That is, as long as Endfile returns false, the next Readline on the same file will succeed.)

Further, two special files "output" and "input" are defined in IEEE.TextIO to support console I/O operations. In conjunction with the readline, writeline, read, and write procedures, they may be used to print text to the screen or to read data from the keyboard.

Note that the TextIO package is for simulation only! Hence, it cannot be synthesized. If you want to feed source code that includes some TextIO related statements to your synthesis tool, then use appropriate synthesis commands to switch off synthesis for the offending statements (e.g., with Synopsys synthesis tools you may use embedded "synthesis off / synthesis on" pseudo-comments).  

4.2.13 Signal Drivers

Each concurrent statement (see FAQ Part 4 - B.44) that executes (namely, processes, concurrent signal assignment statements, and concurrent procedure calls) has a separate driver (see FAQ Part 4 - B.78) for the longest static prefix (see FAQ Part 4 - B.151) of each signal that is target of a signal assignment statement within the concurrent statement (Concurrent asserts execute, but are always passive; that is, they contain no drivers as they never assign to signals).

It does not matter whether or not a specific signal assignment statement will actually be executed. For example, the following process "p" contains two drivers, one each for signals "s1" and "s2":

        signal s1, s2 : integer;
        ...
        p: process (s1, s2)
        begin
          s1 <= 1;
          if false then -- note, the condition always evaluates to
          false
            s2 <= 1; -- this line actually will be never executed!
          end if;
        end process; 
Hence, should there be another process driving signal "s2", the model contains multiple drivers for an unresolved signal (see FAQ Part 4 - B.204), which is an error.

Note that in the case of a process, there is a maximum of one driver per signal driven, no matter how many assignments to that signal appear within the process. For example, the following process has two drivers, one each for each of the signals Q and QBar:

        process (Clk, Clr)
        begin
          case To_X01( Clr ) is
            when '1' =>
              Q <= '0';
              QBar <= '1';

            when '0' =>
              if rising_edge( Clk ) then
                Q <= D;
                QBar <= not D;
              end if;

            when 'X' =>
              Q <= 'X';
              QBar <= 'X';
          end case;
        end process; 
The initial value of a driver is defined by the default value associated with the signal (see FAQ Part 4 - B.58). Because in the first example no explicit default value is given for "s1" and "s2", the initial value of the drivers for both signals will be set to the left bound of the integer type (usually -2147483647).

Further, VHDL needs to be able to statically (that is, during static elaboration) determine all drivers of a signal, in order to create a static network topology. A driver is created for the longest static prefix of each target signal. During elaboration the compiler analyzes the target of each signal assignment statement to determine the smallest portion of the signal that can be statically determined as being driven by the concurrent statement. For example, the following model is erroneous, as both the process "p" and the concurrent signal assignment both drive "sig(3)", an unresolved signal.

        architecture behave of test is
          signal sig : bit_vector(0 TO 7);
          constant c : integer := 3;
        begin
          p: process (sig)
          begin
            for i in 1 to 1 loop
              sig(i) <= '1'; -- signal assignment statement
            end loop;
          end process;

          sig(c) <= '1'; -- concurrent signal assignment driving
			 -- "sig(3)"
        end behave; 
In this example, the longest static prefix of the target of the assignment statement "sig(i) <= '1'" is the entire signal "sig", since "sig" is a static signal name and "i" is a loop constant and hence not static. Consequently, "p" has a driver for the entire signal "sig", although actuality only "sig(1)" will be driven by the process. Further, the longest static prefix of the concurrent signal assignment is "sig(3)", since "c" is a statically elaborated constant equal to 3. Hence, an error message should be generated to the effect that several processes are driving "sig(3)".

Note that the longest static prefix of a target of a signal assignment is determined at elaboration time. For example,

        entity test is
          generic (g : integer range 0 to 7);
        end test;
        architecture behave of test is
          signal sig : bit_vector(0 to 7);
        begin
          sig(2) <= '1'; -- concurrent signal assignment #1
			 -- driving "sig(2)"
          sig(g) <= '1'; -- concurrent signal assignment #2
			 -- driving "sig(g)"
        end behave; 
The longest static prefix of "sig(g)" is the element of "sig" determined by the generic parameter "g" (and NOT all elements of signal "sig"), since "g" is constant during elaboration. Hence, an error will only occur if "g" is set to 2 during elaboration:
        -- this component instantiation will produce no error

        comp_ok: entity test(behave) generic map(0);

        -- the following instantiation is erroneous since
	-- both concurrent signal assignment statements of
	-- component "comp_error" are driving "sig(2)"
	-- (which is not of a resolved type).
        comp_error: entity test(behave) generic map(2); 
There are situations where it is useful to drive a signal by several processes; for example, when the signal represents a tristate bus. However, VHDL does not build in any resolution mechanisms, so this situation requires that a resolution function (see FAQ Part 4 - B.202) be associated with the multiply driven signal. The following simple example shows how to use the resolved type "std_logic" (defined in the package "std_logic_1164") to drive a signal with two concurrent signal assignments:
        library IEEE;
        use IEEE.std_logic_1164.all;
        architecture test_arch of test is
          -- "std_logic" is a resolved multi-value logic type
	  -- defined in the package "std_logic_1164".
          signal source1, source2, target : std_logic;
          signal control : bit;
        begin
          -- depending on the value of "control" either the value
	  -- of "source1" or "source2" is assigned to "target".
          target <= source1 when control = '1' else 'Z'; -- driver #1
          target <= source2 when control = '0' else 'Z'; -- driver #2
        end test_arch; 
 

4.2.14 Procedures and Drivers

Procedures may contain signal assignment statements. In this case, the driver or drivers (see FAQ Part 4 - B.78) corresponding to these assignments are not associated with the procedure, but with the process(es) calling the procedure. As stated in Section 4.2.13, VHDL needs to be able to statically determine all drivers of a signal. Hence, unless the procedure is declared within a process (and therefore callable only by the process), the procedure must drive only signals passed as parameters to the procedure. This restriction allows the elaborator to determine the signals that are driven by a given process, so that the drivers for the process can be identified during elaboration.

For example,

        architecture behave of test is
          signal s1, s2 : std_logic;

          -- "global" may be called by several processes.
	  -- Consequently, it must drive only signals passed
	  -- as parameters.
          procedure global(signal proc_sig : out std_logic) is
          begin
            proc_sig <= '0';
          end global;

        begin
        -- "p1" has a driver for "s1" and "s2"
        p1: process (...)
          -- "local" can be called by "p1" only. Hence it can
	  -- directly write to signal "s1"
          procedure local is
          begin
            s1 <= '0';
          end local;
        begin
          local; -- creates a driver for "s1"
          global(s2); -- creates a driver for "s2"
        end process;

        -- "p2" has a driver for "s1"
        p2: process (...)
        begin
          global(s1); -- created a driver for "s1"
        end process;
        end test_arch; 
 

4.2.15 Case Statement

For each case statement the compiler must verify that alternatives defined by the type of the selection expression are covered exactly once by the set of choices. (This condition is also required of selected signal assignment statements.) A necessary condition of this check is that the choices must be locally static (see FAQ Part 4 - B.147 and Section 4.2.39); i.e., they must be determinable at compilation time and thereafter fixed. The following example meets these conditions:
        subtype my_int is integer range 0 to 2;
        signal sig : my_int;
        constant const_a : my_int := 0;
        ...
        case sig is
           when const_a => ...
           when 1 => ...
           when 1+1 => ...
        end case; 
while this example fails to compile as it violates the above requirements:
        entity test is
          generic (gen_b : bit := '1');
        end test;
        architecture erroneous of test is
          signal sig : bit;
          variable var_a : bit := '0';
        begin
          ...
          case sig is
             when var_a => ... -- error: "var_a" is not locally
			       -- static!
             when gen_b => ... -- error: "gen_b" is not locally
			       -- static!
          end case;
          ...
        end erroneous; 
If you find that you must use non-locally static choices in a case statement, you must instead use a if-then-else chain. For example, the above architecture can be rewritten correctly as:
        architecture correct of test is
          signal sig : bit;
          variable var_a : bit := '0';
        begin
          ...
          if sig = var_a then
            ...
          elsif sig = gen_b then
            ...
          end if;
        ...
        end correct; 
 

4.2.16 How to Monitor Signals

VHDL does not grant direct access to signals at arbitrary locations in the design hierarchy. Hence, you cannot directly read or write signals at the top-level interface from the testbench.

There are three solutions to this problem:

 

4.2.17 Resolving Ambiguous Procedure/Function/Operator Calls

VHDL uses the parameter and result type profiles of functions and procedures to uniquely determine the subprogram to call. (Enumeration literals also have parameter and result type profiles.) In detail, it uses: to identify the subprogram (see also FAQ Part 4 - B.177 and faq4ref(parameter and result type profile, B.178)). For example, consider the following two packages, "A" and "B":
        package A is
          function func(p1 : in integer; p2 : in bit := '1')
						return integer;
          procedure proc(p1: inout bit);
          function "="(p1 : bit; p2 : bit) return bit;
        end package;

        package B is
          -- note, "func" of both packages differ in their result
	  -- type only
          function func(p1 : in integer; p2 : in bit) return bit;
          -- "proc" has the same parameter profile than "proc"
	  -- of package "A"
          procedure proc(x1: inout bit);
          function "="(p1 : bit; p2 : bit) return bit;
        end package; 
The subprograms "func" and "proc" may be used as follows
        use work.A.all; -- make declarations of package "A" visible
        use work.B.all; -- make declarations of package "B" visible
        architecture behave of test is
          signal int_signal : integer;
          signal bit_signal : bit;
        begin
          -- calls "func" from package "A":
          int_signal <= func(int_signal, bit_signal);
        
          -- calls "func" from package "A":
          int_signal <= func(int_signal);
        
          -- calls "func" from package "B" because the result type of
          -- "B.func" matches the type of "bit_signal"
          bit_signal <= func(int_signal, bit_signal);
        
          -- calls "proc" from package "A"
          proc(p1 => bit_signal);
        
          -- calls "proc" from package "B"
          proc(x1 => bit_signal);
        end behave; 
There are situations where it is not possible for the compiler to uniquely select a subprogram. Expanded names can often be used to identify the correct subprograms in such cases (see also FAQ Part 4 - B.90):
        use work.A.all; -- make declarations of package "A" visible
        use work.B.all; -- make declarations of package "B" visible
        architecture behave of test is
          signal int_signal : integer;
          signal bit_signal : bit;
        begin
          -- use selected names to identify which subprogram to call
          work.A.proc(bit_signal); -- calls "proc" from package "A"
          work.B.proc(bit_signal); -- calls "proc" from package "B"
        
           -- selected names can be use to identify operators as well
           -- calls "=" from "A":
           bit_signal <= work.A."="(bit_signal, bit_signal);
        
           -- calls "=" from "B":
           bit_signal <= work.B."="(bit_signal, bit_signal);
        end behave; 
 

4.2.18 How to Resolve Type Ambiguities in Expressions

VHDL is a strongly typed language. Hence, the compiler does not perform any implicit type conversions or attempt to "guess" the type of an expression. VHDL provides two mechanisms to change or fix the type of an expression:
1. "Type conversion" may be used to change the type of an expression (see also FAQ Part 4 - B.243). In VHDL, type conversions are allowed only between types that are "closely related" (see also FAQ Part 4 - B.40). Two types are closely related if and only if one of the following conditions hold:

Note, during conversion some information might be lost. E.g., converting a real to an integer will round. A type conversion is coded by enclosing the expression to be converted in parenthesis and prepending the target type name. The following example shows how to convert between values of the types integer and real:

        variable int : integer;
        variable float : real;
        ...
        int := integer(float); -- converting real to integer
        real := real(int); -- converting integer to real 
2. "Qualified expressions" may be used to explicitly identify the type, and possibly the subtype, of an expression. Such qualification is a "hint" to the compiler, informing it as to the desired interpretation of the expression being qualified. Hence, such qualification is legal only if the expression can be legally interpreted as a value of the qualifying type. In contrast to type conversion, no information loss can occur.

The syntax of a qualified expression is similar to the syntax of a type conversion. The difference is that a "tick" (the ''' character) is inserted between the type name and the parentheses surrounding the expression, as shown in the next example:

        -- note, both enumeration type declarations have some common
        -- enumeration items
        type color1 is (violet, blue, green, yellow, red);
        type color2 is (black, blue, green, red);

        -- this array type has an index type "color1". Without
        -- using type qualification this example will not compile
        -- because the compiler cannot determine the type
        -- of "blue" (might be "color1" or "color2").
        type a1 is array(color1'(blue) to color1'(red));

        -- this array type has an index type "color2". Without
        -- using type qualification this example will not compile
        type a2 is array(color2'(blue) to color2'(red)); 
A common use for qualified expressions is in disambiguating the meaning of strings and aggregates (FAQ Part 4 - B.7), whose type can only be determined from context. For example, the textio package has write procedures for both strings and bit vectors, so the following example is ambiguous:
        variable L: Std.TextIO.Line;
        ...
        Std.TextIO.Write( L, "1001" ); 
The problem here is that the string "1001" can be interpreted either as a character string or as a bit vector. Since textio contains write procedures for both strings and bit vectors, the VHDL analyzer cannot determine which write procedure to call, so an error is generated.

The fix is simple: Use a qualified expression to disambiguate the string. Either of the following lines may be substituted for the ambiguous line above:

        Std.TextIO.Write( L, String'("1001") ); 
or
        Std.TextIO.Write( L, Bit_Vector'("1001") ); 
For many typical conversion problems that cannot be solved by VHDL "type conversion" or "type qualification" a set of appropriate conversion functions were defined in various packages. Hence, before writing your own routine check out the corresponding packages (e.g., a table of conversion functions defined in ieee.std_logic_1164 and ieee.numeric_std is available from http://www.ce.rit.edu/pxseec/VHDL/Conversions.htm).  

4.2.19 How to Use Bit Strings as Argument to the To_StdLogicVector Function

An easy way to initialize a std_logic_vector or std_ulogic_vector in VHDL is to define the bit pattern via a bit string. However, when using the To_StdLogicVector function to convert a bit string into a std_logic_vector VHDL'87 and VHDL'93 compliant compilers behave differently. For example:
        use IEEE.std_logic_1164.all;
        use IEEE.std_logic_unsigned.all; -- See Section 4.11

        -- This will NOT compile with a VHDL'93 compliant compiler!
        constant c : std_logic_vector(31 downto 0)
                := To_StdLogicVector(x"0080_0000"); 
While the example shown above is valid in VHDL'87, the function call to To_StdLogicVector is ambiguous in VHDL'93. In VHDL'87, bit strings (such as x"0080_0000") can only be of type Bit_Vector. However, in '93, bit strings can be of any one-dimensional array type that includes the values '1' and '0'. So, in '93, there are three possible interpretations of the bit string in the above example: Since both the following functions exist, the expression is ambiguous in '93:
        function To_StdLogicVector (P: Bit_Vector)
                return std_logic_vector;

        function To_StdLogicVector (P: std_ulogic_vector)
                return std_logic_vector; 
The expression is not ambiguous in '87 since there's only one interpretation of the bit string.

A universal way (i.e., one that works in both versions of VHDL) to create a constant is:

        constant c: std_logic_vector(31 downto 0)
                := To_StdLogicVector(Bit_Vector'(x"0080_0000"));
Here, type qualification is used to select the desired interpretation of the bit string. Therefore, the call to To_StdLogicVector is unambiguous in VHDL'93.

Of course, for this particular example, it may be easier to use an aggregate and avoid this whole issue:

        constant c: std_logic_vector(31 downto 0)
                := (22 => '1', others => '0'); 
Note, while all simulators should handle the above constructs, synthesis tools still vary widely in their ability to handle aggregates and conversions.  

4.2.20 Conflicting Compare Operators

As described in Section 4.11, not all packages that are usually stored in library IEEE are really standardized. E.g., while std_logic_1164 is a standard package approved by the IEEE, package std_logic_unsigned is provided by Synopsys but not supported by IEEE. Unfortunately, Synopsys introduced a flaw into std_logic_unsigned (as well as into package std_logic_signed) that is flagged by some compilers. An example where this error may show up is shown below:

       use ieee.std_logic_1164.all;	-- IEEE package
       use ieee.std_logic_unsigned.all;	-- Synopsys package
       ...
       variable v1, v2 : std_logic_vector(3 downto 0);
       ...
       if v1 = v2 then	-- error! Neither the implicit "="
			-- operator (defined in std_logic_1164 nor the
			-- explicit operator (defined in std_logic_unsigned)
			-- are directly visible here.
          ...
In std_logic_1164, type STD_LOGIC_VECTOR is defined along with an appropriate compare operator, "=", that is automatically (and implicitly) created by the type declaration of STD_LOGIC_VECTOR. This implicitly defined operator conflicts with an explicitly defined compare operator "=" introduced in std_logic_unsigned. Due to the visibility rules of VHDL, both operators become invisible after the second use clause. As a result, the subsequent compare operation, "v1 = v2", is invalid as no appropriate (directly visible) "=" operator is found at this point in the source code (see also
Section 4.2.36).

While some compilers ignore this conflict and automatically (and improperly) choose the explicit operator declaration (from package std_logic_unsigned), other tools (properly) flag an error. There are several solutions to overcome this problem:

Note that the reason for this conflict is that the operator function "=" from std_logic_unsigned is not located in the same package as the type STD_LOGIC_VECTOR (which resides in package std_logic_1164). To prevent these kind of problems, type definitions and their associated explicit operators should be located in the same package (same declarative region). Then, the compiler will always choose the explicit operator.

 

4.2.21 How to Convert Between Enumeration and Integer Values

An enumeration type declaration defines a type as an ordered set of enumeration literals (FAQ Part 4 - B.85). Each enumeration literal has a unique "position number," which is an integer value. (Values of integer and physical types also have position numbers.) The position number of the leftmost enumeration literal is 0, the next literal has the position number 1, etc. For example,
        -- In the following type, the enumeration literal "violet"
	-- has the position number 0, "blue" has the position
	-- number 1, "green" has 2, "yellow" has 3, and "red" has
	-- the position number 4.
       type color is (violet, blue, green, yellow, red); 
Two predefined attributes exist to convert between values and their associated position numbers (FAQ Part 4 - B.77). The predefined attribute 'pos, when given a type and the value, will provide the position number. The predefined attribute 'Val, when given a type and a position number, will return the value corresponding to the position number (FAQ Part 4 - B.24). For example, given the above type color, the following assertions never fail:
        assert color'pos(violet) = 0;
        assert color'val(0) = violet; 
(The type must be provided since enumeration literals may be overloaded.) For example, the following assertion does not fail:
        assert bit'pos('0') /= character'pos('0'); 
Some other examples:
        signal hat : color := blue;
        signal int : integer := 0;
        ...

        -- this is equal to "int <= 1"
        int <= color'pos(green);

        -- assigns the position number of enumeration
	-- value associated with the value of "hat"
        int <= color'pos(hat);

        -- this is equal to "hat <= violet"
        hat <= color'val(0);

        -- assigns the enumeration value associated
	-- with position "int"
        hat <= color'val(int); 
Note that the predefined type character is an enumeration type. Hence, attributes 'pos and 'val may be used to convert character to and from their integer (ASCII) equivalent:
       signal char : character := 'a';
       signal int : integer := 32;
       ...
       -- converts character 'b' to integer (ASCII)
       int <= character'val ('b');

       -- converts character stored in char to
       -- integer (ASCII)
       int <= character'val (char);

       -- converts integer stored in int to
       -- character
       char <= character'pos (int);
 

4.2.22 How to Convert Between ASCII and Characters

See Section 4.2.21.  

4.2.23 How to Convert Between Scalar Values and Strings

In VHDL'93 a mechanism to convert between strings and scalar types is implemented. The attribute (FAQ Part 4 - B.24) "T'image(...)" converts a scalar value into its string representation, while "T'value(...)" is used to transform a string into the corresponding value of type T, where T is the name of a scalar type or subtype. T'image(...) is often used to report the value of an scalar object on the screen during simulation, as shown in the following example:
        type color is (violet, blue, green, yellow, red);

        process (...)
          variable var : color;
          variable int : integer;
          variable str : string := "yellow";
        begin
          -- "color'image(var)" may be used to output the
	  -- value of variable "var" on the screen
          report "The value of var is" & color'image(var);

          -- "color'value(str)" returns the value
	  -- associated with the string "str"
          var := color'value(str);

          -- "integer'image(int)" returns the textual
	  -- representation of "int"
          report " while the value of int is " & integer'image(int);

          -- the following code sequence will store the
	  -- value 123 into "int"
          str := "123";
          int := integer'value(str);
        end process; 
However, VHDL'87 does not provide this attribute. On such a system the appropriate "write" function from the textio package may be used to plot the value into a string.

Another option to convert enumeration values to their corresponding string representation in VHDL'87 is to create an array of strings where each array element stores the string representation of an enumeration item. For example,

        type color is (violet, blue, green, yellow, red); -- the type
	-- next, define an array of strings index by "color"
	-- and create a table of "color names"
	type color_table is array (color) of string(1 to 7);
	constant color2str : color_table :=
	   ( "violett", "blue ", "green ", "yellow ", "red ");
	...
	variable color_obj : color;
	...
	-- color2str usage
	report "value of color_obj is " & color2str(enum_obj); 
 

4.2.24 How to Convert Bit/Std_Logic_Vectors to Strings

As mentioned in the previous section the predefined attribute "image" is only applicable on scalars. Hence, bit_vectors or std_logic_vectors cannot be directly converted to strings using build-in VHDL mechanisms. However, there are packages listed in Section 4.10 that provide this functionality.  

4.2.25 How to Convert Between Integer and Bit/Std_Logic-Vectors

There are two IEEE-standard packages that provide functionality to convert between integers and either bit_vectors or std_logic_vectors: Both packages (see Section 4.8 on how to get these packages) define two new vector types: SIGNED and UNSIGNED. SIGNED vectors represent two's-complement integers, while UNSIGNED vectors represent unsigned-magnitude integers. Each package includes four conversion functions:
        function TO_INTEGER (ARG: UNSIGNED) return NATURAL;
        -- Result subtype: NATURAL.  Value cannot be negative since
        -- parameter is an UNSIGNED vector.
        -- Result: Converts the UNSIGNED vector to an INTEGER.

        function TO_INTEGER (ARG: SIGNED) return INTEGER;
        -- Result subtype: INTEGER
        -- Result: Converts a SIGNED vector to an INTEGER.

        function TO_UNSIGNED (ARG, SIZE: NATURAL) return UNSIGNED;
        -- Result subtype: UNSIGNED(SIZE-1 downto 0)
        -- Result: Converts a non-negative INTEGER to an UNSIGNED
        -- vector with the specified size.

        function TO_SIGNED (ARG: INTEGER; SIZE: NATURAL) return
        SIGNED;
        -- Result subtype: SIGNED(SIZE-1 downto 0)
        -- Result: Converts an INTEGER to a SIGNED vector of the
        -- specified size.  
The first two functions may be used to convert a UNSIGNED or SIGNED vector to an integer. For example,
        library IEEE;
        use IEEE.std_logic_1164.all;
        use IEEE.numeric_std.all;
        ...
        variable int : integer;
        variable unsigned_vec : UNSIGNED(0 to 7);
        variable signed_vec : SIGNED(0 to 7);
        ...
        int := TO_INTEGER(unsigned_vec);
        int := TO_INTEGER(signed_vec); 
Note that a value of type bit_vector or std_logic_vector must be converted to SIGNED or UNSIGNED before calling TO_INTEGER. This conversion (see also Section 4.2.18) is necessary in order to determine the interpretation of the most-significant bit of the vector:
        variable int : integer;
        variable bvec : bit_vector(0 to 7);
        ...
        int := TO_INTEGER(UNSIGNED(bvec)); -- bvec is treated as a
                                           -- unsigned magnitude
                                           -- representation of an
					   -- integer value
        int := TO_INTEGER(SIGNED(bvec)); -- bvec is treated as a
					 -- two's-complement
					 -- representation of an
                                         -- integer value 
TO_UNSIGNED and TO_SIGNED may be used to convert a integer value into a corresponding signed or unsigned vector, as shown in the following example. The second parameter to each function determines the size of the resulting vector:
        variable int : integer := 1;
        variable unsigned_vec : UNSIGNED(0 to 7);
        variable signed_vec : SIGNED(0 to 7);
        ...
        unsigned_vec := TO_UNSIGNED(int, unsigned_vec'Length);
        signed_vec := TO_SIGNED(int, signed_vec'Length); 
A SIGNED or UNSIGNED value can be transformed into a std_logic_vector using explicit conversion (see also Section 4.2.18 and Section 4.2.26). The conversion from std_logic_vector (or std_ulogic_vector) to bit_vector is done using function To_bitvector (from package ieee.std_logic_1164):
        variable bvec : bit_vector(0 to 7);
        variable slvec : std_logic_vector(0 to 7);
        ...
        slvec := std_logic_vector(TO_UNSIGNED(int, slvec'Length));
        bvec := to_bitvector(slvec);
Note: Synopsys has produced three packages: std_logic_arith, std_logic_signed, and std_logic_unsigned that are intended to provide functionality similar to numeric_bit and numeric_std. In particular, the same two types, SIGNED and UNSIGNED are defined. Moreover, the packages include functions to convert between vectors and integers. These packages are typically even installed in the library IEEE. However, these packages are NOT standard, and different vendors have different and mutually incompatible versions. Also, there are naming clashes when some of these packages are used together. So, it is recommended that numeric_bit or numeric_std be used in preference to these non-standard packages.

See Section 4.11 for a more detailed discussion on arithmetic packages for bit_vectors and std_logic_vectors.  

4.2.26 How to Convert Between bit_vector, std_logic_vector, std_ulogic_vector, signed and unsigned

In the package ieee.std_logic_1164 a set of functions to convert between bit_vectors, std_logic_vectors and std_ulogic_vectors are defined. The functions to transform either std_logic_vectors or std_ulogic_vectors to bit_vectors are:
        function To_bitvector(s : std_logic_vector; xmap : BIT := '0')
                 return bit_vector;

        function To_bitvector(s : std_ulogic_vector; xmap : BIT := '0')
                 return bit_vector;
The std_(u)logic values '0' and 'L' are translated to bit value '0' while '1' and 'H' are mapped to '1'. The translation of the remaining std_logic values ('U', 'X', 'Z', '-' and 'W') is determined by parameter xmap (whose default is '0').

The following two functions are provided to convert from std_logic_vector and std_ulogic_vector to bit_vector:

        function To_StdLogicVector(b : bit_vector)
                 return std_logic_vector;

        function To_StdULogicVector(b : bit_vector)
                 return std_ulogic_vector;
An example showing the usage of these functions is:
        variable slv_vec  : std_logic_vector(0 to 7);
        variable sulv_vec : std_ulogic_vector(0 to 7);
        variable bvec     : bit_vector(0 to 7);
        ...
        slv_vec  := To_stdlogicvector(bvec);
        sulv_vec := To_stdulogicvector(bvec);
        bvec     := To_bitvector(slv_vec);
        bvec     := To_bitvector(sulv_vec);
Note that the types std_logic_vector, std_ulogic_vector, signed and unsigned are all closely related to each other (see FAQ Part 4 - B.40 and Section 4.2.18). Hence, the explicit conversion can be used to transform the types as needed--no conversion functions are, in fact required (although they are provided by the packages std_logic_1164 and numeric_std). An example showing the use of explicit conversion is:
        variable slv_vec  : std_logic_vector(0 to 7);
        variable sulv_vec : std_ulogic_vector(0 to 7);
        variable uns_vec  : unsigned(0 to 7);
        variable sgn_vec  : signed(0 to 7);
        ...
        slv_vec  := std_logic_vector(sulv_vec);
        sulv_vec := std_ulogic_vector(slv_vec);
        slv_vec  := std_logic_vector(uns_vec);
        slv_vec  := std_logic_vector(sgn_vec);
        uns_vec  := unsigned(slv_vec);
        sgn_vec  := signed(sulv_vec);
        uns_vec  := unsigned(sgn_vec);
Conversion from bit_vector to either signed or unsigned takes place in two steps. First, the value must be converted to a std_logic_vector and then to the target type (see also Section 4.2.25):
        variable bvec    : bit_vector(0 to 7);
        variable uns_vec : unsigned(0 to 7);
        variable sgn_vec : signed(0 to 7);
        ...
        bvec    := to_bitvector(std_logic_vector(uns_vec));
        sgn_vec := to_unsigned(to_stdlogicvector(bvec));
 

4.2.27 Reduction Operators for Bit-Vectors

There is no predefined VHDL operator to perform a reduction operation on all bits of vector (e.g., to "or" all bits of a vector). However, the reduction operators can be easily implemented:
        signal a : bit;
        signal a_vec : bit_vector(0 to 10);
        ...
        -- this concurrent assignment performs an "or"
	-- reduction on "a_vec"
        a <= '0' when (a_vec = (a_vec'range => '0')) else '1';

        -- while this calculates an "and" reduction
        a <= '1' when (a_vec = (a_vec'range => '1')) else '0'; 
Note that these approaches may not produce the same results as a chain of or/and gates if the input vectors are of type std_(u)logic_vector and contain other values than '0' or '1' (e.g., 'X' or 'Z'). For example, the or-reduction approach will assign '1' to the output signal if at least one element of the input vector is 'X'. However, if all other elements are '0' the result should actually be 'X'.

A more general method (which also handles 'X' values correctly) is to loop through the bits in the manner shown for an or-reduction operator on std_logic_vectors:

        function or_reduce( V: std_logic_vector )
					return std_ulogic is
          variable result: std_ulogic;
        begin
          for i in V'range loop
            if i = V'left then
              result := V(i);
            else
              result := result OR V(i);
            end if;
            exit when result = '1';
          end loop;
          return result;
        end or_reduce;
        ...
        b <= or_reduce( b_vec ); 
Finally, a package including various reduce operator functions (and_reduce, or_reduce, xor_reduce, ...) can be downloaded from http://vhdl.org/vi/vhdlsynth/reduce_pack.vhd.  

4.2.28 Gray Code Counter Model

The following model implements a simple Gray code counter with adjustable counter width (SIZE). For a more sophisticated model see Section 4.10.
        entity gray_counter is
          generic (SIZE : Positive range 2 to Integer'High);
          port (clk : in bit;
                gray_code : inout bit_vector(SIZE-1 downto 0));
        end gray_counter;

        architecture behave of gray_counter is
        begin

        gray_incr: process (clk)
           variable tog: bit_vector(SIZE-1 downto 0);
        begin
          if clk'event and clk = '1' then
            tog := gray_code;
            for i in 0 to SIZE-1 loop
              tog(i) := '0';
              for j in i to SIZE-1 loop
                tog(i) := tog(i) XOR gray_code(j);
              end loop;
              tog(i) := NOT tog(i);
              for j in 0 to i-1 loop
                tog(i) := tog(i) AND NOT tog(j);
              end loop;
            end loop;
            tog(SIZE-1) := '1';
            for j in 0 to SIZE-2 loop
              tog(SIZE-1) := tog(SIZE-1) AND NOT tog(j);
            end loop;
            gray_code <= gray_code XOR tog;
          end if;
        end process gray_incr;

        end behave; 
Based on a posting by Rajkumar.  

4.2.29 Is There a printf() Like Function in VHDL?

The easiest way to implement a similar functionality in VHDL is by using the image attribute of VHDL-93. See Section 4.2.21 for further information.

For a package providing C-style formatted printing see Section 4.10.  

4.2.30 How to Code a Clock Divider

The following example will divide the clock frequency of the "ClkIn" signal by "Modulus" and output it on "ClkOut". It produces a symmetric output waveform if "Modulus" is even, otherwise it stays low for one input clock longer than it stays high (for a VHDL model with 50%-duty-cycle for odd divisor rates see http://www.e-insite.net/ednmag/archives/1997/081597/17di_01.htm; the architecture of some "unusual" clock dividers is shown in http://www.xilinx.com/xcell/xl33/xl33_30.pdf).
        entity ClockDivider is
          generic(Modulus: in Positive range 2 to Integer'High);
          port(ClkIn: in bit;
               Reset: in bit;
               ClkOut: out bit);
        end ClockDivider;

        architecture Behavior of ClockDivider is
        begin
          process (ClkIn, Reset)
            variable Count: Natural range 0 to Modulus-1;
          begin
            if Reset = '1' then
              Count := 0;
              ClkOut <= '0';
            elsif ClkIn = '1' and ClkIn'event then
              if Count = Modulus-1 then
                Count := 0;
              else
                Count := Count + 1;
              end if;
              if Count >= Modulus/2 then
                ClkOut <= '0';
              else
                ClkOut <= '1';
              end if;
            end if;
          end process;
        end Behavior; 
 

4.2.31 How to Stop Simulation

In VHDL, simulation normally stops when there are no more pending events (see FAQ Part 4 - B.88) anywhere in the system. Since, with the exception of clocks, it is often the case that there are only a finite number of events coded in a test bench, a simulation will naturally come to an end when all input events are exhausted, provided that clocks do not run indefinitely. An easy way to stop a clock is to use a clock enable signal. For example, here is a small design entity that generates a clock that will run only for a certain period of time:
        library IEEE;
        use IEEE.std_logic_1164.all;
        entity ClkGen is
          port( ClkPd: in Time range 0 ns to Time'High;
                RunTime: in TIme range 0 ns to Time'High;
                ClkOut: out std_ulogic);
        end ClkGen;

        architecture Behavior of ClkGen is
          signal ClkEna: std_ulogic := '1';
          signal IntClk: std_ulogic := '0';
        begin
          ClkEna <= '0' after RunTime;
          IntClk <= ClkEna and not IntClk after ClkPd/2;
          ClkOut <= IntClk;
        end Behavior; 
This model generates a clock with a 50% duty cycle with period ClkPd that runs only for the interval 0 ns <= T <= RunTime.

Sometimes this method is not convenient. For example, in the event of a catastrophic error, simulation should come to an end immediately. In these circumstances, it may be possible to use the following method. However, there are some tool dependencies involved in its use.

A concurrent or sequential assert statement (see FAQ Part 4 - B.18) may be used to stop simulation under model control. For instance a concurrent assert statement to stop simulation might look like

        assert not STOP_CONDITION
          report "Simulation stopped"
          severity failure; 
where "STOP_CONDITION" is a Boolean expression which becomes true when the simulation should stop. Note that "STOP_CONDITION" is re-evaluated only when a signal included in the expression changes its value.

The assert statement may be also embedded into a process. However, in this case the statement and the corresponding stop condition will be evaluated only when the normal rules of process execution and sequential code flow dictate. An example process which stops simulation after a fixed simulation time limit has been reached is:

	stop_sim: process
	begin
	  wait for 1 ms; -- stop simulation after 1 ms
	  assert false 
	    report "End simulation time reached"
	    severity failure;
        end process;

A severity condition of failure is used in the example assert in order to maximize the possibility that the simulation will stop when the STOP_CONDITION is met. However, whether the simulation actually stops depends on the simulator being used, and possibly on the switches used to control the simulator. Some simulators have settings that allow one to suppress the display of assert messages whose severity is less than a certain value; other switches may exist to prevent halting on assertion failures whose severity is less than a certain value. The proper setting of these switches, if they exist in the tool used, is essential to the correct functioning of this approach.

Finally, some simulators also provide means to control execution using scripting engines (e.g., Tcl/Tk). However, solutions based on these features are simulator dependent and hence not portable.  

4.2.32 Ports of Mode Buffer

Ports of mode buffer (see FAQ Part 4 - B.183 and FAQ Part 4 - B.157) can be both read and written. However, there is only a single source allowed to drive any net containing a buffer port, and that source must be internal to the port. While this restriction enables detection of unintended "multiple driver" errors during compilation (see also Section 4.2.13), a flaw in their definition makes them hard to use in the context of other designs. The following example is illegal in VHDL'87 and VHDL'93 because a port of mode out or inout must not be connected with a port of mode buffer:
	entity SRLatch is
          port( S, R: in bit;
                Q, QBar: buffer bit); -- "Q" and "Qbar" are of
                                      -- mode buffer!
        end SRLatch;

        architecture Structure of SRLatch is
          component Nor2
            port( I1, I2: in bit;
                  O: out bit); -- "O" is of mode out
          end component;
        begin
          Q1: Nor2 port map (I1 => S, -- ok
                             I2 => QBar, -- ok
                             O => Q); -- illegal

          Q2: Nor2 port map (I1 => R, -- ok
                             I2 => Q, -- ok
                             O => QBar); -- illegal
        end Structure; 
The component instantiation statements in this example are illegal because port "O" of "Nor2" is of mode "out" and hence cannot be associated with a buffer port. So, for the moment, the use of buffer ports may require that multiple libraries of standard cells be defined, one with out ports, the other with buffer ports. In most situations, this extra effort is not justified and therefore the use of buffer ports is discouraged.

However, the unnecessary restrictions on buffer ports were removed in VHDL 2000, so that buffer ports are more useful now (if your tool supports VHDL 2000).  

4.2.33 Multi-Dimensional Arrays

There is no upper bound on the dimensionality of arrays in VHDL.

Arrays (see FAQ Part 4 - B.15) with two or more dimensions can be implemented in VHDL easily using a type declaration. For example, the type declaration:

        type two_dim_array is array(0 to 63, 0 to 7) of bit; 
declares a two dimensional array with element type bit. A single array element is addressed as follows:
        signal sig : two_dim_array;
        ...
        sig(0,0) <= sig(63,7); -- assign bit at index (63,7) to (0,0)
While a fully compliant VHDL tool can handle arrays with an arbitrary number of dimensions, there are synthesis tools which accept only one- dimensional arrays. However, usually these tools are able to synthesize arrays where each element itself is an array. For example, instead of the two-dimensional array of type bit shown above, one may implement an array of bit_vectors (a one-dimensional array whose element type is itself a one-dimensional array--an "array of arrays"):
        subtype elem is bit_vector(0 to 7);
        type array_of_bitvec is array(0 to 63) of elem; 
Or even simpler:
        type array_of_bitvec is array(0 to 63) of bit_vector(0 to 7);
Note that a one-dimensional array whose element type is itself a one-dimensional array is indexed differently than the corresponding two-dimensional array. For example, a single bit of the array or arrays shown above is accessed using a sequence of two index values, each enclosed in braces:
        signal sig : array_of_bitvec;
        ...
        sig(0)(0) <= sig(63)(7); -- assign bit at index (63,7) to (0,0) 
Notice that the first index corresponds to the outermost type declaration. Arrays are "row major" in VHDL, which is to say that "the last index varies fastest" between adjacent elements.

Another advantage of this array of arrays technique is that an entire row can now be accessed in a whole. For example, the following code assigns row number 63 to row number 0 using a single assignment statement:

        signal sig : array_of_bitvec;
        ...
        sig(0) <= sig(63); -- assign bit 0 to 7 of row 63 to row 0

4.2.34 Multi-Dimensional Array Literals

In the previous section, we show that multi-dimensional arrays are different from arrays of arrays; in particular, we noted that elements are accessed using a different syntax.

However, there is one area where there is no difference between multi- dimensional arrays and arrays of arrays: when constructing literal (see FAQ Part 4 - B.144) values, there is no syntactical difference.

For example, consider the following set of type declarations, which might be part of a simple, multi-valued logic type system:

        type MVL is ('X', '0', '1', 'Z');

        type resolveTableType1 is array (MVL, MVL) of MVL;

        type MVL_Vector is array(MVL) of MVL;
        type resolveTableType2 is array (MVL) of MVL_Vector; 
Note that resolveTableType1 is a two-dimensional array of MVLs, while resolveTableType2 is a one-dimensional array of a one-dimensional array of MVLs.

However, objects of either type are initialized in an identical manner:

        constant resTable1: resolveTableType1 :=
                ( -- 'X' '0' '1' 'Z'
                    ('X','X','X','X'), -- 'X'
                    ('X','0','X','0'), -- '0'
                    ('X','X','1','1'), -- '1'
                    ('X','0','1','Z') -- 'Z'
                );

        constant resTable2: resolveTableType2 :=
                ( -- 'X' '0' '1' 'Z'
                    ('X','X','X','X'), -- 'X'
                    ('X','0','X','0'), -- '0'
                    ('X','X','1','1'), -- '1'
                    ('X','0','1','Z') -- 'Z'
                ); 
The first constant is of the two-dimensional array type, while the second constant is of the one-dimensional array type whose element is of another one-dimensional array. However, both are initialized as if they are of the latter type!

Notice that the literal initializing either array is a four-element aggregate (see FAQ Part 4 - B.7). Each element of the outer aggregate is itself a four- element sub-aggregate (see FAQ Part 4 - B.232). The elements of the sub-aggregates in all cases is a single MVL value.

Finally, notice that, once again, these arrays are row major. That is, to get to the element that is in the second row and the third column, use the following references:

        resTable1('0','1') 
in the case of the first array type, or
        resTable2('0')('1') 
in the case of the second array type.  

4.2.35 Conditional Compilation

The generate statement combined with generic parameters or constants may be used to achieve a behavior similar to "conditional compilation" in C. An example is:
        entity test is
          generic (switch : boolean);
        end if;

        architecture struct of test is
        begin
           -- instantiate "adder1" if "switch" is true
           First: if switch generate
               Q: adder1 port map (...);
           end generate;
           -- instantiate "adder2" if "switch" is false
           Second: if not switch generate
               Q: adder2 port map (...);
           end generate;
	end behave; 
Component "adder1" will be included into the design only if the generic parameter "switch" is true. Otherwise, "adder2" is instantiated. Note that the generate statement is "executed" at elaboration time (see
FAQ Part 4 - B.81), the entire architecture has to be analyzed by the compiler regardless of the generate parameter value (note that the values of generic parameters are unknown at compile time). Thus, syntactically or semantically incorrect code inside of generate statements will be flagged, regardless of whether the statements are ever elaborated. (As a consequence, both instantiation statements in the example above must be legal.) Further, the generate statement cannot be used to configure the interface of an entity; i.e., to add or remove ports depending on the value of a generic parameter. However, the sizes of array ports can be controlled through the use of generics.

In order to achieve the same functionality as the "#if" and "#ifdef" constructs of C/C++, the macro processor of an ordinary C compiler may be used. Usually, C compiler have a special command line switch to stop compilation after the preprocessing stage (e.g., "-E" for the GNU C/C++ compiler). Hence, VHDL source code can be augmented with C macros or "#ifdef...#endif" statements and then preprocessed with a C compiler before it is compiled with a VHDL compiler. A "make" tool may help to automate this two-stage approach.

Another solution is to use a special macro processor, like "m4", which is available for most Unix platforms as well as for Windows (e.g., from http://www.cs.colorado.edu/~main/mingw32/; see also FAQ Part 3, Section 1.5).  

4.2.36 Remarks on Visibility of Declarations

VHDL controls visibility of declarations using the notion of the scope of a declaration. The name of a declaration is visible only within the scope of the declaration (see FAQ Part 4 - B.212). (Note that a given declaration may be hidden (see FAQ Part 4 - B.116) by another declaration in certain circumstances.) The scope of a declaration extends from the beginning of the declaration to the end of the innermost declarative region (see FAQ Part 4 - B.56) containing the declaration. (E.g., a constant declared immediately within a given architecture is visible from the end of the constant declaration to the end of the architecture and also within any configuration declaration configuring that architecture.) An example is:
	process (sig)
           constant A : integer := 100;
           -- "A" becomes visible here and means this constant.
           ...

           procedure Test is
           -- "Test" becomes visible here and means this procedure.

              constant B : bit := '1';
              -- "B" becomes visible here and means this constant.
           begin
              ...
           end Test; -- "B" is no longer visible

        begin
           ...
        end process; -- "Test" and "A" are no longer visible 
A declaration may be hidden from direct visibility (see FAQ Part 4 - B.74) by another declaration, but it is always visible by selection, as shown in the following example:
        P: process (Sig)
           constant A : integer := 100;
           -- "A" becomes visible here and means this integer
           constant.

           procedure Test is
              constant A : bit := '1';
              -- "A" becomes visible here and means this bit constant.
              -- Hence, "A" no longer means the integer constant.
              -- However, the integer constant may be referred to as
              "P.A"
              variable Var : bit := A; -- "A" is of type bit
           begin
              if P.A > 50 then
                Var := not A;
              end if;
           end Test; -- The bit "A" is no longer visible.  Thus, "A"
                     -- once again means the integer constant.

           variable Var : integer := A; -- "A" is of type integer
        begin
           Var := A + 1; -- "A" is of type integer
        end process; -- "A" is no longer visible 
In the above example the integer constant A was "visible by selection" as "P.A" within the procedure Test, even though it is not directly visible within this procedure. Note that the declarations within packages are always visible by selection, as are the contents of libraries.

Use clause work by providing direct visibility of declarations that are visible by selection. Such declarations must be within libraries or packages. Note that there are cases where use clauses can cause conflicts in visibility, either between two declarations that are visible by selection, or between one directly visible declaration and another declaration that is visible by selection. For example:

A simple solution to bypass visibility conflicts is to use selected names (see FAQ Part 4 - B.213) to address a declaration within a specific package or declarative region (see FAQ Part 4 - B.56). This approach works because the names continue to be visible by selection. The following code demonstrates how to apply this technique on the example shown above:
        architecture Arch of Test is
           signal Sig1 : integer := WORK.Pack1.A; -- ok
           signal Sig2 : bit := WORK.Pack2.A; -- ok
        begin
        end Arch; 
 

4.2.37 Difference between std_logic and std_ulogic

The type std_ulogic is an enumeration type defined in the package IEEE.std_logic_1164. The type std_logic is a subtype of std_ulogic. Both are intended to model scalar logical values in ASICs and FPGAs.

As the 'u' in meant to convey, std_ulogic is an unresolved type. That is, it may be driven by a maximum of one source. Conversely, std_logic is a resolved (sub)type (see FAQ Part 4 - B.204), which means it may be driven by any number of sources. Resolved (sub)types have resolution functions associated with them--the resolution function (see FAQ Part 4 - B.202)) associated with the subtype std_logic, called resolved, is also defined in the package IEEE.std_logic_1164.

The fact that std_logic is a subtype of std_ulogic means that operations and assignments on values and objects of type std_logic and std_ulogic may be freely intermixed and no type conversion (see FAQ Part 4 - B.243) is necessary. An example showing this usage of both types is:

        library IEEE;
        use IEEE.std_logic_1164.all;
        architecture Arch of Test is
           signal unres_sig : std_ulogic := '0'; -- unresolved signal
           signal res_sig : std_logic := '0'; -- resolved signal
        begin
           -- this concurrent signal assignment is a source for
           unres_sig
           unres_sig <= not res_sig;

           -- this process is a source for res_sig
           p: process (res_sig)
           begin
              res_sig <= not unres_sig;
           end process;

           -- this concurrent signal assignment
           -- is a second source for res_sig
           res_sig <= '1' when control = '1' else 'Z';
        end Arch; 
Since "res_sig" has multiple sources (see FAQ Part 4 - B.223), it must be of a resolved type. However, since "unres_sig" has but a single source, it may be of either a resolved or unresolved type.

In addition to the scalar logical types std_logic and std_ulogic, there are vector types defined in IEEE.std_logic_1164. One, a resolved type, is called std_logic_vector. It is defined as a one-dimensional, unconstrained array of std_logic elements. The other, an unresolved type, is called std_ulogic_vector and is defined as a one-dimensional, unconstrained array of std_ulogic elements. Unlike the case of the scalar types std_logic and std_ulogic (where std_logic is a subtype of std_ulogic) the types std_logic_vector and std_ulogic vector are distinct types. (This difference comes about because of the details of VHDL's type system.) Consequently, care must be taken when intermixing std_logic_vector and std_ulogic_vector values and objects in expressions and assignments.

Ideally, all signals in a model should be declared using the unresolved types std_ulogic or std_ulogic_vector (depending on whether a scalar or vector signal is needed), except those signals that are to be multiply driven; for example, tristate busses. In that case, the types std_logic and std_logic_vector should be used as appropriate.

If this policy is adhered to, then signals that are multiply driven in error will be caught by either the analyzer or elaborator, depending on the exact circumstances. For example, consider the following model:

        library IEEE;
        use IEEE.std_logic_1164.all;
        architecture Arch of Test is
           component comp
              port (a : in std_ulogic);
           end component;

           signal sig : std_ulogic := '0'; -- unresolved signal
        begin
           -- a source for signal sig
           sig <= not sig;

           c: comp port map( a => sig );
        end Arch; 
The component comp contains a port of mode in. Hence, the instance c of this component does not create a source for the signal sig.

Should the mode of the port a be inadvertently changed to out, inout or buffer, the instance c then becomes an additional source for the signal sig. Since we took care to define "sig" to be of an unresolved type, this will be flagged prior to simulation start.

However, if we had instead declared "sig" to be of type std_logic, it would now be legal to multiply drive "sig", and no error would be reported by the tools. Instead, careful examination of waveforms and inspection of the model would be required to locate the problem.

(Sections 4.2.13 and 4.2.14 contain additional information on signal sources and drivers.)

Unfortunately, at least until recently, not all tools provided good support for std_ulogic and std_ulogic_vector. As a consequence, before deciding whether to follow our recommendation, you must take a look at your tools and determine whether they adequately support std_ulogic and std_ulogic_vector.

Typically, simulators are not the problem, it's the synthesis tools that might lack full support for the unresolved types. Consider a RTL model that uses std_ulogic_vectors as interface (port) signals and an appropriate testbench to stimulate the design. Often, the gate level model that is generated by the synthesis tool from this RTL description will use std_logic_vectors as port types. As a result, the gate level model cannot directly be connected with the testbench without using type conversion.

 

4.2.38 VHDL and Synthesis

Synthesis is the automatic process to generate hardware (gates, flipflops, latches and the like) from a formal description (e.g., VHDL). A synthesis tool translates the HDL source code into a net of hardware primitives. The primitives are defined by the target technology (e.g., FPGA or ASIC) and vendor. Ideally, using VHDL for synthesis decouples the design process from the actual target technology, vendor and synthesis tool chain. In this manner, a design expressed in VHDL can, in theory, be easily retargeted to another technology vendor or synthesis tool.

In reality, different synthesis tools will usually generate identical hardware only for simple VHDL models. Tightening the synthesis constraints (e.g., on required operating speed, power consumption or device count) will often require tool-specific modifications to the VHDL source in order to meet the constraints. Because of this fact, to obtain similar results from different synthesis tools, one must typically modify the VHDL source when moving from one tool to another.

While a significant part of the VHDL language can be synthesized, there are constructs that cannot be handled by today's synthesis tools. Some of the restrictions are simply due to a the lack of corresponding counterparts in hardware (e.g., report statements and files). Other restrictions are due to the fact that all statements of a synthesisable VHDL description must ultimately be statically mapped to a set of corresponding hardware primitives. Currently, each synthesis tool supports a different subset of the VHDL standard. This situation should be temporary, as there is an effort underway to develop a layered set of synthesis interoperability standards (see http://www.eda.org/siwg/ for further information).

The following list discusses a number of issues related to synthesis that show up frequently in comp.lang.vhdl:

 

4.2.39 Locally and Globally Static

In VHDL a locally static expression can be evaluated at compile time (see also FAQ Part 4 - B.147). Similarly, the value of a globally static expression can be determined at elaboration time (see also FAQ Part 4 - B.108 and FAQ Part 4 - B.81); i.e. when the design hierarchy in which it appears is elaborated. In detail, there is a set of rules that determine whether an expression is locally static, globally static or not static. A summary of the most important rules is given in the following (for the complete detailed description see the LRM).

An expression is said to be locally static if it is

An expression is said to be globally static if it is In addition, a globally static expression, subtype, type, or range must appear in a statically elaborated context.

For example, consider the following model:

     entity Test is
       port (port_bit : in bit);
       generic (gen_bit : bit);
     end Test;

     architecture Structure of Test is
       constant a : bit := '1'; -- locally static
       constant b : bit := '1' and '0'; -- locally static
       constant c : bit := gen_bit; -- globally static

       constant d : bit_vector(0 to 3) := "0000"; -- locally static
       constant e : bit_vector(0 to 1) := d(0 to 1); -- globally s.
       constant f : bit := vec(0); -- globally static

       pure function test1 (constant p : bit) return bit is
       begin
         return not p;
       end test;
       constant g : bit := test1('1'); -- globally static

       impure function test return bit is
       begin
         return not a;
       end test;
       constant h : bit := test2; -- NOT static

       begin
         ...
       end Structure; 
Note that constants "e" and "f" are globally but not NOT locally static because slices of an array or references to array elements are not locally static (even if the array is locally static). Constant "g" is globally (and NOT locally) static because the initial value contains a call to a user defined (pure) function.

The choices of a case statement are required to be locally static (see also Section 4.2.15). This enables compile time checking to ensure that all alternatives are actually covered by the choices. Hence, the following will NOT compile:

       entity test is
         generic (gen : bit_vector(0 to 1) := "00");
       end test;

       architecture erroneous of test is
         signal sig : bit_vector(0 to 1);
         constant a : bit_vector(0 to 3) := "0100";
         constant b : bit_vector(0 to 1) := a(0 to 1);-- globally s.
         constant c : bit_vector(0 to 1) := '1' & '0';-- globally s.
         constant d : bit_vector(0 to 1) := a(1) & a(1);-- globally s.
       begin
         ...
         case sig is
           when gen => ... -- error: "gen" is not locally static!
           when b => ... -- error: "b" is not locally static!
           when c => ... -- error: "c" is not locally static!
           when d => ... -- error: "d" is not locally static!
         end case;
         ...
       end erroneous; 
VHDL does not require the compiler to evaluate slices (or references to array elements) at compile time. Hence, "b" and "d" cannot be used as choices. This raises some difficulties when constant arrays or records are used to increase readability of the source code. An example is:
       type rec is record is
         value : integer;
         flag : boolean;
       end record;

       constant c_rec : rec := (value => 32, flag => true);
       constant c_value : integer := rec.value; -- globally s.
       constant c_flag : boolean := rec.flag; -- globally s.
Due to the LRM rules, constants "c_value" and "c_flag" are only globally static and hence cannot be used as choices within a case statement. Fortunately, "reversing" the constant definitions resolves the problem:
       type rec is record is
         value : integer;
         flag : boolean;
       end record;

       constant c_value : integer := 32; -- locally static!
       constant c_flag : boolean := true; -- locally static!
       constant c_rec : rec := (value => c_value, flag => c_flag);
 

4.2.40 Arithmetic Operations on Bit-Vectors

VHDL has no predefined arithmetic operators for bit_vectors or std_logic_vectors predefined. This situation exists as VHDL does not assume the interpretation to be applied to such vectors.

Instead, the following data types can be used in synthesizable designs to represent numbers:

There are also other numeric packages provided by different vendors. Some even provide arithmetic operations for std_logic_vectors. However, it is recommended that you use "numeric_std" whenever possible (see Section 4.11 for further information).

Finally, VHDL also provides floating point numbers (see FAQ Part 4 - B.100). However, as they are currently not synthesizable (this is going to change; see the Floating-Point HDL Packages Home Page at http://www.eda.org/fphdl/) their usage is restricted to testbenches or similar model types.  

4.2.41 VHDL'93 Generates Different Concatenation Results from VHDL'87

Certain VHDL tools report that the VHDL'93 concatenation operator generates different results from the same operator in VHDL'87. This section describes the differences. The operator "&" may be used to concatenate one-dimensional arrays (or their elements) to form a new array consisting of the elements of the left operand followed by the elements of the right operand. An example is
       constant c1 : bit_vector(0 to 3) := "1101";
       constant c2 : bit_vector(0 to 3) := "0010";

       -- value of "c3" is "11010010"
       constant c3 : bit_vector(0 to 7) := c1 & c2;

       -- value of "c4" is "11101"
       constant c4 : bit_vector(0 to 4) := '1' & c1;

       -- value of "c5" is "01"
       constant c5 : bit_vector(0 to 1) := '0' & '1'; 
Note that the concatenation operator may be also used to concatenate an array element with an array (see "c4") or two array elements (see "c5").

Due to a flaw in the definition of VHDL'87 the result of a concatenation may produce illegal array bounds. In VHDL 87 the left bound of the result is the left bound of the left operand, unless it is a null array (an array of length 0; see also FAQ Part 4 - B.165), in which case it is the left bound of the right operand. The direction of the result is derived from the left and right operand in a similar fashion. Consider the following sequence

       type r is 0 to 7;
       type r_vector is array (r <> range) of bit;

       constant k1 : r_vector(1 downto 0) := "10";
       constant k2 : r_vector(0 to 1) := "01";

       constant k3 : r_vector := k2 & k1;
       constant k4 : r_vector := k1 & k2; 
According to the VHDL'87 rules for concatenation, the left bound of constant "k3" is 0 and the right bound is 3. Similarly, the left bound of "k4" is 1 while the right bound is -2. Obviously, the right bound of "k4" is illegal as it is not within the range defined by type "r".

To fix this problem in VHDL'93 the corresponding rules has been modified: if both operands are null arrays, then the result of the concatenation is the right operand. Otherwise, the direction and bounds of the result are determined from the the index subtype of the base type of the result. The direction and the left bound of the result are taken from the index subtype while the right bound is calculated based on the left bound, the direction and the length of the array.

In the previous example the index subtype of the base type of "k3" and "k4" is "r". Hence, the left bounds of "k3" and "k4" are set to 0 and the directions are "to". Finally, the right bounds of both constants equals to 0 + length of array - 1 = 0 + 4 - 1 = 3:

k3'left k3'right k4'left k4'right
VHDL'87 0 3 1 -2
VHDL'93 0 3 0 3

Note that the resulting sequence of bits is the same in either case. The only difference between the operators is in the construction of the index bounds of the result.

So, as long as the index values of the result don't go out of bounds, and the code using the concatenation does not depend on specific index values of the result (which is a bad idea in any case), the differences between the VHDL'87 and VHDL'93 concatenation operators are of no concern. In fact, the differences are precisely to avoid the condition where the index values needlessly go out out bounds.  

4.2.42 rising_edge(clk) versus (clk'event and clk='1')

In general, there are two ways in VHDL to describe edge sensitive elements (we assume that signal "clk" is of type std_logic): For synthesis, there is usually no difference between both alternatives as the synthesis tool assumes that transitions on clock signals are "clean" (i.e., clock signals toggle between '0' and '1' only). However, for simulation it is more save to use "rising_edge" (or "falling_edge").  

4.3 What do I Need to Generate Hardware from VHDL Models

Usually, VHDL is used to describe digital designs that are either programmed into FPGAs (field-programmable gate arrays) or integrated into ASICs (application-specific integrated circuits) to perform a specific function. In order to build (or, in case of FPGAs, program) a chip, several tools are needed: While this should give a rough overview on what is needed to design circuits using a VHDL based design flow, it is by no way an exact and exhaustive description of all tools that may be required and all problems that may be encountered. ASIC design flows, especially, are sometimes far more complex than described here. Hence, please refer to vendor documentation or appropriate literature for further information.

4.4 PUBLIC DOMAIN Tools?

Actually as far as I know, there is only few PD software on VHDL. If you know about something, please let us know. See products posting for more detailed information.

4.5 VHDL Validation Suite Available?

Yes. The latest version of the test validation suite is available via anonymous ftp://vhdl.org/pub/validation/vi_suite.tar.Z The current suite covers 36% of VHDL-1987. See http://www.vhdl.org/validation/ for further information.

See also FAQ Part 3, Section 1.5

4.6 Status of Analog VHDL (VHDL-AMS, 1076.1)

The IEEE Standard 1076.1-1999 (Analog and Mixed-Signal Extensions to VHDL) has been approved on March 18, 1999 and the 1076.1 Language Reference Manual is available from the IEEE. VHDL 1076.1-1999 is a strict extension of VHDL 1076-1993, i.e. it includes the full VHDL 1076-1993 language. Extensions have been carefully designed to maintain the existing philosophy and to smoothly integrate new language definitions.

There is a free VHDL-AMS (Analog and Mixed Signal) simulator/compiler called SEAMS available. See part 3, Section 3.1 for further information on SEAMS.

4.7 How Can People Get More Information about VHDL-AMS (1076.1)

The 1076.1 study group is maintaining an email bulletin board for distribution of announcements and as a forum for technical discussions. see above (official contacts).

4.8 Standards and Standard Packages

Actually as far as I know no complete list with addresses of all documents exists. In general it's a good idea to look at http://www.eda.org/. Or check out the vhdl.org ftp server, fetch the most actual version of the groups list: /pub/docs/groups.txt (see also Official Contacts ... above). The short names listed there are usually also used as directory names at vhdl.org - just look for /pub/. Somewhere below your chance to find the actual working documents is very good.

If someone has a partial or complete list, please send it to the author. I'll incorporate it here, if possible. Here is, what I have:

4.8.1 Functions and Operators Defined in Package numeric_std

There are two IEEE-standard packages that provide functionality to handle bit vectors as coded numeric values. The types/functions based on bits are found in the package IEEE.numeric_bit, and the types/functions based on std_logic are found in IEEE.numeric_std. Of the two, numeric_std is the more commonly used.

Both packages (see Section 4.8 on how to get these packages) define two new vector types: SIGNED and UNSIGNED. SIGNED vectors represent two's-complement integers, while UNSIGNED vectors represent unsigned-magnitude integers.

Note: Synopsys produced another set of packages named std_logic_arith, std_logic_signed, and std_logic_unsigned that are intended to provide functionality similar to numeric_bit and numeric_std. In particular, the same two types, SIGNED and UNSIGNED are defined. These packages are typically even installed in the library IEEE. However, these packages are NOT standard, and different vendors have different and mutually incompatible versions. Also, there are naming clashes when some of these packages are used together. So, it is recommended that numeric_bit or numeric_std be used in preference to these non-standard packages.

In order to help designers making best use of numeric_std the following tables lists operators and functions defined in this package. In particular, the tables list

In order to save space type names in the table are abbreviated as follows

type name abbreviation remarks
integer int
natural nat integer >= 0; subtype of integer
boolean bool
bit_vector bvec
std_ulogic sul
std_ulogic_vector sulv
std_logic sl resolved subtype of sul
std_logic_vector slv
unsigned uns
signed sgn

The function and operators are clustered into groups "logical operators", "arithmetic operators", "compare operators", "/rotate functions", "conversion functions" and "miscellaneous functions":

4.9 Where to Obtain the comp.lang.vhdl FAQ

http://vhdl.org/comp.lang.vhdl/ (See also here)

4.10 "Frequently Requested" Models/Packages

Not all models shown here are really frequently requested but I did not find a better place to list them.

4.11 Arithmetic Packages for bit/std_logic-Vectors

The IEEE did not, originally, define a standard set of types and overloaded functions to handle vectors which contained coded numeric values. This meant that individual vendors were free to define their own types and functions.

Synopsys produced three packages - std_logic_arith, std_logic_signed, and std_logic_unsigned. std_logic_signed/std_logic_unsigned operated on type std_logic_vector, and gave an implicit meaning to the contents of the vector. std_logic_arith, however, defined two new types, SIGNED and UNSIGNED, and operated on these types only. Unfortunately, Synopsys decided that these packages should be compiled into library IEEE. Other vendors, including Cadence and Mentor, now produced their own versions of std_logic_arith, which were not the same as Synopsys's. They also required their packages to be placed in library IEEE.

Finally, the IEEE decided to standardize this situation, and produced packages numeric_bit and numeric_std (see Section 4.8 on how to obtain numeric_bit and numeric_std). numeric_bit is based on type bit, numeric_std on on type std_logic. Both packages followed Synopsys in defining new types, SIGNED and UNSIGNED. However, the package functions did not have the same names, or parameters, as the Synopsys functions.

Currently many vendors support numeric_bit/numeric_std. Hence, for maximum portability, avoid using a package called std_logic_signed or std_logic_unsigned, and always use SIGNED or UNSIGNED types (or integers) for arithmetic. If you are using Synopsys, use std_logic_arith, and if you are not using Synopsys, use numeric_std (if it is supported). This is not completely portable, since the functions are still different (for example, TO_UNSIGNED vs. CONV_UNSIGNED), but it is a lot better than using different types in different environments.

Partially extracted from an article by Evan Shattock.

4.12 Where Can I Find More Info

Of course, there are the other three parts of this FAQ.

Other good starting points are http://www.vhdl.org/docs/groups.txt or Section 3. VHDL on the Web.

If other resources should be listed here, please let me know.


Part 2: books on VHDL
Authors:
Tom Dettmer, Edwin Naroska