第3章 硬件描述语言VHDL的基本框架介绍

更新时间:2023-11-10 08:42:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

第三章 硬件描述语言VHDL的基本框架介绍

3.1 概述

3.1.1 硬件描述语言的简介

硬件描述语言(Hardware Description Language, HDL),是描述硬件电路的功能、信号连接关系及时序关系的语言。常用硬件描述语言有VHDL、Verilog HDL和ABEL语言。

VHDL的英文全名是VHSIC(Very High Speed Integrated Circuit)Hardware Description Language(超高速集成电路硬件描述语言)。它诞生于1982年美国国防部提出的超高速集成电路计划,其目的是为了在各个承担国防部订货的集成电路厂商之间建立一个统一的设计数据和文档交换格式。1987年底被IEEE 和美国国防部确认为标准硬件描述语言。自IEEE公布了VHDL的标准版本(IEEE-1076)之后,各EDA公司相继推出了自己的VHDL设计环境,或宣布自己的设计工具可以和VHDL接口。此后VHDL在电子设计领域得到了广泛的接受,并逐步取代了原有的非标准硬件描述语言。1993年,IEEE对VHDL进行了修订,从更高的抽象层次和系统描述能力上扩展VHDL的内容,公布了新版本的VHDL,即IEEE标准的1076-1993版本。1995年,我国国家技术监督局制定的《CAD通用技术规范》推荐VHDL作为我国电子设计自动化硬件描述语言的国家规范。现在,VHDL和Verilog作为IEEE的工业标准硬件描述语言,又得到众多EDA公司的支持,在电子工程领域,已成为事实上的通用硬件描述语言。

3.1.2 VHDL与高级语言的联系与区别

1. VHDL语言的特点

VHDL主要用于描述数字系统的结构、行为、功能和接口。相对于传统原理图描述方法,它主要的特点如下:

1)具有强大的电路描述能力——综合功能

VHDL既可以描述如编码器、译码器、计数器等组合逻辑电路和时序逻辑电路等门级的电路,也可用来描述如包括一个完整功能的计算机系统等系统级电路。在描述方式上,

1

既可以寄存器传输描述或者结构描述,也采用行为描述,当然可以采用三者的混合描述方式。其中行为描述可以避开具体的器件结构,而从逻辑行为上描述和设计电子系统,可以极大地提高电路设计的效率。

工程师设计的VHDL代码经过一种称之为综合器的EDA(电子设计自动化)软件工具转换后可以生成数字系统中的电路模型,这个转化过程也就是综合的过程。

2)具有设计仿真与验证功能

VHDL不仅可以根据设计的代码生成电路模型,还含有丰富的仿真语句。利用仿真功能,设计者可以在系统设计的早期进行系统功能特性的仿真模拟,对整个工程设计的结构和功能可行性做出决策,还可以对生成的电路模型做仿真模拟,以检查设计的正确性。

仿真是VHDL的另一大功能,它由另一个称之为模拟器的EDA(电子设计自动化)软件工具完成。如果只仿真电路输入/输出之间的逻辑关系而不考虑电路之间的延迟,则为功能仿真(function simulate);如果要根据所采用的工艺考虑电路之间的延迟,仿真电路输入/输出之间的逻辑关系,则称为时序仿真(timer simulate)。

仿真功能可以及时发现设计中存在的大部分错误,提高设计效率和缩短设计周期。 综合器在把VHDL代码生成电路模型的过程中,仿真语句被简单忽略。而有些语句即可以用于仿真模拟,也可以被综合成电路模型。

3)具有层次结构性

VHDL程序在结构的一个特点是将一项工程设计,或称设计实体(可以是一个元件、一个电路模块或一个系统),分成外部和内部两部分。外部为可视部分,即端口;内部为不可视部分,即设计实体的内部功能和算法完成部分。对一个设计实体定义了外部界面后,一旦内部开发完成后,其他的工程设计中就可以直接调用这个实体。这种分开描述方式如同计算机C程序中的程序结构调整一样,有助于层次化的设计。

4) 设计具有共享和复用的能力

VHDL采用基于库的设计方法。库中可以存放大量预先设计或者以前设计项目中曾经设计的模块,这样设计人员在新项目设计的过程中可以直接复用这些功能模块,从而大大减少了工作量,缩短了开发周期。由于VHDL是一种描述、仿真、综合、优化和布线的标准硬件描述语言,因此它可以使电子系统设计成果在各个公司、团体或者设计人员之间进行交流和共享。

与C语言一样,VHDL提供了大量的标准库函数可供使用。同时,也有一些组织、公

2

司提供各种称之为IP核(知识产权内核)的商业电路模块可供开发者使用。利用这些IP核,可提高设计效率。

5)具有独立于器件的能力

VHDL设计的描述具有相对独立性,与硬件的结构无关,也不必关心最终设计的目标器件工艺是什么。正因为VHDL的硬件描述与具体的工艺技术和硬件结构无关,VHDL设计程序的硬件实现目标有广泛的选择范围,其中包括各系列的CPLD、FPGA及各种门阵列实现目标。该特点有利于设计的电路在不同的器件上移植实现。

6)VHDL与数字逻辑电路、微机原理、信号处理等课程具有密切的关系

虽然综合器具有强大的电路优化功能,还可以支持对行为描述的综合,这些功能都极大地简化了设计,提高了设计效率。但并没有降低对设计者对电路基础的专业知识要求。正好相反,由于设计效率的提高,设计电路规模也随之增大,这要求设计者具有更高的电路专业知识。

2. VHDL语言与高级语言的联系与区别

VHDL语言在形式上与C语言有许多相似之处,具有C语言尤其是C++基础的读者更容易学习和掌握,主要体现在以下几点:

1)VHDL语句与C中的语句类似

VHDL中的大部分语句如变量赋值语句、IF语句、CASE语句、LOOP语句、NEXT语句、过程调用语句等,在C语言中均有类似的含义和语法形式。

2)VHDL结构上也与C 语言类似

VHDL中和C语言一样,有库、说明和语句部分,也有函数、 程序、及其参数、调用方式等内容。这些内容在概念上和C基本是一样。

对有C++基础的读者,也可以找到VHDL和C++的相似之处。 3)VHDL在诸多语言要素上也与C 语言类似

VHDL中和C语言一样,在常量、变量、数据对象,数据类型,以及数据对象的说明方式,数据对象的作用范围等诸多语言要素上和C基本一致。

4)VHDL中的实体(ENTITY)与C++中的类(CLASS)概念类似

对于VHDL中的实体,均可以将它与C++中的类相对应。而与实体对应的结构体(ARCHITECTURE)则可以从前面的实体类中派生,这样它就可以共享在实体类中定

3

义的所有数据。

5)VHDL中的进程(PROCESS)也是派生类

VHDL中的进程(PROCESS)继续从相应的结构体类中派生,这样它就可共享所有的结构体类中定义的数据。当一个结构体中包含多个进程时,每一个进程都是结构体的派生类,为了避免同一结构体对象的重复定义,可以通过虚基类的方式进行派生。

6)VHDL中的端口概念可以映射到C++中的函数参量,类属则可以映射为函数的默认参量。

7)VHDL的层次设计与C++中的成员类对象对应

VHDL的层次设计的实现方式主要由元件说明、例化等来完成,可以对应于C++语言中的成员类对象的概念。对于不同结构体的组装,则可以在定义成员类对象时,通过给出不同构造函数的参量选取不同的结构体。

