1 VHDL语言程序的基本结构
完整的VHDL语言程序包含实体(Entity)、构造体(Architercture)、配置(Configuration)、包集合(Package)和库(Library)五部分。其中,实体是用来描述设计系统的外部接口;构造体用于描述系统内部的结构和行为;包集合存放共享的数据类型、常数和子程序等;配置用于从库中选取所需单元来组成系统设计的不同版本;库存放已经编译的实体、构造体、包集合和配置。
1.1 实体
实体是用来描述设计单元接口的语句,该部分规定了设计单元的的输入、输出接口或引脚。
1.1.1 实体的结构
ENTITY 实体名 IS
[类属参数说明];
[端口说明];
END ENTITY 实体名;
1.1.2 类属参数说明
类属参数说明必须放在端口说明之前,用于指定参数。例如:
GENERIC(m: TIME:=1ns);
该句指定了构造体内m的值为1ns。
tmp1:=d0 AND sel after m;
表示d0和sel两个输入信号相与后,经1ns延迟才送到tmp1。
1.1.3 端口说明
端口说明是对基本设计实体(单元)与外部接口的描述,也可以说是对外部引脚信号的名称、数据类型,以及输入、输出方向的描述。书写形式如下:
PORT( 端口名: 方向 数据类型名;
端口名: 方向 数据类型名);
端口名是赋予每个外部引脚的名称。端口方向是用来定义外部引脚的信号方向是输入还是输出,输入用IN表示,输出用OUT表示, 双向端口用INOUT表示,还有一种特殊情况BUFFER表示输出(这种情况构造体内部可以再使用改变量)。
1.2 构造体
构造体是一个基本设计单元的功能描述体,具体指明该基本设计单元的行为、元件及内部的连接关系,总的说就是定义了设计单元的具体功能。构造体的描述语句分为行为描述(基本设计单元的数学模型描述)、寄存器描述(数据流描述通俗来讲就是用基本元件描述)和结构描述(逻辑元件连接描述)三种。
1.2.1 构造体结构
ARCHITECTURE 构造体名 OF 实体名 IS
[定义语句] 内部信号,常数,数据类型,函数等的定义;
BEGIN
[并行处理语句];
END ARCHITECTURE 构造体名;
构造体的名称是对本构造体的命名,它是该构造体的唯一名称。OF后面紧跟的实体名表明了该构造体所对应的是哪一实体,用IS结束构造体命名。
- 定义语句
定义语句位于ARCHITECTURE和BEGIN之间,用于对构造体内部所使用的信号、数据类型和函数进行定义。
信号定义和端口说明的语句一样,应该有信号名和数据类型的说明,但是信号只是内部连接使用,故没有方向的说明。 - 并行处理语句
并行处理语句在BEGIN和END之间,这些语句具体地描述了构造体的行为及其连接关系。这里对构造体的语句不过多介绍,后面会详细介绍其语法。
1.2.2 构造体的子结构描述
在设计规模比较大的电路结构时,全电路都用唯一的模块描述很不方面。为此,设计者们希望将整个电路分成若干相对独立的模块来进行电路的描述。VHDL语言有三种形式的子结构描述语句:
- BLOCK语句结构
- PROCESS语句结构
- SUBPROGRAM语句结构
1.2.2.1 BLOCK语句
- BLOCK语句结构
块结构名:
BLOCK
BEGIN
...
END BLOCK 块结构名
- BLOCK块与子原理图的关系
设计者在构建较大的电路系统时,会将大规模的电路原理图分割为多张子原理图,进行输入和存档。在VHDL当中有同样的设计思路,构造体对应整个电路图,而每一个BLOCK块对应一张子原理图。BLOCK语句可以将大的程序积木化,便于设计过程中的编程、调试、再利用。如下图所示:
- BLOCK中语句的并发性
在对程序进行仿真时,BLOCK语句中所描述的各个语句是可以并行执行的,与书写的顺序无关。构造体内部直接书写的语句也是并发的。VHDL当中将可以并发执行的语句称为并发语句(Concurrent Statement)。
- 卫式BLOCK(Guarded BLOCK)
前面叙述的BLOCK语句都只是无条件执行的。但是在实际电路设计中,往往会碰到这样的情况,即当某一条件得到满足时,BLOCK语句才可以被执行;反之,该BLOCK语句不能被执行。
其语法结构如下:
BLOCK [卫式布尔表达式]
1.2.2.2 PROCESS语句
- PROCESS语句结构
[进程名]: PROCESS(信号1,信号2,信号......) IS
BEGIN
...
END PROCESS [进程名];
注意这里的进程名可以有,也可以不写。
- PROCESS语句的顺序性
在VHDL语句当中,与BLOCK语句一样,某一个功能独立的电路在设计时也可以用一个PROCESS语句结构来描述。但是与BLOCK语句不同的是,在系统仿真时,PROCESS结构中的语句是一句一句顺序执行的。 - PROCESS语句的启动
[进程名]: PROCESS(信号1,信号2,信号......) IS
这里面中的信号1、信号2、信号3在VHDL当中成为敏感量,只要其中有一个信号发生呢过变化,都将启动执行PROCESS语句。一旦启动,就会从上到下将PROCESS中的语句执行一遍,执行完一遍会重新进入等待执行状态。
1.2.2.3 SUBPROGRAM语句
- SUBPROGRAM语句结构
SUBPROGRAM语句结构又称为子程序语句结构,就是在主程序调用它之后能将处理结果返回主程序的程序模块,其含义和其他高级语言的子程序的概率差不多。子程序可以多次调用,所以在调用时应该进行初始化,执行结束时子程序就会终止,再次调用时又重新初始化。所以子程序内部的值不能保持,子程序返回之后才能重新被调用,是一个非重入的程序。我理解非重入程序就是会改变程序其他全局变量的程序
。 - SUBPROGRAM分类
过程
函数
过程与其他高级语言的子程序相当,函数与其他高级语言中的函数相当。
1.2.2.3.1 过程(PRODURE)
- SUBPROGRAM语句结构
PROCEDURE 过程名(参数1; 参数2; ...)IS
[定义语句];(变量等的定义)
BEGIN
[顺序处理语句];
END PROCEDURE 过程名;
注意这里的参数是有完成的定义。参数可以是 输入、输出
。可以表示为:
[变量名]: IN/OUT/INOUT [数据类型]
在过程调用过程中,IN类型是作为常量,OUT/INOUT是作为变量;在过程运行结束之后OUT、INOUT参数都将拷贝到调用者的信号或变量中去。
- 过程结构中的顺序性
子程序当中的过程结构(PRODURE)与进程(PROCESS)的顺序性相同,都是并行的。
1.2.2.3.2 函数(FUCTION)
- 函数语句结构
FUNCTION 函数名(参数1; 参数2; ...)
RETURN 数据类型名 IS
[定义语句];(变量等的定义)
BEGIN
[顺序处理语句];
RETURN [返回变量名]
END FUNCTION 过程名;
在函数当中所有参数必须是输入,所以在参数定义时可以将IN省略。函数中的输入值由调用者拷贝到输入参数中,如果没有特别指定,则在函数语句中按常数处理。
通常函数都是存放在各种包集合或者库里面。以下我们将做详细介绍哦。
1.3 库
库(Library)是经编译后的数据的集合,它存放包集合定义、实体定义、构造体定义和配置定义。在VHDL当中,库的说明总是放在设计单元的最前面,即
LIBRARY 库名;
这样,在设计单元内的语句就可以使用库中的数据。库的好处是使设计者共享已经编译过的设计结果。VHDL当中可以存在多个不同的库,但是库和库之间是独立的,不能相互嵌套。
1.3.1 库的种类
VHDL当中,库分为五种:IEEE库、STD库、面向ASIC的库、用户自定义库和WORK库。
- IEEE库
IEEE库是一些公司或者机构并且被认证的库。 - STD库
STD库是VHDL的标准库,在库中存放有名为“STANDARD”的包集合。由于它是VHDL的标准配置,因此设计者要调用“STANDARD”中的数据可以不按照标准格式说明。在使用包集合中的数据时,应该先说明库和包集合名。例如:
LIBRARY STD;
USE STD.STD_LOGIC_1164.ALL;
STD库貌似也可以不进行说明
- 面向ASIC的库
在VHDL中,为了进行门级仿真,各公司可提供面向ASIC的逻辑门库,在该库中存放着与逻辑门一一对应的实体。 - 用户自定义库
用户为自身设计需要所开发的共用的包集合和实体等,也可以汇集在一起定义成一个库。 - WORK库
WORK库是现行作业库。设计者所描述的VHDL语句不需要任何说明,都将存放在WORK库中。使用该库是时,不需要任何说明。
1.3.2 库的使用
- 库的说明
五种库当中,除了WORK库和STD库使用时,可以不经说明外,其他库在使用时,应当进行说明。说明语句包含两句,第一句是:
LIBRARY [库名];
第二句要说明设计者要使用的是库中哪一个包集合以及包集合中的项目名(如过程名、函数名等)。第二句的格式如下:
USE LIBARARY_name.packge_name.ITEM.name
- 库的作用域
库的作用范围是从一个实体的说明开始,到它所属的构造体、配置为止。并且每个实体都应该有库的说明。
1.4 包集合
包集合只是单纯用来罗列VHDL语言中所要用到的信号定义、常数定义、元件语句、函数定义和过程定义等,它是一个可编译的设计单元,也是库结构当中的一个层次。要使用包集合时,可以用USE语句说明。
PACKAGE 包集合名 IS
[说明语句];
END PACKAGE 包集合名;--包集合标题
PACKAGE BODY 包集合名 IS
[说明语句];
END PACKAGE BODY 包集合名;--包集合体
一个包集合有标题(Header)和包集合体(Package Body)构成。包集合体只是一个可选项。这是因为包集合标题允许使用数据赋值和有实质性的操作语句。一般来说,包集合标题列出所有项目的名称,而包集合体给出具体的细节。
包集合体和包集合标题分开描述好处是:当函数的功能需要作某些调整或数据赋值需要变化时,只需要改变包集合体的相关语句能够减小编译的单元。
1.5 配置
配置(Configuration)语句描述层与层之间的连接关系以及实体与结构之间的联系关系。比如:在实体中,可以通过配置语句选择不同的构造体。
配置语句的基本书写格式如下:
CONFIGURATION [配置名] OF 实体名 IS
[语句说明];
END CONFIGURATION [配置名]
- 缺省配置:
CONFIGURATION [配置名] OF 实体名 IS
FOR 选配构造体名
END FOR;
END CONFIGURATION [配置名]
缺省配置用于不包含块(BLOCK)和元件(COMPONENTS)的构造体。
2 VHDL的数据类型和运算操作符
2.1 客体
在VHDL语言中,凡是可以赋予一个值的对象称为客体。客体主要包括以下四种:信号(Signal)、变量(Variable)、常数(Constant)、文件(File)。
客体类别 | 含义 | 说明场合 |
---|---|---|
信号 | 信号是全局量 | ARCHITECTURE, PACKAGE, ENTITY |
变量 | 共享变量是全局量,局部变量是局部量 | PROCESS, FUNCTION, PROCEDURE |
常数 | 常数是全局量 | 上下两种场合均存在 |
文件 | 文件是全局量 | ARCHITECTURE, PACKAGE, ENTITY |
2.1.1 常数
常数是一个固定的值,所谓常数就是对某一常数名赋予一个固定的值。赋值通常在程序开始前进行。其一般格式如下:
CONSTANT 常数名: 数据类型:=表达式;
例子:
CONSTANT VCC : REAL=5.0;
CONSTANT DALY : TIME=100ns;
CONSTANT FBUS : BIT_VECTOR=“0101”;
2.1.2 变量
在93版的协议中,变量引入了共享变量(Shared Variable),但是请注意共享变量应该谨慎使用
。
- 共享变量
信号可以整个构造体中使用是全局量,而变量只能在过程和子程序当中使用。变量想要传递出过程或者子程序时,只能先将其传递给信号,再进行传输。这里为了方便使用将进程或子程序中的结果以变量形式进行数据传递,就定义了共享变量。
共享变量的说明格式如下:
SHARED VARIABLE [变量名]: [子类型名] :=初始值; --初始值可以没有
并且共享变量不能在进程和子程序的说明域不能使用。
- 局部变量
局部变量只能在进程语句、函数语句和过程语句当中使用,只是一个局部量。局部变量的赋值是立即生效的。并不像信号那样到了规定的仿真时间才进行赋值。
其格式如下:
VARIABLE 变量名: 数据类型 约束条件:=表达式;
2.1.3 信号
信号(Signal)是电子电路内部硬件连接的抽象。与端口的概念很像,在构造体、包集合和实体中进行说明。
信号的定义语句如下:
SIGNAL 变量名: 数据类型 约束条件:=表达式; --注意这里赋值的方式
- 信号和变量的区别
变量的赋值符号是":=“,该符号在使用后会立即赋值,下一句会立即使用赋值之后的值。
信号的赋值符号是”<=",该语句即便被执行也不会使信号立即发生代入,下一句语句执行时,仍会使用原来的信号值。信号的代入语句会同时进行处理。实际上,代入过程和代入语句的处理是分开进行的。
2.1.4 文件
在VHDL中,提供一个预先定义的包集合——文本输入/输出包集合(TEXTIO),在该TEXTIO中包含有对文本文件进行读/写的过程和函数。这些文本文件都是ASCII码文件,其格式可根据编程人员的需要设定。TEXTIO按行对文件进行处理,一行为一个字符串,并以回车符、换行符作为行结束符。
- 文件格式定义的格式
TYPE 类型名 IS FILE OF 类型/子类型名;
例如,
TYPE index IS RANGE 0 TO 15;
- 文件操作语句
TEXTIO中提供了打开文件、关闭文件、从文件读/写一行的过程及检查文件结束的函数:
FILE_OPEN(文件名, "外部文件名", 文件读写类型)
FILE_CLOSE(文件名)
READ
WRITE
ENDFILE(文件名)
TEXTIO还包含行读和行写两个子过程:
READLINE(目的行变量,源行变量)
WRITELINE(目的行变量,源行变量)
2.2 VHDL数据类型
在VHDL中,信号、变量、常数都要指定数据类型,因此VHDL提供了多种多样标准的数据类型。另外,为使用户设计方便,还可以由用户自定义数据类型。尽管听起来,VHDL语言对数据类型要求不严,但实际上VHDL对数据类型的定义相当严格。
2.2.1 标准的数据类型
数据类型 | 含义 |
---|---|
整数 | 32位,-(231-1)~(231-1) |
实数 | 浮点数,-1.0E+38~+1.0E_38 |
位 | 逻辑"0"或"1" |
位矢量 | 位矢量 |
布尔量 | 逻辑"真"或逻辑"假" |
字符 | ASCII码 |
时间 | 时间单位fs, ps, ns, us, ms, sec, min, hr |
错误等级 | NOTE, WARNING, ERROR, FAILURE |
自然数, 正整数 | 整数的子集(自然数:大于等于0的整数;正整数:大于0的整数) |
字符串 | 字符矢量 |
- 整数(Integer)
整数与数学中的整数定义一样。但是也不是完全一样的。千万不要把一个实数(含有小数点的数)赋予一个整数变量,这是因为VHDL是一个强类型语言,赋值语句当中的数据类型必须匹配
。尽管整数值在电子系统中,可能是用一系列的二进制位值来表示,但是整数不能看做位矢量,也不能按位来访问,对整数不能用逻辑操作符
。实在要进行位操作时,可以用转换函数将整数转化为位矢量。
在电子系统中,整数也可以作为对信号总线状态的一种抽象手段,用来准确地表示总线的某一中状态。
- 实数(Real)
在进行算法研究或者实验时,作为硬件方案的抽象方案,常常采用实数四则运算。注意:实数有正负,书写时一定要有小数点
例如:
-1.0
+2.5
-1.0E+38
有些数可以用整数表示,也可以用实数表示;但是整数和实数的意义是不一样的,主要是因为数据类型不一样。
- 位(Bit)
在数字系统中, 信号值通常是用一个位来表示。位的表示方法是:用字符’0’或者’1’来表示。位与整数中的1和0不同,'1’和’0’仅仅表示一个位的两种取值。位值也可以显式说明,例如:
BIT('1')
- 位矢量 (Bit_Vector)
位矢量是93版扩展的数据类型,它是用双引号扩起来的扩展的数字序列。例如:
B"001_101_010"--9位二进制位串
X"A_F0_FC"--20位十六进制位串
O"3701"--12位八进制位串
X" "--空位串
- 布尔量(Boolean)
布尔量有两个状态:真和假。虽然布尔量也是二值枚举量,但它和位不同,没有数值的含义,不能进行算术运算。
例如,它可以在IF语句中被测试,测试结果产生一个布尔量TRUE或者FALSE。
一个布尔量常用来表示信号的状态或者总线上的情况。如果某个信号或者变量被定义为布尔量,那么在仿真中将自动地对其赋值进行核查。一般布尔量的初始值为FALSE。 - 字符(Character)
字符也是一种数据类型,所定义的字符量通常用单引号括起来,如’A’。一般情况下VHDL对大小写不敏感,但是对字符量中的大、小写字符则认为是不一样的。注意字符’1’、整数1和实数1.0的意思都是不一样。当明确指出1是字符数据,可写为
CHARACTER('1');
- 字符串(String)
字符串是由双引号括起来的一个字符序列,也称为字符矢量或字符串数组。例如:
"iteger range"
- 时间(Time)
- 时间是一个物理量数据。完整的时间量数据应包含整数和单位两部分,
而且整数和单位之间应该至少留有1个空格的位置
,例如:
20 us, 100ns, 3sec
在系统仿真时,时间数据特别有用,用它表示信号延时,从而使模型系统更逼近实际的运行环境。
- 错误等级(Severty level)
错误等级类型数据是用来表征系统的状态,共有四种:NOTE(注意)、WARNING(警告)、ERROR(出错)、FAILURE(失败)。这些是在仿真中,提示系统当前的工作情况。 - 自然数(Natural)、正整数(Positive)
这两类数据类型只是整数的子类。
2.2.2 用户定义的数据类型
VHDL当中,用户可以根据自己的需求来定义数据,定义格式如下:
TYPE 数据类型名 {,数据类型名} IS 数据类型定义;
不完整的用户定义的数据类型的书写格式:
TYPE 数据类型名 {,数据类型名};
以下将介绍几种可由用户定义的数据类型
- 枚举(Enumerated)类型
在逻辑电路中,所有的数据都用0或者1表示,但是在考虑逻辑关系时,只有数字往往不太方便。比如在表示星期几时,只用数字就显得不太直观。为此,定义一个叫"Week"的数据类型。
TYPE Week IS(sun, mon, tue, wed, thu, fri, sat);
由上述定义可知,凡是用于代表星期二的日子都可以用tue代替,这比用代码"010"表示星期二直观多了,使用也不容易出错。
枚举类型数据定义如下:
TYPE 数据类型名 IS (元素, 元素, 元素 , ......);
- 整数(Integer)类型
是整数的一种定义。
TYPE digit IS INTEGER RANGE 0 TO 9;
定义格式如下:
TYPE 数据类型名 IS 数据类型定义约束范围;
- 实数(Real)类型
是实数的一种定义。
TYPE current IS INTEGER RANGE -1.0E4 TO 1.0E4;
定义格式如下:
TYPE 数据类型名 IS 数据类型定义约束范围;
- 数组(Array)类型
数组是将相同类型的数据集合在一起所形成的一个新的数据类型。可以是一维,也可以是多维。
定义格式:
TYPE 数据类型名 IS ARRAY 范围 OF 原数据类型名;--范围默认是整数类型
例子,
TYPE STD_LOGIC_VECTOR IS ARRAY (NATRUAL RANGE<>) OF STD_LOGIC;
这里的范围由RANGE<>指定,这是一个没有范围限定的数组。这时候应该由信号的说明语句来限定范围。例如:
SIGNAL aaa: STD_LOGIC_VECTOR(3 DOWNTO 0);
二维数组需要用两个范围来限定,例如
TYPE memarray IS ARRAY (0 TO 5, 7 DOWNTO 0) OF STD_LOGIC;
-
存取(Access)类型
-
文件(File)类型
-
记录(Recode)类型
记录是将不同数据类型的数据和数据名组织在一起形成的新客体。定义格式如下:
TYPE 数据类型名 IS RECORD
元素名: 数据类型名;
元素名: 数据类型名;
...
END RECORD
在提取数据时应当使用“.”,格式如下:
数据名.元素名
- 时间(Time)类型
时间类型在仿真时必不可少,其书写格式如下:
TYPE 数据类型名 IS 范围;
UNITS 基本单位;
单位;
END UNITS;
2.2.3 用户定义的子类型
用户定义的子类型是用户对已定义的数据类型进行一些范围限而形成的一种新的数据类型。格式一般如下:
SUBTYPE 子类型名 IS 数据类型名;
2.2.4 数据类型的转换
注意:
"STD_LOGIC_VECTOR"的值只能是二进制值。BIT_VECTOR的值除2进制值外,可以是八进制,十六进制。
2.2.5 数据类型的限定
这个例子好像不对,但是思想就是这样
这里地数据类型的限定类似于C语言当中的强转。
具体问题如下:
CASE (a&b&c) IS
WHEN "001"=>Y<="01111111";
WHEN "010"=>Y<="10111111";
...
END CASE;
在该例中,a&b&c的数据类型不定,那么会发生错误。此时需要对数据进行类型限定。类型限定的实现方式是在数据前加上“类型名”。例如:
a<=STD_LOGIC_VECTORN ("01101010");
SUBTYPE STD3BIT STD_LOGIC_VECTOR(0 TO 2);
CASE STD3BIT (a&b&c) IS
WHEN "000"=>Y<="01111111";
WHEN "001"=>Y<="10111111";
...
END CASE;
2.2.6 IEEE标准"STD_LOGIC"和"STD_LOGIC_VECTOR"
VHDL基本数据类型当中的bit是一个逻辑型的变量,这类数据只能是“0”或者“1”。这种数据类型有一些缺陷,比如没有高阻态,不能很好地仿真,也很难描述双向数据总线。
为此,1993年制定出新的标准(IEEE STD1164),使得“STD_LOGIC”型数据可以具有如下9种不同的取值:
U--初始值
X--不定
0--0
1--1
Z--高阻态
W--弱信号不定
L--弱信号O
H-- 弱信号1
- --不可能情况
2.3 VHDL语言的运算操作符
VHDL当中共有四类操作符,分为是逻辑(Logic)运算符、关系(Relational)运算符、算术(Arithmetic)运算符和并置(Concatenation)运算符。请注意操作数的类型应该和操作符的要求一致,并且操作符是有优先级的
优先级顺序 | 操作运算符类型 | 操作符 | 功能 |
---|---|---|---|
优 | 逻辑运算符 | AND | 逻辑与 |
先 | 逻辑运算符 | OR | 逻辑或 |
级 | 逻辑运算符 | NAND | 逻辑与非 |
从 | 逻辑运算符 | NOR | 逻辑或非 |
上 | 逻辑运算符 | SLL | 逻辑左移 |
到 | 逻辑运算符 | SRL | 逻辑右移 |
下 | 逻辑运算符 | SLA | 算术左移 |
递 | 逻辑运算符 | SRA | 算术右移 |
增 | 逻辑运算符 | ROL | 逻辑循环左移 |
逻辑运算符 | ROR | 逻辑循环右移 | |
关系运算符 | = | 等于 | |
关系运算符 | /= | 不等于 | |
关系运算符 | < | 小于 | |
关系运算符 | > | 大于 | |
关系运算符 | <= | 小于等于 | |
关系运算符 | >= | 大于等于 | |
算术运算符 | + | 加 | |
算术运算符 | - | 减 | |
并置运算符 | & | 并置 | |
正、负运算符 | + | 正 | |
正、负运算符 | - | 负 | |
算术运算符 | * | 乘 | |
算术运算符 | / | 除 | |
算术运算符 | MOD | 求模 | |
算术运算符 | REM | 取余 | |
算术运算符 | ** | 指数 | |
算术运算符 | ABS | 取绝对值 | |
逻辑运算符 | NOT | 取反 |
2.3.1 逻辑运算符
93版中逻辑运算符共有12种,分别是
NOT--取反
使用例子:
a<=not b;
AND--yu与
OR--或
NAND--与非
NOR--或非
XOR--异或
使用例子
a<=b[运算符]c;
SLL--逻辑左移
SRL--逻辑右移
SLA--算术左移
SRA--算术右移
ROL--逻辑循环左移
ROR--逻辑循环右移
移位运算符使用时,应该调用ieee.numeric_std库。一般是使用连接符实现移位,并且这样更接近底层。
使用例子
a<=a [移位运算符] [位数];
逻辑运算符可以对“STD_LOGIC”和“BIT”等逻辑型数据、“STD_LOGIC_VECTOR”逻辑型数组及布尔型数据进行逻辑运算。请注意逻辑运算符的左边和右边,以及代入信号的数据类型必须相同
当一个语句中存在两个以上相同逻辑表达式时,在C语言中有自左向右的优先级顺序,但是在VHDL中,左右没有优先级差别。
简单说一下,逻辑运算符和算术运算符的区别。逻辑移位不考虑符号位,算术移位考虑符号位
2.3.2 算术运算符
VHDL中有10种算术运算符。
+ --加
- --减
* -- 乘
/ --除
MOD --求模
REM --取余
+ --正(一元运算符)
- --负(一元运算符)
** --指数
ABS --取绝对值
- 一元运算符的操作数可以为任何数值类型(整数、实数、物理量)。
- 加法和减法的操作数也可以为任何数值类型,但应该有相同的数据类型。
- 乘法和除法的操作数可以同为整数和实数。
- 物理量可以被整数或实数相乘或相除,其结果仍是一个物理量。
- 物理量除以同一类型的物理量即可得到一个整数量。
- 求模和取余的操作数必须是同一个整数类型数据。
- 一个指数的运算符的左操作数可以是任意整数或实数,而右操作数应为一整数(只有在左操作数是实数时,右操作数才可以是负整数)。
实际上,能够真正综合逻辑电路的算术运算符只有“+”、“-”、“*”。对于算术运算符“/”、“MOD”、“REM”,分母的操作数为2的常数时,逻辑电路综合是有可能得的。
当对“STD_LOGIC_VECTOR”进行“+”(加)“-”(减)运算时,若两边的操作数和代入变量的位长不一致时,会产生语法错误。另外,乘运算符两边的位长相加的值与变量位长不同时,也会出现语法错误。
2.3.3 关系运算符
VHDL当中有6中关系运算符:
= --等于
/= --不等于
< --小于
<= --小于等于
> --大于
>= --大于等于
- 进行关系运算时,关系运算符左右两边的操作数的数据类型一定要一致,长度可以不一定相同。
- 等号和不等号适用于所有类型数据,其他关系运算符可以使用于整数、实数、位、位矢量。
为了能使位矢量进行关系运算,在包集合“STD_LOGIC_UNSIGNED”中对“STD_LOGIC_VECTOR”关系运算重新作了定义。在利用关系运算符对位矢量数据进行比较时,比较过程是从最左边的位开始,向右按位开始比较。
2.3.4 并置运算符
并置运算符就是连接符
&
下面直接用例子说明并置运算符的使用。
tmp_b<=b AND (en&en&en&en) ; --这里en是1 bit数据,b是4 bit数据。
tmp_b<=b AND (en, en, en, en); --这两句是等效的
``但是上述第二句这种方法不适用于位矢量的连接``
tmp_b<=b AND (3=>en, 2=>en, 1=>en, 0=>en); --这两数字1是指将en赋给变量的第几位
tmp_b<=b AND (3 DOWNTO 0=>EN); --这种方法与前一种方法类似
tmp_b<=b AND (OTHERS=>en); --这种方法与前两种方法达到的目的是一样的
--但是注意,OTHERS要放在集合体的最后。假若b位矢量的脚标b(2)的选择信号为'0',其他信号均为en,则表表达式为:
tmp_b<=(2=>'0', others=>en);
3 VHDL 构造体的描述方式
VHDL有三种不同风格的描述方式,即行为描述、寄存器描述(或数据流描述)、结构描述。一般来说,后两种描述方式能够直接综合成电路,而第一种描述方式在大多数时候是用来进行系统仿真,少数时候能够逻辑综合成电路。
3.1 行为描述
不同语言对行为描述的定义不同,但是有一点是确定的,行为描述方式是对系统数学模型的描述,其抽象程度比寄存器传输描述更高。不能综合的原因是在行为描述中有大量采用算术运算、关系运算、惯性延时、传输延时等难以进行逻辑综合和不能进行逻辑综合的VHDL语句。一般来说行为描述语句主要用于系统数学模型的仿真或者系统工作原理的仿真
。
3.1.1 代入语句
代入语句格式如下:
信号量 <= 敏感信号量表达式;
例如,
a<=b;
该语句是a得到b的值。这时候,b就是一个敏感信号量,只要b的值发生变化时,该语句就会被执行。
具有延时时间的代入语句如下:
a<=b AFTER 5ns; --``注意这里语句只能用来仿真``
下面以一个例子展示代入语句:
ENTITY and2 IS
PORT (a,b : IN BIT;
c : OUT BIT;
END ENTITY and2;
ARCHITECTURE and2_behav OF and2 IS
BEGIN
c<=a AND b AFTER 5ns;
END ARCHITECTURE and2_behav;
3.1.2 延时语句
VHDL当中,延时语句有两证类型:惯性延时和传输延时。
- 惯性延时
在惯性模型中,系统或器件输出信号要发生变化必须有一段时间的延迟,这段延时时间称为系统或器件的惯性。注意这里的惯性延时是指物理器件在物理特性上的延迟,不可改变。
当一个系统或器件的输入信号变化周期小于系统或器件的惯性延迟时,其输出将保持不变。几乎所有的器件都会有惯性延迟,因此为了仿真逼近实际,设计人员在代入语句中几乎都会加上惯性延时时间的说明。例如:
b<=a AFTER 20 ns;
在VHDL当中,惯性延时是缺省的,级在语句中不作特别说明,产生的延时一定是惯性延时,这是因为大多数器件在行为仿真时都会呈现这种惯性延时。
惯性延迟说明只在行为仿真时有意义,在逻辑综合时将被忽略,或者在逻辑综合前必须去掉
- 传输延时
VHDL中传输延迟不可缺省,传输延时常用来描述总线延时、连接线的延时及ASIC芯片中的路径延时。
具有传输延迟的代入语句如下:
b<=TRANSPORT a AFTER 20ns;
TRANSPORT是专门用于说明传输延迟的前置词。
在93版中,信号延时可指定脉冲宽度限制,在信号延迟表达式中REIECT用来限制脉冲宽度。例如:
dout1<=a AND b AFTER 5ns;
dout2<=REJECT 3ns INTENAL a AND b;
3.1.3 多驱动器描述语句
VHD中,创建一个驱动器可以有一条信号代入语句实现。当有多个代入语句时,就有可能出现多个驱动器连接到同一信号线上的情况。
ARCHITECTURE sample OF sample IS
BEGIN
a<=b AFTER 5ns;
a<=d AFTER 5ns;
END ARCHITECTURE sample;
这时候就存在多个驱动器驱动同一信号的情况,就存在一个问题:怎样选择输出。在VHDL中,为解决这一问题,在包集合STD_LOGIC_1164中专门定义了一种描述判决函数的数据类型,称为判决子类型。判决函数解决的问题就是在多个驱动器同时驱动一个信号时,定义输出哪一个值的函数。判决函数如下:
FUNCTION resolved (s: STD_ULOGIC_VECTOR) RETURN STD_ULOGIC;
--STD_ULOGIC_VECTO是专门定义的一种数据类型
--resolved函数是判决函数
3.1.4 GENERIC语句
GENERIC语句常用于不同层次的信息传递。可以传递的信息包括但不仅限于位矢量的常数,数组的长度以及器件的延时时间。该语句所涉及的数据除整数类型外,如涉及其他类型的数据,则不能进行逻辑综合。
使用GENRIC语句易于使器件模块化和通用化。例如,要描述二输入与门的行为。二输入与门的逻辑关系是明确的,但是由于工艺、材料的不同会有不同的延时时间。为实现通用化,需要开发一个通用的而输入与门的程序模块。如下:
ENTITY and2 IS
GENERIC (rise, fall: TIME);
PORT( a,b: IN BIT;
c: OUT BIT);
END ENTITY and2;
ARCHITECTURE behav OF and2 IS
SIGNAL internal: BIT;
BEGIN
internal<=a AND b;
c<= internal AFTER (rise) WHEN internal='1' ELSE
internal AFTER (fall);
END ARCHITECTURE behav;
下面是调用这个模块。
ENTITY sample IS
GENERIC (rise,fall: TIME);
PORT(ina,inb,inc,ind: IN BIT
q: OUT BIT);
END ENTITY sample;
ARCHITECTURE behav OF sample IS
COMPONENT and2 IS
GENERIC (rise,fall: TIME);
PORT( a,b: IN BIT;
c: OUT BIT);
END COMPONENT and2;
SIGNAL U0_C, U1_C: BIT;
BEGIN
U0: and2 GENRIC MAP(5ns, 5ns);
PORT MAP(ina, inb, U0_C);
U1: and2 GENRIC MAP(8ns, 10ns);
PORT MAP(inc, ind, U1_C);
U2: and2 GENRIC MAP(9ns, 11ns);
PORT MAP(U0_C, U1_C, c);
END ARCHITECTURE behav;
在这里可以看出VHDL语言GENERIC语句的灵活性,在元件调用时,可以代入不同参数,完成灵活的配置。
3.2 构造体的寄存器传输(RTL)描述方式
RTL描述是真正可以进行逻辑综合的描述方式。RTL描述也被称为数据流描述。
3.2.1 RTL描述方式的特点
VHDL中的语句有一些限制:要么采用寄存器硬件一一对应的直接描述,要么采用寄存器之间的功能描述。
功能描述的RTL描述方式例子如下:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY mux2 IS
PORT (input: IN STD_LOGIC_VECTOR(1 DOWMTO 0);
sel: IN STD_LOGIC;
y: OUT STD_LOGIC);
END ENTITY mux2;
ARCHITECTURE rtl OF mux2 IS
BEGIN
y<=input(0) WHEN sel='1' ELSE
input(1);
END ARCHETECTURE rtl;
硬件一一对应的RTL描述方式例子如下:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY mux2 IS
PORT (input: IN STD_LOGIC_VECTOR(1 DOWMTO 0);
sel: IN STD_LOGIC;
y: OUT STD_LOGIC);
END ENTITY mux2;
ARCHITECTURE rtl OF mux2 IS
SIGNAL tmp1, tmp2, tmp3: STD_LOGIC;
BEGIN
tmp1<=in0 AND sel;
tmp2<=in1 AND (NOT sel);
tmp3<=tmp1 OR tmp2;
y<=tmp3;
END ARCHETECTURE rtl;
寄存器之间的功能描述是一种逻辑描述,只要知道外部特性和功能就行。寄存器硬件一一对应的描述是一种具体的描述,需要了解电路的构成,内部采用了哪些门电路。
3.2.2 RTL描述使用时注意的问题
- "X"状态的传递
所谓“X”态的传递,实质上是不确定信号状态的传递,它将是逻辑电路产生不确定的结果。不确定状态在RTL仿真时是允许出现的,但是在综合后的门级仿真中不允许出现。
例子将说明不定态“X”的传输。
PROCESS (sel) IS
BEGIN
IF(sel='1') THEN
y<='0';
ELSE
y<='1';
END IF;
END PROCESS;
改变书写顺序,
PROCESS (sel) IS
BEGIN
IF(sel='1') THEN
y<='1';
ELSE
y<='0';
END IF;
END PROCESS;
这两个例子只是书写顺序不一致,当sel='X’时,却会有不同的输出。为防止这种不合理的结果,增加一项,
PROCESS (sel) IS
BEGIN
IF(sel='1') THEN
y<='1';
ELSE IF(sel='0')
y<='0';
ELSE
y<='X';
END IF;
END PROCESS;
- 寄存器RTL描述的限制
1、禁止一个进程中存在两个寄存器描述。RTL规定:一个进程中只能描述一个寄存器。
2、禁止IF语句中的ELSE项。在用IF语句描述寄存器功能时,禁止采用ELSE项
3、寄存器描述中必须代入信号值。
- 关联性强的信号的处理
在多输入多输出的电路中,有些信号的关联度高,有些信号的关联度低。在这种情况下,为了在逻辑综合以后,使其电路面积和速度指标更高,通常将关联度高的信号放在一个进程中,将电路分成几个进程来描述。
3.3 构造体的结构描述方式
结构描述方式就是在多层次的设计中,高层次的设计模块调用低层次的设计模块,或者直接调用门电路设计单元来构成一个复杂逻辑电路的描述方法。结构描述方式最能提高设计效率,同时灵活性也是最高的。
3.3.1 构造体结构描述的基本框架
结构描述是用COMPONENT语句(类似于C语言的声明)指明了在该电路中所使用的已生成模块,供本构造体调用。用PORT MAP语句将生成模块的端口与设计的各模块的端口联系起来,并定义相应的信号,来表示所设计的各模块的连接关系。
- ASIC级结构描述
ASIC级结构描述实际就是使用用基本的门电路构成电路。 - 插板级结构描述
插板级电路就是使用ASIC级电路构成电路。 - 系统级结构描述
系统级结构描述就是使用插板级电路构成电路。
3.3.2 COMPONENT语句
COMPONENT语句是基本的描述语句。该语句指定了本构造体中所调用的是哪一现成的逻辑描述模块。其格式如下:
COMPONENT 元件名 IS
GENERIC 说明; --参数说明
PORT 说明; --端口说明
END COMPONENT 元件名;
COMPONENT语句可以在ARCHITECTURE、PACKAGE及BLOCK的说明部分中使用。COMPONENT语句当中有GENERIC语句和PORT语句。GENERIC语句通常用于该元件的可变参数的代入或赋值。PORT语句用于说明该元件的输入、输出端口的信号规定。
3.3.3 COMPONENT_INSTANT语句
COMPONENT_INSTANT语句是结构化描述中不可缺少的一个基本语句(类似于C语言当中的调用语句)。其格式如下:
标号名 : 元件名 PORT MAP(信号,...);
例如:
u2: and2 PORT MAP(nsel, d1, ab);
标号架子啊元件名的前面,在该构造体的说明汇总该标号名一定是唯一的。下一层元件的端口信号与实际连接的信号用PORT MAP的映射关系联系起来。映射的方法有两种:位置映射和名称映射。
- 位置映射
所谓位置映射是指在下一层端口说明中的信号书写顺序位置和PORT MAP()中指定的实际信号书写顺序位置一一对应。例如:
PORT(a, b: IN BIT;
c: OUT BIT);
u2: and2 PORT MAP(nsel, d1,ab);
--这里nsel和a对应,b和d1对应,c和ab对应。
- 名称映射方法
所谓名称映射是指将已经存于库中的现成模块的各端口名称赋予设计中模块的信号名。例如:
u2:and2 PORT MAP(a=>nsel, b=>d1, c=>ab);
在输出信号没有连接的情况下,对应端口的描述可以省略。
4 VHDL语言的主要描述语句
在用VHDL语言描述系统硬件行为时,按语句执行顺序对其进行分类,可以分为顺序(Sequential)描述语句和并发(Concurrent)描述语言。
4.1 顺序描述语言
顺序描述语言只能出现在进程或子程序中,由它定义进程或子程序所执行的算法。顺序描述语句中所涉及到的系统行为有时序流、控制、条件和迭代等。顺序描述语句的功能操作有算术、逻辑运算,信号和变量的赋值,子程序调用等。顺序描述语句主要由有:
- WAIT语句
- 断言语句
- 信号代入语句
- 变量赋值语句
- IF语句
- CASE语句
- LOOP语句
- NEXT语句
- EXIT语句
- 过程调用语句
- NULL语句
NULL(空)语句表示只占位置的一种空处理操作,但是它可以用来为所对应信号赋一个空值,表示该驱动器被关闭
4.1.1 WAIT(等待)语句
进程在仿真运行中总是处于下述两种状态之一:执行或挂起。进程状态的变化受等待语句的控制,当进程执行到等到语句时,会被挂起,并设置好再次执行的条件。等待语句可以设置4种不同的条件:无限等待、时间到、条件满足以及敏感信号量变化。这几种条件可以混用。其书写格式如下:
WAIT --无线等待
WAIT ON --敏感信号量的变化
WAIT UNTIL --条件满足
WAIT FOR --时间到
- 1、WAIT ON语句
完整的书写格式如下:
WAIT ON 信号 [,信号];
WAIT ON a, b; ---这是WAIT ON 后面接着多个信号量的语句
--上语句表明:它等待信号量a或者b发生变化。只要a或者b中有一个信号量发生变化,进程就结束挂起状态,而继续执行WAIT ON语句后续语句。
下面以两个例子直接来说明WAIT ON语句在实际使用的作用。实际上,两个例子的作用是一样的。并且如果PROCESS语句已经有了敏感信号量,在PROCESS语句中便不能再使用WAIT ON语句。
PROCESS(a,b) IS
BEGIN
y<=a AND b;
END PROCESS;
**************
PROCESS
BEGIN
y<=a AND b;
WAIT ON a,b;
END PROCESS;
- 2、WAIT UNTIL语句
WAIT UNTIL语句书写格式如下:
WAIT UNTIL 表达式; --这里的表达式是布尔表达式,当进程执行到该语句时将被挂起,直至表达式返回一个“真”值。
实际上,表示式是一个隐式的敏感信号量表。当表中的任何一个信号量发生变化时,立即对表达式进行一次评估。
实例如下:
WAIT UNTIL((x*10)<100); --当信号量的值大于或者10时,进程执行到该语句时将被挂起;反之,进程将被挂起。
- 3、WAIT FOR 语句
WAIT FOR语句的完整书写格式如下:
WAIT FOR 时间表达式; --进程执行到该语句时将被挂起,直到指定的等待时间到时,进程再开始执行WAIT FOR语句后续的语句。
实例如下:
WAIT FOR 20ns;
WAIT FOR (a*(b+c));--注意WAIT FOR的语句一定是时间量
- 4、多条件WAIT语句
多条件WAIT语句中,等待的条件是混合量。
WAIT ON nmi, interrupt UNTIL((nmi=TRUE)OR(interrupt=TRUE)) FOR 5us;
上述语句有三个等待的结束条件:
(1)信号量nmi和interrupt中任何一个有一次新的变化;
(2)信号量nmi和interrupt中任何一个取值为“真”;
(3)该语句已等待5us。
注意在多条件WAIT语句表达式的值至少应该至少包含一个信号量的值
- 5、超时等待
在实际程序设计中,等待语句所等待的条件在实际执行时不能保证一定会碰到。在这种情况下,等待语句通常要加一项超时等待项,以防止等待语句进入无限期的等待状态。但是,如果采用这种方法,则应作适应的处理,否则就会产生错误的行为。
具体实例如下:
WAIT UNTIL (sedB='1') FOR 1us;
ASSERT(sendB='1')
REPORT"sendB timed out at '1' "
SEVERIRT ERROR;
上例中,每个等待语句的超时表达式用1us说明。如果等待语句的等待时间超过了1us,则进程执行下一条ASSERT语句。ASSERT语句的判断条件为“假”,就向操作人员提供错误输出信息输出,帮助操作人员了解在进程中发生了超时等待。
4.1.2 断言语句
断言(ASSERT)语句主要用于程序仿真、调试的人机对话,它可以给出一个文字串作为警告和错误信息,其给程序调试带来极大好处。其书写格式如下:
ASSERT 条件 [REPORT 输出信息] [SEVERITY 级别];
当执行ASSERT语句时就会对条件进行判决。如果条件为“真”,则向下执行另一个语句;如果条件为“假”,则输出错误信息和错误严重程度的级别。REPORT后面跟的是设计者所写字符串,通常是说明错误原因,文件串应该用" "括起来。SEVERITY后面跟的是错误严重程度的级别。VHDL中错误有四个级别:FAILURE, ERROR, WARNING, NOTE。
例子:
ASSERT(sendB='1')
REPORT"sendB timed out at '1' "
SEVERIRT ERROR
4.1.3 信号代入语句
信号代入语句的书写格式如下:
目的信号量<=信号量表达式;
该语句表明:将右边信号量表达式的值赋予左边的目的信号量。注意:代入符号两边信号量的类型和位长度应该一致
另外,代入语句的符号“<=”和关系操作的小于等于符号“<=”是相同的,在使用时应该正确判别不同的操作关系。
4.1.4 变量赋值语句
变量赋值语句的书写格式如下:
目的变量:=表达式;
该语句表明:目的变量的值将由表达式所表达的新值代替。注意:赋值符号左右两边的变量的数据类型必须相同
目的变量的类型、范围及初值在实现应已给出过。右边表达式可以是变量、信号或字符。
4.1.5 IF语句
IF语句是根据所指定的条件来确定执行哪些语句,有三种书写格式:门闩控制、二选择控制、多选择控制。
``注意IF语句的条件判断输出是布尔量,即是“真( TRUE)”或“假“FALSE”。因此IF语句的条件表达式中只能使用关系运算操作符。
- IF语句的门闩控制
门闩控制的书写格式如下:
IF 条件 THEN
顺序处理语句
END IF;
当程序执行到IF语句时,判断IF语句所指定的条件是否成立。如果成立,则IF语句所包含的顺序处理语句将被执行;反之,程序将跳过IF语句所包含的顺序处理语句,向下执行后续语句。
实例:
IF(a='1') THEN
c<=b;
END IF;
这是门闩控制的一个实例,该语句会被综合为一个D触发器。
- IF语句的二选择控制
IF语句用作二选择控制书写格式如下:
IF 条件 THEN
顺序处理语句;
ELSE
顺序处理语句;
END IF;
这种语句是一个非此即彼的语句,用条件来选择两条不同程序执行的路径。
典型实例二选一电路如下:
ARCHITECTURE rtl OF mux2 IS
BEGIN
PROCESS(a, b, sel) IS
BEGIN
IF(sel='1') THEN
c<=a;
ELSE
c<=b;
END IF;
END PROCESS;
END ARCHITETUREL rtl;
- IF语句的多选择控制
IF语句的多选择控制又称为IF语句的嵌套,其书写格式如下:
IF 条件 THEN
顺序处理语句;
ELSEIF 条件 THEN
顺序处理语句;
···
ELSEIF 条件 THEN
顺序处理语句;
ELSE
顺序处理语句;
END IF;
典型的多选择语句是多选一电路。
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY mux4 IS
PORT(input: IN STD_LOGIC_VECTOR (3 DOWNTO 0);
sel: IN STD_LOGIC_VECTOR (1 DOWNTO 0);
y: OUT STD_LOGIC);
END ENTITY mux4;
ARCHITECTURE rtl OF mux4 IS
BEGIN
PROCESS(input, sel) IS
BEGIN
IF(sel="00") THEN
y<=input(0);
ELSEIF(sel="01") THEN
y<=input(1);
ELSEIF(sel="10") THEN
y<=input(2);
ELSE
y<=input(3);
ENS IF;
END PROCESS;
END ARCHITECTURE;
4.1.6 CASE语句
CASE语句用来描述总线或编码、译码的行为,从许多不同语句的序列中选择其中之一来执行。CASE语句的书写格式如下:
CASE 表达式 IS
WHEN 条件表达式=>顺序处理语句;
END CASE;
CASE语句的条件表达式可以有如下4种不同的表达形式:
WHEN 值 =>顺序处理语句;
WHEN 值|值|值|···|值 =>顺序处理语句;
WHEN 值 TO 值 =>顺序处理语句;
WHEN OTHERS =>顺序处理语句; --这里的OTHERS表示其他所有值的缺省。
实例:
LIBARRY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY mux4 IS
PORT(a,b,i0,i1,i2,i3: IN STD_LOGIC;
q: OUT STD_LOGIC);
END ENEITY mux4;
ARCHITECTURE mux4_behave of mux4 IS
SIGNAL sel: INTEGER ranger 0 TO 3;
BEGIN
B: PROCESS(a,b,i0,i1,i2,i3) IS
BEGIN
sel<='0';
IF(a='1')THEN
sel<=sel+1;
END IF;
IF(b='1')THEN
sel<=sel+2;
END IF;
CASE sel IS
WHEN 0=> q<=i0;
WHEN 1=> q<=i1;
WHEN 2=> q<=i2;
WHEN 3=> q<=i3;
END CASE;
END PROCESS;
END ARCHITECTURE;
CASE语句和IF语句的对比:CASE语句是一种并行处理语句,IF语句是串行处理语句。在CASE语句中,WHEH的条件是相互独立并且应该将所有的取值一一列举出来。'U'、'X'、'Z'等状态尽管在逻辑电路综合时没有用,但是在CASE语句中必须将所有情况罗列。
如果在CASE语句中的OTHERS之前将所有的所有有意义的输入项(可以逻辑综合的项)都罗列出来,在逻辑综合前就不会有什么不利影响。
下面讨论当OTHERS<="XXX···XXX"时,如果未罗列完所有意义的输入项(可以逻辑综合的项),在综合时,可以认为未罗列的项不可能出现,这会大大简化逻辑电路的设计。但此时如果OTHERS<=“XXX···XXX”(可以逻辑综合),则会使得逻辑综合时,电路规模大大增加。目前VHDL语言的标准中还没有能对输入任意项进行处理的方法。
下面介绍VHDL中,优先项的描述。
WHEN "XXXXXXX0"=> y<="111";
WHEN "XXXXXX01"=> y<="110";
但是这种描述语句在VHDL还未制定出来,因此不能使用这正非法语句。
不晓得这个对不对
这时可以利用IF语句描述优先级编码器。
4.1.7 LOOP语句
LOOP语句语句与其他高级语句中的循环语句一样,使程序能进行有规则的循环,循环次数受迭代算法控制。VHDL中,LOPP语句常用来描述位片逻辑及迭代电路的行为。一般有两种书写格式:FOR循环变量、WHILE条件。
- FOR循环变量
FOR循环变量书写格式如下:
[标号]: FOR 循环变量 IN 离散范围 LOOP -- 标号不是必须的
顺序处理语句;
END LOOP [标号];
具体例子如下:
ASUM: FOR i IN 1 TO 9 LOOP
sum:=i+sum; --sum初始值为0;
END LOOP ASUM;
下面看一个完整的例子。
LIBRARY IEEE
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY parity_check IS
PORT(a: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
y: OUT STD_LOGIC);
END ENTITY parity_check;
ARCHITECTURE rtl OF parity_check IS
BEGIN
PROCESS(a) IS
VARIABLE tmp: STD_LOGIC;
BEGIN
tmp:='0';
FOR i IN 0 TO 7 LOOP
tmp:=tmp XOR a(i);
END LOOP;
y<=tmp;
END PROCESS;
END ARCHITECTURE;
--tmp是变量,它只能在进程内部说明,因为他是一个局部量。
--FOR-LOOP中的i在信号说明和变量说明中都未涉及,它是一个循环变量(整数类型的)。信号和变量都不能1代入此循环变量中。
--tmp是进程内部的变量,如想带出进程,必须将其代入信号量中。
- WHILE条件
WHILE条件的LOOP循环的书写格式如下:
[标号] : WHILE 条件 LOOP
顺序处理语句;
END LOOP [标号];
实例如下:
LIBRARY IEEE
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY parity_check IS
PORT(a: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
y: OUT STD_LOGIC);
END ENTITY parity_check;
ARCHITECTURE rtl OF parity_check IS
BEGIN
PROCESS(a) IS
VARIABLE tmp: STD_LOGIC;
BEGIN
tmp:='0';
i:=0;
WHILE(i<8) LOOP
tmp:=tmp XOR a(i);
i:=i+1;
END LOOP;
y<=tmp;
END PROCESS;
END ARCHITECTURE;
在VHDL当中一般不使用WHILE-LOOP语句进行RTL描述。
4.1.8 NEXT语句
在LOOP语句中,NEXT语句用来跳出本次循环, 其书写格式如下:
NEXT [标号] [WHEN 条件]
NEXT语句执行时将停止本次迭代,而转入下一次新的迭代。NEXT后跟的“标号”表示下一次迭代的起始位置,WHEN条件表明NEXT语句执行的条件。如果NEXT后面既无“标号”,也无“WHEN条件”,那么只要执行到该语句就立即无条件跳出本次循环,并从LOOP的起始位置开始下一次循环。
例子如下:
L1: WHILE i<10 LOOP
L2: WHEN j<20 LOOP
···
NEXT L1 WHEN i=j;
···
END LOOP L2;
END LOOP L1;
4.1.9 EXIT语句
EXIT语句也是LOOP语句当中的控制语句。但是与NEXT语句不同,EXIT语句是直接将LOOP语句结束。其书写格式如下:
EXIT [标号] [WHEN 条件]
如果EXIT后面没有跟“标号”和“WHEN条件”,则程序执行到该语句时无条件地从LOOP语句中跳出,结束循环状态。EXIT语句实例:
PROCESS(a) IS
VARIABLE int_a: INTEGER;
BEGIN
int_a:=a;
FOR i IN 0 to max_limit LOOP
IF(int_a<=0) THEN
EXIT;
ELSE
int_a:=int_a-1;
q(i)<=3.1416/REAL(a*i);
END IF;
END LOOP;
END PROCESS;
EXIT有四种具体的情况。
第一、EXIT语句有“循环标号”和“WHEN条件”。这时,应该进行判断跳转到“循环标号”位置处。
第二、EXIT语句有“WHEN条件”但无“循环标号”。这时,应该进行判断,然后结束本LOOP循环。执行下面的语句。
第三、EXIT语句有“循环标号”但无“WHEN条件”。此时直接跳转到“循环标号”处。
第四、EXIT语句既无“循环标号”也无“WHEN条件。此时立即结束本LOOP循环。
EXIT语据是一条很有用的控制语句。当程序需要处理保护、出错和警告状态时,它能提供一个快捷、简便的方法。
4.2 并发描述语句
在VHDL中,能够进行并发处理的语句有:进程(PROCESS)语句、并发信号代入(Concurrent Signal Assignment)语句、条件信号代入(Conditional Signal Assignment)语句、选择信号代入(Selective Signal Assignment)语句、并发过程调用(Concurrent Procedure Call)语句和块(Block)语句。并发语句既可以是结构性的,也可以是行为性的。并且在并发语句中最关键的就是进程语句。
4.2.1 进程语句
进程语句(PROCESS)是一种并发语句,在一个构造体中多个PROCESS语句可以同时并发运行。PROCESS语句有以下几个特点:
1、进程之间是并行的,进程可以存取构造体或实体名中定义的信号; 2、进程内部所有语句顺序执行; 3、进程结构中必须包含一个显式的敏感信号量或者包含一个WAIT语句; 4、进程之间的通信是通过信号量传递来实现。
4.2.2 并发信号代入语句
并发代入语句实际就是一种在构造体内部但是在进程外部的代入语句。并发信号代入语句实际上就是一个进程的缩写。下面举一个例子说明并发信号代入语句。
ARCHITECTURE behav OF a_var IS
BEGIN
OUTPUT<=a(i);
END ARCHITECTURE behav;
–可以等效为:
ARCHITECTURE behav OF a_var IS
BEGIN
PROCESS(a(i)) IS;
BEGIN
output<=a(i);
END PROCESS;
END ARCHITECTURE behav;
上述两种方式实际上就是一样的效果。
4.2.3 条件代入语句
条件代入语句也是并发描述语句,它可以根据不同条件将不同的多个表达式之一的值代入信号量,其书写格式如下:
目的信号量<=表达式1 WHEN条件1 ELSE
表达式2 WHEN条件2 ELSE
表达式3 WHEN条件3 ELSE
···
ELSE
表达式n;
具体例子如下:
ENTITY mux4 IS
PORT(i0,i1,i2,i3,a,b: IN STD_LOGIC;
q: OUT STDLOGIC);
END ENTITY mux4;
ARCHITECTURE rtl OF mux4 IS
SIGNAL sel: STD_LOGIC_VECTOR(1 DOWNTO 0);
BEGIN
sel<=b&a;
q<=i0 WHEN sel="00" ELSE
i1 WHEN sel="01" ELSE
i2 WHEN sel="10" ELSE
i3 WHEN sel="11" ELSE
'X';
END ARCHITECTURE;
条件代入语句中ELSE语句一定要有。条件代入语句不能进行嵌套。受制于没有自身值代入的描述,不能生成锁存电路。
4.2.4 选择信号代入语句
选择信号代入语句类似于CASE语句,它对表达式进行测试,当表达式取值不同时,将使不同的值代入目的信号量。其书写格式如下:
WITH 表达式 SELECT
目的信号量<=表达式1 WHEN 条件1
表达式2 WHEN 条件2
···
表达式n WHEN 条件n;
具体表达式如下:
ENTITY mux4 IS
PORT(i0,i1,i2,i3,a,b: IN STD_LOGIC;
q: OUT STDLOGIC);
END ENTITY mux4;
ARCHITECTURE behav OF mux4 IS
SIGNAL sel: INTEGER;
BEGIN
q<=i0 WHEN 0;
i1 WHEN 1;
i2 WHEN 2;
i3 WHEN 3;
‘X’ WHEN OTHERS;
sel<=0 WHEN a='0' AND b='0' ELSE
1 WHEN a='1' AND b='0' ELSE
2 WHEN a='0' AND b='1' ELSE
3 WHEN a='1' AND b='1' ELSE
4;
END ARCHITECTURE behav;
4.2.5 并发过程调用语句
并发过程调用语句,这里对过程调用时需要注意的几个问题作以下说明。
1、并发过程调用语句是一个完整的语句,在其前面可以加标号;
2、并发过程调用语句应该有IN/OUT或者INOUT参数,它们应列于过程名后跟的括号内;
3、并发过程调用可以有多个返回值,但这些返回值应该通过过程定义的输出参数带回。
在构造体中采用并发过程调用语句的实例如下:
ARCHITECTURE···
BEGIN
Vector_to_int(z,x_flag);
···
END ARCHITECTURE;
上例中的Vector_to_int兵法过程调用时对位矢量z进行数制转换,使之变成十进制的整数q;x_flag是标志位,当标志位为“真”时表明转换失败,当为“假”时表明转换成功。
实际上这种并发调用语句就是一个进程调用过程的简写,其效果等效于在进程当中调用一个过程
4.2.6 块语句
块语句的书写格式如下:
标号: BLOCK
块头
{说明语句};
BEGIN
{并发处理语句};
END BLOCK 标号名;
“块头”主要用于信号的映射及参数的定义,通常通过GENRIC语句、GENERIC_MAP语句、PORT语句和PORT_MAP语据来实现。
“说明语句”主要是对块使用的客体进行说明。说明的项目包括:USE语句、子程序说明及子程序体、类型说明、常数说明、信号说明和元件说明。
块是可以嵌套并且重复调用的。
4.3 其他语句和有关规定的说明
本节主要说明说明语句、定义语句和一些具体的规定。
4.3.1 命名规则和注释的标记
- 命名规则如下:
1、名字最前面应该是英文字母;
2、能使用的字符只有英文字母、数字和_;
3、不能连续使用_符号,在名字的最后不能使用’_‘;
4、尽管VHDL对大小写不敏感,但是有两种情况是例外,即:’ '和" "括起来的字符。 - 注释
–是注释符号,目前只有单行注释
4.3.2 ATTERIBUTE(属性)描述与定义语句
VHDL语句有属性预定义功能,该功能有很多重要的应用,例如检测时钟边沿、完成定时检查、获得未约束的数据类型的范围等。
预定义的类型有:数值类、函数类、信号类、数据类型类、数据区间类和用户自定义的属性。
4.3.2.1 数值类属性
数值类属性用来得到数值、块或者一般数据的有关值。
- 一般数据的数值类属性
T'LEFT --得到数据类或子类区间的最左端的值
T'RIGHT --得到数据类或子类区间的最右端的值
T'HIGH --得到数据类或子类区间的高端值和出现的次序相关出现早的次序低
T'LOW --得到数据类或子类区间的低端值和出现的次序相关出现早的次序低
- 数组的数值属性
T'LENGTH --数组的长度值
- 块的数值属性
T'STRUCTURE --如果块和构造体中只有COMPONENT语句,则会得到“TRUE”。
T'BEHAVIOR --块或构造体有标号名,并且块和构造体中不存在COMPONENT语句,得到“TRUE”。
4.3.2.3 函数类属性
所谓函数类属性是指属性以函数的形式,让设计人员得到有关数据类型、数组、信号的某些信息。
函数类属性有以下三种:数组类型属性函数、数组属性函数、信号属性函数。
- 数组类型属性函数
数组类型属性函数可以得到有关数据类型的各种信息。有下面6种属性函数:
T'POS(x) --得到输入值x的位置序号
T'VAL(X) --得到输入位置序号x的值
T'SUCC(x) --得到输入x值的下一个值
T'PRED(x) --得到输入x值的前一个值
T'LEFTOF(X) --得到邻近输入x值左边的值
T'RIGHTOF(x) --得到临接输入x值右边的值
对于递增区间有:
T’SUCC(x)=T’RIGHTOF(x)
T’PRED(x)=T’LEFTOF(X)
对递减区间有:
T’SUCC(x)=T’LEFTOF(X)
T’PRED(x)=T’RIGHTOF(x)
- 数组属性函数
利用数组属性函数可以得到数组的区间。数组属性函数可分为以下4种:
T'LEFT(n) --得到索引号为n的区间的左端位置号。这里的n实际上是多维数组中所定义的多维区间的序号。当n缺省时,代表对一维区间进行操作。
T'RIGHT(n) --得到索引号为n的区间的右端位置号
T'HIGH(n) --得到索引号为n的区间的高端位置号
T'LOW(n) --得到索引号为n的区间的低端位置号
对于递增区间有:
T’HIGH(x)=T’RIGHTOF(x)
T’LOW(x)=T’LEFTOF(X)
对递减区间有:
T’HIGH(x)=T’LEFTOF(X)
T’LOW(x)=T’RIGHTOF(x)
- 信号属性函数
信号属性函数用来得到信号的行为信息。例如,信号是否变化,从最后一次变化到现在经过了多长时间,信号变化前=变换前的数值是多少。
S'EVENT --如果在当前一个相当小的时间间隔内时间发生,那么函数将返回一个为“真”的布尔量,否则返回“假”
S'ACTIVE --如果在当前一个相当小的时间间隔内信号发生变化,函数返回“真”;反之,返回“假”
S'LAST_EVENT --返回一个时间值,即从信号前一个事件发生到现在所经历的时间
S'LAST_VALUE --返回一个值,即从信号最后一次发生改变以前的值
S'LAST_ACTIVE --返回一个时间值,即从信号前一次改变到现在的时间
S’EVENT常用来作边沿检测。
4.3.2.3 信号类属性
信号类属性用于产生一种特别的信号,该信号是以所加属性信号为基础而形成的。信号类属性得到的相关信息类似于用函数类属性得到的信息。但是,信号类属性可以用于任何一般的信号,也包括敏感信号量表中指示的信号。信号类属性有以下四种:
S'DELAYED[(time)] --产生一个延时的信号,信号类型与该属性所加的信号相同。即是以属性所加信号为参考信号,经括号内的时间表达式所确定的时间延时后得到的延迟信号。
S'STABLE[(time)] --该属性可建立一个布尔信号,在括号内的·时间表达式所说明的时间内,若参考信号没有发生转换或其他时间,则得到一个“真”的结果。
S'QUIET[(time)] --该属性可以建立一个布尔信号,在括号内的时间表达式所说明的时间内,若参考信号没有发生转换或其他时间,其属性可以得到“真”的结果。
S'TRANSACTION --该属性可以建立一个BIT类型的信号,当属性所加信号发生转换或者其他事件时,其值将改变。
S’DELAYED[(time)] 可以建立一个所加信号的延迟版本。其实,该功能也可以使用传送延时赋值语句(Transport delay)实现。但是,后者要求编程人员用传送延时赋值的方式记入程序中,而且带有传送延时赋值的信号是一个新的信号。
4.3.2.4 数据类型类属性
利用该属性可以得到数据类型的一个值。它仅仅是一个类型属性,而且必须使用数值类或函数类属性的值来表示。例如:
t'BASE;
用该属性可以的带数据t的类型或者子类型,它仅仅作为其他属性的前缀来使用。例如:
a:=color_gun'BASE'RIGHT; --a=BLACK
4.3.2.5 数据区间类属性
VHDL中仅有两类数据区间类属性,这两类属性仅用于受约束的数据类型数据,并且可返回所选择输入参数的索引区间。这两个属性如下:
a'RANGE[(n)]; --返回一个由参数n所指出的第n个数据区间
a'REVERSE_RANGE[(n)]; --返回一个次序颠倒的数据区间
4.3.2.6 用户自定义的属性
自定义属性的书写格式如下:
ATTRIBUTE 属性名: 数据子类型名;
ATTRIBUTE 属性名: OF 目标名: 目标集合 IS 公式;
在对要使用的属性进行说明以后就可以对数据类型、信号、变量、实体、构造体、配置、子程序、元件、标号进行具体的描述。
用户自定义属性的值在仿真中是不能改变的,也不能用于逻辑综合。用户自定义的属性主要用于从VHDL逻辑综合及ASIC的设计工具、动态解析工具的数据的过渡。
4.3.3 GENERATE语句
GENERATE语句用来产生多个相同的结构,由FOR-GENERATE和IF-GENERATE两种使用形式:
标号: FOR 变量 IN 不连续区间 GENERATE
<并发处理语句>;
END GENERATE [标号名];
标号: IF 条件 GENERATE
<并发处理语句>;
END GENERATE [标号名];