Featured post

Top 5 books to refer for a VHDL beginner

VHDL (VHSIC-HDL, Very High-Speed Integrated Circuit Hardware Description Language) is a hardware description language used in electronic des...

Showing posts with label VHDL. Show all posts
Showing posts with label VHDL. Show all posts

Tuesday, 15 June 2021

Top 5 books to refer for a VHDL beginner



VHDL (VHSIC-HDL, Very High-Speed Integrated Circuit Hardware Description Language) is a hardware description language used in electronic design automation to describe digital and mixed-signal systems such as field-programmable gate arrays and application-specific integrated circuits. To start with learning VHDL we are posting here the list of 5 VHDL books which are a good references to get started with VHDL coding.

1. VHDL: PROGRAMMING BY EXAMPLE - by Douglas L. Perry


 

  2. Digital Design | With an Introduction to the Verilog HDL, VHDL, and SystemVerilog - by M. Morris Mano


 

  3. A Vhdl Primer - by Bhasker


 

  

4. Fundamentals of Digital Logic with VHDL Design - by Stephen Brown and Zvonko Vranesic


 

 

5. Circuit Design and Simulation With VHDL - by Pedroni V.A




Please feel free to send your suggestions if there is any good book in your list. We will be happy to share here.
 

Wednesday, 24 December 2014

OSVVM – Thinking beyond constrained random

osvvm_logo_thumb What is OSVVM?

OSVVM stands for "Open Source VHDL Verification Methodology". OSVVM is a set of VHDL packages, initially developed by Jim Lewis of Synthworks. OSVVM helps you adopt modern constrained random verification techniques using VHDL.

Constraint random verification approach :

In testbenches, we generally want one each of a large set of test cases (transactions and/or sequences). Uniform randomization does not generate one each. Instead it has a significant amount of repetition. In general, uniform randomization takes O(N*LogN) randomizations to generate N unique test cases. As a result, it repeats Log N test cases. Even for small numbers such 64 test cases, constrained random will generate more than 4X more test cases than needed - actual results will vary with the randomization seed. Constrained random comes with this fundamental problem. Randomization is intended to be uniform over time. However constraint random verification has a number of benefits:

  • If you simulate longer, you generate more test vectors.
  • You may find bugs due to unexpected combinations of inputs, or extreme input values. With directed testing, it is all too easy just to test what you expect to happen, rather than trying to test what you don't expect to happen.
  • Once you have developed an automated test, it can still be used for directed testing.

Still what we need is an approach that only requires O(N) randomizations to generate N unique test cases. Generally these approaches are referred to as being Intelligent Testbenches. Indeed there are some tools out there that handle this. However, when we use a tool based approach we end up with a vendor specific solution. This removes one of the major benefits of a programming language based approach - encounter a issue (pricing or functionality) with one vendor and you can easily switch to another.

What we really need is a methodology for Intelligent Testbenches that is based on a standard language and works on numerous vendor tools.

OSVVM :

VHDL's Open Source VHDL Verification Methodology (OSVVM). OSVVM's methodology leverages the functional coverage you must write when you are using any randomization based approach. Intelligent Coverage™, the main randomization methodology for OSVVM, randomly selects a hole in the coverage and passes this to the stimulus generation process. The stimulus generation process uses this information, perhaps refines it using any methodology (directed, algorithmic, constrained random or file based), and then generates one or more transactions to accomplish generate the item that needs covered.

OSVVM can be used in your current VHDL testbench, in part or in whole as needed.  It allows mixing of our signature “Intelligent Coverage” methodology with other verification methodologies, such as directed, algorithmic, file based, and constrained random. Don’t throw out your existing VHDL testbench or testbench models, re-use them.

There is no new language to learn. There are no specialized “OO” approaches – just plain old VHDL entities and architectures. As a result, it is accessible to RTL designers. In fact, it is our goal to make our testbenches readable to verification (testbench), design (RTL), system, and software engineers.

OSVVM works with any VHDL testbench and is particularly effective when coupled with a transaction based testbench. For us, VHDL and OSVVM are the step beyond constrained random and SystemVerilog. Maybe it is time we update VHDL's acronym to mean Verification and Hardware Design Language.

Intelligent Coverage™ Methodology :

Verification starts with a test plan that identifies all items in a design that need to be tested.  OSVVM, like other advanced methodologies, uses functional coverage to observe conditions on interfaces and within the design to validate that the items identified in the test plan have occurred.  As such, functional coverage helps determine when testing is done.

Unlike other methodologies, in OSVVM’s Intelligent Coverage methodology,  functional coverage is the prime directive – it is where we start our process.  Intelligent Coverage is done in the following steps.

  • Write a high fidelity functional coverage (FC) model
  • Randomly select a hole in the functional coverage 
  • Refine the initial randomization with sequential code 
  • Apply the refined sequence (one or more transactions) 
  • Observe Coverage

The key point of Intelligent Coverage is that we randomize using the functional coverage. Then, if necessary, we refine the randomization using sequential code and any sequence generation method, including constrained random, algorithmic, directed, or file reading methods.

OSVVM is a Low Cost Solution :

The packages are free. OSVVM works on regular VHDL simulators (such as Mentor’s ModelSim and Aldec’s Active-HDL) without additional licenses. The only special language support required is VHDL-2002 protected types and VHDL-2008 type integer_vector (for older simulators, we have a work around for this).

To learn more about OSVVM, see:


OSVVM is an open source VHDL library that is free to use (no license fees) and works with any simulator that supports VHDL-2008 (or VHDL-2002 with a little work).
What is currently in the OSVVM library is only the beginning. Over time, I will be releasing our generic scoreboard package, memory modeling package, and others.

Thursday, 31 July 2014

VHDL Coding Tips For Excellent Design

Here are the Ten Commandments of Excellent Design:
  1. All state machine outputs shall always be registered
  2. Thou shalt use registers, never latches
  3. Thy state machine inputs, including resets, shall be synchronous
  4. Beware fast paths lest they bite thine ankles
  5. Minimize skew of thine clocks
  6. Cross clock domains with the greatest of caution. Synchronize thy sig- nals!
  7. Have no dead states in thy state machines
  8. Have no logic with unbroken asynchronous feedback lest the fleas of myriad Test Engineers infest thee
  9. All decode logic must be crafted carefully—eschew asynchronicity
  10. Trust not thy simulator—it may beguile thee when thy design is junk
How to Write Ten-Commandment Code

Conforming to the Ten Commandments is not difficult. In this section you’ll see how to write VHDL (your author doesn’t do Verilog, but the translation is easy) that complies with the rules. Robust design and first-silicon success are the goals!

The philosophy behind Ten-Commandment code is that synthesizers are not to be trusted too much. Most of the code you will see is close to the structural level; some more overtly than others. Most of the code is self-explanatory. It is assumed that the reader is familiar with VHDL. Signal names are also obvious to anyone “skilled in the art.”

How to Create a Flip-Flop
One of the basic primitives that we need to create robust synchronous designs is the D-type flip-flop. Look at the code in Code Sample 1: A D-type Flip Flop

-- VHDL Code for a D-Type Flip-Flop with an -- Asynchronous Clear
D_Type_Flip_Flop: process(Reset_n, Clock_In)
  begin
    if (Reset_n = ’0’) then
      Q_Output <= ’0’ after 1 ns;
  elsif (Clock_In’event and Clock_In = ’1’) then
      Q_Output <= D_Input after 1 ns;
  end if;
end process D_Type_Flip_Flop;

This flip-flop has the following properties:
  • An asynchronous active-low clear input sets the Q output to zero.
  • It is triggered on the rising edge of the clock.

How to Create a Latch
While the Ten Commandments specifically forbid the use of latches, there are still those heretics who will insist on the use of latches. The code to instantiate a transparent latch is shown in Code Sample 2: A Transparent Latch