正是由于VHDL和C具有许多相同或类似的方面,在学习时更要注意二者差异之处。二者存在以下区别:

1)VHDL和C语言在语句方面的差异

计算机高级语言中有说明语句和可执行两种性质语句,可执行语句按逻辑顺序(不一定与书写的顺序相同,但与书写顺序有关)依次被CPU译码执行,最后得到数值计算或数据处理得结果。无论CPU处理多强大、计算速度多快,执行一个语句或一个程序段需要一定得时间,更大的程序代码往往也意味着消耗更长得时间。而说明语句只用于为编译器提供必要的信息(分配存储器、连接不同代码等需要的参数),便于把源程序代码转换为可执行的二进制代码。

VHDL中也有说明语句,其作用也是只为综合器综合电路(或模拟器仿真)提供做需要的信息,本身不被综合成任何电路模型(或生成仿真波形),这个意义上基本是一致的。除说明语句外,VHDL还可根据执行方式分为顺序语句和并发语句两种语句。并发性主要体现在它在执行次序上所有的并行语句是同时执行的,与书写顺序无关,如同一个电路系统中所有的模块同时工作一样。顺序语句在执行方式上和C类似,其语句效果与语句的书写次序有关。

另外,无论并行语句还是顺序语句,程序代码长度的增加和它的执行时间无关,但最后生成的电路规模也越大。事实上,VHDL的程序代码没有执行时间的这一概念。这里所

4

说的执行过程,只是综合器对代码分析的过程或模拟器仿真的过程。

2) 二者本质不同

应当注意,VHDL是用来描述电路功能(或仿真电路)的,而C语言是用来描述数据处理流程的,二者具有本质的区别。这一点在学习中要特别注意。比如同样一条语句,A=A+B(VHDL中书写为A<=A+B),C只表示一次加法运算,在CPU执行后会得到固定的运算结果,而VHDL中却表示要生成一个累加器电路,而不是一次具体的运算。

由于这种差别,在学习时要注意电路设计的合理性。对于 A=A+B语句来说,在C程中基本不存在任何困难,但在VHDL中须有限定条件,否则会引起错误的用法。这个问题后面章节中要提到。

3)VHDL语言中具有许多与数字电路的结构直接相关的概念

VHDL语言中除常量和变量数据对象外,还引入了一种“信号”的数据对象来描述电路,还有表示信号上升沿和下降沿等电路特征的信号属性,同时在数据类型中还增加了表示时间信息的物理类型,也有表示电路逻辑状态的数据类型等等。

另外,VHDL语言中的“元件”,它是数字电路硬件结构——“黑盒”或称为“模块”的抽象。VHDL利用“元件”的概念实现电路系统的层次设计,在一个电路中对同一个元件可多次引用。VHDL中元件的定义和普通电路的定义是完成一样的,它由实体和结构体两部分组成的,其中实体描述的是数字电路的输入/输出的端口,结构体描述的是数字电路的输入、输出之间的行为与功能。

这些内容都是VHDL与普通程序语言的显著区别。

3.2 VHDL的基本结构

在VHDL中,一个完整的VHDL程序通常也称为一个设计实体,它通常包含实体(Entity)、结构体(Architecture)、配置(Configuration)、程序包(Package)和库(Library)五个部分,其基本结构图如图3-1所示。对于一个VHDL程序的五个部分,其中实体和结构体是设计实体必须的,而库、程序包和配置则不是必须的,它们一般是根据编程的需要来添加的。可见,不管是复杂的设计实体还是简单的设计实体,它都包括实体和结构体两个部分。

5

设计实体库、程序包使用说明GENERIC类属说明实体(ENTITY)PORT端口说明结构体说明结构体(ARCHITECTURE)配置(CONFIGURATION)结构体功能描述图3-1 VHDL的基本结构图

下面通过一个计数器的设计实例,从整体上初步认识VHDL的基本结构和语句特点。例3.2.1 是用VHDL语言描述16进制计数器的程序代码。 [例3.2.1] 用VHDL语言描述16进制计数器。

1 LIBRARY IEEE; 2 USE IEEE.STD_LOGIC_1164.ALL; 3 USE IEEE.STD_LOGIC_UNSIGNED.ALL; 4 ENTITY counter16 is 5 GENERIC(n: INTEGER:=4); 6 PORT(clk: IN STD_LOGIC; --时钟输入端 7 co:OUT STD_LOGIC; --进位输出端 8 q :OUT STD_LOGIC_VECTOR(n-1 DOWNTO 0));--状态输出端 9 END counter16; 10 ARCHITECTURE beh OF counter16 IS --结构体说明语句部分 11 CONSTANT t: STD_LOGIC_VECTOR(n-1 DOWNTO 0):= (OTHERS=>'1'); --定义全1的一个常量; 12 SIGNAL q_temp: STD_LOGIC_VECTOR(n-1 DOWNTO 0); 13 BEGIN 14 PROCESS(clk,q_temp) 15 BEGIN 16 IF (clk'EVENT AND clk='1') THEN 17 IF q_temp = t THEN 18 q_temp<=(OTHERS=>'0'); co<='1'; 19 ELSE 20 q_temp<=q_temp+1; co<='0'; 21 END IF; 22 END IF; 23 END PROCESS; 24 q<=q_temp; 25 END beh; 对VHDL语言来讲,字母的大小写是不加区分的。本书为了阅读方便,对VHDL文件中

6

使用关键字的地方用大写字母来表示,小写字母则表示设计者自己定义的部分。在VHDL程序的任何一行中,程序中使用双横线“--”来表示注释,不参加编译和综合。

[例3.2.1]这段VHDL程序只包含了库和程序包的使用说明、实体、结构体四个部分。在库和程序包的说明部分只使用了一个IEEE库,而且引用了IEEE库中的两个程序包STD_LOGIC_1164.ALL和USE IEEE.STD_LOGIC_UNSIGNED.ALL;。4-9句是实体说明部分,它定义了电路与外部的接口方式:输入输出信号及端口信号方向和类型,第5句是类属说明语句。实体说明部分指明了以下内容:电路的名字为counter16,该电路有clk 一个输入端和两个输出端q、co,同时指明clk、q、co的数据类型为STD_LOGIC。10-25句是结构体部分,结构体部分说明了电路要完成的功能和电路的实现方式。

由于具有相同功能的电路可以有多种设计方式,VHDL语言也提供了用不同方式实现相同功能的方法。例3.2.1是用进程语句来设计计数器的一种方式(例中只给出这一种),也正因为只给出了唯一的设计方式,也省略了配置语句部分。

特别提示:用不同的设计方式设计相同功能的电路,它们的性能参数上会有明显的差异。即使是同一个设计者,为了不同的目的,比如为了追求高的速度、为了节省资源成本等等原因,有可能给出不同的实现方式。但是他们之间具有共同的对外接口。所以一个电路实体,有可能对应多个结构体,此时也必须通过配置语句进一步指明电路最后实现时采用的具体方式。

3.2.1 实体说明

实体是VHDL程序设计中必要的组成部分,也称为实体说明。主要用来描述实体的外部特性和外部接口信号,定义设计文件的输入/输出信号,是设计实体对外的一个通信界面,但它并不描述设计的具体功能。

实体说明的语法结构为:

ENTITY 实体名 IS [GENERIC(类属表);] [PORT(端口表);] END 实体名; 实体说明单元必须以语句“ENTITY 实体名 IS”开始,以语句“END 实体名;”结束。需要注意的是:方括号中的内容是可选的,根据所设计的电路的功能和要求选择。

7

1. 实体名

实体具体取名由设计者自定,但由于实体名实际上表达的是该设计电路的器件名,所以最好根据相应电路的功能来取定。实体名的命名要满足VHDL语言定义标识符的规则。

所谓标识符是VHDL语言中各种成分的名称,这些成分包括常量、变量、信号、端口、子程序或参数等。定义标识符需要遵循的规则与C语言中变量标识符规则相同,必须是字母打头的字符串:标识符中有效的字符包括26个大小写英文字母,数字0~9以及下划线“_”;任何标识符必须以英文字母开头;当含有下划线的时候,下划线不能连用,且其前后都必须有英文字母或数字;标识符中的英文字母不分大小写;标识符允许包含图形符号(如回车符、换行符等),也允许包含空格符;VHDL的保留字不能用于作为标识符使用。如74LS138、OR2等都是不允许的。另外在程序设计过程中,要求实体名与存储的文件名一致。

2.类属参数说明语句(GENERIC)

类属参数说明语句必须放在端口说明语句之前,是可选项,用于指定如数据总线宽度、器件延时时间等参数。类属参数与C语言中的宏定义参数相似,类属参数的使用对代码移植和IP(知识产权)核的设计具有重要意义。类属参数说明语句的语法结构为:

GENERIC(常数名:数据类型[:=设定值]; ? 常数名:数据类型[:=设定值]); 类属参数说明和常量不同,常量只能从设计实体的内部得到赋值且不能改变,而类属参量的值可由设计实体的外部提供。因此设计者可以从外面通过类属参量的重新设定而方便地改变一个设计实体或一个元件的内部电路结构和规模,为便捷而迅速地改变电路的结构和规模提供了捷径。在本例中的GENERIC(n: INTEGER:=4);就是类属参数说明语句,通过改变n 就可以实现任意二进制计数器。

例如:GENERIC(trise, tfall: TIME: =1ns; Addwidth: INTEGER : =16);

这里类属参数说明中参数为trise上升沿宽度、tfall为下降沿宽度,用于仿真模块的设计;定义地址总线的宽度Addwidth为16位。类属值Addwidth的改变将使结构体中所有相关的总线定义同时改变,由此使整个设计实体的硬件结构发生变化。

3.端口(PORT)说明语句

8

端口说明语句指明实体的输入/输出信号及其模式。端口说明语句的语法结构为:

PORT(端口名:端口模式 数据类型; ? 端口名:端口模式 数据类型); 1) 端口名

