任务6.2 硬件描述语言及结构
6.2.1硬件描述语言基本结构
VHDL语言主要由实体、结构体、库、程序包及配置构成,如图6-13所示。

1.实体
在VHDL中,实体就是电路模块或电路系统与外部电路的接口。实体规定了设计单元的输入/输出接口信号或引脚。实体是VHDL的基本设计单元,它可以对一个门电路、一个芯片、一块电路板及至整个系统进行接口描述。实体说明格式如下:
ENTITY实体名IS
[GENERIC(类属参数说明)]
[PORT(端口说明)]
END实体名
在实体说明语句中应给出实体名,实体名可以理解为这个电路所对应的名称。
【例6-1】是一个D触发器的实体说明。
ENTITYdffIS
GENERIC(m∶TIME:=5ns)
PORT(clk,d∶INbit
Q,qb∶OUTbit )
ENDdff
上面给出的程序是1位D触发器的实体说明。实体说明以ENTITY开始,dff是实体名,GENERIC为类属参数表,PORT后为输入输出端口表,其端口有两个输入信号和一个输出信号,输入信号和输出信号的类型相同。
类属参数说明必须放在端口说明之前,用以设定实体或元件的内部电路结构和规模。
类属参数说明格式如下:
GENERIC(常数名∶数据类型:=设定值;
…
常数名∶数据类型:=设定值);
例6-1程序中的GENERIC(m∶TIME:=5ns)定义了一个5ns的时间信号m。如果实体内部电路大量使用了m这个时间值,则当设计者需要修改时间值时只需要一次性修改类属参数语句“GENERIC(m∶time:=某时间常数);”中的常数即可,从而使设计电路变得方便快捷。
端口说明是对设计实体中输入和输出端口的描述,格式如下:
PORT(端口名(,端口名):方向数据类型名;
端口名(,端口名):方向 数据类型名);
端口名是赋予每个系统引脚的名称,通常用几个英文字母组成,一般采用代表管脚信号实际意义的英文表示。各个端口名必须是唯一的,不能重复,不能与VHDL的保留字相同。
端口方向是引脚信号的方向,指明其是输入、输出或其他,详细的方向类型见表6-1。

