2000 © Deepak George
www.oocities.org/deepakgeorge2000
Deepak George's VLSI Design: VHDL Book - Chapter 2
Contact: deepak.g@angelfire.com
CHAPTER 2
Language Constructs
CONTANTS:
2.0 Introduction
2.1 VHDL Data Types
2.2 VHDL Objects
2.2.1 Signals
2.2.2 Variables
2.2.3 Constants
2.3 VHDL Operators
2.4 VHDL Attributes
2.5 VHDL Examples
2.6 Exercise
2.0 Introduction
This chapter will introduce you to the basic language constructs: starting from data types, objects, operators and attributes. After going through this chapter you should be able to write simple VHDL codes.
2.1 VHDL Data types
There are three kinds of objects, which store data, namely signals, variables and constants. A signal type object maintains a time history of its value changes while a variable type object store only its current value. A constant can be assigned value only at the time of its definition.
2.1.1 Scalar Types:
Scalar data types consist of a single data element.
Objects belonging to scalar data types have an ordering relation defined for them. I.e. if A and B are two objects of a scalar type, then a relation like A<B is defined for the entire range of values of A and B.
Defining a new type- enumeration:
Any new data type can be defined in VHDL by enumerating it.
Syntax:
type
TYPENAME is <(IDENTIFIER LIST)>;e.g.
type
FOUR_VALUED_LOGIC is (’X’,’Z’,’0’,’1’);type
STATE_TABLE is (RESET,S0,S1,S2,FINAL);
The type declaration statement describes only the internal structure of an object. It does not have any physical existence. The objects derived from the above classes have a physical existence in the sense that memory is allocated for them. Defining an object of a class is called instantiation of the class. The syntax for object instantiation is as follows.
Instancing A Class:
<object class name> <identifier> : <data type name> [:=initial value];
Examples:
Variable A: FOUR_VALUED_LOGIC;
Signal PRESENT_STATE, NEXT_STATE: STATE_TABLE;
Constant C: STATE_TABLE: = S0;
In the above statements, the colon ":" is used as "derived from". In the enumeration identifier list, the value of a symbol increases from left to right. E.g. in the FOUR_VALUED_LOGIC enumeration given above, ‘X’ is the smallest element and ‘1’ is the largest element.
The STANDARD package starts from scratch to define the basic data types using the enumeration method. Some basic declarations are given below.
type BOOLEAN is (FALSE, TRUE);
type BIT is ('0', '1');
type INTEGER is range -2147483648 to +2147483647;-- 32 bits signed
type REAL is range -0.179769313486231e+309 to +0.179769313486231e+309;
type CHARACTER is (
NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL,
BS, HT, LF, VT, FF, CR, SO, SI,
DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB,
CAN, EM, SUB, ESC, FSP, GSP, RSP, USP,
' ', '!', '"', '#', '$', '%', '&', ''',
'(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '[', '\', ']', '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', '{', '|', '}', '~', DEL);
We can define our own versions of numeric types having a different range.
E.g.
Type RAM_T is range 0 to 8191; --custom integer
Type MY_REAL is range –7.5 to 12.5; --custom real
Type TTL_VCC is range 4.75 to 5.25; --custom real
Note that the above user defined types are completely new types. Only numerical operations are permitted on them. Mixing them with standard integers or any other user defined numerical types is not permitted. Also, the operators and functions, which are overloaded for integers or real numbers, are not applicable to the user defined numeric types. If we want the new type to inherit all the overloading properties of the base type, then we must declare them as subtypes of integer or real as explained later.
VHDL is a highly typed language
.
The type of all objects in an arithmetic operation and the type of the target object must be same. If we want to operations on mixed type objects, then suitable conversion functions must be called. Some conversion functions are defined in the IEEE package.
Example:
Library IEEE;
Use IEEE.STD_LOGIC_1164.ALL;
Use IEEE.STD_LOGIG_UNSIGNED.ALL;
Use IEEE.STD_LOGIC_ARITH.ALL;
Entity …..
Port ( addr : in std_logic_vector ( 3 downto 0 );
Data : inout std_logic_vector (7 downto 0);
WR,RD : in std_logic );
End entity;
Architecture ……….
Type Ram_t is array 0 to 15 of std_logic_vector (7 downto 0);
Signal Ram : Ram_t;
begin
…………
Process ………
Begin
………….
Ram (
conv_integer(addr)) <= data; -- Convert Integer
……………….
End Process;
End Architecture;
In this example we use the conv_integer function defined in the IEEE package to convert from data type std_logic_vector to integer.
The STANDARD package, which was a part of the original release of the IEEE standard, declares only one data-type for representing binary numbers, namely BIT. Unfortunately, this data type does not adequately represent many practical situations where we need to consider the strength of the driver, unknown state, don’t care state and high Impedance State. To tackle this problem, many tool developers defined their own multi valued logic types, which made portability a big problem. To solve these difficulties, IEEE later released the STD_LOGIC_1164 package, which declares a data type STD_ULOGIC having 9 values as follows.
TYPE std_ulogic IS ('U', -- Uninitialized
'X', -- Forcing Unknown
'0', -- Forcing 0
'1', -- Forcing 1
'Z', -- High Impedance
'W', -- Weak Unknown
'L', -- Weak 0
'H', -- Weak 1
'-' -- Don't care
);
The std_ulogic is an
unresolved type. I.e. it does not include a resolution function to take care of two outputs driving a single signal. Such functions are required for describing tri-state buses and wired-or logic. For this purpose, the package declares a subtype called STD_LOGIC which is derived from STD_ULOGIC as follows.
SUBTYPE std_logic IS resolved std_ulogic
;
The function "resolved" returns a single value for the signal considering the state and strength of all its drivers. For new designs, it is recommended to use either BIT or std_logic data types so that portability will not be a problem.
2.1.2 Physical type:
VHDL allows definition of physical types, i.e. quantities having physical units like time, length, resistance, capacitance etc. The STANDARD package defines one physical type called TIME as follows.
type TIME is range -9223372036854775807 to +9223372036854775807
units
fs; -- femtosecond
ps = 1000 fs; -- picosecond
ns = 1000 ps; -- nanosecond
us = 1000 ns; -- microsecond
ms = 1000 us; -- millisecond
sec = 1000 ms; -- second
min = 60 sec; -- minute
hr = 60 min; -- hour
end units;
Internally, the physical type is stored as an integer in terms of the base unit. E.g. a time of 5 ns is equivalent to 5,000,000 fs and so, it is stored as 5,000,000. Since it is basically an integer type, it can be multiplied by another integer giving a result in physical units. Similarly, when a physical data-type is divided by the base unit, the result is an integer. E.g.
Signal IntTime: integer;
Signal PhyTime: time:= 5 ns;
IntTime <= PhyTime/ 1 fs;-- The value of IntTime will be 5,000,000
NOTE: Physical Data Type cannot be synthesized
Subtypes:
Subtype is a type with a range constraint. The set of values that a subtype can have is a subset of the values of the base type. The arithmetic and logical operations that are defined for the base type are valid for the subtype also because they inherit all properties of the base type. Subtypes are basically used for range checking during simulation.
E.g.
Subtype SMALL_INT is integer range 0 to 15;
Subtype NUM_CHAR is character range ‘0’ to ‘9’;
Variable x: SMALL_INT;
Variable y: NUM_CHAR;
Renaming a standard type: A subtype declaration need not necessarily impose a range constraint. In that case, it just becomes an another name for the base type like the "typedef" command of "C". E.g.
Subtype int is integer;
Subtype float is real;
Variable x: int;
Signal y:float;
The STANDARD package declares following subtypes.
subtype NATURAL is INTEGER range 0 to INTEGER'HIGH;
subtype POSITIVE is INTEGER range 1 to INTEGER'HIGH;
INTEGER'HIGH denotes the highest value of the integer type. HIGH is an attribute of the integer data type. Attributes are discussed in detail later.
Numeric literals:
Numeric values in an expression are called literals. Underscore characters may be freely inserted in a number to improve its readability. Following are the examples of valid integer literals.
5 , 55E4, 0, 5_657_554, -70
Real literals must have a decimal point in the mantissa and an optional exponent. Following are the examples of valid real literals.
16.26, 0.0, 62.3 E –2
We can express literals in other number systems also. Such literals are called "Based literals". The general syntax is:
Form 1: Base # Based value #
Form 2: Base # Based value # E exponent
The base value and the exponent are given in decimal notation.
e.g.
2#1101_0011# (Binary)
16#1_B3# (Hex,1*256+11*16+3)
16#A#E2 (Hex,10*256)
2.1.3 Composite Type:
As opposed to scalars:
Composite type contains a collection of data elements.
If the type of all the elements in a composite data type is identical, then it is called an "ARRAY"
If it has many data types it is called a "RECORD".
Type
<ARRAY_NAME> is array <(index range)> of <ELEMENT_TYPE>;
The range may be either ascending or descending. For declaring multi dimensional arrays, multiple ranges have to be specified. E.g.
type INT_ARR is array(0 to 15) of integer;
type BIT_ARRAY is array(0 to 32, 7 downto 0) of BIT;
The objects of the above types can be declared as:
Signal MY_INT_ARR: INT_ARR;
Signal MY_BIT_ARR: BIT_ARRAY;
Instead of giving the index range in the declaration of an array type, we can also give a data-type name in its place. The index range then becomes the range of that data type. Using this method, we can declare a 2D array as follows.
type COLUMN is range 1 to 20;
type ROW is range 1 to 10;
type Table is array(ROW, COLUMN) of Boolean;
variable MY_TAB:Table;
Unconstrained arrays:
We can declare an array type whose range is indefinite. The clause "data type range <>" represents an indefinite range. E.g.
Type FloatArray is array (integer range<>) of real;
When we instantiate an object of the Unconstrained array class, we have to specify the range. E.g.
Signal MyFloatArray: FloatArray(0 to 31);
The STANDARD package declares two unconstrained array types namely STRING and BIT_VECTOR as follows.
type STRING is array ( POSITIVE range <> ) of CHARACTER;
type BIT_VECTOR is array ( NATURAL range <> ) of BIT;
A two-dimensional array can also be declared as a one-dimensional array whose elements are themselves a one dimensional array. E.g.
Type RAM is array (0 to 1023) of BIT_VECTOR(7 downto 0);
Signal MY_RAM: RAM;
The maximum number of dimensions for an array is not specified in VHDL but most synthesis tools do not support more than 2 dimensions.
We can access the elements of an array by giving the object name followed by the index value in parenthesis. E.g.
MY_INT_ARR (8), MY_BIT_ARRAY (4,6), MY_RAM (654).
STRING LITERALS:
String constants appearing in assignment expressions are called "string literals". String literals are enclosed in double quote marks such as "My program". An important subset of string literals is the "Bit string literal". Bit string literals are very convenient for assigning values to bit vectors. E.g.
MY_RAM(22)<="11000101";
Prefixing a string with "X" makes it a hex string. Similarly, prefixing with "O" makes it an octal string. E.g.
MY_RAM(22)<=X"FF";
Note that a string like "1101" is treated either as a bit string or a std_logic string depending the type of the target object. However, the X and O prefixes always convert the string into a bit string and therefore its target object must be a BIT_VECTOR. If the target is std_logic_vector, then the bit string literal must be converted into a std_logic string literal by using the "To_StdLogicVector" function defined in the std_logic_1164 package. E.g.
Signal S1, S2, S3: std_logic_vector (7 downto 0);
Signal B1, B2, B3: bit_vector (7 downto 0);
B1<= "11001100";
B2<= X"FF";
B3<= "11" & O"77";-- Match the length by concatenating 2 bits with two octal digits
S1<= "11001100";
S2<= To_StdLogicVector (X"FF");
S3<= To_StdLogicVector (O"377");-- Length taken care by To_StdLogicVector( )
Integers and bit vectors:
Logically speaking, integers and bit vectors are both groups of bits. But since VHDL is a strongly typed language it treats them quite differently. The arithmetic operations are defined only for the integers and the concatenation operation is defined only for bit vectors. The ieee.std_logic_arith package overloads the arithmetic operators on std_logic_vectors so that we can perform arithmetic operations on them. But if you want to assign an integer value to a std_logic_vector or vice versa, then you have to explicitly use conversion functions
CONV_STD_LOGIC_VECTOR and CONV_INTEGER. The prototype of CONV_STD_LOGIC_VECTOR in std_logic_arith package is as follows.function CONV_STD_LOGIC_VECTOR(ARG: INTEGER; SIZE: INTEGER)
return STD_LOGIC;
The prototype of CONV_INTEGER in
std_logic_unsigned package is as follows.function CONV_INTEGER(ARG: STD_LOGIC_VECTOR) return INTEGER;
Examples of type conversion
Consider the declarations:
type int is range 0 to 15;
subtype int1 is integer range -30 to 30;
signal x,y,z:int1;
signal h:string(1 to 6):="Hellow";
signal A: Bit_vector ( 7 downto 0);
signal B: std_logic_vector( 7 downto 0);
signal I1: int1:=5;
signal I2: integer;
Now let us consider the validity of some statements.
A<="00001111";--OK
A<=X"0F";--OK
A<=O"377";--not OK as length of RHS string is 3*3=9 and LHS=8
A<="11"&O"77";--OK version of above
B<="00001111"; --OK
B<= X"0F"; -- not OK as B is std_logic_vector
B<=To_StdLogicVector (X"0F");--OK version of above
B<= O"377";" -- not OK as B is std_logic_vector
B<=To_StdLogicVector (O"377");--OK version of above
I2<=B; -- not OK as I2 is integer and B is std_logic_vector
I2<=conv_integer (B);--OK version of above
B<=CONV_STD_LOGIC_VECTOR (I,8);--OK
I2<=conv_integer (A); -- not OK as conv_integer is not defined for bit_vector
I2<=conv_integer (To_stdLogicVector(A)) ;--OK version of above
A4<=A4+1; -- not OK as + operator is not overloaded for bit_vector
B4<=B4+1; --OK as + operator is overloaded for std_logic_vector
I1<=I1+1;--OK
I2<=I1; not OK as types are different
2.1.3.2 RECORDS:
Record is a heterogeneous composite data type. The concept of record is similar to the "struct" declaration in "C". The syntax of record declaration is as follows.
Type TYPE_NAME is record
MEMBER_NAME1: MEMBER_TYPE1;
MEMBER_NAME2: MEMBER_TYPE2;
MEMBER_NAMEn: MEMBER_TYPEn;
End record;
Example of record type.
Type address is bit_vector (11 downto 0);
Type instruction is bit_vector (3 downto 0);
type REGISTER_BANK is record
F0: real;
F1: real;
I0: integer;
I1: integer;
AD: address;
IN: instruction;
End record;
An object of the above type can be defined as:
Signal MY_BANK: REGISTER_BANK;
Members of a record can be addressed by means of the dot operator on the object.. E.g.
MY_BANK.F0 <= 6.7;
MY_BANK.R0 <= 50;
2.1.4 Access type:
Values belonging to access types are memory addresses of dynamically created objects of some other types. They are similar to "pointers" in "C".
e.g. type FIFO is array (0 to 63) of BIT_VECTOR(7 downto 0);
type FIFO_PTR is access to FIFO;
A FIFO-type object can be dynamically created by using the "new" operator.
FIFO_PTR= new FIFO;
Members of this object can be accessed, as FIFO_PTR (n) where n is the index.
The object can be deleted by the DEALLOCATE procedure. E.g.
DEALLOCATE (FIFO_PTR);
NOTE: Access type is not supported by synthesis tools.
2.1.5 File type:
Objects of file type represent files in the host environment. They provide a mechanism by which a VHDL design communicates with the host environment. The syntax of file type is:
Type FILE_TYPE is file of ELEMENT_TYPE;
e.g. type TEST_VECTOR is file of BIT_VECTOR( 7 downto 0);
While declaring a file object, we have to also specify its mode (in or out) and file name.
e.g. file TESTFILE:TEST_VECTOR is in "C:\Deepak\Vhdl_eg\vec.dat";
The data is transferred with the READ and WRITE commands as described later.
NOTE: File type is not supported by synthesis tools.
2.2 VHDL Objects:
Anything that stores information is called an "object " in VHDL. In VHDL there are 3 types of data objects.
3. Constants
2.2.1 Signals
Syntax:
signal <
object_name> : < signal_type > (range constraint) (:= Val)
For signal objects, the simulator maintains a data structure called driver of the signal that records the time and a value of each assignment to the signal (called transaction). When a new transaction is added, certain old transactions have to be dropped from the driver in order to be consistent with the delay model used. The rules for adding transaction are explained in the next section.
Each concurrent statement produces one driver for the target signal. If more then one concurrent statement assign values to the same target signal, then it becomes a multi-writer signal and it must be declared as a resolved signal type. A signal can be declared as "resolved signal type" by specifying a resolution function in the declaration of the signal. The resolution function takes all values that the individual drivers wish to assign to the signal and resolves them to produce a single value that is actually assigned to the signal. Such functions are used to describe a tristate bus or a wired OR logic. e.g.
signal Z : WIRED_OR BIT;
Here, Z is resolved signal of type BIT, with WIRED_OR as the resolution function. The procedure for writing resolution functions is described in chapter 5.
If we want to declare many resolved type signals, it is more convenient to declare a resolved subtype of a base type and then use this subtype to declare the signals.
subtype RESOLVED_BIT is WIRED_OR BIT;
signal Z : RESOLVED_BIT;
Remember that the type STD_LOGIC has been already defined as a resolved subtype of the STD_ULOGIC type in the std_logic_1164 package.
Signal declaration zones:
Objects of signal class are declared in following zones.
Examples:
Entity XX is
Port (a, b: in BIT; c: out BIT);
Signal d, e, f: BIT;
End XX;
Architecture YY of XX is
signal X : BIT ;
signal Y : BIT := ‘0’ ;
signal X_BUS : bit_vector (7 downto 0) := X "FF" ;
begin
end YY;
Creating signal waveforms
We can conveniently generate waveforms by using assignment statements with multiple transactions. Suppose we want to create a waveform on the signal "phase" as shown in fig 2.1.
PHASE ----> T (0), T+8 (1) ,T+13 (0) ,T+50 (1) - ns
The waveform can be generated by the following statement.
Phase <= ‘0’, ‘1’ after 8 ns, ‘0’ after 13ns, ‘1’ after 50 ns;
Signals drivers
A signal driver consists of an array of time-value pairs. Consider the statement:
RESET <= 3 after 5 ns, 21 after 10 ns, 14 after 17 ns;
It produces following entries in the driver.
RESET curr@now 3@T+5ns 21@T+10ns 14@T+17ns
Effect of transport delay on signal drivers
Recall that a transport delay faithfully reproduces the input waveform at the output after a specified delay. Consider following multiple assignments in a process to the signal RX_DATA.
Entity TEST is end TEST;-- this entity does not have any ports
Architecture T of TEST is
Signal RX_DATA: integer;
begin
process
begin
RX_DATA <=transport 11 after 10ns;
RX_DATA <=transport 20 after 22ns;
RX_DATA <=transport 35 after 18ns;
Wait;
end process;
end T;
The transactions are added to the driver according to the following rules.
RULES FOR ADDING A TRANSACTION TO A TRANSPORT DELAY.
According to these rules, the driver looks like this after the first two statements.
RX_DATA curr@now -->
11@T+10ns 20@T+22ns
The third statement causes the 22 ns transaction to be deleted and the driver now looks like this.
RX_DATA curr@now 11@T+10ns 35@T+18ns
Effect of inertial delay on signal drivers
Consider the following process block.
Entity TEST is end TEST;
Architecture T of TEST is
Signal TX_DATA: integer;
begin
process
begin
TX_DATA <= 11 after 10 ns;
TX_DATA <= 22 after 20 ns;
TX_DATA <= 33 after 15 ns;
wait; --suspends indefinitely .
end process;
end T;
In the inertial delay model, following rules apply for adding a new transaction.
The driver of the TX_DATA signal looks like this at the end of the process.
TX_DATA curr@now 11 @ 10ns 22 @ 20 ns
TX_DATA curr@now 33@15ns
Let us consider another example, which clearly demonstrates how the above rules filter out narrow pulses.
Entity TEST is end TEST;
Architecture T of TEST is
Signal A, B, C: BIT;
begin
A<= ‘1’ after 10 ns, ’0’ after 18 ns;-- pulse of 8 ns width
B<= A after 15 ns;-- large inertial delay
C<= A after 5 ns;-- small inertial delay
End T;
At t=10, the first event on A places transactions (1,15) and (1,5) on the drivers of B and C respectively. After 5 ns, i.e. at t=15, C becomes ‘1’. At t=18, second event on A places a transaction (0,15) on B and (0,5) on C. By this time, the driver of C is empty and the driver of B contains the old transaction which now looks as (1,7). Since the value of the new transaction (i.e. 0) is different from the old transaction (i.e.1), the old transaction is deleted and therefore, B remains ‘0’ throughout. C changes to 0 at t=23.
2.2.2 VARIABLES
Variable a : std_logic;
a : = b and c after 10 ns;
--- WRONG
If multiple variable assignments are made then the order of the assignments decides the value of variable.
EXAMPLE:
inside process
a:= b and c;
c<= not b;
a:= not d;
end process;
Objects of the variable class only have a current value and no time history. They are used as intermediate storage locations and for passing parameters to functions in the sequential bodies. Variables can not be defined in the concurrent portion of the code. Although 1993 standard has introduced shared variables, most tools don’t support them. They can be declared only in the variable declaration zone of the process, function or procedure body.
Scope and Value retention:
The scope of the variables is only limited to the process, procedure or a function where it is declared. Hence variables can not be used to transmit information from one process to another. Process variables retain their values between two invocations but Procedure and function variables are initialized each time the procedure is called.
Sometimes the programmer has a choice to choose the object type as a signal or a variable. In that case, it is preferable to choose variable because the synthesis tool does not distinguish between a variable and a signal but a simulation tool assigns a driver only to the signals. Unnecessarily defining too many signals slows down the simulation.
Assignment to variables:
Assignment to a variable can be made only within a sequential portion of the code. The assignment operator ":=" is used for variables. The target variable immediately assumes the value of the RHS expression. In contrast, recall that an assignment to a signal, made in a sequential body takes effect only at the end of that body.
Declaration of variables:
Variables are declared before the "begin" keyword of the sequential body. E.g.
Process
Variable a, b, c: BIT;
Begin
end process;
2.2.3 CONSTANTS
Example:
constant a : std_logic := ‘0’;
constant a : std_logic_vector (7 downto 0) := "01011100";
constant b: integer := 4;
Objects of the constant class are similar to the variables but they have an additional restriction that they can be assigned values only during their declaration.
Constant can be declared wherever signals and variables are declared. They can also be declared in the package declarations and package bodies.
Assignment statements in VHDL are of the form:
OBJECT_NAME <assignment operator> <Expression> [delay specifier];
Assignment operator "<=" is used if the target object is of signal type, otherwise operator ":=" is used. For initializing an object, assignment should be made during its definition. These initializing assignments always use the ":=" operator irrespective of the type of the object. Constants can be assigned value only during their definition. The only exception being that a constant can be declared in the package declaration and given a value in the package body.
Expression is either a single object or a literal or a combination of objects and literals connected by suitable operators.
Delay specifier can be given only if the target of the assignment is a signal.
Types of assignments: Concurrent and sequential
An assignment statement in concurrent portion of the program is called a concurrent assignment and an assignment statement in sequential portion of the program is called a sequential assignment. Every concurrent assignment produces a "DRIVER" for the signal, which contains the schedule for its future assignments. A sequential body like a process may contain several assignments to the same signal, but they are all added to a single driver. The time, when an assignment becomes effective, is different for signals and variables. If a process makes an assignment to a variable, then it takes effect immediately but if it makes an assignment to a signal, then it takes effect in the next simulation cycle. In other words, during one invocation of a process, the signals retain their old values till the end of the process while the variables change their values as the program progresses.
Another interesting property of concurrent assignment statements is that all concurrent statements get fired at t=0 irrespective of their sensitivity lists and they operate on the initialized data. All processes also get fired once at t=0.
Following are some examples of assignment statements.
Signal A: BIT;
Signal B: BIT:=’0’;-- Initialization
variable C: string(1 to 6);
variable D,E: integer;
constant F: integer :=4;
A<= B or ‘1’;
C<="Hellow";-- The length of the target array must exactly match the length of string
D := E+F+5;
Composite assignments:
VHDL has many constructs, which make assignments to composite objects easy. To illustrate composite assignments, let us define some objects using the type declarations given section 1.1.
BANK1, BANK2: REGISTER_BANK:
RAM1, RAM2: RAM;
A, B: bit_vector (7 downto 0);
A single assignment statement can copy a whole object as shown below.
BANK1 <= BANK2;
RAM1 <= RAM2;
A <= B;
If we want to make an assignment to a portion of an array rather than the whole array, keeping the other bits intact, then we can use the "slice" operator. The syntax of slice is as follows.
ARRAY_OBJ_NAME (RANGE)
The direction of the slice range must be same as the direction of the parent array.
SLICE EXAMPLES
Let us define some array types as follows.
Type nibble is array ( 3 downto 0) of BIT;
Type byte is array ( 7 downto 0) of BIT;
Type word is array (15 downto 0) of BIT;
signal W :word ;
signal BL : byte :="00001111";
signal BH : byte :="11110000";
signal n1,n2,n3,n4 :nibble;
signal B :BIT;
W <= BH & BL; -- W becomes "1111000000001111"
n1 <= BL(7 downto 4);-- upper nibble of B is copied to n1
n2 <= n1;-- whole array is copied
B <= n2(2);-- single bit B receives a bit from an array n2
n1(2 downto 0 ) <= W(13 downto 11 );--both source and target are slices
n3<=n1(0) & n1(1) & n1(2) & n1(3); -- copy in reverse order
Remember that the direction of the slice range must be same as the direction of the parent array. Hence copying in reverse order has to be done by concatenating individual bits.
Aggregates:
Aggregates are a set of quantities separated by commas and enclosed in parenthesis. They can be used for making assignments to arrays or records. E.g.
n1<= "1001"; can also be written as n1<=(‘1’,’0’,’0’,’1’);
Assignments to records can be made using aggregates. E.g.
MY_BANK <= (1.2, 4.8, 50, 25, "110011001100","1001");
The number of elements in the target object must exactly match the number of elements in the aggregate.
Aggregates can also be used as targets of assignments. E.g.
(n1, n2, n3, n4) <= W;
Named association for array assignments:
We have seen how to make a whole array assignment and a sliced array assignment. If you want to make an assignment only to some randomly selected elements of an array, keeping the others elements intact, then you can use named association. E.g. suppose you want to assign ‘0’ and ‘1’ respectively to bits 7 and 13 of W without affecting the remaining bits. This can be achieved as follows.
W <= (7 => ‘0’, 13 => ‘1’);
If you want to make bits 8 and 10 equal to ‘1’ and make the remaining bits equal to ’0’, then we can write:
W <= (8=> ‘1’, 10 => ‘1’, others => ‘0’);
The "others" keyword denotes all unspecified bits. If none are specified, then it applies to all the bits. This property is very useful to reset all bits of an array, especially if the length of the array is unknown. E.g.
W <= (others => ‘0’); -- makes the whole array ‘0’
Alias declaration:
Any object or its slice can be given an alternative name by means of the alias declaration. This improves the readability of the program. Let us consider that there is a 16-bit data register that receives data from memory. The bits 15-13 are op code, bits 12-11 are addressing mode and bits 10-0 are address. We could declare following convenient aliases for them.
Alias OP_CODE : bit_vector(2 downto 0) is DATA_REG(15 downto 13);
Alias AD_MODE: bit_vector(1 downto 0) is DATA_REG(12 downto 11);
Alias ADDRESS : bit_vector(10 downto 0) is DATA_REG(10 downto 13);
The alias names can be used in place of signal names thereafter. E.g.
OP_CODE_REG <= OP_CODE;
MAR <= ADDRESS;
2.3 VHDL Operators:
The operators in VHDL are classified into 6 categories:
The precedence priority of operators increases from category 1 to category 6. The operators within the same category are at the same level. The expression is evaluated from left to right. Parenthesis may be used to clarify the order of evaluation.
Logical operators: and, or, nand, nor, xor, not.
These operators are valid for BOOLEAN, BIT and STD_LOGIC types.
E.g.
variable SUM, A, B, C, D :BIT;
SUM := A xor B;
C:= (A and B) or (A and C);
D:= not A;
Relational operators: =, /=, <, <=, >, >=.
These operators are normally used in IF statements. The result of a relational operator is TRUE or FALSE. The relational operators are valid for all scalars including enumerated variables. E.g. consider the statement-
type FOUR_LOGIC_LEVEL is (‘U’,’0’,’1’,’Z’);
signal a,b: FOUR_LOGIC_LEVEL; a<=’U’ ; b<= ‘Z’;
Since the value of enumerated literals increases from left to right, an expression like "a<b" will evaluate to TRUE. The operators <=, <, >, >= are also valid for array types provided that the array elements are scalars. The comparison is made one element at a time starting from left to right. The operators = and /= are valid for all objects except FILE objects.
Shift operators: sll, srl, sla, sra, rol, ror.
These operators were introduced in the1993 standard and some tools do not support them. These operators stand for shift left logical, shift right logical, shift left arithmetic, shift right arithmetic, rotate right and rotate right respectively.
E.g.
"10001010" sll 2 gives "00101000"
"10001010" rol 2 gives "00101010"
"10000000" sra 3 gives "11110000"
If your tool does not support these functions, then you can achieve the same result by using the slice and concatenation operator.
Adding operators: +, -, &
The + and – operators are valid for all numeric types. The concatenation operator & is valid for strings. It joins the two argument strings together.
Examples.
variable SUM1, SUM2, A, B : integer;
signal S3, S4:string;
constant S1 : string := "abc";
constant S2 : string := "def";
SUM1 := A+B;
SUM2 := A-B;
S3 <= S1 & S2;--value of S3 will be "abcdef"
S4 <= S1 & ‘1’;--value of S4 will be "abc1"
Multiplying operators *, /, mod (modulus), rem (remainder).
The operators * and / have their usual meanings for all numeric data types. The operators mod and rem operate on integers and the result is also an integer.
A rem B is defined as A –(A/B)*B.
e.g. (-7) rem 4 has value-3.
The result or rem operator has a sign of the first operand.
A mod B is defined as A-B*N---for some integer N
e.g. 7 mod (-4) has value –1.
The result of mod operator has a sign of the second operand.
Miscellaneous operators: abs (absolute), **(exponentiation).
Operator abs (absolute value) is defined for any numeric type. Exponentiation operator "**" is defined when the left operand is an integer or floating-point type and the exponent is of integer type.
2.4 VHDL ATTRIBUTES
The VHDL objects have various properties apart from their value. These properties are called attributes. There are several predefined attributes in the language. The programmer can add his own attributes to objects if desired. Such attributes are used to either convey some additional information to the synthesis tool or for documentation purpose.
The attribute is accessed by the expression:
<object name>’<attribute name>.
Signals Attributes:
event, stable, last_event, last_value, delayed, quiet, active, last_active, transaction
Consider the statement
Signal A:BIT;
The meanings of the attributes become clear with following illustration.
A’event = TRUE if A has changed state at current simulation cycle
A’stable = TRUE if A has not changed state at current simulation cycle
A’stable(5 ns) = TRUE if A has not changed within last 5 ns
A’last_event = time elapsed since last event on A (FALSE If A’event is true)
A’last_value = value of A before the most recent event
A’delayed(10 ns) = another copy of A but delayed by 10ns
A’quiet(10 ns) = TRUE if no transaction has been placed on A in last 10 ns
A’active = TRUE if a transaction has been placed on A in the current simulation cycle
A’last_active= time elapsed since last transaction on A (FALSE If A’active is true)
Notice the similarity between event and active attributes. Event is true only if the value of A changes but active is true even if the same value is placed on A again. Can you spot two more similar pairs in the above list?
Array Attributes:
left, right, high, low, range, reverse_range, length
The attribute names are self-explanatory.
Consider the declarations.
Type Nibble is array(3 downto 0) of BIT;
Type NibVect is array(3 downto 0,0 to 7) of BIT;
Now let us see the attribute values of these arrays.
qvect’left(1) = 3 qvect’right(1) = 0
qvect’high(2) = 7 qvect’low(2) = 0
qvect’range(1)= 3 downto 0 qvect’range(2) = 0 to 7
qvect’length= 4 qvect’length(1)=4
q’range = 3 downto 0 q’reverse_range = 0 to 3
Since qvect is a multi dimensional array, we need to specify which index we are talking about. The index number (1 or 2) should be given in bracket after the attribute name. In absence of the index specification, the compiler automatically assumes the index to be 1. Thus qvect’length is same as qvect’length(1). Note that for an ascending range, left = low and right = high. For a descending range, left = high and right = low.
Type Attributes:
left, right, high, low, base
Consider the following declarations. The meaning of the attributes becomes clear with these examples.
subtype bit_position is integer range 15 downto 0;
Type fraction is range -.999 to .999;
Bit_position’low= 0 Bit_position’left=15
Bit_position’high= 15 Bit_position’right=0
Bit_position’base= integer Bit_position’base’high=
2147483647Fraction’right = .999 Fraction’left = -.999
Additional attributes for integers and enumerated data types:
pos, val, succ, pred, leftof, rightof
Attribute pos is zero based index of the given symbol in the enumeration list. Val is the value at the given index in the enumeration list. Pred is predecessor and succ is successor.
Consider the type declarations
Type T1 is (A, B, C, D);
T1’Pos (B) = 1
T1’val (3) = D
T1’succ (C)=D
T1’pred (C)=B
T1’leftof (B)=A
T1’rightof (A)=B
T1’rightof (D)=error
Listing of Type Attributes
< type name >’ left
returns the left array number.
< type name >’ right
returns the right array number.
< type name >’ high
returns the highest array number.
< type name >’ low
returns the lowest array number.
< type name >’ length
returns the data type length.
< type name >’ succ (data value)
returns the next value in the data type of data value
< type name >’ pred (data value)
returns the previous value in the data type of data value
< type name >’ rightof (data value)
returns the value right of the data value.
< type name >’ leftof (data value)
returns the value left of the data value.
USER DEFINED ATTRIBUTES
In addition to the above attributes, the user can define his own attributes to various "entity class" elements. The entity class includes not only the entity design unit but also architecture, configuration, procedure, package, type, subtype, constant, signal, variable, component and label. The user-defined attribute has to be defined in two steps. In the first step, the attribute name and its type is declared as follows.
Attribute ATTR_NAME: ATTR_DATA_TYPE; e.g.
Type DATE is record
MM:integer;
DD:integer;
YYYY:integer;
End DATE;
Attribute AUTHOR_NAME: string;
Attribute UPDATE_DATE: DATE;
In the second step, the attribute is associated with the desired object as follows.
Attribute ATTR_NAME of OBJ_NAME: OBJ_CLASS is ATTR_VAL;
Example:
Entity CPU is
Port (-------);
Attribute AUTHOR_NAME of CPU: entity is "Dr. S. S. Limaye";
Attribute UPDATE_DATE of CPU: entity is (5, 12, 1999);
End CPU;
User defined attributes don’t have any computational significance. They can be used for various purposes. Some synthesis tools use these attributes to convey some additional parameters of the objects being described. They can also be used for documentation.
2.5 VHDL EXAMPLE FILES:
--TWO INPUT AND GATE
entity AND2 is
port (A, B: in BIT; Y: out BIT);
end AND2;
architecture and2_a of AND2 is
begin
Y <= A and B ;
end and2;
--HALF ADDER
entity HADD is
port (A,B: in BIT; S,C: out BIT);-- S = sum, C= Carry
end HADD;
architecture HADD_A of HADD is
begin
S<= A xor B ;
C<= A and B ;
end HADD_A;
--FULL ADDER
entity FADD is
port (A, B, Ci: in BIT;
S, Co: out BIT);
end FADD;
architecture FADD_A of FADD is
begin
S<= A xor B xor Ci ;
C<= (A and B) or (A and Ci) or (Ci and B) ;
end FADD_A;
-- 2:1 MUX
entity MUX2 is
port (A, B, SEL: in BIT;
Y: out BIT);
end MUX2;
architecture MUX2_A of MUX2 is
begin
Y<= (A and (not S)) or (B and S);
end DATAFLOW;
-- QUAD INPUT AND GATE
entity AND2_4 is
port (A, B: in BIT_VECTOR(3 downto 0);-- A, B are inputs
Y: out BIT_VECTOR( 3 downto 0));-- Y is output
end AND2_4;
architecture and2_4_A of AND2_4 is
begin
Y <= A and B ;
end AND2_4_A;
-- 2:4 DECODER
entity DECODER2_4 is
port (A1, A0: in BIT;
Y0, Y1, Y2, Y3 : out BIT);
end DECODER2_4;
architecture DECODER2_4_A of DECODER2_4 is
begin
Y 0<= (not A1) and (not A0);
Y 1<= (not A1) and A0;
Y 2<= A1 and (not A0);
Y 3<= A1 AND A0;
end DECODER2_4_A;
2.6 EXERCISE:
2.6.1 a) Declare a numeric type called NewInt having range –2 to 6.
b) Declare a numeric type called NewFloat having range –5.3 to 16.2.
c) Declare an enumerated data type called VOWEL containing only vowel characters.
d) Declare a subtype of integer called int1 having range 0 to 15.
e) Declare a 2 by 5 array of integers called IntArray.
f) Declare a type called slv4_16 which is a 4 element array of 16 bit std_logic_vector.
2.6.2 a) Define objects named NI, NF, VO, I1, IA, SLV for the above types.
b) Write assignment statements to do the following.
2.6.3 a) Derive subtypes from std_logic_vector to represent a nibble, byte and word.
b) Define two nibble objects n1, n2, one byte object B and one word object W.
c) Transfer 4 MSB’s of W to n1.
d) Transfer contents of n2 to the upper nibble of B in reverse order.
e) Transfer contents of B to LSB of W.
f) Make bits 3 and 11 of W ‘1’ and make the other bits ‘0’.
g) Rotate the contents of n1 right by one position using concatenation.
2.6.4
a) Declare a record called MY_REC having two bytes B0 and B1, two integers I0 and I1 and two real numbers R0 and R1.
b) Define two record objects REC1 and REC2 derived from above record.
c) Fill the members of REC1 with following values: X"FF", X"00", 5, 6, .2, 4.5.
d) Transfer the contents of B0 and R1 of REC1 to B1 and R0 of REC2.
2.6.5
a) Using a single concurrent statement, generate a symmetrical 1 MHz clock. Can it be synthesized?
b) Using process and wait statements, generate a 1 MHz clock that is high for 600 ns and low for 400ns.
2.6.7
a) Write a single concurrent statement that produces a 10 ns wide pulse on a signal called EDGE_PULSE whenever a signal called INPUT_SIG changes state.
Hint: Use Delayed attribute and XOR
b) Repeat above so that EDGE_PULSE is generated only for the positive transitions of INPUT_SIG.
2.6.7 a) Write the description of a CMOS inverter entity INV with a propagation delay of 10 ns.
b) Rewrite architecture of INV such that the propagation delay PD is first defined as a constant with a value of 3RC where R is output impedance and C is load capacitance. Let the values be 1.5E4 ohms and 0.1E-12 F respectively.
c) Declare a physical data type CAP with ff as the base unit and another data type RES with ohm as the base unit. Write another architecture for INV where R and C are defined in physical units.
2.6.8 Consider following VHDL program.
Entity E28 is end E28;
Architecture A of E28 is
Signal X,Y: BIT;
Begin
X <= ‘0’, ‘1’ after 80 ns, ‘0’ after 110 ns, ‘1’ after 175 ns;
Process (X)
Begin
Y <= ‘1’, ‘0’ after 40 ns, ‘1’ after 80 ns;
End process;
End A;
Draw the wave forms for X and Y.
2000 © Deepak George: Deepak George's VLSI Design - Chapter 2
You can use and make as many copies of the book, without any modifications. If any modifications need to be made, I should be informed.
For free distribution over the web
www.oocities.org/deepakgeorge2000
Contact: deepak.g@angelfire.com