端口名对应于元件符号的每个外部引脚的名称,一般由英文字母组成。名字的定义有一定的惯例,如clk表示时钟,d开头的端口名表示数据,a开头的端口名表示地址等。

端口中定义的端口名,实际上是一种全局的信号,它的作用域是整个包括结构体在内的整个程序。

2) 端口模式

端口模式也称端口方向,用来定义外部引脚的上的数据的流动方向和方式,即定义外部引脚是输入还是输出。IEEE 1076标准包中定义了4种常用的端口模式,各端口模式的功能及符号分别如表3-1和图3-2所示。

图3-2 端口模式符号 表3-1 端口模式的功能

端口模式 IN OUT INOUT 端口模式说明 输入模式,仅允许信号经端口从实体外部输入到实体内部。 输出模式,仅允许信号从实体内部输出。 输入/输出双向模式,信号即可以进入实体内部,也可以从实体内部输出。该模式的输出缓冲器是三态控制的,输入输出分时使用的。 BUFFER 缓冲模式,与OUT类似可作为输出使用,但也可把输出的信号作为输入使用。比如计数器和累加器 从图3.23可以看出, BUFFER为缓冲模式,采用缓冲模式输出具有可以提高传输速度。BUFFER作为端口其传输方向为是输出,不可以作为输入口使用,但输出到端口的信号可以反馈至器件内部,如计数器的设计,可将计数器输出的计数信号回读,以作下一计数

9

值的初值。但BUFFER模式不能用作顶层设计输出端口,所以我们后面介绍计数器的实例并不采用BUFFER模式。

INOUT是双向I/O接口,既可以作为输入,又可以作为输出,但同一时刻只能是一个方向。INOUT端口有两个内部信号,如图3-3中的a和b,其中a是作为输出端口使用的,我们可以看到输出是三态输出;b是作为输入端口使用的,并且当INOUT作为输入时,其输出部分一定处在高阻状态。实际应用中,RAM的数据端口,单片机的I/O端口,我们往往采用INOUT模式。

BUFEER与OUT的区别: OUT和BUFFER的区别如图3-3所示。如果构造体内部要使用该端口信号,那么该端口的模式必须说明为BUFFER,而不能说明为OUT。另外,当一个构造体用BUFFER说明输出端口时,与其连接的另一个构造体的端口也要用BUFFER说明,而对OUT则没有这样的要求。

INdqOUTIN&INdqBUFFERINclkclk

图3-3 OUT和BUFFER的区别

3) 端口数据类型

常见的数据类型有多种,如BIT、BIT_VECTOR、BOOLEAN、INTEGER、STD_LOGIC、STD_LOGIC_VECTOR等,我们将在3.3.1节中详细介绍。我们建议:端口数据类型只使用BIT、BIT_VECTOR、STD_LOGIC、STD_LOGIC_VECTOR等具有电路特性的类型,而不使用BOOLEAN、INTEGER 等数学意义明显的类型。

VHDL是一种强类型语言,因此,只有同类型的数据才能进行通信。

3.2.2 结构体(构造体)

结构体的作用是描述设计实体的逻辑功能或内部电路结构,从而建立设计实体输出与输入之间的关系。一个设计实体可以有多个结构体,但每个结构体的取名不能重复。

构造体的语法结构为:

10

ARCHITECTURE 结构体名 OF 实体名 IS [说明语句;] BEGIN [并行语句;] END 结构体名; 结构体必须以语句“ARCHITECTURE 结构体名 OF 实体名 IS”开始,以语句“END 结构体名;”结束。

1. 结构体的命名

结构体名称是对本结构体的命名,它是该结构体的唯一名称。结构体的名称可以由设计者自由命名。但是在大多数的文献和资料中,通常把结构体的名称命名为beh(behavioral行为描述)、rtl或dataf(寄存器传输描述或dataflow数据流描述)、str(structural结构描述)。这三个名称分别是结构体的三种基本描述方式的名称,具体将在3.4给大家介绍。当设计者采用某一种描述方式来描述结构体时,该结构体的结构名称就命名为哪一个名称。这样可使得阅读VHDL语言程序的人能直接了解设计者所采用的描述方式。

2. 结构体中的说明语句

结构体中的说明语句是对该结构体内部将要用到的信号、常数、元件、函数和过程加以定义。结构体的信号的定义和实体的端口说明一样,应有信号名称和数据类型的定义,但不需要定义信号的模式,不用说明信号方向,因为是结构体内部连接用的信号。

例如:SIGNAL x: real;

CONSTANT vcc: real:=3.3;

在某个结构体中的说明语句定义的信号、常数、元件、函数和过程等等,它的作用范围只局限在这个结构体中使用,超出了这个作用范围也就失去了其意义。