2.结构体
结构体(ARCHITECTUE)描述用于描述设计实体的内部结构以及实体端口间的逻辑关系。
结构体将具体实现一个实体。每个实体可以有多个结构体,每个结构体对应着实体不同结构和算法实现方案,其间的各个结构体的地位是同等的,它们完整地实现了实体的行为,但同一结构体不能为不同的实体所拥有。结构体不能单独存在,它必须有一个界面说明,即一个实体。当把这个符号例化成一个实际的器件安装到电路上时,则需配置语句为这个例化的器件指定一个结构体
(1)结构体的一般语句格式。
结构体的语句格式如下:
ARCHITECTURE结构体名OF实体名IS
[元素说明语句]
BEGIN
[功能描述语句]
END[ARCHITECTURE][结构体名]
(2)结构体说明语句。
结构体中的说明语句是对结构体的功能描述语句中将要用到的信号、数据类型、常数、元件、函数和过程等加以说明的语句。但在一个结构体中说明和定义的数据类型、常数、元件、函数和过程只能用于这个结构体中,若希望其能用于其他的实体或结构体中,则需要将其作为程序包来处理。
(3)功能描述语句结构。
VHDL的功能描述语句包含五种不同类型的语句,分别为块语句、进程语句、信号赋值语句、子程序调用语句和元件例化语句,以并行方式工作。其结构图如图6-13所示。而在每一语句结构的内部可能含有并行运行的逻辑描述语句或顺序运行的逻辑描述语句。各语句结构的基本组成和功能分别是:
①块语句是由一系列并行执行语句构成的组合体,它的功能是将结构体中的并行语句组成一个或多个模块。
②进程语句定义顺序语句模块,用以将从外部获得的信号值,或内部的运算数据向其他的信号进行赋值。
③信号赋值语句将设计实体内的处理结果向定义的信号或界面端口进行赋值。
④子程序调用语句用于调用一个已设计好的子程序。
⑤元件例化语句对其他的设计实体作元件调用说明,并将此元件的端口与其他的元件、信号或高层次实体的界面端口进行连接。
【例6-2】ENTITYnaxIS -实体定义
PORT(d,s∶INBIT;
y0,y1∶OUTBIT)
ENDnax;
ARCHITECTUREdataflowOF naxIS -结构体定义
BEGIN
y0<=(nots)andd
y1<=sand d
ENDdataflow
6.2.2硬件描述语言要素
1.VHDL文字规则
VHDL文字主要包括标识符和数值。
(1)标识符。
标识符用来定义常数、变量、信号、端口、子程序或参数的名字。VHDL93版含有基本标识符(短标识符)和扩展标识符两部分。
VHDL的基本标识符由26个大小写英文字母(a~z,A~Z)、数字0~9以及下划线“_”组成的字符串。下划线和大小写通常用来增强可读性。
VHDL扩展标识符的特点:
①扩展标识符用反斜杠来定界。
②允许包含图形符号、空格符。
③反斜杠之间的字符可以用保留字。
④扩展标识符的界定符两个斜杠之间可以用数字打头。
⑤扩展标识符中允许多个下划线相连。
⑥扩展标识符区分大小写。
⑦扩展标识符与短标识符不同。
所有这些标识符必须遵从如下规则:
①标识符的第一个字符必须是英文字母,不可以是数字开头。
②标识符的最后一个字符不能是下划线“_”。
③标识符不容许连续使用下划线“_”,下划线前后都必须有英文字母或数字。
④标识符不区分字母大小写。
⑤VHDL的保留字(关键字)不能用于作为标识符使用。
(2)数值型文字。
数值型文字主要有数字型、字符串型、位串型。
①数字型文字。
1)整数文字:整数文字都是十进制的数。
2)实数文字:实数文字也都是十进制的数,但必须带有小数点。
3)数制基数表示的文字:第一部分,用十进制数标明数制进位的基数;第二部分,数制隔离符号“#”;第三部分,表达的文字;第四部分,指数隔离符号“#”;第五部分,用十进制表示的指数部分,这一部分的数如果是0可以省去不写。
4)物理量文字。
②字符串型文字。
VHDL中有两种类型的字符串:文字字符串和数位字符串。
1)文字字符串:文字字符串是用双引号引起来的一串文字
2)数位字符串:数位字符串也称位矢量,是预定义的数据类型BIT的一位数组,它们所代表的是二进制、八进制或十六进制的数组,其位矢量的长度即为等值的二进制数的位数。
2.VHDL数据对象
在VHDL中,数据对象(DataObjects)类似于一种容器,它接受不同数据类型的赋值。
数据对象主要有四种,即常量(CONSTANT)、变量(VARIABLE)、信号(SIGNAL)、文件(FILES)。
(1)常量(CONSTANT)。
常量是一个恒定不变的值,一旦作了数据类型的赋值定义后,在程序中不能再改变,因而具有全局意义。常量的定义形式如下:
CONSTANT常量名:数据类型﹕=表达式
【例6-3】常量使用示例。
CONSTANTFBUS﹕BIT_VECTOR﹕=“010115”
CONSTANTVCC﹕REAL﹕=50
CONSTANTDELY﹕TIME﹕=25ns
VHDL要求所定义的常量数据类型必须与表达式的数据类型一致。常量的数据类型可以是标量类型或复合类型。
(2)变量(VARIABLE)。
在VHDL语法规则中,变量是一个局部量,只能在进程和子程序中使用。变量的定义形式如下:
VARIABLE变量名:数据类型:=初始值;
【例6-4】
VARIABLEA∶INTEGER; -定义A为整数型变量
VARIABLEB,C∶INTEGER:=2;-定义B和C为整型变量,初始值为2
在仿真过程中变量的初始值仅在仿真时有效,综合时将略去所有的初始值。变量赋值格式如下:
目标变量名:=表达式;
变量数值的改变是通过变量赋值来实现的。
(3)信号(SIGNAL)。
信号是描述硬件系统的基本数据对象,信号定义的格式如下:
SIGNAL信号名[,信号名,…]:数据类型[约束条件][:=初始值];
【例6-5】
SIGNALa∶INTEGERRANGE 0 TO 15
SIGNALflaga,flagb∶BIT
SIGNALdata∶STD_LOGIC_VECTOR(7DOWNTO 0):=“11000110”
【提示】在程序中给信号赋的初始值仅在仿真时有效,不能用于综合,因为电路在上电后并不能保证它的初始状态是什么。信号值的代入使用符号“<=”,信号代入时可以产生附加延时。
【例6-6】
a<=10
flag<=‘1’
data<=“10110110”AFTER 10 ns
【提示】在VHDL程序中,实体中所定义的端口都可以作为信号来处理,但要注意它的方向。
信号的使用和定义范围包括实体、结构体和程序包。信号的赋值语句表达式如下:
目标信号名<=表达式;
信号与变量是经常使用的两种数据对象,两者有很大的区别,归纳起来主要有:
①赋值语句不同,信号赋值的符号为“<=”,而变量为“:=”。
②通常变量的值可以给信号赋值,但信号的值却不能给变量赋值。
③信号是全局量,是一个实体内各部分之间以及实体之间进行通信的载体;而变量是一个局部量,只允许定义并作用在进程和子程序中,如果要把变量的值从它定义的区间传输出去,必须先把该变量赋值给一个信号,通过信号把值传输出去。
④信号可以作为进程的敏感信号,但是变量却不可以作为进程的敏感信号,让进程启动,至少要有一个敏感信号发生变化。
⑤操作过程不相同。
3.VHDL数据类型
作为一种硬件描述语言,VHDL与其他高级语言一样,其信号、变量、常量都要指定数据类型。
(1)预定义(标准)数据类型。
①布尔(BOOLEAN)数据类型。
程序包STANDARD中定义布尔数据类型的源代码如下:
TYPEBOOLEAN IS(FALES,TRUE)
②位(BIT)数据类型。
位数据类型也属于枚举型,取值只能是1或0。在程序包STANDARD中定义的源代码是:
TYPEBIT IS (‘0’,‘1’);
③位矢量(BIT_VECTOR)数据类型。
位矢量只是基于BIT数据类型的数组,使用位矢量必须注明位宽,即数组中的元素个数和排列,在程序包STANDARD中定义的源代码是:
SIGNALA﹕BIT_VECTOR(7TO 0);
④字符(CHARACTER)数据类型。
⑤整数(INTEGER)数据类型。
⑥自然数(NATURAL)和正整数(POSITIVE)数据类型。
自然数是整数的一个子类型,非负的整数,即零和正整数;正整数也是整数的一个子类型,它们在STANDARD程序包中定义的源代码如下:
SUBTYPENATURALISINTEGER RANGE 0TOINTEGER’HIGH
SUBTYPEPOSITIVE ISINTEGER RANGE 1TOINTEGER’HIGH
⑦实数(REAL)数据类型。
VHDL的实数类型类似于数学上的实数,或称浮点数。实数常量的书写方式举例如下:
65971.333333-十进制浮点数
8#43.6#E+4-八进制浮点数
43.6E-4-十进制浮点数
⑧字符串(STRING)数据类型。
字符串数据类型是字符数据类型的一个非约束型数组,或称为字符串数组。字符串必须用双引号标明。如:
VARIABLESTRING_VAR﹕STRING(1TO 7)
…
STRING_VAR﹕“AB C D”
⑨时间(TIME)数据类型。
VHDL中唯一的预定义物理类型是时间。完整的时间类型包括整数和物理量单位两部分,整数和单位之间至少留一个空格,
⑩错误等级(SEVERITY_LEVEL)。
在VHDL仿真器中,错误等级用来指示设计系统的工作状态,共有四种可能的状态值:NOTE(注意)、WARNING(警告)、ERROR(出错)、FAILURE(失败)。在仿真过程中,可输出这四种值来提示被仿真系统当前的工作情况。其定义如下:
TYPESEVERITY_LEVEL IS (NOTE,WARNING,ERROR,FAILURE)
(2)IEEE预定义标准逻辑位与矢量。
①标准逻辑位STD_LOGIC数据类型。
为了更好地描述数字系统,在IEEE库的包集合STD_LOGIC和STD_LOGIC_1164中有如下的定义:
TYPEstd_logicIS (‘U’,‘X’,‘1’,‘0’,‘Z’,‘W’,‘L’,‘H’,‘-’)
std_logic数值类型具有9种不同的值:U(未初始化的)、X(强未知的)、0(强0)、1(强1)、Z(高阻态)、W(弱未知的)、L(弱0)、H(弱1)、-(忽略)。
②标准逻辑矢量(STD_LOGIC_VECTOR)数据类型。
STD_LOGIC_VECTOR类型定义如下:
TPYESTD_LOGIC_VECTOR IS ARRAY(NATURALRANGE<>)OFSTD_LOGIC;
STD_LOGIC_VECTOR数据类型的数据对象赋值的原则是:同位宽、同数据类型的矢量间才能进行赋值。
【例6-7】
…
TYPET_DATA IS ARRAY(7DOWNTO 0)OFSTD_LOGIC -自定义数组类型
SIGNALdatabus,memory∶T_DATA;
CPU∶PROCESS-CPU工作进程开始
VARIABLEreg1∶T_DATA; -定义寄存器变量reg1
BEGIN
…
databus<=reg1;-向8位数据总线赋值
ENDPROCESS CPU; -CPU工作进程结束
MEM∶PROCESS—RAM工作进程开始
BEGIN
…
databus<=memory;
ENDPROCESS MEM;
…
【提示】描述总线信号,使用STD_LOGIC_VECTOR是方便的,但需注意的是总线中的每一个信号都必须定义为同一种STD_LOGIC数据类型。
(3)其他预定义标准数据类型。
VHDL综合工具配带的扩展程序包中,定义了一些有用的类型。如Synopsys公司在IEEE库中加入的程序包STD_LOGIC_ARITH中定义了如下的数据类型:无符号型(UNSIGNED)、有符号型(SIGNED)和小整型(SMALL_INT)。
在程序包STD_LOGIC_ARITH中的类型定义如下:
TYPEUNSIGNED IS ARRAY (NATURALRANGE <>)OFSTD_LOGIC;
TYPESIGNED IS ARRAY (NATURALRANGE<>)OFSTD_LOGIC;
SUBTYPESMALL_INT IS INTEGER RANGE 0 TO 1;
【提示】在使用上面程序包中定义的运算符之前,请注意必须加入下面的语句:
LIBRARYIEEE;
USEIEEESTD_LOGIC_ARITHALL;
UNSIGNED类型和SIGNED类型是用来设计可综合的数学运算程序的重要类型,UNSIGNED用于无符号数的运算,SIGNED用于有符号数的运算。
(4)用户自定义数据类型方式。
VHDL允许用户自行定义新的数据类型,它们可以有多种,如枚举类型(ENUMERATIONTYPE)、整数类型(INTEGERTYPE)、数组类型(ARRAYTYPE)、记录类型(RECORDTYPE)、时间类型(TIMETYPE)、实数类型(REALTYPE)等。用户自定义数据类型是用类型定义语句TYPE和子类型定义语句SUBTYPE实现的,以下将介绍这两种语句的使用方法。
①TYPE语句用法。
TYPE语句语法结构如下:
TYPE数据类型名IS数据类型定义[OF基本数据类型];
②SUBTYPE语句。
子类型SUBTYPE只是由TYPE所定义的原数据类型的一个子集,它满足原始数据类型的所有约束条件,原数据类型称为基本数据类型。子类型SUPTYPE的语句格式如下:
SUBTYPE子类型名IS基本数据类型RANGE约束范围;
子类型的定义只在基本数据类型上作一些约束,并没有定义新的数据类型。子类型定义中的基本数据类型必须是已经存在的数据类型。
【例6-8】SUBTYPEDIGITS IS INTEGER RANGE 0 TO 9;
其中,INTEGER是标准程序包中已定义过的数据类型,子类型DIGITS只是把INTEGER约束为只含10个值。
由于子类型与其基本数据类型属同一数据类型,因此属于子类型的和属于基本数据类型的数据对象间的赋值和被赋值可以直接进行,不必进行数据类型的转换。
③枚举类型。
枚举类型是TYPE的特殊用法,VHDL中的枚举数据类型用文字符号来表示一组实际的二进制的类型(若直接用数值来定义,则必须使用单引号)。
【例6-9】枚举数据类型定义。
TYPEM_STATE IS( STATE1,STATE2,STATE3,STATE4,STATE5);
SIGNALCURRENT_STATE,NEXT_STATE∶M_STATE;
在这里,信号CURRENT_STATE和NEXT_STATE的数据类型定义为M_STATE,它们的取值范围是可枚举的,即从STATE1~STATE5共5种,而这些状态代表5组唯一的二进制数值。
在综合过程中,枚举类型文字元素的编码通常是自动的,编码顺序是默认的,一般将第一个枚举量(最左边的量)编码为0,以后的依次加1。如上例中用于表达5个状态的位矢长度应该为3,编码默认值为如下方式:STATE1=‘000’;STATE2=‘001’;STATE3=‘010’;STATE4=‘011’;STATE5=‘100’;
于是它们的数值顺序便成为STATE1<STATE2<STATE3<STATE4<STATE5。一般而言,编码方法因综合器不同而不同。
④整数自定义类型和实数自定义类型。
实际应用中,VHDL仿真器通常将整数或实数类型作为有符号数处理,VHDL综合器对整数或实数的编码方法是:
1)对用户已定义的数据类型和子类型中的负数,编码为二进制补码。
2)对用户已定义的数据类型和子类型中的正数,编码为二进制原码。
编码的位数,即综合后信号线的数目只取决于用户定义的数值的最大值。在综合中,以浮点数表示的实数将首先转换成相应数值大小的整数。因此在使用整数时,VHDL综合器要求使用数值限定关键词RANGE,对整数的使用范围作明确的限制。如下例所示:
【例6-10】整数数据类型定义。
TYPEINT1 IS RANGE 0 TO 100;-7位二进制原码
TYPEINT2 IS RANGE 10 TO 100;-7位二进制原码
TYPEINT3 IS RANGE –100 TO 100;-8位二进制补码
SUBTYPEINT4 IS INT3 RANGE 0 TO 6;-3位二进制原码
⑤数组类型。
数组类型属复合类型,是将一组具有相同数据类型的元素集合在一起,作为一个数据对象来处理的数据类型。数组可以是一维(每个元素只有一个下标)数组或多维数组(每个元素有多个下标)。VHDL仿真器支持多维数组,但VHDL综合器只支持一维数组。
VHDL允许定义两种不同类型的数组,即限定性数组和非限定性数组。它们的区别是,限定性数组下标的取值范围在数组定义时就被确定了,而非限定性数组下标的取值范围需留待随后根据具体数据对象再确定。
1)限定性数组定义语句格式如下:
TYPE数组名 ISARRAY (数组范围)OF数据类型;
【例6-11】TYPESTBISARRAY(7DOWNTO 0)OFSTD_LOGIC;
这个数组类型的名称是STB,它有8个元素,其下标排序为7~0,各元素的排序是STB(7)~STB(0)。
2)非限定性数组的定义语句格式如下:
TYPE数组名ISARRAY(数组下标名RANCE<>)OF数据类型;
⑥记录类型。
由已定义的、数据类型不同的对象元素构成的数组称为记录类型的对象。定义记录类型的语句格式如下:
TYPE记录类型名IS RECORD
元素名:元素数据类型;
元素名:元素数据类型;
…
ENDRECORD [记录类型名];
【例6-12】
TYPERECDATA IS RECORD –将RECDATA定义为四元素记录类型
ELEMENT1∶TIME;–将元素ELEMENT1定义为时间类型
ELEMENT2∶TIME;–将元素ELEMENT2定义为时间类型
ELEMENT3∶STD_LOGIC;–将元素ELEMENT3定义为标准逻辑位类型
ENDRECORD
⑦数据类型转换。
VHDL是一种强类型语言,因此不同数据类型的数据对象在相互操作时,需要进行数据类型转换。
1)类型转换函数方式。
类型转换函数的作用就是将一种属于某种数据类型的数据对象转换成属于另一种数据类型的数据对象。
【例6-13】
LIBRARYIEEE;
USEIEEESTD_LOGIC_1164ALL;
LIBRARYDATAIO;
USEDATAIOSTD_LOGIC_OPSALL;
ENTITYCNT4 IS
PORT(CLK∶INSTD_LOGIC;
P∶INOUT STD_LOGIC_VECTOR(3DOWNTO 0))
ENDENTITY CNT4
ARCHITECTUREART OF CNT4 IS
BEGIN
PROCESS(CLK)IS
BEGIN
IFCLK=‘1’AND CLKEVENT THEN
P<=TO_VECTOR(2,TO_INTEGER(P)+1);
ENDIF;
ENDPROCESS;
ENDARCHITECTURE ART;
利用类型转换函数来进行类型转换需定义一个函数,使其参数类型为被转换的类型,返回值为转换后的类型。在实际运用中经常使用类型转换函数,VHDL的标准程序包中提供了一些常用的转换函数,见表6-2。