-- VHDL Code for a Transparent Latch
Latch_Data: process(Latch_Open, D_Input)
  begin
    if (Latch_Open = ’1’) then
      Latched_Data <= D_Input;
    -- If Latch_Open = 0, then Latched_Data keeps its old value, -- i.e. the latch is closed.
    end if;
end process Latch_Data;

This latch has the following properties:
  • A latch control that opens the latch when high (the latch is transparent).
How to Create a Metastable- Hardened Flip-Flop

The use of a metastable-hardened flip flop is nothing more than the direct instantiation of a suitable library element in this case, a “dfntns” flip-flop. This is pure structural VHDL. The component declaration is shown in Code Sample 3:  A Metastable-Hardened Flip-Flop, Component Declaration

-- VHDL Code for a Nice Metastable-Hardened Flip-Flop
component dfntns
  Port (
    CP : In std_logic;
    D : In std_logic;
    Q : Out std_logic
  );
end component;

To use the flip-flop in your circuit, instantiate it as shown in Code Sample 4: A Metastable-Hardened Flip-Flop, Instantiation

-- VHDL Code to Instantiate the Metastable-Hardened Flip-Flop
Metastable_Hardened_Flip_Flop_Please: dfntns port map (
  D => D_Input,
  CP => Clock_In,
  Q => Q_Output
  );

This flip-flop has the following properties:
  • A maximum clock-to-out time under worst-case setup and hold time violations. This time is available in the library element specifications.
The Care and Feeding of Toggle Signals

Receiving a Toggle Signal
The Ten Commandments paper suggested that a method for exchanging single- point information across clock domains is by the use of toggle signals. Here, it is assumed that the toggle event should generate an active-high pulse to pass to a state machine. Every toggle—rising edge and falling edge—must create the pulse. In addition, the pulse must be synchronized correctly to the receiver’s clock. The code to accomplish this is shown in Code Sample 5: Receiving a Toggle Signal

-- VHDL Code to Create a Pulse from an Asynchronous -- Toggle Signal
-- First, use a metastable-hardened flip-flop to synchronize the -- toggle input
Metastable_Hardened_Flip_Flop_Please: dfntns port map (
  D => Handshake_T,
  CP => Clock_In,
  Q => Sync_Handshake_T
  );