本节中只介绍信号和常数的说明方式,元件、函数和过程的说明放在后面章节介绍。但我们要注意说明语句的位置应该放在构造体的BEGIN END之前。 [例3.2.2] 结构体说明语句的举例

11

LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY ex2 is --该设计实体实现的逻辑功能如下图所示 PORT (a, b : IN STD_LOGIC; --输入端为a,b z, x, y : OUT STD_LOGIC); --输出端为z, x, y END ex2; ARCHITECTURE rtl of ex2 is --从图中可以看出,ai, bi为结构体内的连接信号 SIGNAL ai, bi: STD_LOGIC; BEGIN ai <= a; bi <= b; y <= (ai AND bi); z <= (ai OR bi); x <= ?1? END rtl; 结构体内部的定义只能用于这个结构体中,如果要将这些定义用于其他实体或结构体,则需要将其作为程序包处理。

3. 结构体中的功能描述语句——并行语句

结构体的主体是放在结构体的BEGIN 和END之间的若干并行语句,这些语句共同描述了一个完整的电路功能。对于简单的电路来说,BEGIN 和END之间可能只有一条语句,复杂的结构体的功能描述语句总是由多种并行语句构成。其中的每一个语句甚至可以理解为一个电路模块,它们通过定义的端口信号和内部信号相互连接起来。从电路工作的特点上看,所有的电路模块同时在工作的,没有运行顺序的问题,也不存在语句之间的书写顺序问题。换句话说,电路的功能与这些语句的书写顺序是无关的。因此,这些语句被称为并行语句。

用于描述电路的并行语句可以归纳为三种不同的语句类型。一类语句可以看成是通过布尔表达式推广演变而来,它可以直接对应于电路输入输出之间的逻辑真值表。这类语句直接描述了电路中的输入输出信号之间的逻辑关系,这种描述方式被称为数据流描述方式;第二类语句可以看成是原理图设计的语言表达,它借助于信号连接一些已有的元件实现电路功能的设计,该描述方式被称为结构化设计方式。结构化描述方式必须熟悉所使用元件内部的详细逻辑关系——电路功能,也需要知道元件的外部接口——引脚特性。第三种类型,实际就是称之为进程(PROCESS)的语句,它实际上是通过具有逻辑上关联的一系

12

列语句组成的程序段共同描述的电路逻辑行为特性——电路的功能和实现方式。这种描述方式在VHDL语言中被称之为行为描述,它也是VHDL语言的一个特色,也是学习中需要下工夫重点掌握的内容。为和并行语句区分,进程中使用的语句被称为顺序语句。虽然每一条顺序语句都有自己的含义,但必须和上下文结合起来才能表达一个完整的电路功能,而不能断章取义地去理解。

关于三种描述方式和并行语句顺序语句更进一步的知识在第三章和第四章将做详细介绍。在这里我们要先了解数据流描述和结构化描述方式与《数字逻辑电路》中知识的内在关系,便于在后面语句的学习中去仔细体会。

3.2.3 库、程序包及配置

在VHDL中,除了实体和结构体外,还有三个可以进行独立编译的源设计单元:库、程序包和配置。下面分别介绍。

1.库 1)基本概念

库由一个或多个程序包构成。我们往往把一些密切相关的、可重复利用的设计资源(如函数、过程、元件等)放在一个程序包中,再把性质相近、功能类似的程序包归于同一个库中,库的基本组成部分如图3-4所示。

库的建立和使用有利于设计重用和代码共享,同时可以使代码结构更加清晰。 如同C语言开发一样,熟悉并掌握大量的库,可以极大地利用现有的设计成果,提高设计人员的工作效率。读者在掌握VHDL基础知识之后,可以通过专门库参考书籍来了解更多的相关知识,此处重点了解库的概念和使用方法。

库程序包函数过程元件常量 图3-4 库的基本组成部分

13

2)库的种类

在VHDL中常用的库有IEEE库、STD库、WORK库、VITAL库。 (1) STD库

STD库是VHDL的标准库,库中存放有标准(STANDARD)程序包和文本(TEXTIO)程序包。标准程序包(STANDARD)中预定义了BIT、BIT_VECTOR、INTEGER、BOOLEAN、TIME等VHDL综合器支持的数据类型。文本程序包(TEXTIO)定义了对文本文件进行读写操作的过程和函数。

另外,标准程序包(STANDARD)符合VHDL标准,在使用它时不需要在设计实体的开头对它进行说明;而使用文本程序包(TEXTIO)时必须在设计实体的开头对它进行说明。

例如: LIBRARY STD;

USE STD.TEXTIO.ALL;

(2)IEEE库

IEEE库是VHDL设计中最为常见的库,它包含有IEEE标准的程序包和其他一些工业标准的程序包。其中STD_LOGIC_1164程序包是最重要的、也是最常用的程序包,大部分基于数字系统设计的程序包都是以此程序包中的设计的标准为基础的。

另外,需要注意的是,在IEEE库中的程序包中的内容并不没有在VHDL语言标准中定义,因此,在使用它时,需要在设计实体的开头加以说明。

例如:

LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; (3)WORK库

WORK库是现行作业库,即当前工程设计中所描述包、实体等不需要任何说明,都将存放在WORK库。如在某个工程中定义了一个名为“add_pack”程序包,如果没有指定给特定的库,编译以后就会自动地加到WORK库的目录中。但必须注意,WORK库并不是这个目录的目录名,而是一个逻辑名,综合器将指示器指向该目录的路径。

WORK库 是VHDL标准中默认的库,使用时不必进行说明,WORK库可以使设计者个人使用,也可提供给设计组多人使用。但WORK库中的所有程序包是不可见的,使用的时候应该用USE语句加以调用。

(4)VATAL库

使用VITAL库可以提高门级时序仿真的精度,一般在VHDL语言程序进行时序仿真时

14

使用。实际上,由于各FPGA/CPLD生产厂商的适配工具都为各自得芯片生成带时序信息的VHDL门级网表,用VHDL仿真器仿真该网表可以得到精确的时序仿真结果,因此,在FPGA/CPLD设计开发过程中,一般并不需要我们开发者自己去使用VITAL库中的程序包。

(5)用户自定义的库

用户所开发设计的程序包和实体等,也可以汇集在一起定义成一个库,这样的库称为用户自定义库。用户自定义库使用时同样要先用关键字library说明要引用的库名,然后用use语句打开库中程序包。在计算机的硬盘(软盘、U盘也可以)上新建一个文件夹,并将文件夹改名为用户自定义“库名”,如mylib,将编辑的程序包等以文件的形式存在该文件夹下,此时该文件夹即为用户自定义库。

表3-2 VHDL中常用的库和包的内容

库名 程序包名 STANDARD STD TEXTIO STD_LOGIC_1164 ST_LOGIC_SIGNED 有符号的算术运算(如“+”、“-”、“*”“<”等) 定义STD_LOGIC、STD_LOGIC_VECTOR数据类型的 IEEE STD_LOGIC_UNSIGNED 无符号的算术运算(如“+”、“-”、“*”“<”等) 定义了UNSIGNED 、SIGNED、SMALL_INT三种数据类型,并定STD_LOGIC_ARITH 义了上述类型及STD_LOGIC的混合算术运算、 比较操作函数、数据类型转换函数。 3) 库的使用