2)直接类型转换方式。
直接类型转换的一般语句格式是:
数据类型标识符(表达式)
一般情况下,直接类型转换仅限于非常关联(数据类型相互间的关联性非常大)的数据类型之间,且必须遵守以下规则:
a.所有的抽象数字类型是非常关联的类型(如整型、浮点型),如果浮点数转换为整数,则转换结果是最近的一个整型数。
b.如果两个数组有相同的维数,两个数组的元素是同一类型,并且在各处的下标范围内索引是同一类型或非常接近的类型,那么这两个数组是非常关联类型。
c.枚举型不能被转换。
4.VHDL操作符
在VHDL中共有4类运算操作符,即逻辑操作符(LogicalOperator)、关系操作符(RelationalOperator)、算术操作符(ArithmeticOperator)和并置操作符(ConcatenationOperator),如表6-3所示。


操作符是有优先级的,其优先级如表6-4所示。
【提示】在编程时要注意各操作符的优先级,为了保险起见,可多用括号。

(1)逻辑操作符。
由表6-4知,逻辑操作符可以对位、标准逻辑位和布尔类型的数据进行操作,要求运算符左右的数据类型必须相同。
【例6-14】
SIGNALa,b,c∶STD_LOGIC_VECTOR(0TO 4);
SIGNALd,e,f,g∶STD_LOGIC_VECTOR(1DOWNTO 0);
SIGNALm,n,i,p,k∶BOOLEAN;
c<=aOR b; -a、b相或后赋值给c
d<=eAND f AND g;-两个操作符AND相同,不需括号
k<=(iXOR n)AND(mOR p);-操作符不同,必须加括号
m<=nAND p OR k;-表达错误,两个操作符不同,未加括号
b<=aXOR f;-表达错误,操作数a与f的位矢长度不一致
n<=iAND g-表达错误,i和n的数据类型不同,不能相互作用
(2)关系操作符(RelationalOperator)。
VHDL语言共有六种关系操作符。关系操作符的作用是将相同数据类型的数据对象进行数值比较或关系排序判断,并将结果用布尔类型(BOOLEAN)的数据表示出来,即TRUE或FALSE。
(3)算术操作符。
在算术运算符中,赋值语句两边的数据位长应一致,否则编译时将会出错。比如在对STD_LOGIC_VECTOR进行加、减运算时,要求操作数两边的操作数和运算结果的位长相同,否则编译时将会给出语法出错信息。
【例6-15】
VARIABLEa∶STD_LOGIC_VECTOR(6DOWNTO 0):=“1001001”
aSLL 1; -a=“0010010”
aSRL 1; -a=“0100100”
aSLA 1; -a=“0010011”
aSRA 1; -a=“1100100”
aROL 1; -a=“0010011”
aROR 1; -a=“1100100”
算术运算符有一种特殊的运算,称为并置运算(或连接运算),用符号“&”表示,它表示两部分的连接关系。例如“VH”&“DL”的结果为“VHDL”;“0”&“1”的结果为“01”,连接操作常用于字符串。但在实际运算过程中,要注意并置操作前后的数组长度应一致,并且并置运算符&不允许出现在赋值语句左边。