-- Now pass the synchronized toggle through another flip-flop
Toggle_Reg_Proc: process(Clock_In)
  begin
    if (Clock_In'event and Clock_In = ’1’) then
      Reg_Handshake_T <= Sync_Handshake_T after 1 ns;
    end if;
  end process Toggle_Reg_Proc;

-- Finally XOR the two synchronized signals to create a pulse
Toggle_Pulse <= Reg_Handshake_T xor Sync_Handshake_T;

When synthesizing this code, remember to use the “fix hold” option so a fast path doesn't occur between the two flip-flops in this circuit.

Generating a Toggle Signal
Recall that a toggle signal is generated by simply inverting a level to pass the information. The trivial code to do this is shown in Code Sample 6:  Generating a Toggle Signal

The suffix “_T” is used to denote a toggle signal.

-- VHDL Code to Create a Toggle Signal
Handshake_T <= not (Handshake_T) after 1 ns;

Coding State Machines
The creation of state machines is a mixture of art and science. A well-crafted state machine will possess a sense of elegance; it will be appealing, both functionally and visually.
Here, a very simple example is presented as an illustration of state machine design. The state diagram for the Flintstones State Machine is shown in figure below popularly known as The Flintstones State Machine.


The Flintstones State Machine operates as follows:
  1. The State Machine has two states, State Bed and State Rock.
  2. There is one output, Fred, which takes the value 0 in State Bed and 1 in State Rock.
  3. A reset, caused by a low level on Reset_n, puts the State Machine into State Bed.
  4. The State Machine waits in State Bed while Barney is low, and enters State Rock when Barney goes high.
  5. The State Machine then waits in State Rock while Wilma is low, and returns to State Bed when Wilma goes high.
Implementing the Flintstones State Machine

An example implementation of the Flintstones State Machine is shown in Code Sample 7 below:

-- VHDL Code to Implement the Flintstones State Machine

Flintstones_SM_Proc: process(Sync_Reset_n, Clock_In)
  -- Enumerate state types:
  type Flintstones_Statetype is (
  Bed, Rock
  );

  -- define the state variable:
  variable Flint_State: Flintstones_Statetype;

  -- Here’s the state machine:
  begin
  -- Define the asynchronously set reset states...
    if (Sync_Reset_n = ’0’) then
      Fred <= ’0’ after 1 ns;
      Flint_State := Bed
      -- Default conditions for each output, in this case identical to the -- reset state:
    elsif (Clock_In’event and Clock_In = ’1’) then
      Fred <= ’0’ after 1 ns;

    -- Here are the state transitions:
    case Flint_State is
      when Bed =>
      -- Transition from Bed to Rock:
        if (Barney = ’1’) then
          Fred <= ’1’ after 1 ns;
          Flint_State := Rock;
          -- Holding term in Bed:
        else
          Flint_State := Bed;
        end if;
      when Rock =>
        -- Transition from Rock to Bed:
        if (Wilma = ’1’) then
          Fred <= ’0’ after 1 ns;
          Flint_State := Bed;
        -- Holding term in Rock:
        else
          Fred <= ’1’ after 1 ns;
          Flint_State := Rock;
        end if;
      -- Default term for dead states:
      when others =>
        Flint_State := Bed;
    end case;
  end if;
end process Flintstones_SM_Proc;


Notes on the State machine Implementation
For the most part, the Flintstones State Machine’s operation should be clear. A few points are worth noting, however:
  1. The reset signal (Sync_Reset_n) is synchronized with Clock_In before being sent to the State Machine.
  2. Barney and Wilma must also be synchronous to Clock_In; at the very least, there must be an assurance that the State Machine’s state and output regis- ter’s setup and hold times are not violated.
  3. This design assigns a default value to each output and to the state variable before entering the case statement. This ensures that only those signals that are not taking default (usually inactive) values need be listed in the case statement. This is optional; it is entirely reasonable to list every signal under each transition term, including inactive signals.
  4. Note that the output signal Fred comes directly from a D-type flip-flop: it is not a decode of the state variable. This ensures Fred’s cleanliness (so to speak).
  5. The “when others” in the case statement handles the possibility that the State Machine might end up in a dead state.
The code examples in this tutorial should be considered as examples only. There are many ways to code excellent VHDL; this code is a place to start. If you have a neat snippet of VHDL to add to the list, then please post your valuable comments below.

Monday, 31 December 2012

Access Type

Formal Definition

A type that provides access to an object of a given type. Access to such an object is achieved by an access value returned by an allocator; the access value is said to designate the object.

Simplified Syntax

access subtype_indication
type identifier;

Description

Access type allows to manipulate data, which are created dynamically during simulation and which exact size is not known in advance. Any reference to them is performed via allocators, which work in a similar way as pointers in programming languages.
The subtype_indication in the access type declaration denotes the type of an object designated by a value of an access type. It can be any scalar, composite or other access type (example 1). File type is not allowed here.
The only objects allowed to be of the access type are variables.
The default value of an access type is null which designates no object at all. To assign any other value to an object of an access type an allocator has to be used (see allocator for details).
The access type allows to create recursive data structures (dynamic lists of objects created during simulation) which consist of the records that contain elements of access types - either the same or different than the actually declared. In order to handle declarations of such recursive data structures so called incomplete type declaration is needed which plays a role of an "announcement" of a type which will be declared later on.
For each incomplete type declaration there must be a corresponding full type declaration with the same name. The complete declaration must appear in the same declarative part. A type declared as incomplete may not be used for any other purposes than to define an access type before the complete type definition is accomplished. Such an incomplete type declaration is presented in example 2 below.

Examples

Example 1
-- declaration of record type Person:
type Person is record
            address:ADRESS_TYPE;
            age:DATE;
end record Person;
-- declaration of access type Person_Access:
type Person_Access is access Person;
The Person_Access type defines a pointer (dynamic link) to a record declared earlier and called Person.
Example 2
-- declaration of an incomplete type Queue_Element:
type Queue_Element;
-- declaration of an access type Queue_Element_Ptr:
type Queue_Element_Ptr is access Queue_Element;
-- declaration of an full record type Queue_Element:
type Queue_Element is record
                  name:STRING(1 to 20);
                  address:ADRESS_TYPE;
                  age:DATE;
                  succ:Queue_Element_Ptr;
end record Queue_Element;
The type Queue_Element contains the Queue_Element_Ptr field, which in turns points to Queue_Element. In order to declare both types sequentially, first the Queue_Element is declared in an incomplete way, which allows declaring the access type Queue_Element_Ptr. Finally, complete declaration of Queue_Element must be provided.

Important Notes

· Application of access types is restricted to variables: only variables can be of an access value. Also, only variables can be designated by an access value.
· Although access types are very useful for modeling potentially large structures, like memories or FIFOs, they are not supported by synthesis tools.













Aggregate

Formal Definition

A basic operation that combines one or more values into a composite value of a record or array type.

Syntax:

aggregate ::= ( element_association { , element association } ) element_association ::= [choices => ] expression choices ::= choice { | choice } choice ::= simple expression          | discrete_range          | element_simple_name          | others

Description

The aggregate assigns one or more values to the elements of a record or array creating the composite value of this type. Aggregates are composed of element associations, which associate expressions to elements (one expression per one or more elements). Element associations are specified in parentheses and are separated by commas.

An expression assigned to an element or elements must be of the same type as the element(s).

Elements can be referred to either by textual order they have in the object declaration (so called positional associations - example 1) or by its name (named associations - example 2). Both methods can be used in the same aggregate, but in such a case all positional associations appear first (in textual order) and all named associations appearing next (in any order). In any case if the association others is used, it must be the last one in an aggregate.

The choice clause, denoting selection of element(s) can have any of the following forms: simple expression, discrete range, simple name or reserved word others.

A value of simple expression can be applied in arrays only and must belong to discrete range of an array type. A simple expression specifies the element at the corresponding index value. Example 2 illustrates this concept.

A discrete range must meet the same conditions as a simple expression. It is useful when several consecutive elements of an array are assigned the same value (example 3). A discrete range serves for defining the set of indexes only and the direction specified or implied has no significance.

The use of element simple name as a choice is restricted to records only. In this case, each element is identified by its name (Example 4).

When some elements are assigned different values and the remaining elements will receive some other value, reserved word others can be used to denote those elements (Example 5). Such a choice must be the last in an aggregate and can be used both in arrays and in records, provided that the remaining elements of the records are of the same type.

The choice others can serve as a very convenient way to assign the same value to all elements of some array, e.g. to reset a wide bus (Example 6).

If several elements are assigned the same value, a multiple choice can be used. In such a case a bar sign (|) separates references to elements (Example 7). If a multiple choice is used in an array aggregate, it may not be mixed with positional associations.

Examples

Example 1

variable Data_1 : BIT_VECTOR (0 to 3) := ('0','1','0','1');

Bits number 0 and 2 are assigned the value '0', while bits 1 and 3 are assigned '1'. All element associations here are positional.

Example 2

variable Data_2 : BIT_VECTOR (0 to 3) := (1=>'1',0=>'0',3=>'1',2=>'0');

Like in the previous example, bits number 0 and 2 are assigned the value '0', while bits 1 and 3 are assigned '1'. The element associations here, however, are named. Note that in this case the elements can be listed in arbitrary order.

Example 3

signal Data_Bus : Std_Logic_Vector (15 downto 0);
. . .
Data_Bus <= (15 downto 8 => '0', 7 downto 0 => '1');

Data_Bus will be assigned the value of "0000000011111111". The first element is associated a value in positional way (thus it is bit number 15), and the other two groups are assigned values using discrete ranges.

Example 4

type Status_Record is record
     Code : Integer;
     Name : String (1 to 4);
end record;
variable Status_Var : Status_Record := (Code => 57, Name => "MOVE");

Choice as an element simple name can be used in record aggregates - each element is associated a value (of the same type as the element itself).

Example 5

signal Data_Bus : Std_Logic_Vector (15 downto 0);
. . .
Data_Bus <= (14 downto 8 => '0', others => '1');

Data_Bus will be assigned the same value as in example 3 ("1000000011111111"), but this aggregate is written in more compact way. Apart from bits 14 through 8, which receive value '0' all the others (15 and 7 through 0) will be assigned '1'. Note that the choice others is the last in the aggregate.

Example 6

signal Data_Bus : Std_Logic_Vector (15 downto 0);
. . .
Data_Bus <= (others => 'Z');

Instead of assigning "ZZZZZZZZZZZZZZZZ" to Data_Bus in order to put it in high impedance state, an aggregate with the others choice representing all the elements can be used.

Example 7

signal Data_Bus : Std_Logic_Vector (15 downto 0);
. . .
Data_Bus <= (15 | 7 downto 0 => '1',
others => '0');

Note the multiple choice specification of the assignment to the bits 15 and 7 through 0. The result of the assignment to Data_Bus will be the same as in examples 3 and 5 ("1000000011111111").

Important Notes

· Associations with elements' simple names are allowed in record aggregates only.

· Associations with simple expressions or discrete ranges as choices are allowed only in array aggregates.

· Each element of the value defined by an aggregate must be represented once and only once in the aggregate.

· Aggregates containing the single element association must always be specified using named association in order to distinguish them from parenthesized expressions.

· The others choice can be only the last in an aggregate.

Alias

Formal Definition

An alternate name for an existing named entity.

Simplified Syntax

alias alias_name : alias_type is object_name;

Description

The alias declares an alternative name for any existing object: signal, variable, constant or file. It can also be used for "non-objects": virtually everything, which was previously declared, except for labels, loop parameters, and generate parameters.

Alias does not define a new object. It is just a specific name assigned to some existing object.

Aliases are prevalently used to assign specific names to slices of vectors in order to improve readability of the specification (see example 1). When an alias denotes a slice of an object and no subtype indication is given then the subtype of the object is viewed as if it was of the subtype specified by the slice.

If the alias refers to some other object than a slice and no subtype indication is supported then the object is viewed in the same way as it was declared.

When a subtype indication is supported then the object is viewed as if it were of the subtype specified. In case of arrays, the subtype indication can be of opposite direction than the original object (example 2).

Subtype indication is allowed only for object alias declarations.

A reference to an alias is implicitly a reference to the object denoted by the alias (example 3).

If an alias denotes a subprogram (including an operator) or enumeration literal then a signature (matching the parameter and result type) is required (example 4). See signature for details.

Examples

Example 1

signal Instruction : Bit_Vector(15 downto 0);
alias OpCode : Bit_Vector(3 downto 0) is Instruction(15 downto 12);
alias Source : Bit_Vector(1 downto 0) is Instruction(11 downto 10);
alias Destin : Bit_Vector(1 downto 0) is Instruction(9 downto 8);
alias ImmDat : Bit_Vector(7 downto 0) is Instruction(7 downto 0);

The four aliases in the example above denote four elements of an instruction: operation code, source code, destination code and immediate data supported for some operations. Note that in all declarations the number of bits in the subtype indication and the subtype of the original object match.

Example 2

signal DataBus : Bit_Vector(31 downto 0);
alias FirstNibble : Bit_Vector(0 to 3) is DataBus(31 downto 28);

DataBus and FirstNibble have opposite directions. A reference to FirstNibble(0 to 1) is equivalent to a reference to DataBus(31 downto 30).

Example 3

signal Instruction : Bit_Vector(15 downto 0);
alias OpCode : Bit_Vector(3 downto 0) is Instruction(15 downto 12);
. . .
if Opcode = "0101" -- equivalent to if Instruction(15 downto 12) = "0101"
then
    . . .

Both conditions are exactly the same, but the one where alias is used is more readable.

Important Notes

· VHDL Language Reference Manual uses the name 'entity' to denote a language unit, i.e. object, parameter etc. It is completely different idea than a design entity.

· Many synthesis tools do not support aliases.

Allocator

Formal Definition

An operation used to create anonymous, variable objects accessible by means of access values.

Simplified Syntax

new subtype_indication

new qualified_expression

Description

Each time an allocator is evaluated, a new object is created and the object is designated (pointed) by an access value (pointer). The type of the object created by an allocator is defined either by a subtype indication (example 1 and 2) or a qualified expression (example 3 and 4).

In case of allocators with a subtype indication, the initial value of the created object is the same as the default initial value of a directly declared variable of the same subtype (example 1 and 2). When qualified expression is used, the initial value is defined by the expression itself (example 3 and 4).

If an allocator creates an object of the array type, then the array must be constrained. This can be achieved through using a constrained subtype or specified in the subtype indication with an explicit index constraint (example 2).

Copying a value of a variable with allocated object to other variable does not create new object. Instead, both variables point to the same object (example 5).

See also access type.

Examples

Example 1

type Table is array (1 to 8) of Natural;
type TableAccess is access Table;
variable y : TableAccess;
...
y := new Table; -- will be initialized with
-- (0, 0, 0, 0, 0, 0, 0, 0)

The allocator (note that the allocator is of the access type) creates a new object of the Table type, which is initialized to a default value, equal in this case to (0, 0, 0, 0, 0, 0, 0, 0).

Example 2

z:= new BIT_VECTOR(1 to 3);

This allocator creates a new object of the BIT_VECTOR type, consisting of three elements. The default initial value of this object is equal to ('0','0','0'). Note that the subtype indication is constrained as the BIT_VECTOR type is unconstrained.

Example 3

type test_record is record
            test_time : time;
            test_value : Bit_Vector (0 to 3);
end record test_record;
type AccTR is access test_record;
variable x,z : AccTR;
x := new test_record'(30 ns, B"1100"); -- record allocation with aggregate
z := new test_record;
z.test_time := 30 ns;
z.test_value := B"1100";

Initial values can be assigned to an object (in this case a record) created by an allocator both using a qualified expression (in this case with an aggregate - allocator for x) or using a subtype indication and later on direct assignments (allocator for z). In both cases above the objects created will be identical (although it will not be the same object).

Example 4

type AccBV is access Bit_Vector(7 downto 0);
variable Ptr1, Ptr2 : AccBV;
Ptr1 := new Bit_Vector(7 downto 0);
Ptr2 := Ptr1;

There is no allocator assigned to Ptr2, thus no new object will be created for it. Instead it will point to the same object, which was created for Ptr1.

Important Notes

· For each access type an implicitly declared procedure Deallocate is defined. The procedure reverses the evaluation of an allocator, i.e. releases the storage occupied by an object created by an allocator.

· Allocators (and access types) are not synthesizable.

· A subtype indication in allocator must not include a resolution function.

· An object created by an allocator has not its own name (indicator). Instead, it is referred to through the name, which it was allocated to.

· The concept of access types and allocators is very much the same as the concept of pointers in software programming languages.

Architecture

Formal Definition

A body associated with an entity declaration to describe the internal organization or operation of a design entity. An architecture body is used to describe the behavior, data flow, or structure of a design entity.

Simplified Syntax

architecture architecture_name of entity_name is

  architecture_declarations

begin

  concurrent_statements

end [ architecture ] [ architecture_name ];

Description

Architecture assigned to an entity describes internal relationship between input and output ports of the entity. It consists of two parts: declarations and concurrent statements.

First (declarative) part of an architecture may contain declarations of types, signals, constants, subprograms (functions and procedures), components, and groups. See respective topics for details.

Concurrent statements in the architecture body define the relationship between inputs and outputs. This relationship can be specified using different types of statements: concurrent signal assignment, process statement, component instantiation, concurrent procedure call, generate statement, concurrent assertion statement and block statement. It can be written in different styles: structural, dataflow, behavioral (functional) or mixed.

The description of a structural body is based on component instantiation and generate statements. It allows to create hierarchical projects, from simple gates to very complex components, describing entire subsystems. The connections among components are realized through ports. Example 1 illustrates this concept for a BCD decoder.

The Dataflow description is built with concurrent signal assignment statements. Each of the statements can be activated when any of its input signals changes its value. While these statements describe the behavior of the circuit, a lot of information about its structure can be extracted form the description as well. Example 2 contains this type of description for the same BCD decoder as in the previous example.

The architecture body describes only the expected functionality (behavior) of the circuit, without any direct indication as to the hardware implementation. Such description consists only of one or more processes, each of which contains sequential statements (Example 3).

The architecture body may contain statements that define both behavior and structure of the circuit at the same time. Such architecture description is called mixed (Example 4).

Examples

Example 1

architecture Structure of Decoder_bcd is
signal S: Bit_Vector(0 to 1);
component AND_Gate
  port(A,B:in Bit; D:out Bit);
end component;
component Inverter
port(A:in Bit; B:out Bit);
end component;
begin
Inv1:Inverter port map(A=>bcd(0), B=>S(0));
Inv2:Inverter port map(A=>bcd(1), B=>S(1));
A1:AND_Gate port map(A=>bcd(0), B=>bcd(1), D=>led(3));
A2:AND_Gate port map(A=>bcd(0), B=>S(1), D=>led(2));
A3:AND_Gate port map(A=>S(0), B=>bcd(1), D=>led(1));
   A4:AND_Gate port map(A=>S(0), B=>S(1), D=>led(0));
end Structure;

The components Inverter and AND_Gate are instantiated under the names Inv1, Inv2, A1, A2, A3 and A4. The connections among the components are realized by the use of signals S(0), S(1) declared in the architecture's declarative part.

Example 2

architecture Dataflow of Decoder_bcd is
begin
   led(3) <= bcd(0) and bcd(1);
   led(2) <= bcd(0) and (not bcd(1));
   led(1) <= (not bcd(0)) and bcd(1);
   led(0) <= (not bcd(0)) and (not bcd(1));
end Dataflow;

All the four statements here are executed concurrently and each of them is activated individually when any of its input signals changes its value.

Example 3

architecture procedural of Decoder_bcd is
signal S: bit_vector (3 downto 0);
begin
P1: process (bcd, S)
   begin
     case bcd is
         when "00" => S <= "0001" after 5 ns;
         when "01" => S <= "0010" after 5 ns;
         when "10" => S <= "0100" after 5 ns;
         when "11" => S <= "1000" after 5 ns;
     end case;
led <= S;
   end process;
end procedural;

The clause "after 5 ns" here allows to introduce time delay of the circuit. The assignment of a new value to the led signal will be done only after 5 nanoseconds of the simulated time.

Example 4

architecture Mixed of Decoder_bcd is
signal S: Bit_Vector(0 to 2);
component Inverter
port(A: in Bit; B: out Bit);
end component;
begin
Inv1: Inverter port map (A=>bcd(0), B=>S(0));
Inv2: Inverter port map (A=>bcd(1), B=>S(1));
P: process (S, bcd)
begin
      led(0) <= S(0) and S(1) after 5 ns;
      led(1) <= S(0) and bcd(1) after 5 ns;
      led(2) <= bcd(0) and S(1) after 5 ns;
      led(3) <= bcd(0) and bcd(1) after 5 ns;
end process;
end Mixed;

Above, two Inverter component instantiation statements define the circuit responsible for determining the value of the signal S. This signal is read by behavioral part i.e. the process statement P. In this process, the values computed by the and operation are assigned to the led output port.

Important Notes

· Single entity can have several architectures, but architecture cannot be assigned to different entities.

· Architecture may not be used without an entity.

· All declarations defined in an entity are fully visible and accessible within each architecture assigned to this entity.

· Different types of statements (i.e. processes, blocks, concurrent signal assignments, component instantiations, etc.) can be used in the same architecture.

Array

Formal Definition

A type, the value of which consists of elements that are all of the same subtype (and hence, of the same type). Each element is uniquely distinguished by an index (for a one-dimensional array) or by a sequence of indexes (for a multidimensional array). Each index must be a value of a discrete type and must lie in the correct index range.

Simplified Syntax

type type_name is array (range) of element_type

type type_name is array (type range <>) of element_type

Description

The array is a composite object, which elements are of the same subtype. Each of the elements is indexed by one or more indices belonging to specified discrete types. The number of indices is the number of dimensions, i.e. one-dimensional array has one index, two-dimensional has two indices, etc. The order of indices is significant and follows the order of dimensions in the type declaration (example 1).

An array may be either constrained or unconstrained. The array is constrained if the size of the array is constrained. The size of the array can be constrained using a discrete type mark or a range. In both cases, the number of the elements in the array is known during the compilation. Several declarations of constrained arrays are presented in example 1.

The array is said to be unconstrained if its size is unconstrained: the size of the unconstrained array is declared in the form of the name of the discrete type, which range is unconstrained. The number of elements of unconstrained array type is unknown. The size of a particular object is specified only when it is declared. Example 2 presents several declarations of unconstrained arrays.

Package STANDARD contains declarations of two one-dimensional unconstrained predefined array types: STRING and BIT_VECTOR. The elements of the STRING type are of the type CHARACTER and are indexed by positive values (i.e. counted from 1), and the elements of the BIT_VECTOR type are of the type BIT and are indexed by natural values (i.e. counted from 0). See string type and Bit_Vector for details.

Array elements are referenced by indices and can be assigned values individually or using concatenation, aggregates, slices or any mixture of those methods. See respective topics for details.

Examples

Example 1

type Real_Matrix is array (1 to 10) of REAL;
type BYTE is array (0 to 7) of BIT;
type Log_4_Vector is array (POSITIVE range 1 to 8, POSITIVE range 1 to 2) of Log_4;
type X is (LOW, HIGH);
type DATA_BUS is array (0 to 7, X) of BIT;

The type Real_Matrix is an array consisting of 10 elements, each of which is of the type REAL. Log_4_Vector is a two-dimensional array 82 and its elements are of type Log_4 (which must have been declared earlier). Also the type DATA_BUS is a two-dimensional array of the same size, but note that one of the dimensions is defined as enumeration type.

Example 2

-- unconstrained array of element of Real type:
type Real_Matrix is array (POSITIVE range <>) of Real;
variable Real_Matrix_Object : Real_Matrix (1 to 8);
-- unconstrained array of elements of Log_4 type:
type Log_4_Vector is array (NATURAL range <>, POSITIVE range<>) of Log_4;
variable L4_Object : Log_4_Vector (0 to 7, 1 to 2);

Examples of unconstrained types: Real_Matrix is when an unconstrained type and an object of this type is declared (Real_Matrix_Object) it is restricted to 8 elements. In similar way L4_Object is constrained from an unconstrained two-dimensional type Log_4_Vector.

Important Notes

· Synthesis tools do generally not support multidimensional arrays. The only exceptions to this are two-dimensional "vectors of vectors". Some synthesis tools allow two-dimensional arrays.

· Arrays may not be composed of files.

Assertion Statement

Formal Definition

A statement that checks that a specified condition is true and reports an error if it is not.

Simplified Syntax

assert condition

  report string

  severity severity_level;

Description

The assertion statement has three optional fields and usually all three are used.

The condition specified in an assertion statement must evaluate to a boolean value (true or false). If it is false, it is said that an assertion violation occurred.

The expression specified in the report clause must be of predefined type STRING and is a message to be reported when assertion violation occurred.

If the severity clause is present, it must specify an expression of predefined type SEVERITY_LEVEL, which determines the severity level of the assertion violation. The SEVERITY_LEVEL type is specified in the STANDARD package and contains following values: NOTE, WARNING, ERROR, and FAILURE. If the severity clause is omitted it is implicitly assumed to be ERROR.

When an assertion violation occurs, the report is issued and displayed on the screen. The supported severity level supplies an information to the simulator. The severity level defines the degree to which the violation of the assertion affects operation of the process:

· NOTE can be used to pass information messages from simulation (example 1);

· WARNING can be used in unusual situation in which the simulation can be continued, but the results may be unpredictable (example 2);

· ERROR can be used when assertion violation makes continuation of the simulation not feasible (example 3);

· FAILURE can be used when the assertion violation is a fatal error and the simulation must be stopped at once (example 4).

Assertion statements are not only sequential, but can be used as concurrent statements as well. A concurrent assertion statement represents a passive process statement containing the specified assertion statement.

Examples

Example 1

assert Status = OPEN_OK
  report "The call to FILE_OPEN was not successful"
  severity WARNING;

Having called the procedure FILE_OPEN, if the status is different from OPEN_OK, it is indicated by the warning message.

Example 2

assert not (S= '1' and R= '1')
  report "Both values of signals S and R are equal to '1'"
  severity ERROR;

When the values of the signals S and R are equal to '1', the message is displayed and the simulation is stopped because the severity is set to ERROR.

Example 3

assert Operation_Code = "0000"
  report "Illegal Code of Operation"
  severity FAILURE;

Event like illegal operation code are severe errors and should cause immediate termination of the simulation, which is forced by the severity level FAILURE.

Important Notes

· The message is displayed when the condition is NOT met, therefore the message should be an opposite to the condition.

· Concurrent assertion statement is a passive process and as such can be specified in an entity.

· Concurrent assertion statement monitors specified condition continuously.

· Synthesis tools generally ignore assertion statements.

Attributes (predefined)

Formal Definition

A value, function, type, range, signal, or constant that may be associated with one or more named entities in a description.

Simplified Syntax

object'attribute_name

Description

Attributes allow retrieving information about named entities: types, objects, subprograms etc. VHDL standard defines a set of predefined attributes. Additionally, users can define new attributes, and then assign them to named entities by specifying the entity and the attribute values for it. See attributes (user-defined) for details.

Predefined attributes denote values, functions, types, and ranges that characterize various VHDL entities. Separate sets of attributes are predefined for types, array objects or their aliases, signals and named entities.

Each type or subtype T has a basic attribute called T'Base, which indicates the base type for type T (Table 1). It should be noted that this attribute could be used only as a prefix for other attributes.

Table 1. Attributes available for all types

Attribute

Result

T'Base

base type of T

Scalar types have attributes, which are described in the Table 2. Letter T indicates the scalar type.

Table 2. Scalar type attributes

Attribute

Result type

Result

T'Left

same as T

leftmost value of T

T'Right

same as T

rightmost value of T

T'Low

same as T

least value in T

T'High

same as T

greatest value in T

T'Ascending

boolean

true if T is an ascending range, false otherwise

T'Image(x)

string

a textual representation of the value x of type T

T'Value(s)

base type of T

value in T represented by the string s

Discrete or physical types and subtypes additionally have attributes, which are described in Table 3. The discrete or physical types are marked with letter T before their names.

Table 3. Attributes of discrete or physical types and subtypes

Attribute

Result type

Result

T'Pos(s)

universal integer

position number of s in T

T'Val(x)

base type of T

value at position x in T (x is integer)

T'Succ(s)

base type of T

value at position one greater than s in T

T'Pred(s)

base type of T

value at position one less than s in T

T'Leftof(s)

base type of T

value at position one to the left of s in T

T'Rightof(s)

base type of T

value at position one to the right of s in T

Array types or objects of the array types have attributes, which are listed in the Table .4. Aliases of the array type objects have the same attributes. Letter A denotes the array type or array objects below.

Table 4. Attributes of the array type or objects of the array type

Attribute

Result

A'Left(n)

leftmost value in index range of dimension n

A'Right(n)

rightmost value in index range of dimension n

A'Low(n)

lower bound of index range of dimension n

A'High(n)

upper bound of index range of dimension n

A'Range(n)

index range of dimension n

A'Reverse_range(n)

reversed index range of dimension n

A'Length (n)

number of values in the n-th index range

A'Ascending(n)

True if index range of dimension n is ascending, False otherwise

Signal attributes are listed in Table 5. Letter S indicates the signal names.

Table 5. Signals attributes

Attribute

Result

S'Delayed(t)

implicit signal, equivalent to signal S, but delayed t units of time

S'Stable(t)

implicit signal that has the value True when no event has occurred on S for t time units, False otherwise

S'Quiet(t)

implicit signal that has the value True when no transaction has occurred on S for t time units, False otherwise

S'Transaction

implicit signal of type Bit whose value is changed in each simulation cycle in which a transaction occurs on S (signal S becomes active)

S'Event

True if an event has occurred on S in the current simulation cycle, False otherwise

S'Active

True if a transaction has occurred on S in the current simulation cycle, False otherwise

S'Last_event

the amount of time since last event occurred on S, if no event has yet occurred it returns Time'High

S'Last_active

the amount of time since last transaction occurred on S, if no event has yet occurred it returns Time'High

S'Last_value

the previous value of S before last event occurred on it

S'Driving

True if the process is driving S or every element of a composite S, or False if the current value of the driver for S or any element of S in the process is determined by the null transaction

S'Driving_value

the current value of the driver for S in the process containing the assignment statement to S

The named entities have attributes described in Table 6. Letter E denotes the named entities.

Table 6. Attributes of named entities

Attribute

Result

E'Simple_name

a string representing the simple name, character literal or operator symbol defined in the declaration of the item E

E'Path_name

a string describing the path through the design hierarchy, from the root entity or package to the item E

E'Instance_name

a string describing the path through the design hierarchy, from the root entity or package to the item E, but including the names of the entity and architecture bound to each component instance in the path

Paths which can be written using E'Path_name and E'Instance_name are used for reporting and assertion statements. They allow specifying precisely where warnings or errors are generated. E'Simple_name attribute refers to all named entities, E'Path_name and E'Instance_name can refer to all named entities apart from the local ports and generic parameters in the component declaration.

There is one more predefined attribute: 'Foreign' that allows the user to transfer additional information to the simulator. The information contains the instruction for special treatment of a given named entity. The exact interpretation of this attribute, however, depends on its implementation in particular simulator.

Examples

Example 1

type Table is array (1 to 8) of Bit;
variable Array_1 : Table := "10001111";
Array_1'Left, the leftmost value in index range of Table array, is equal to 1.

Example 2

type Table is array (Positive range <>) of Bit;
subtype Table_New is Table (1 to 4);
Table_New'Base, the base type of the Table_New subtype is Table.

Example 3

type New_Range is range 1 to 10;
New_Range'Ascending is TRUE (the New_Range type is of ascending range).

Example 4

type New_Values is (Low, High, Middle);
New_Values'Pred(High) will bring the 'Low' value.

Example 5

type Table is array (1 to 8) of Bit;

Table'Range(1) is the range of the first index of Table type and returns '1 to 8'; Table'Range will have the same interpretation for one dimensional array.

Important Notes

  • Not all predefined attributes are supported by synthesis tools; most tools support 'high, 'low, 'left, 'right, range, 'reverse_range, 'length and 'event. Some also support'last_value and 'stable.

Attributes (user-defined)

Formal Definition

A value, function, type, range, signal, or constant that may be associated with one or more named entities in a description.

Simplified Syntax

attribute attribute_name: type;             -- attribute declaration

attribute attribute_name of item : item_class is expression; -- attribute specification

Description

VHDL allows attaching additional information to design elements through new attributes for specified types. In order to assign an attribute to a given design element, attribute specification is used. The values assigned this way can be referred in the expressions through declared attribute name.

The attribute declaration defines a new attribute within the scope of the given declarative area. It consists of an identifier specification, which represents user defined attribute and type mark that indicates value type for this attribute. A user-defined attribute can be of any VHDL type, except for an access type, file type, and any complex type with elements of any of the two types. See Example 1.

Attribute specification assigns an attribute declared earlier to a chosen named entity. The named entities that can be assigned attributes are: entity, architecture, configuration, procedure, function, package, type, subtype, constant, signal, variable, component, label, literal, units, group, or file. The named entities are enumerated in entity names list. In such a way the attribute is assigned to a given language unit. Finally, the attribute specification contains an expression, which sets an attribute value for the entities listed in the specification. See Example 2.

The attribute specification for most named entities must be declared together with declarations of those entities. For some entities, however, the attribute specification is written in other places.

The attribute specification for library units such as entity, architecture configuration and package cannot be directly placed in the library, which contains library unit declarations. Because of that, the attribute specification is placed in the declaration part of named entity (Example 3).

The attribute specification for a subprogram must be declared in the same visibility region as the subprogram's declaration. In case of overloaded procedures and functions, signatures must be used to point to the subprogram to which the attribute is assigned (Example 4). If no signature is used the attribute relates to all subprograms with the same name.

Functions declared as the operators are always overloaded, and that is why they always require the signature in the attribute specification to differentiate functions (Example 5).

Attribute specifications for ports and generic parameters are placed in the declaration part of the design entity or the block statement to which they belong. The attribute specifications for formal parameters of subprograms are placed in the declaration part of these subprograms (Example 6).

Attribute specifications for labeled statements cannot be located directly in the place of the statements' declarations. Therefore, the attribute specifications for the label connected with any concurrent or sequential statements are placed in the declaration part before the occurrence of a given statement (Example 7).

In case of the sequential statement labels, the attribute specification is placed in the declaration part of the process or subprogram.

The attribute specification for literal must be declared in the same visibility block as the literal declaration. In case when there are several literals with the same names, the attribute specification for a given literal uses a signature in order to distinguish which type a given literal belongs to (Example 8). If no signature is used the attribute can be applied to all literals with the same name.

When specifying attributes reserved words: others and all can be used as entity (item) names. In the first case, the attribute specification refers to all the remaining visible named entities of a given entity class which do not have the attribute value assigned to them. Such an attribute specification must be the last in the declaration that refers to this attribute. When the keyword all is used, the attribute specification refers to all named entities of the given class. Such an attribute specification must be the first in the declaration part, which relates to this attribute. See Example 9.

Examples

Example 1

package Attr_pkg is
attribute Component_symbol : String;
attribute Pin_code : Positive;
attribute Max_delay: Time;
type Point is record
  x, y: Real;
end record;
attribute Coordinate : Point;
    ........
end package Attr_pkg;

The Atrr_pkg contains several attribute declarations, which can be later specified and used in other design units.

Example 2

package Some_declarations is
use Work.Attr_pkg.Component_symbol,
    Work.Attr_pkg.Coordinate,
    Work.Attr_pkg.Pin_code,
    Work.Attr_pkg.Max_delay;
constant Const_1: Positive := 10;
signal Sig_1: Bit_vector (0 to 31);
component Comp_1 is
port ( ... );
end component;
attribute Component_symbol of Comp_1: component is "Counter_16";
attribute Coordinate of Comp_1: component is (0.0, 17.5);
attribute Pin_code of Sig_1: signal is 17;
attribute Max_delay of Const_1: constant is 10 ns;
    .........
end package Some_declarations;

The package Some_declarations specifies attributes, which were declared in the package Attr_pkg.

Example 3

package Test_pkg is
attribute Package_atr : String;
attribute Package_atr of Test_pkg:
package is "Training_package";
    .........
end package Test_pkg;

The specification of a package attribute Package_atr for the package Test_pkg is declared in the declaration part of the package.

Example 4

procedure Sub_values (a, b : in Integer; result: out Integer);
procedure Sub_values (a, b : in Bit_vector; result: out Bit_vector);
attribute Description : String;
attribute Description of
        Sub_values [Integer, Integer, Integer]:procedure is "Integer_sub_values";
attribute Description of
        Sub_values [Bit_vector, Bit_vector, Bit_vector] :
procedure is "Bit_vector_sub_values";

The specification of the attribute Description intended for overloaded procedure Sub_values, which subtracts two values of integer or Bit_vector types, requires signature specification. These signatures (simplified parameter lists) enable to distinguish versions of the procedure.

Example 5

function "-" (a, b : New_logic) return New_logic;
attribute Characteristic : String;
attribute Characteristic of
  "-" [ New_logic, New_logic return New_logic]: function is "New_logic_op";

To identify operator ”-” overloaded for two values of type New_logic it is necessary to use signature that will unambiguously identify the overloading function.

Example 6

procedure Insert (fifo : inout Fifo_type; element: in Elem_type) is
attribute Number of fifo: variable is 50;
attribute Trace of element: constant is "Integer/Decimal";
   .........
end procedure Insert;

The procedure Insert has two formal parameters of different classes. Specifications of attributes Number and Trace for parameters fifo and element, respectively, are placed in the declaration part of the procedure.

Example 7

architecture Struct of ALU is
component Adder is
port (...)
end component;
attribute Coordinate of the_Adder: label is (0.0, 0.12);
begin
the_Adder : Adder port map ( ... );
.........
end architecture Struct;

Specification of the attribute Coordinate for the label the_Adder for component instantiation statement is located in the declarative part of the corresponding architecture body Struct.

Example 8

type Three_level_logic is (Low, High, Idle);
type Four_level_logic is (Low, High, Idle, Uninitialized);
attribute Hex_value : string (1 to 2);
attribute Hex_value of Low [return Four_level_logic]: literal is "F0";
attribute Hex_value of High [return Four_level_logic]: literal is "F1";
attribute Hex_value of Idle [return Four_level_logic]: literal is "F2";
attribute Hex_value of Uninitialized: literal is "F3";

As the literals Low, High, Idle are overloaded, it is necessary to use signature indicating their type in the specification of the attribute Hex_value for these literals. However, this is not necessary for the literal Uninitialized as it is not overloaded.

Example 9

B1: block
signal S1, S2, S3: Std_logic;
attribute Delay_attr: Time;
attribute Delay_attr of all: signal is 100 ps;
begin
   .........
end block;

The Delay_attr relates to all signals in the block B1.

Important Notes

· Common attributes can be declared for objects of different classes using one construct - group. See the group topic for details.

Bit

Formal Definition

The Bit type is predefined in the Standard package as an enumerated data type with only two allowable values: '0' and '1'.

Syntax:

type bit is ('0','1');

Description

The bit type is the basic type to represent logical values. Note that there are only two values defined for the bit type and it is not possible to use it for high impedance and other non-trivial values such as Unknown, Resistive Weak, etc. (see Std_logic).

According to the type definition, its leftmost value is '0', therefore the default value of any object of the bit type is '0'.

As the bit type is defined in the Standard package, it can be used in any VHDL specification without additional declarations.

Signals of the bit type are not resolved which means that such a signal can be assigned to an expression only once in the entire architecture.

Examples

Example 1

signal BitSig1, BitSig2 : bit;
. . .
BitSig1 <= '1';
BitSig2 <= not BitSig1;

The BitSig1 and BitSig2 signals are declared without an initial value, therefore by default they will be assigned the '0' value. In the next statement BitSig1 is assigned the '1' value. This value is complemented in the following statement and is assigned to BitSig2. Any additional assignment either to BitSig1 or BitSig2 would be illegal.

Important Notes

· Unlike in traditional ("hand-based") digital design, logical values 0 and 1 (bit type values '0' and '1') are NOT identical to boolean values (false and true), respectively. In VHDL, the latter items form completely different type (Boolean).

· Logical values for object of the bit type MUST be written in quotes to distinguish them from Integer values.

Bit_Vector

Definition:

The Bit_Vector type is predefined in the Standard package as a standard one-dimensional array type with each element being of the Bit type.

Syntax:

type bit_vector is array (natural range <>) of bit;

Description

The Bit_vector type is an unconstrained vector of elements of the bit type. The size of a particular vector is specified during its declaration (see the example below). The way the vector elements are indexed depends on the defined range and can be either ascending or descending (see range).

Assignment to an object of the Bit_vector type can be performed in the same way as in case of any arrays, i.e. using single element assignments, concatenation, aggregates, slices or any combination of them.

Examples

Example 1

signal DataBus : Bit_vector(7 downto 0);
signal FlagC : Bit;
DataBus(0) <= '1';                           -- 1
DataBus <= '0' & "111000" & FlagC;           -- 2
DataBus <= ('0', others => '1');             -- 3
DataBus <= DataBus(6 downto 0) & DataBus(7); -- 4
DataBus <= "01110001";                       -- 5

There is one bit_vector defined in this example - DataBus. Its range is defined as descending, therefore the most significant bit will be DataBus(7). Line 1, marked in the comment field, illustrates assignment of a single element (bit). The line 2 shows typical use of concatenation. Note that both single bits, groups of bits (with double quotes!) and other signals (as long as their type is compatible) can be used. The line 3 demonstrates the use of aggregates. The line 4 illustrates how slices can be used together with concatenation. The value of DataBus will be rotated left in this example. Finally, in line 5 DataBus is assigned an explicit value, specified with double quotes.

Despite that each of the numbered lines above is correct, it would be illegal to put them together in one specification as shown above, due to the fact that bit_vector is an unresolved type and there can be only one assignment to an object of this type in an architecture.

Important Notes

· Logical values for objects of the Bit_vector type MUST be written in double quotes. Single elements, however, are of the bit type, therefore all values assigned to single elements are specified in single quotes.

Block Statement

Formal Definition

The block statement is a representation of design or hierarchy section, used for partitioning architecture into self-contained parts.

Simplified Syntax

block_label : block (optional_guard_condition)

   declarations

begin

   concurrent statements

end block block_label;

Description

The block statement is a way of grouping concurrent statements in an architecture. There are two main purposes for using blocks: to improve readability of the specification and to disable some signals by using the guard expression (see guard for details).

The main purpose of block statement is organizational only - introduction of a block does not directly affect the execution of a simulation model. For example, both the upper and lower sections of code in Example 1 will generate the same simulation results.

Each block must be assigned a label placed just before the block reserved word. The same label may be optionally repeated at the end of the block, right after the end block reserved words.

A block statement can be preceded by two optional parts: a header and a declarative part. The latter allows to introduce declarations of subprograms, types, subtypes, constants, signals, shared variables, files, aliases, components, attributes, configurations, disconnections, use clauses and groups (i.e. any of the declarations possible for an architecture). All declarations specified here are local to the block and are not visible outside it.

A block header may contain port and generic declarations (like in an entity), as well as so called port map and generic map declarations. The purpose of port map and generic map statements is to map signals and other objects declared outside of the block into the ports and generic parameters that have been declared inside of the block, respectively. This construct, however, has only a small practical importance. The Example 2 illustrates typical block declarations.

If an optional guard condition is specified at the beginning of the block then this block becomes a guarded block. See guard for details.

The statements part may contain any concurrent constructs allowed in an architecture. In particular, other block statements can be used here. This way, a kind of hierarchical structure can be introduced into a single architecture body.

Examples

Example 1

A1: OUT1 <= '1' after 5 ns;
LEVEL1 : block
begin
A2: OUT2 <= '1' after 5 ns;
A3: OUT3 <= '0' after 4 ns;
end block LEVEL1;
A1: OUT1 <= '1' after 5 ns;
A2: OUT2 <= '1' after 5 ns;
A3: OUT3 <= '0' after 4 ns;

Both pieces of code above will behave in exactly the same way during simulation - block construct only separates part of the code without adding any functionality.

Example 2

entity X_GATE is
generic (LongTime : Time; ShortTime : Time);
port (P1, P2, P3 : inout BIT);
end X_GATE;
architecture STRUCTURE of X_GATE is
-- global declarations of signal:
signal A, B : BIT;
begin
LEVEL1 : block
-- local declaration of generic parameters
generic (GB1, GB2 : Time);
-- local binding of generic parameters
generic map (GB1 => LongTime, GB2 => ShortTime);
-- local declaration of ports
port (PB1: in BIT; PB2 : inout BIT );
-- local binding of ports and signals
port map (PB1 => P1, PB2 => B);
-- local declarations:
constant Delay : Time := 1 ms;
signal S1 : BIT;
begin
   S1 <= PB1 after Delay;
   PB2 <= S1 after GB1, P1 after GB2;
end block LEVEL1;
end architecture STRUCTURE;

The signals PB1 and PB2 have here the same values as P1 and B (in port map statement), respectively, and the generics GB1 and GB2 (see generic map statement) have the same values as Long Time and Short Time, respectively. However, such assignment is redundant because a block may use any declarations of an entity, including generics and ports. The Example 2 is presented here only for illustration purpose of the block syntax.

Important Notes

· Guarded blocks are generally not synthesizable.

· Unguarded blocks are usually ignored by synthesis tools.

· It is strongly recommended NOT to use blocks in non-VITAL designs - the package Std_logic_1164 supports mechanisms and multiple value logic which make the reserved words bus, disconnect, guarded and register unnecessary. Also, instead of guarded blocks for modeling sequential behavior it is recommended to used clocked processes.

· VITAL specifications require the use of blocks.

· VHDL supports a more powerful mechanism of design partitioning which is called component instantiation. Component instantiation allows connecting a component reference in one entity with its declaration in another entity.

Boolean

Definition:

The Boolean type is predefined in the Standard package as an enumerated data type with two possible values: false and true.

Syntax:

type boolean is (false,true);

Description

The boolean type is used for conditional operations. Boolean objects can be used with any of the relational operators <, >, <=, >=, = or /=.

According to the definition type, the leftmost value of the Boolean type is false, therefore the default value of any object of the Boolean type is false.

Since the boolean type is defined in the Standard package, it can be used in any VHDL specification without additional declarations.

Examples

Example 1

signal CondSup : boolean;
. . .
CondSup <= true;
. . .
if CondSup then -- could be: if CondSup = true then

The CondSup signal is declared as boolean but without any initial value. Therefore, by default it will be assigned the false value. A conditional operation could have been used instead as shown in the comment, but such a form would contain useless redundancy and should be avoided.

Important Notes

· Unlike in traditional ("hand-based") digital design, boolean values (false and true) are NOT identical to logical 0 and 1, respectively. In VHDL, the latter form is a completely different type and is called the Bit type.

Case Statement

Formal Definition

The case statement selects for execution one of several alternative sequences of statements; the alternative is chosen based on the value of the associated expression.

Simplified Syntax

case expression is

when choice => sequential_statements

when choice => sequential_statements

         . . .

end case;

Description

The case statement evaluates the listed expressions and selects one alternative sequence of statements according to the expression value. The expression can be of a discrete type or a one-dimensional array of characters (example 1).

The case statement contains a list of alternatives starting with the when reserved word, followed by one or more choices and a sequence of statements.

An alternative may contain several choices (example 2), which must be of the same type as the expression appearing in the case statement. For each expression there should be at least one locally static choice. The values of each choice must be unique (no duplication of values is allowed).

A choice can be either a simple name (example 1), a name of a simple element (example 2) or discrete range (a slice, example 3). The choice types can be mixed.

A subtype with a constraint range (example 4) can substitute a slice.

Another option is to use an object name as the choice. The object must be of the same type as the expression in the case statement. Example 5 shows it for a constant.

When all explicitly listed choices do not cover all the alternatives (all the values available for an expression of given type) the others choice must be used because the choicestatements must cover all the alternatives, see example 5).