使用一个库之前,需要首先对库进行声明。经过声明之后,在设计中就可以调用库中的代码了。库说明语句的一般语法结构为:

LIBRARY 库名; USE 库名.程序包名.项目名; --当项目名用“ALL”代替时,表示开放该程序包的所有项目 例如:LIBRARY IEEE;

USE IEEE.STD_LOGIC_1164.ALL; ――开放该程序包的所有项目

15

定义内容 定义BIT、BIT_VECTOR、INTEGER等数据类型 对文本文件进行读写操作的过程和函数 定义STD_LOGIC、STD_LOGIC_VECTOR数据类型 定义STD_LOGIC、STD_LOGIC_VECTOR数据类型的

USE IEEE.STD_LOGIC_UNSIGNED. “+”; ――只开放“+”运算操作符

4)库的作用范围

库说明语句的作用范围是从一个实体说明开始到它所属的结构体为止,实际上可以认为在实体中是全局的。 2. 程序包

已在设计实体中定义的数据类型、子程序或数据对象对于其他设计实体是不可用的。为了使已定义的数据类型、子程序或数据对象能够被其他的设计实体方便地访问和共享,可以将它们收集在一个VHDL程序包中。

所以程序包的作用是收集被多个VHDL设计实体共享的数据类型、子程序或数据对象,使之适用于更一般的访问和调用范围。

如果要使用某个程序包中的所有定义,可利用USE语句按如下方式去调用该这个程序包。

USE 库名.程序包名.项目名; 3. 配置

在VHDL中,每个实体可以拥有多个不同的结构体,而每个结构体的地位是相同的,在这种情况下,可以利用配置把特定的结构体指定给一个确定的实体。我们将在4.4节详细介绍。

3.3 VHDL语言要素

VHDL形式上类似于计算机编程语言,其语言要素也包括数据对象、数据类型和运算操作符。但由于VHDL与C等计算机编程语言在本质上的差异,这些语言要素上在理解和使用也有一些差异。如能在学习中留意二者的异同支出,学习效率将会显著提高。本节在叙述时也会特别提醒

3.3.1 数据对象

在VHDL中,数据对象(Data Objects)类似于一种容器,它接受不同数据类型的赋值。数据对象有三种,即常量(CONSTANT)、变量(VARIABLE)和信号(SIGNAL)。

1. 常量

16

常量除语法差异外,在概念和用法上与C语言中一致,熟悉C语言的读者可以快速浏览。常量是指在设计实体中不会发生变化的值,一旦定义赋值,程序中不能修改。任何高级语言中的常量都可以使程序容易阅读和修改。定义常量后,程序中所有用到该常量的地方都用定义的常量名表示,需要修改该常量时,要在该常量名定义处修改。

常量定义的语法格式为:

CONSTANT 常量名:数据类型 := 表达式 ; 注意:常量定义的语法格式中的数据类型和表达式的数据类型必须一致,并且可以使用除了文件类型和存取类型外的任何数据类型。 例如:

CONSTANT width: POSITIVE: = 8;

CONSTANT vcc : REAL := 5.0; CONSTANT delay : TIME := 10ns;

常量可以在程序包、实体说明、结构体的说明部分进行说明,也可以在语句的说明部分进行说明,是一个全局量。在程序包声明的常量可以被所有调用该程序包的实体使用。实体说明中说明的常量可以在该实体的所有结构体中使用。类似地,定义在结构体中的常量只能在该结构体内部使用,进程语句中的说明的常量只能在本进程语句中使用。

2. 信号

利用信号可以描述电路模块中消息的传递通道、也可以通过信号之间的逻辑关系描述一种门电路或一个信息存储单元。信号是电子电路内部连接和电路的抽象表述,也是描述电路的最基本方式。可以把端口也看成是一种连接外界电路的特殊信号,但它与一般的信号相比,还可以表示数据流动的方向。

信号通常在构造体说明语句、程序包首和实体说明语句中定义,不能在进程的说明语句和子程序(包括函数和过程)的说明语句中定义。

信号定义的语法结构为:

SIGNAL 信号名:数据类型 [约束条件][ := 表达式]; 信号名:符合VHDL符号定义规则,与C程序变量相同;

约束条件:也称约束区间。由于VHDL语言属于强类型语言,在仿真过程中,首先要检查赋值语句中的类型和区间,任何一个信号和变量的赋值均需落入给定的约束区间,也就是说要落入有效数值的范围中。约束区间通常跟在数据类型说明的后面。

例如:a,b: INTEGER RANGE 9 DOWNTO 0 :=0;

17

c:BIT_VECTOR(3 DOWNTO 0);

表达式:可以为信号定义初值,初值可以由表达式给出。表达式含义和C语言中一致。

在信号定义后就可以对信号赋值,信号赋值语句的语法结构为:

目标信号名 <= 表达式[ AFTER 时间表达式 ]; 电信号在经过容性电路器件(抗性也一样,下同)滞后延迟,电容效应越大延迟时间也越大,延迟时间直接限制了电路所能通过的信号频率。另一方面,电信号在理想的纯电阻传输线上传导时也会产生时间上的延迟,这种延迟与前者性质不同,不影响电路的工作频率。但两种性质的延迟都可能导致电路的输入/输出之间的逻辑关系发生错误。在VHDL语言中,信号及其相关的延时语句明显地体现了这一硬件系统的特征。在VHDL中,在信号赋值时可以指定延迟的性质与时间,如果省略不指定时则表示前一种性质延迟,并且信号赋值后至少有δ延时,即c <= a;和c <= a AFTERδ;是等价的。 [例3.3.1] 信号定义语句和赋值语句的举例 ARCHITECTURE m1 OF or_1 IS SIGNAL c, d: BIT ; --信号可以在结构体说明语句中定义,而变量不行 BEGIN PROCESS(a,b,d ) --其中a,b为输入端口,端口类型BIT BEGIN ? c <= a; --信号赋值部分 d <= b AFTER 20ns ; --信号延时10ns后再赋值 END; 3. 变量

与信号类似,在VHDL语言中,常用来表示临时存储的中间数据,以便于实现程序的算法,如门与门间的连线及其连线上的信号值。变量也可以是电子电路内部连接和电路的抽象表述,它也是描述电路的一种方式。变量也必须在定义后才能使用,与信号不同,变量只能定义在进程的说明语句和子程序(包括函数和过程)的说明语句中,不可在其外部定义。所以变量是一个局部量,只在定义的代码段中使用,不能将信息带出到其他模块。考虑到进程的挂起机制和变量的局部性质,变量不具有保存过去值的功能,不作为存储元素

18

使用,一般只保存临时信息。

变量定义的语法结构为: VARIABLE 变量名[,… ,变量名][约束条件] : 数据类型 [:=表达式 ]; 变量定义中的变量名、约束条件、表达式的含义和信号定义中的信号名、约束条件、表达式的含义类似,这里不再重复介绍。

变量定义后才可以对变量赋值,与信号不同,变量的赋值是理想化数据传输,其赋值是立即生效的,不存在任何的延时行为。下面是变量赋值语句的语法结构。

变量赋值语句的语法结构为:

目标变量名 :=表达式; 注意:赋值语句 “:=” 右边的表达式必须与目标变量具有相同的数据类型,这个表达式

