在数字逻辑电路的设计中,VHDL的常用语句比较完整的描述了数字系统的逻辑功能及其硬件结构。VHDL常用语句主要有并行语句和顺序语句等两种。
顺序语句(Sequential):
顺序语句总是处于进程(PROCESS)的内部,每一条顺序语句的执行顺序与它们在程序中的位置有关,从仿真的角度来看是顺序执行的。VDHL语言有六类顺序语句,分别是赋值语句、等待语句、流程控制语句、返回语句、子程序调用语句、空操作语句。
并行语句(Concurrent):
并行语句总是处于进程(PROCESS)的外部。所有并行语句都是并行执行的,即与它们出现的先后次序无关。
VHDL常用语句有赋值语句、进程语句、IF语句、CASE条件语句、WAIT语句、LOOP语句、元件例化语句等。
1)赋值语句
赋值语句的功能是将一个值或一个表达式的运算结果传送给其他数据对象。赋值语句分为信号赋值语句和变量赋值语句。每一种赋值语句有3个基本组成部分,依次为赋值目标、赋值符号、赋值源。信号赋值语句的格式为“赋值目标<=赋值源”,而变量赋值语句的格式为“赋值目标:=赋值源”。VHDL语言规定,赋值目标和赋值源的数据类型必须完全一致。在这里要注意,赋值目标不应是输入信号(IN),赋值源也不应是输出信号(OUT)。
2)进程语句
进程语句格式如下:
PROCESS(敏感信号表)
顺序语句;
END PROCESS;
其含义是敏感信号表中的任一信号发生变化时,即某一信号从‘0’跳变为‘1’或从‘1’跳变为‘0’时,启动进程语句,执行该进程中的顺序语句,执行完后进入等待状态,直至敏感信号表中的某一信号再次发生跳变,再次启动进程语句。
在VHDL语言中,顺序语句应置于进程语句中。一个结构体中可以包含多个进程语句,它们之间为并行关系;每个进程语句内的语句为顺序关系。
3)IF语句
IF语句是VHDL顺序语句中最重要、最常用的语句结构之一,其用法相当灵活,可以用来描述数字电路中的组合逻辑电路及时序逻辑电路等。一般,IF语句的语言结构有下列几种用法。
(1)IF条件THEN
顺序语句
ELSE
顺序语句
END IF;
这种IF语句又称为完整条件语句,可以用来描述组合逻辑电路。在执行中,当判断到IF后面的条件是false,并不直接跳过本IF语句,而是去执行ELSE后面的顺序语句。这种完整条件语句在使用中,给出的条件应是相互对立的条件,以供该条件语句判断执行。
(2)IF条件THEN
顺序语句
END IF;
这种IF语句又称为不完整条件语句,一般用来描述时序逻辑电路。在执行该语句时,首先判断IF后面的条件是true还是false,如果是true,即去执行顺序语句中列出的各条语句,直至执行到END IF,如果IF后面的条件是false,则跳过本IF语句,直接结束本IF语句。
(3)IF条件THEN
IF条件THEN
…
END IF;
END IF;
这种条件语句称为多重IF语句嵌套条件语句。既可以用来产生组合逻辑电路,也可以产生时序逻辑电路等。这种条件语句在使用中,IF和END IF应该一一对应实现先进后出,后进先出的原则。
(4)IF条件THEN
顺序语句
ELSIF条件THEN
顺序语句
…
ELSE
顺序语句
END IF;
这种条件语句可以通过ELSIF(否则如果的含义,即上面的条件不满足时,重新给出一个判断条件,以供选择)设定多个判断条件,从而使得顺序语句可以去执行多个条件分支。
下面举例说明IF条件语句的一些使用方法及必要的语法说明。
【例8.2】 VHDL语言描述二选一的数据选择器,当控制端s为0时,输出端y=a,当控制端s为1时,输出端y=b,用VHDL语言描述该电路时。
在设计该逻辑电路时,可以类似在数字电路中所学的组合逻辑电路的设计方法,通过列真值表、写表达式及化简,得逻辑表达式,然后用例8.1的设计方法加以设计,也可以直接描述二选一数据选择器的行为,下面用行为描述的方法加以设计。
在上面的二选一数据选择器的VHDL语言的描述中,可以看出本例中采用了完整条件语句,也可以看出IF条件语句必须置于PROCESS语句之中。上例中PROCESS(a,b,s)语句的含义是:当敏感信号(a,b,s)中的任一信号发生变化时,启动进程语句后面的条件语句执行,当判断到“s=0”条件成立时(即“s=0”为TRUE),将a赋值给y,反之当“s=0”条件不成立时(即“s=0”为FALSE),将b赋值给y。
【例8.3】 采用VHDL语言设计具有异步复位的D触发器。即当复位信号(RESET)为0时D触发器输出为0,否则在检测到脉冲上升沿时,Qn+1=D。
从上面例子中可以看出,当检测到“reset”信号为低电平时,对输出信号Q清零,否则,也即当检测到“reset”信号为高电平时,并且检测到脉冲信号的上升沿,将输入信号D赋值给输出信号Q。同样,要将条件IF语句放置于进程语句PROCESS中。
上例中,clk'event and clk=′1′实际上一条信号上升沿检测语句。其含义为若检测到CLK信号发生变化并且发生变化后clk信号为高电平。这也是本书以后常用来表述检测信号上升沿的语句。若将该语句改成clk'event and clk=′0′就可以理解为检测信号下降沿的语句。检测信号的上升沿、下降沿是实现时序逻辑电路中的必要的手段。常用来检测信号上升沿的语句还有rising_edge(clk),clk=′1′and clk'last_value=′0′,wait until clk=′1′等。
对于多重IF嵌套条件语句,可以用来描述同步预置的电路。
【例8.4】 采用VHDL语言设计具有同步复位、同步置位的D触发器。即当检测到时钟信号的上升沿且置位信号(SET)为1时D触发器输出为1,同样当检测到时钟信号的上升沿且复位信号(RESET)为1时D触发器输出为0,当检测时钟信号的上升沿且复位信号、置位信号都为0时,Qn+1=D,否则输出Q信号保持。
从【例8.4】可以看出,在判断到(clk'event and clk=′1′)为TRUE,且继续判断到(set=′1′)为TRUE,则将Q置为1,若(clk'event and clk=′1′)为TRUE,但(set=′0′)为FALSE,转而去检测(reset=′1′),若为TRUE,则将将Q置为0,若(reset=′1′)也为FALSE,则将输入信号D赋值给Q,如果判断到(clk'event and clk=′1′)为FALSE,则输出信号Q保持不变。在【例8.4】中,采用多重IF嵌套条件语句,用来描述了电路的同步预置功能,即先判断脉冲条件是否满足,在满足脉冲条件下,继续判断置位(复位)信号是否满足。否则就跳过IF语句,转而执行IF语句后面的程序。
对于第4种IF语句,可以用来描述一些具有优先次序的器件,比如优先编码器。
【例8.5】 用VHDL语言设计一个八线——三线优先编码器。输入信号为input(7)~input(0),输出信号为y(2)~y(0)。在8个输入信号中,input(0)优先级最高,当input(0)信号请求编码时,不论input(7)~input(1)信号状态如何,优先响应input(0)信号,使输出信号为“111”;在input(0)信号不请求编码时,此时优先响应input(1)信号,使输出信号为“110”;以此类推。
end behave;从【例8.5】的程序中可以看出,y<=″110″执行的条件是(input(0)=′1′)AND(input(1)=′0′),以此向下,y<=″000″执行的条件是(input(0)=′1′)AND(input(1)=′1′)AND(input(2)=′1′)AND(input(3)=′1′)AND(input(4)=′1′)AND(input(5)=′1′)AND(input(6)=′1′)AND(input(7)=′0′),说明第4种IF语句有向下相与的功能。
4)CASE条件语句
CASE语句属于顺序语句,同样该语句必须放置于PROCESS进程语句中。CASE语句的一般表达式为:
CASE<表达式>IS
WHEN<选择值或标识符>=><顺序语句>;…<顺序语句>;
WHEN<选择值或标识符>=><顺序语句>;…<顺序语句>;
…;
END CASE;
使用CASE语句,常用来描述总线、编码和译码的行为。在使用CASE语句中,WHEN的条件表达式可以有4种形式。
【例8.6】 试用VHDL语言设计4选1的数据选择器。该4选1的数据选择器的功能表如表8.10所示。表中,ST表示使能控制信号,A1、A0表示地址信号。
表8.10 4选1数据选择器功能表
从【例8.6】中可以看出,当执行到CASE语句时,首先计算<表达式>的值,然后判断WHEN条件句中与之相同的<选择值或标识符>,然后执行对应的<顺序语句>,最后执行END CASE。在本例中,when others=>null;含义是当出现不是上述4种情况时那就无效。在使用CASE语句时,一般在最后都会加上这条指令。在CASE语句中,“=>”不是原来的赋值语句,在这儿有那么(THEN)的含义。
WHEN的条件表达式可以有4种形式:
(1)单个值,如3。
(2)数值选择范围,如(3 T0 7),表示取值为3、4、5、6、7。
(3)并列数值,如2|6,表示取值为2和6。
(4)混合方式,指以上几种形式的组合,例如3 T0 5|7,表示取值为3,4,5,7。
5)WAIT语句
在进程(包括过程)中,当执行到WAIT语句时,运行程序被挂起,直到满足次语句设置的结束条件后,才重新开始执行进程或过程中的程序,对于不同的结束挂起条件的设置,WAIT语句有四种不同的语句格式。
WAIT; ——无限等待(第一格式)
WAIT ON 信号表;——敏感信号变化(第二格式)
WAIT UNTIL 条件表达式;——条件表达式满足(第三格式)
WAIT FOR 时间表达式;——等到时间到(第四格式)
在第一格式中,由于未设置执行的条件,表示永远挂起。
在第二格式中,当信号表中的任意条件发生变化时,程序将结束关起,重新执行。
在第三格式中,增加了程序重新执行的条件。要求当条件表达式中的信号发生改变并且能够满足WAIT UNTIL后的条件,程序将再次执行。
在第三格式中,在此语句后设置一个时间条件,表示在执行程序到WAIT语句开始,程序被挂起,当超出此时间后,程序自动恢复执行。
VHDL语言规定,在进程PROCESS后已列出敏感信号的进程中,不能使用任何形式的WAIT语句。【例8.7】与【例8.8】具有相同的逻辑功能。【例8.7】的PROCESS后有敏感信号a,b;但【例8.8】中的PROCESS后没有敏感信号,原因就在于【例8.8】使用的是WAIT语句。
【例8.7】
PROCESS(a,b)
BEGIN
y<=a AND b;
END PROCESS;
【例8.8】 PROCESS
BEGIN
y<=a AND b;
WAIT ON a,b;
END PROCESS;
【例8.9】 表8.11为某四位移位寄存器功能表,具有同步清零、同步左移、同步右移、同步预置及数据保持功能,用VHDL语言设计实现这个功能。
表8.11 某四位移位寄存器功能表
END BEHAVE;在该例中,Q1<=(OTHERS=>′0′)的含义等同于Q1<=“0000”。利用OTHERS=>′0′可以将多个信号同时清零,免除了在信号位数较多时,逐个写入的不便。符号“&”的含义是并置,可以将多个信号合并为一个位矢量信号。
对于并置,若在【例8.9】程序做如下调整:(www.xing528.com)
【例8.10】 LIBRARY IEEE;
在【例8.10】中,将原输入S定义为S0,S1:STD_LOGIC;后在结构体中定义SIGNAL S:STD_LOGIC_VECTOR(1 DOWNTO 0);接着采用并置语句S<=S1&S0;也可以实现与【例8.9】一样的功能。
6)LOOP语句
LOOP语句就是循环执行语句。实现了在LOOP语句的顺序语句被循环重复执行,执行的次数有循环参数指定。其格式如下:
格式一:[LOOP标号]:LOOP
顺序语句;
END LOOP[LOOP标号]
对于LOOP标号,可以选择需不需要。
对于格式一,需在循环方式中加进其他控制语句,如EXIT(退出返回)语句。
例如:A:LOOP
X1<=X1+2;
EXIT A WHEN X1>20;
END LOOP A;
含义是对X1执行加2操作,当X1大于20时结束对X1执行加2操作。
格式二:[LOOP标号]:FOR循环变量IN循环变量次数:LOOP
顺序语句;
END LOOP[LOOP标号];
对于格式二,循环变量属于LOOP的局部变量,不必事先定义。循环变量在使用中只能作为赋值源,不能被赋值。循环变量次数的作用是规定了顺序语句被执行的次数,循环语句从循环次数的初始值开始,每执行一次顺序语句,循环变量自动加1,直到执行到循环次数的终了值,退出循环语句。
例如:B:FOR m IN 1 TO 4 LOOP
A(m)<=B(m)OR C(m);
END LOOP B;
在程序中,执行上述语句实际上等同于执行下列语句:
A(1)<=B(1)OR C(2);
7)元件例化语句
例化名:元件名PORT MAP(端口名=>连接端口名,...);
元件例化是实现EDA技术中自上而下层次化设计这一特点的一种重要手段。元件例化实质上就是引入一种连接关系,将预先设计好的设计实体定义为一个元件,然后利用元件例化语句将此元件与当前的设计实体中的指定端口相连接,从而使得当前设计实体中包含低一级的设计层次。可以理解为当前设计实体相当于一个较大的电子线路系统,所定义的例化元件相当于插在这个系统上的一块芯片元件。
【例8.11】 试用元件例化语句实现图8.23所示电路。
图8.23 【例8.11】原理图
从图8.23可以看出,该电路有2个二输入异或门组成,在设计中先设计二输入异或门,后采用元件例化语句实现该电路的顶层设计。
以上为整个电路系统的设计。注意的是,在本设计中,应将二输入异或门(文件名:xor2)的设计,整个电路系统(文件名:xor22)的设计放置于同一文件夹中,否则在编译程序中应用软件将会告知找不到设计文件xor2。
【例8.11】 中,port map即为元件例化语句。在设计中,首先将低层文件设计好。后在顶层文件的设计中,首先采用component调用低层设计文件,同时定义中间连接线(signal),最后告知整个电路系统的连接关系。PORT MAP是端口映射的意思,其中的端口名是在元件定义语句中的端口名表中已定义好的元件端口的名字,连接端口名则是当前系统与准备接入的元件对应端口相连的通信端口,相当于插座上各插针的引脚名。
8)重载函数
VHDL语言属于强数据类型语言,对于不同数据类型的信号,相互之间不允许操作(含赋值或数据运算等)。对于强类型语言,需要预定义类型转换函数,在操作数类型不一致时,调用类型转换函数。在VHDL的IEEE库中,预定义了很多类型转换函数,主要是数据类型转换函数。表8.12为IEEE库类型转换函数表。
表8.12 IEEE库类型转换函数表
【例8.12】 试用VHDL语言设计一个具有异步清零、同步置数的十进制加法计数器。其中CP为脉冲信号,CR信号为清零信号(低电平有效),LD信号为置数信号(低电平有效),EN为控制信号,在CR信号、LD信号无效时,检测到CP脉冲信号的上升沿,实现加1操作。
程序一:
程序二:
在程序一中,将输出信号Q定义为整数类型INTEGER,而在程序二中,将输出信号Q定义为标准逻辑位矢量类型STD_LOGIC_VECTOR。而标准逻辑位矢量类型只具有逻辑运算功能,不具有算术运算功能,不能完成Q1<=Q1+1的算术运算功能,为了能够实现加1的算术运算功能,在库文件的引用中,利用USE IEEE.STD_LOGIC_UNSIGNED.ALL打开了库STD_LOGIC_UNSIGNED,利用该库的重载函数将std_logic_vector转换为整数integer,从而能够完成Q1<=Q1+1的运算功能。
9)用户数据自定义TYPE及有限状态机
用户数据自定义TYPE用于用户自行定义数据类型。VHDL语言除了标准的预定义数据类型(如INTEGER、BOOLEAN、BIT、STD_LOGIC等)外,用户还可以自行定义数据类型。
用户数据自定义TYPE格式如下:
TYPE数据类型名IS数据类型定义OF基本数据类型;
或者
TYPE数据类型名IS数据类型定义;
例如:TYPE DATA IS ARRAY(0 TO 7)OF STD_LOGIC;
TYPE WEEK IS(SUN、MON、TUE、WED、THU、FRI、SAT);
上面第一句定义数据类型DATA是一个具有8位数组的数据类型,数组中的每一个元素的基本数据类型都是STD_LOGIC。第一句定义数据类型WEEK是一种用文字符号表示的数据类型,属于枚举型,实际上是属于用文字符号表示的一组二进制数。VHDL综合器在编码过程中能自动将每个枚举元素转化为位矢量,一般情况下,编码顺序是默认的,从最左边的数据从0开始,依次加1。如SUN=“000”,MON=“001”,…SAT=“110”。
有限状态机可以理解为输入集合和输出集合都是有限的,并只有有限数目的状态。在给定一个输入集合,根据对输入的接受次序来决定一个输出集合。在有限状态机中,状态寄存器的下一个状态不仅与输入信号有关,而且还与当前状态有关,因此有限状态机又可以认为是组合逻辑和寄存器逻辑的一种组合。
用户数据自定义TYPE主要用于有限状态机的描述。利用有限状态机是数字系统设计一种比较实用的方法,同时也是一种实现可靠逻辑控制的途径。
【例8.13】 试用VHDL语言编写实现图8.24所示的状态转换的程序。
图8.24 【例8.13】状态转换图
在很多控制系统中,当外部有模拟量输入到控制系统中时,需要A/D转换模块。如果某一控制系统中,A/D转换模块电路采用ADC0809,它是8路8位A/D转换器。图8.25为ADC0808的工作时序图。图中:D0~D7为转换所得八位输出数据,D7是最高位,D0是最低位;ALE为地址锁存允许信号,输入,高电平有效;START为启动脉冲输入端。在时钟脉冲频率为640 k Hz时,START脉宽应大于100ns-200ns;EOC为转换结束信号端,在A/D转换期间,EOC=O表示转换正在进行,输出数据不可信,转换完毕后立即使EOC=1表示转换己经完成,输出数据可信;OE为数据输出允许信号,输入,高电平有效。当A/D转换结束时,从该管脚输入一个高电平,从而打开输出三态门,输出数字量。
根据ADC0809的工作时序,将0809的采样周期分成了5个阶段,如图8.26所示。①对ADC0809初始化,各采样控制信号置零;②将START置1,启动采样进程;③检测EOC信号,若EOC=‘0’表示转换进行中,若EOC=‘1’表示转换结束;④AD转换结束后将OE置1,允许8位数据输出有效;⑤将输出数据锁存。ADC0809逻辑控制真值表见表8.13所示。
图8.25 ADC0809工作时序图
图8.26 ADC0809状态转换图
表8.13 AD0809逻辑控制真值表(X表示任意)
10)VHDL运算符
VHDL为构造计算数值的表达式提供了许多预定义算符。预定义算符可分为四种类型:算术运算符,关系运算符,逻辑运算符与连接运算符。如表8.14所示。
表8.14 VHDL运算符
11)VHDL语言中的数据对象
在VHDL语言常用的数据对象为信号(signal)、常量(constant)、变量(variable)。信号、变量与常量三者在使用前都必需先声明后使用,否则会产生编译错误。
(1)信号
信号定义的一般格式为:signal信号名:数据类型(:=初始值);
例如:signal s:std_logic;
signal b:std_logic:=′1′;
signal q:std_logic_vector(3 downto 0);
信号用于声明内部信号,而非外部端口(外部端口对应为in,out,inout,buffer),相当于连接线,在元件之间起互联作用。信号是电子线路内部硬件连接的抽象,既可以接受外部信号,也可以赋值给外部信号。它除了没有数据流动方向说明以外,其他性质几乎和“端口”一样。信号可以在architecture、package、entity中声明,是一个全局量,它可以用来进行进程之间的通信。即使是在实体中定义的信号,在结构体中仍然可以使用。在整个结构体的任意位置,都可以获得同一信号的赋值。信号也可在状态机中表示状态变量。在VHDL语言中,对信号赋值是按仿真时间进行的,到了规定的仿真时间才进行赋值。
信号赋值符号为“赋值目标<=赋值源”。
(2)常量
常量定义的一般表达式为:
constant常量名:数据类型:=表达式;
例如:constant a:std_logic_vector:=″11001100″;
Constant b:integer:=10
常量就是一个定值,对某些特定类型数据赋予的数值。定义一个常量主要是为了使设计实体中的某些量易于阅读和修改。如利用常量可设计不同模值的计数器,模值存于一常量中,不同的设计,改变模值仅需改变此常量值即可。
(3)变量
变量定义的一般表达式为:variable变量名:=数据类型(:=初始值);
例如:variabl s:std_logic;
variabl b:std_logic:=′1′;
variabl q:std_logic_vector(3 downto 0);
变量是一个局部量,其适用范围仅限于定义了变量的进程或子程序的顺序语句中。在这些语句结构中,同一变量的值将为随着变量赋值语句的前后次序的改变而改变,因为变量是立即赋值的。
(4)常量与信号、变量的使用差别
从硬件电路系统来看,常量相当于电路中的恒定电平,如GND或VCC,而变量和信号则相当于数字系统中模块与模块之间连接及其连线上的信号值。
(5)信号与变量的使用差别
①从行为仿真和VHDL语句功能上看,信号与变量的区别主要表现在接受和保持信号的方式、信息保持与传递的区域大小上。例如信号可以设置延时量,而变量则不能;变量只能作为局部的信息载体,只能在其定义的进程中使用;而信号则可作为全局的信息载体,在 整个结构体中使用。变量的设置有时只是一种过渡,最后的信息传输和界面间的通信都靠信号来完成。
②从综合后所对应的硬件电路结构来看,信号一般将对应更多的硬件结构,但在许多情况下,信号和变量并没有什么区别。例如在满足一定条件的进程中,综合后它们都能引入寄存器。这时它们都具有能够接受赋值这一重要的共性,而VHDL综合器并不理会它们在接受赋值时存在的延时特性。
③虽然VHDL仿真器允许变量和信号设置初始值,但在实际运用中,VHDL综合器并不会把这些信息综合进去。这是因为实际的FPGA/CPLD芯片在上电后,并不能确保其初始状态的取向。因此,对于时序仿真来说,设置的初始值在综合时是没有实际意义的
④信号是硬件中连线的抽象描述,他们的功能是保存变化的数据值和连接子元件,信号在元件的端口连接元件。变量在硬件中没有类似的对应关系,他们用于硬件特性的高层次建模所需要的计算中。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。