Examples

Example 1

P1:process
variable x: Integer range 1 to 3;
variable y: BIT_VECTOR (0 to 1);
begin
C1: case x is
      when 1 => Out_1 <= 0;
      when 2 => Out_1 <= 1;
      when 3 => Out_1 <= 2;
  end case C1;
C2: case y is
      when "00" => Out_2 <= 0;
      when "01" => Out_2 <= 1;
      when "10" => Out_2 <= 2;
      when "11" => Out_2 <= 3;
  end case C2;
end process;

Depending on the values of the variable x and y, we assign the values 0, 1, 2 or 3 (in the second case) to the signals Out_1 and Out_2 (both of type Integer).

Example 2

P2:process
type Codes_Of_Operation is (ADD,SUB,MULT,DIV);
variable Code_Variable: Codes_Of_Operation;
begin
C3: case Code_Variable is
      when ADD | SUB => Operation := 0;
      when MULT | DIV => Operation := 1;
  end case C3;
end process;

When two or more alternatives lead to the same sequence of operations then they can be specified as a multiple choice in one when clause.

Example 3

P3:process
type Some_Characters is ('a','b','c','d','e');
variable Some_Characters_Variable: Some_Characters;
begin
C4: case Some_Characters_Variable is
      when 'a' to 'c' => Operation := 0;
      when 'd' to 'e' => Operation := 1;
   end case C4;
end process;

Slices can be used as choices. In such a case, the slice name must come from the discrete range of the expression type.

Example 4

P5:process
variable Code_of_Operation : INTEGER range 0 to 2;
constant Variable_1 : INTEGER := 0;
begin
C6: case Code_of_Operation is
      when Variable_1 | Variable_1 + 1 =>
Operation := 0;
      when Variable_1 + 2 =>
Operation := 1;
  end case C6;
end process;

Constant used as a choice.

Example 5

P6:process
type Some_Characters is ('a','b','c','d','e');
variable Code_of_Address : Some_Characters;
begin
C7:case Code_of_Address is
      when 'a' | 'c' => Operation := 0;
      when others => Operation := 1;
   end case C7;
end process;

If the Code_of_Address variable is equal to 'a' and 'c', then the assignment Operation:=0; will be chosen. For the 'b', 'd' and 'e' values, the assignment Operation:=1; will be performed.

Important Notes

· The case expression must be of a discrete type or of a one-dimensional array type, whose element type is a character type.

· Every possible value of the case expression must be covered by the specified alternatives; moreover, every value may appear only once (no duplicates or overlapping of ranges is allowed).

· The When others clause may appear only once and only as the very last choice.