可以是一个运算表达式也可以是一个数值。变量赋值语句左边的目标变量可以是单值变量,也可以是变量的集合。 [例3.3.2] 变量定义语句和赋值语句的举例 PROCESS( ) VARIABLE a :INTEGER RANGE 0 TO 15;--变量定义在PROCESS的 VARIABLE b, c : INTEGER :=2; --说明部分,BEGIN之前 VARIABLE d :STD_LOGIC; BEGIN ? a :=13; --变量赋值部分 b:=a+1; c:=15; d:=?1?; END;

4. 变量与信号的区别与联系

在VHDL中,变量和信号是两个使用频度很高的数据对象。由于变量和信号具有一定的相似性,比如说,二者都表示电路中门与门间的连线及其连线上的信号值,都必须在定义后方可赋值;二者都可以用来描述信号等等。在许多场合下,常常容易混淆两者的使用规

19

则,从而可能导致设计描述的错误。一般认为,信号具有比较明确的电路含义,而变量的数学意义更明显,所以如果可能时,我们更鼓励使用信号而不是变量。

实际上,变量和信号还是有很大差别的,因此有必要在这里讨论一下两者的区别。通常,变量和信号的主要区别如表3-3所示,而这些区别也可以作为信号和变量使用的规则对待。

表3-3 信号和变量的区别 赋值符号 基本用法 信号 <= 用于作为电路中的信号连接,表示硬件连线和存储元件 变量 := 用于作为进程中临时存储的中间数据,不能表示硬件连线和存储元件特性 行为特性 信号赋值有延迟时间,并且在进程结束时才对信号赋值 适用范围 在整个结构体内的任何地方都适用,即在并行语句和顺序语句中都可使用 对进程影响 用途 进程对信号敏感 用于程序包、实体说明或结构体中。在实体中所有端口默认为信号 含有的信息 可以容纳当前值,也可以保持历史值。这一属性与触发器的记忆功能有很好的对应关系。 由于变量的适用范围与进程有很大的关系,故在进程概念介绍时,将进一步通过例子阐述二者在用法上的差异。

[例3.3.3] 信号和变量区别的举例

变量只有当前值。 进程对变量不敏感 仅用于进程、函数或过程中 只能在顺序语句中使用,不能在并行语句中使用 立即赋值,无时间延迟 20

--文件名为:mux LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY mux IS PORT (a, b, c, d, s0, s1: IN STD_LOGIC; y : OUT STD_LOGIC); END mux ; ARCHITECTURE not_ok OF mux IS SIGNAL sel: INTEGER RANGE 0 TO 3; BEGIN PROCESS(a,b,c,d,s0,s1) BEGIN sel<=0; IF (s0='1') THEN sel<=sel+1; END IF; IF (s1='1') THEN sel<=sel+2; END IF; CASE sel IS WHEN 0 => y<=a; WHEN 1 => y<=b; WHEN 2 => y<=c; WHEN 3 => y<=d; END CASE; END PROCESS; END not_ok; --文件名为:mux1 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY mux1 IS PORT (a, b, c, d, s0, s1: IN STD_LOGIC; y : OUT STD_LOGIC); END mux1 ; ARCHITECTURE ok OF mux1 IS BEGIN PROCESS(a,b,c,d,s0,s1) VARIABLE sel: INTEGER RANGE 0 TO 3; BEGIN sel:=0; IF (s0='1') THEN sel:=sel+1; END IF; IF (s1='1') THEN sel:=sel+2; END IF; CASE sel IS WHEN 0 => y<=a; WHEN 1 => y<=b; WHEN 2 => y<=c; WHEN 3 => y<=d; END CASE; END PROCESS; END ok; 在使用信号时普遍出现的问题是忽略了信号值的更新不能立刻生效这一特点。因此,在第一种方案中虽然用语句“sel<=0;”对信号sel赋值为“0”,但它不能立刻生效,此时sel的值可能是0~3中的任何一个,所以其后的赋值操作的结果也是不确定的。如果使用变量就不会出现这个问题,因为它的更新是立刻生效的。

21

3.3.2 数据类型

在对VHDL的数据对象进行定义时,都要指定其数据类型。VHDL有多种标准的数据类型,并且允许用户自定义数据类型。

1.VHDL的预定义数据类型

预定义数据类型是在VHDL标准程序包STANDARD中定义的,在应用中自动包含进VHDL的源文件,不需要USE语句显示调用。预定义的数据类型共有10种,如表3-4所示。

表3-4 预定义数据类型

数据类型 整数(INTEGER) 实数(REAL) 位(BIT) 位矢量(BIT_VECTOR) 布尔量(BOOLERN) 字符(CHARACTER) 字符串(STRING) 时间(TIME) 自然数、正整数 错误等级(SEVERITYLEVEL) 1)整数(INTEGER)

整数类型与数学中的整数的定义相同,代表正整数、负整数和零,可以使用预定义的运算操作符,如“+”、“-”、“*”、“/”等运算。在VHDL中,整数的表示范围为-2147483547~+2147483646,即从-(231-1)到(231-1),用32位有符号的二进制数表示。在实际应用中,由于VHDL综合器不能对未限定的整数类型的信号或变量进行综合,因此在使用整数时应该用RANGE字句定义整数的限定范围,然后根据所限定的范围来决定表示此信号或信号的二进制数的位数。例如,可以用下面的语句将信号x1定义为整型,范围从0~20,即5位二进制数表示。

含 义 代表32位的正整数、负整数和零,使用时用RANGE字句限定范围 类似数学中的实数,书写时一定要有小数点 用字符‘1’和‘0’来表示 双引号括起来的一组位数据 只有“真”和“假”2个状态,可以进行关系运算 字符通常用单引号括起来,对大小写敏感 双引号括起来的一串字符 时间类型包括整数和物理量单位两部分,中间至少留一个空格 两类数据是整数的子类 用来表征系统的状态,它共有4种:NOTE(注意)、WARNING(警告)、ERROR(出错)、FAILURE(失败)。 SIGNAL x1: INTEGER RANGE 20 DOWNTO 0; 2)实数(REAL)

实数类型类似于数学上的实数,或称浮点数。实数的取值范围为:-1.0E38~1.0E38,

22

书写时一定要有小数点。通常情况下,VHDL综合器不支持实数类型,VHDL仿真器支持该类型。

例如: “1”表示整数,而“1.0”表示实数。 3)位(BIT)

在简单数字系统中,信号值可以用BIT数据类型来表示。位的表示方法是,用字符‘1’和‘0’来表示(将值放在单引号中)。与整数中的1和0不同,位值‘1’和‘0’分别表示数字电路中的“高”、“低”两种逻辑电平。BIT 类型可以进行逻辑运算,进行利用半加器做加法运算。

4)位矢量(BIT_VECTOR)

位矢量(BIT_VECTOR)是BIT类型的扩展, 可用于描述数字系统中的总线,它定义为多个BIT 的组合。使用位矢量(BIT_VECTOR)类型时,要利用指明位矢量的维数(总线宽度)和区间,还要指明电路中BIG _ENDIAN还是LITTLE_ENDIAN。另外,VHDL认为只要维数相同的BIT_VECTOR类型都属于同一类型(参见子类的概念)。

例如:

BIT_VECTOR(15 DOWNTO 8)表示该类型的总线宽度为8位,总线区间是15到8,BIG _ENDIAN总线(即15位表示最高位,8位表示低位);

BIT_VECTOR(23 DOWNTO 0)也有类似的含义;

BIT_VECTOR(0 TO 7)表示该类型的总线宽度为8位,总线区间是0到7,LITTLE _ENDIAN总线(即0位表示最高位,7位表示低位)。

BIG _ENDIAN、LITTLE_ENDIAN分别是数字电路系统中的表示总线的两种格式,只用于指明总线中哪些位是最高位和最低位。在VHDL中用DOWNTO和TO两个关键词指明该总线是BIG _ENDIAN还是LITTLE_ENDIAN。需要说明的是:使用BIG _ENDIAN还是LITTLE_ENDIAN总线,只是系统设计者的个人习惯,二者没有本质的区别,例如在80X86系统中使用BIG _ENDIAN,别的计算机系统中也有使用LITTLE_ENDIAN的。我们建议在电路系统设计中,最好使用统一的总线格式,不鼓励在系统设计中既有BIG _ENDIAN格式又有LITTLE_ENDIAN格式。

位矢量类型的常数是用双引号括起来的一组位数据,如“010101”。用位矢量数据表示总线状态最形象也最方便,在以后的VHDL程序中将会经常遇到。

23

5)布尔量(BOOLEAN)

一个布尔量只有“真”和“假”2个状态,可以进行关系运算,一般用于条件语句。布尔量也是二值枚举量,但是它和BIT不同,没有数值的含义,也不能进行算术运算。 6)字符(CHARACTER)

字符通常用单引号括起来,对大小写敏感,如’a’和‘A’是两个不同的字符。字符可以是a~z,A~Z,0~9中的任一个以及空白或者特殊字符,如$、@、%等。

7)字符串(STRING)

字符串是双引号括起来的一串字符,如“laksdklakld”,也称字符矢量或字符串数组。它常用于程序的提示和说明。

8)时间(TIME)

时间是VHDL中唯一预定义的物理量。完整的时间类型包括整数和物理量单位两部分,整数和单位之间至少留一个空格,如55 ms,20 ns。 时间的单位由fs、ps、ns、μs、ms、sec、min、hr。在系统仿真时,时间数据特别有用,用它可以表示信号延时,从而使模型系统能更逼近实际系统的运行环境。

9)正整数(POSITIVE)和自然数(NATURAL)

这两类数据是整数的子类,自然数只能取0和0以上的整数;正整数则只能为大于0整数。

10)错误等级(SEVERITY LEVEL)

错误等级类型数据用来表征系统的状态,它共有4种:NOTE(注意)、WARNING(警告)、ERROR(出错)、FAILURE(失败)。在系统仿真过程中可以用这4种状态来表示系统当前的工作情况。这样可以使操作人员随时了解当前系统工作的情况,并根据系统的不同状态采取相应的对策。

2. 其他预定义的标准数据类型

上面介绍了STD库中的预定义数据类型,这些数据类型可以直接使用而无需事先声明。除此之外,还有一些应用十分广泛的数据类型,它们是IEEE库STD_LOGIC_1164程序包中定义的标准逻辑位 (STD_LOGIC)数据类型和标准逻辑矢量(STD_LOGIC_VECTOR)数据类型,程序包STD_LOGIC_ARITH中定义的无符号数据类型(UNSIGNED ),有符号数据类型(SIGNED)这四种数据类型。这些数据类型在使用前必须在程序开始处用库调用语句和

24

程序包调用语句加以说明。

1) 标准逻辑位(STD_LOGIC)数据类型

前面介绍的BIT数据类型,只能取‘1’和‘0’两个值,没有不定状态和高阻态,无法用来描述和仿真诸如双向总线、三态门之类的电路逻辑特性。为此引入一个新的数据类型——STD_LOGIC。STD_LOGIC类型是BIT类型的扩充,它可以用来描述更复杂的数字逻辑系统。

在IEEE库程序包 STD_LOGIC_1164中STD_LOGIC的定义如下:

TYPE STD_LOGIC IS ('U','X','0','1','Z','W','L','H','-');

'U' --Uninitialized(未初始化的) 'X' -- Forcing Unknown(强未知的) '0' -- Forcing 0 ( 强0) '1' -- Forcing 1 (强1) 'Z' -- High Impedance (高阻态) 'W' -- Weak Unknown (弱未知的) 'L' -- Weak 0 (弱0) 'H' -- Weak 1(弱1) '-' -- Don't care(可忽略的)

由定义可以看出,STD_LOGIC是标准的BIT数据类型的扩展,共定义了9种值。在仿真过程中,STD_LOGIC可以使设计者精确的模拟一些未知的和高阻状态的线路情况。这九个值在仿真的时候均有意义,可见,用标准逻辑位数据类型来定义信号,远比用BIT位来定义信号所描述的逻辑电路的逻辑特性更完整、更真实。所以,在VHDL程序中,对于逻辑信号的定义,通常采用标准逻辑位来定义。

2) 标准逻辑矢量(STD_LOGIC_VECTOR)数据类型

标准逻辑矢量STD_LOGIC_VECTOR是定义在STD_LOGIC_1164程序包中的标准一维数组,数组中每个元素的数据类型都是以上定义的标准逻辑位STD_LOGIC。

在IEEE库程序包 STD_LOGIC_1164中STD_LOGIC_VECTOR的定义如下: TYPE STD_LOGIC_VECTOR IS ARRAY (NATURAL RANGE<>) OF STD_LOGIC; 在使用中,标准逻辑矢量STD_LOGIC_VECTOR数据类型的数据对象赋值的方式与普

25

对于以上的例子,明显存在着判断错误,但是为什么会出现上面的错误呢?这是因为在VHDL语言中,利用关系运算符对位矢量进行关系运算时,比较过程是从最左边的位开始,从左向右依次进行比较。为了解决这个问题,在STD_LOGIC_ARITH程序包中定义的UNSIGNED数据类型来解决,可将这些进行比较的数据的数据类型定义为UNSIGNED即可。如果上面例子中的a、 b定义为UNSIGNED数据类型,则上述比较的结果就为“FALSE”。

在使用关系运算时,虽然没有严格限制,但关系运算一般用于条件语句;另外,为了避免不必要的错误,参与关系运算的数据应该具有相同的类型。

3.算术运算符

算术运算符用来执行算术运算的操作。在VHDL 语言中有,它们的功能和适用的数据类型列在表3-6中,从表3-6中我们可以看出:+、-、**、ABS适用于整数数据类型,而其余4种适用的数据类型为实数和整数类型,具体使用的时候还有一些限制在表中。这6种算术运算符优先权,具体可见表3-7所示。实际上能够真正综合的只有”+、-、*”这三种算术运算符,其余的是不可综合的。另外,在使用乘法运算符时,应该特别慎重,因为它可以使逻辑门数大大增加。

4.移位运算符

移位运算符用来对数据进行移位操作的,它们是在VHDL 93中引入的。 移位运算符的语法结构为:

操作数 移位运算符 移动的位数 其中,操作数必须是BIT_VECTOR类型的,而整数表示可以对操作数进行移动几位的操作。

SLL :逻辑左移 --数据左移,右端空出来的位置填充‘0’ SRL :逻辑右移 --数据右移,左端空出来的位置填充‘0’

SLA:算术左移 --数据左移,同时复制最右端的位,在数据左移操作后填充在右端空

出的位置上

SRA:算术右移 --数据右移,同时复制最左端的位,在数据左移操作后填充在左端空

出的位置上

ROL:逻辑循环左移 --数据左移,同时从左端移出的位依次填充到右端空出的位置上 ROR:逻辑循环右移 --数据右移,同时从右端移出的位依次填充到左端空出的位置上 例如:令X<=“01001”,那么:

36

y<= x SLL 2; --逻辑左移两位:y<=“00100” y<= x SRL 3; --逻辑右移三位:y<=“00001” y<= x SLA 2; --算术左移两位:y<=“00111” y<= x SRA 2; --算术右移两位:y<=“00010” y<= x ROL 2; --逻辑循环左移两位:y<=“00101”

y<= x ROR 2; --逻辑循环右移两位:y<=“01010” 逻辑移位操作含义可参考微机原理中逻辑移位类对应指令。 5.并置运算符

并置运算符用于位和位矢量的连接运算。所谓位和位矢量的连接运算就是将并置运算符右边的内容接在左边的内容之后,以形成一个新的位矢量。 例如:SIGNAL A, D: BIT_VECTOR(3 DOWNTO 0);

SIGNAL B, C: BIT_VECTOR(1 DOWNTO 0); SIGNAL E: BIT_VECTOR(2 DOWNTO 0); SIGNAL F, H, I: BIT;

A <= NOT B & NOT C; -- ARRAY & ARRAY D <= NOT E & NOT F; -- ARRAY & ELEMENT

6.符号运算符

符号运算符“+”和“-”的操作数只有一个,操作数的数据类型是整数类型,操作运算符“+”对操作数不作任何改变,操作运算符“-”作用于操作数后的返回值是原操作数取负,在实际使用中,取负操作数需加括号。

例如: z:=x*(-y);

表3-6 常用的基本运算符

类型 逻辑 运算符 运算符 AND OR NAND NOR XOR XNOR NOT 功能 与 或 与非 或非 异或 同或 非 数据对象的类型 BIT 、BOOLEAN、STD_LOGIC BIT 、BOOLEAN、STD_LOGIC BIT 、BOOLEAN、STD_LOGIC BIT 、BOOLEAN、STD_LOGIC BIT 、BOOLEAN、STD_LOGIC BIT 、BOOLEAN、STD_LOGIC BIT 、BOOLEAN、STD_LOGIC 37

关系 运算符 = /= < > <= >= 等于 不等于 小于 大于 小于等于 大于等于 加 减 乘 除 取模 取余 乘方 取绝对值 逻辑左移 逻辑右移 算术左移 算术右移 逻辑循环左移 逻辑循环右移 并置 并置 正 负 任何数据类型 任何数据类型 枚举和整数类型,及对应的一维数组 枚举和整数类型,及对应的一维数组 枚举和整数类型,及对应的一维数组 枚举和整数类型,及对应的一维数组 整数类型 整数类型 整数和实数类型,右边的操作数必须是2的乘方 整数和实数类型,右边的操作数必须是2的乘方 整数类型,,操作数必须是2的乘方 整数类型,操作数必须是2的乘方 整数类型 整数类型 BIT或布尔型一维数组 BIT或布尔型一维数组 BIT或布尔型一维数组 BIT或布尔型一维数组 BIT或布尔型一维数组 BIT或布尔型一维数组 BIT或一维数组 BIT或一维数组 整数类型 整数类型 算术 运算符 + - * / MOD REM ** ABS 移位 运算符 SLL SRL SLA SRA ROL ROR 并置 运算符 符号 运算符

& (, , , ) + _ 表3-7 基本运算符的优先级

运算符 NOT ABS ** * / MOD REM 优先级 最高优先级 38

+ (正号) -(负号) + - & SLL SLA SRA SRL ROL ROR = =/ < <= > >= AND OR NAND NOR XOR XNOR 最低优先级

3.3.4 属性

属性是指实体、结构体、类型及信号的一些表现特征。在VHDL中利用属性(ATTRIBUTE)语句可以从指定的客体或对象中获得关心的数据或信息,因此可以使VHDL 代码更加简明扼要,便于理解和掌握。

目前,VHDL提供了大量的预定义属性,所谓预定义属性就是设计人员可以直接使用这些属性而不需对它们进行定义。在编写VHDL程序的过程中,如果设计人员需要进行检查时钟信号的触发沿、获得信号的历史信息和返回数据的区间范围等操作,那么就需要采用VHDL提供的预定义属性来解决。在VHDL中,为设计人员提供了5类预定义属性,它们分别是值类属性、函数类属性、信号类属性、数据类型类属性和数据范围类属性。值类属性用来返回一个简单值;函数类属性用来返回一个值执行函数请求;信号类属性用来从另一个信号得出值建立一个新信号的值;数据类型类属性用来返回一个类型标识;数据范围类属性用来返回一个范围的值。

我们将重点讨论值类属性、信号类属性、数据范围类属性,目的是使读者掌VHDL提供的预定义的属性并应用于程序设计中。

1.值类属性

值类属性用于返回有关数组类型、块和常用数据类型的特定值,值类属性还用于返回数组的长度或者类型的最低边界。根据属性作用对象的不同,值类属性分成三个子类:一般数据的值类属性,用来返回一个数据类型的边界值;数组的值类属,用来返回数组长度;块的值类属性,用来返回块的信息。这里的一般数据指的是整型、枚举类型、实数和数组这几种类型及其子类型。常用的值类属性如表3-8所示,表中所列的值类属性都是可综合的,但是不支持枚举类型的值类属性。

表3-8 常用的值类属性

39

属性类型 一般数据 的值类属性 属性 类型`LEFT 类型`RIGHT 类型`HIGH 类型`LOW 功能 返回一个数据类型的左边界值 返回一个数据类型的右边界值 返回一个数据类型的上限值 返回一个数据类型的下限值 返回数组中元素的个数 对于不含有任何元件例化语句的块或结构体,返回为TRUE;否则为FALSE 对于只含有任何元件例化语句的块或结构体,返回为TRUE;否则为FALSE 数组的值类属性 类型`LENGTH 块的值类属性 块(或结构体)`BEHAVIOR 块(或结构体)`STRUCTURE 例如:

SIGNAL A: STD_LOGIC_VECTOR(7 DOWNTO 0); --数组类型 A'LEFT = 7 --数组类型的最左边界值 A'RIGHT = 0 --数组类型的最右边界值 A'LOW = 0 --数组类型的下限值 A'HIGH = 7 --数组类型的上限值 A'LENGTH = 8 --数组中元素的个数

TYPE T IS (A, B, C, D, E); --枚举类型 SUBTYPE S IS T RANGE D DOWNTO B; --枚举子类型

S'LEFT = D --枚举子类型的最左边界值 S'RIGHT = B --枚举子类型的最右边界值 S'LOW = B --枚举子类型的下限值 S'HIGH = D --枚举子类型的上限值 2.信号类属性

信号类属性用来返回有关信号行为功能的信息。大部分信号类属性仅用于仿真,但只有两个信号类属性(EVENT和STABLE)是可以综合的,其中EVENT最常用。

表3-9 常用的信号类属性

属性 信号`EVENT 信号`STABLE 信号`ACTIVE 功能 如果信号的值发生了变化,则返回值为布尔量TRUE,否则返回FALSE 如果信号的值没有变化,则返回值为布尔量TRUE,否则返回FALSE 如果信号当前=‘1’,则返回值为布尔量TRUE,否则返回FALSE 40

本文来源:https://www.bwwdw.com/article/44zv.html

Top