第3章 VHDL基本知识 硬件描述语言(Hardware Description Language,HDL)是一种用于数字系统设计的高级语言,具有很强的电路描述和建模能力,大大简化了硬件设计任务,提高设计的效率和可靠性。以HDL语言设计,以CPLD/FPGA为硬件实现载体,EDA软件为开发环境的现代数字系统设计方法已经被广泛采用。 本章将介绍常用硬件描述语言VHDL的基本知识,包括EDA、VHDL简介,基于VHDL的数字系统设计流程;VHDL语言的基本结构,数据对象、数据类型、运算符和表达式;顺序语句,并行语句;VHDL库和程序包等。
3.1 硬件描述语言VHDL介绍 3.1.1 EDA技术及发展 EDA是电子设计自动化(Electronic Design Automation)的缩写,是20世纪90年代初从计算机辅助设计(CAD)、计算机辅助制造(CAM)、计算机辅助测试(CAT)和计算机辅助工程(CAE)等概念基础上发展而来的新兴电子设计技术。 EDA技术以大规模可编程逻辑器件为设计的载体,依赖功能强大的计算机,在EDA开发软件平台上用软件的方法设计电子系统。采用硬件描述语言描述系统逻辑,生成系统设计文件,软件自动完成逻辑编译、逻辑化简、逻辑分割、逻辑综合及优化、布局布线、逻辑仿真测试,直至实现电子系统的功能。再针对指定的目标芯片适配编译、逻辑映射、编程下载等,最终完成对电子系统硬件功能的实现。 伴随着大规模集成电路制造技术、可编程逻辑器件、计算机辅助工程,以及电子系统设计技术的发展,EDA技术的发展经过了三个主要阶段:
3.1 硬件描述语言VHDL介绍 计算机辅助设计阶段(CAD) 20世纪70年代以后,利用计算机的图形编辑、分析和存储能力,辅助设计工程师进行IC版图设计、PCB布局布线等工作,取代了人工劳动。CAD设计技术初见雏形。 计算机辅助工程阶段(CAE) 20世纪80年代出现的EDA工具除了具备图形绘制功能以外,还增加了结构设计和电路设计功能,代替了部分设计师的工作。在逻辑设计、逻辑仿真分析、布尔方程综合优化、自动布局布线等方面承担了重要的工作。
3.1 硬件描述语言VHDL介绍 电子系统设计自动化阶段(EDA) 进入20世纪90年代,出现了以高级语言描述、系统级仿真和综合技术为特征的新一代EDA工具。设计工程师采用结构化、自顶向下的设计方法,先对整个电子系统进行系统级设计和功能模块划分,采用硬件描述语言HDL对各个功能模块描述,再用EDA工具对设计进行行为描述和结构综合,系统仿真和测试验证,自动布局布线,最后编程下载到CPLD/FPGA中。采用这种设计方法后,大大提高了复杂电子系统设计能力,提高了设计效率,缩短了设计周期。
3.1 硬件描述语言VHDL介绍 3.1.2 VHDL语言简介 硬件描述语言(HDL: Hardware Description Language)是一种用形式化方法来描述数字电路和设计数字逻辑系统的语言,是EDA技术的重要组成部分。 20世纪70年代末和80年代初,面对各个电子系统承包商技术线路不一致,使得产品不兼容,采用各自的设计语言,信息交换和维护困难,设计不能重复利用等情况,由美国国防部牵头,来自IBM、Texas Instruments和Intermetrics公司的专家组成VHDL(Very High Speed Integrated Circuit HDL)工作组,提出了新的硬件描述语言版本和开发环境。IEEE标准化组织进一步发展,经过反复的修改与扩充,在1987年宣布了VHDL语言标准版本,即IEEE STD 1076-1987标准。1993年,VHDL-87标准被重新修订,更新为IEEE STD 1076-1993标准。现在公布的最新版本是IEEE STD 1076-2002。
3.1 硬件描述语言VHDL介绍 1995年,我国国家技术监督局制定的《CAD通用技术规范》推荐VHDL作为我国电子设计自动化硬件描述语言国家标准。从此,VHDL语言在我国迅速普及,成为广大硬件工程师必须掌握的一项技术。 VHDL语言能够成为标准化的硬件描述语言并获得广泛应用,是因为有其它硬件描述语言不具备的有点: 较强的系统级和电路描述能力。 与具体器件无关,可移植性强。 基于库的设计方式,便于复用。 语法规范、易于共享。
3.1 硬件描述语言VHDL介绍 3.1.3 VHDL语言设计开发流程 以CPLD/FPGA为硬件载体,采用VHDL语言的EDA软件进行数字系统设计的的完整流程包括设计方案制定、设计输入、逻辑综合、布局布线、仿真测试、编程下载等。其他硬件描述语言的设计过程也是类似。设计流程图如图3-1所示。 图3-1 设计流程图
3.1 硬件描述语言VHDL介绍 1. 设计方案制定 采用自顶向下、模块化设计的设计方式,确定整个系统的设计方案,划分系统的各个逻辑模块,确定各个模块的功能,以及采用的设计方式。 2. 设计输入 利用EDA软件中的文本编辑器将系统功能或结构用VHDL语言描述出来,保存问VHDL文件格式,为后面的综合优化做准备。
3.1 硬件描述语言VHDL介绍 现代大多数EDA软件除了可以使用HDL语言设计输入以外,通常还支持类似传统电子系统设计的原理图输入方式。原理图输入方式中使用的逻辑模块或符号,可以使用EDA软件库中预制的功能模块,也可以使用VHDL语言设计的模块或原件。实际上,图形输入方式除了原理图输入外还有状态图输入和波形输入等常用方式。 采用模块化设计方式,完成各个功能模块设计后,将各个模块组合在一起,即完成对整个系统的设计。
3.1 硬件描述语言VHDL介绍 3. 逻辑综合 所谓综合就是将较高层次的抽象描述转化为低层次描述的过程,是将软件设计转化为硬件电路的关键步骤。在完成设计输入后,根据硬件结构和约束条件进行编译、优化、综合,最终得到门级甚至更低层次的电路描述网表文件。网表文件就将软件描述和给定的硬件结构形成对应逻辑连接关系。
3.1 硬件描述语言VHDL介绍 4. 布局布线(适配) 布局是指将网表文件中的逻辑连接关系合理地配置到目标器件内部的硬件结构上,通常需要在速度优先还是面积最优间选择。布线就是根据布局的拓扑结构,利用目标器件内部资源,合理地连接各个单元。适配后产生的仿真文件可用于精确的时序仿真,同时生成用于编程下载的文件。
3.1 硬件描述语言VHDL介绍 5. 仿真测试 仿真是EDA设计过程中的重要步骤,通常EDA软件中会提供仿真工具,也可以使用第三方的专业仿真工具。根据不同的实施阶段,分为功能仿真和时序仿真: 功能仿真:在采用不同方式完成设计输入后,即可进行逻辑功能的仿真测试,以了解功能是否满足设计要求。这个阶段的仿真测试不涉及具体的硬件结构、特性。 时序仿真:又称后仿真,是最接近硬件真实运行的仿真。利用布局布线后生成的包含硬件特性参数的仿真文件,对系统和各个模块进行时序仿真,分析其时序关系和延迟信息。
3.1 硬件描述语言VHDL介绍 6. 编程下载 将适配后生成的下载或配置文件,通过编程器或下载线缆下载到目标器件中。一般将对CPLD的下载称为编程,对FPGA的下载称为配置。最后将整个系统进行统一的测试,验证设计在目标系统上的实际工作情况。
3.2 VHDL程序的基本结构 VHDL程序是由库(1ibrary)、程序包(package)、实体(entity declaration)、结构体(architecture body)、配置(configuration)五部分组成。设计实体结构结构图如图3-2所示,其中设计实体必须有实体和结构体,其它部分根据设计需要来添加。 图3-2 设计实体结构图
3.2 VHDL程序的基本结构 实体是VHDL程序的基本单元,类似原理图设计中的而一个元件符号。其中实体说明部分规定了其与外界通信的引脚或接口信号。在实体内部有一个或多个结构体,用来描述设计的逻辑结构或功能。
3.2 VHDL程序的基本结构 【例3-1】 VHDL程序基本结构 LIBRARY IEEE; ——库说明部分 USE IEEE.STD_LOGIC_1164.ALL; ——程序包说明部分 ENTITY nand2 IS ——实体说明部分 PORT ( a,b:IN STD_LOGIC; y:OUT STD_LOGIC); END nand2;
3.2 VHDL程序的基本结构 ARCHITECTURE arch_name OF nand2 IS ——结构体描述部分 BEGIN PROCESS (a , b) VARIABLE comb : STD_LOGIC_VECTOR ( 1 DOWNTO 0 ) ; Comb : = a & b ; CASE comb IS WHEN "00" => y <= '1' ; WHEN "01" => y <= '1' ; WHEN "10" => y <= '1' ; WHEN "11" => y <= '0' ; WHEN OTHERS => y <= '0' ; END CASE ; END PROCESS ; END arch_name ;
3.2 VHDL程序的基本结构 3.2.1 实体说明 实体说明部分的一般结构: ENTITY 实体名 IS [GENERIC (类属表);] 3.2.1 实体说明 实体说明部分的一般结构: ENTITY 实体名 IS [GENERIC (类属表);] [PORT (端口表);] END [ENTITY] 实体名;
3.2 VHDL程序的基本结构 1. 实体名 实体说明部分以“ENTITY 实体名 IS”开始,以“END [ENTITY] 实体名”结束。其中实体名由设计者自定义,一般根据所设计实体的功能来取名,“ENTITY”是VHDL语法规定中的保留关键字。大多数EDA软件中的编译器和适配器是不区分VHDL语言大小写的,但为了保持良好的设计风格和便于阅读,通常将VHDL语言的标识符和保留关键字以大写表示,设计者自定义符号小写表示,如实体名、结构体名、变量名等。
3.2 VHDL程序的基本结构 2. 类属说明 类属参数用来在不同层次的设计模块间传递信息和参数,比如数组长度、位矢量长度、端口宽度、器件延时时间等。这些参数都要求是整数类型。 类属说明的一般格式如下: GENERIC (参数1:参数类型 [ : = 静态表达式]; 参数2:参数类型 [ : = 静态表达式]; …… 参数n:参数类型 [: = 静态表达式]);
3.2 VHDL程序的基本结构 3. 端口说明 端口说明是对设计实体和外部接口的描述,是设计实体和外部通信的通道,对应电路图上的引脚。一个端口就是一个数据对象,包括端口名、数据类型、通信模式。端口说明的一般格式如下: PORT (端口名1 :通信模式 数据类型; 端口名2 :通信模式 数据类型; …… 端口名n :通信模式 数据类型; );
3.2 VHDL程序的基本结构 通信模式说明数据、信号通过端口的流动方向,主要有4种: IN:定义端口为单向只读模式。数据或信号从外部流向实体内部,或者从该端口读取外部数据。 OUT:定义端口为单向输出模式。数据或信号只能从该端口流出,或者向该端口赋值。
3.2 VHDL程序的基本结构 BUFFER:定义端口为缓冲模式。该模式和输出模式类似,区别在于缓冲模式允许实体内部应用该端口信号即允许内部反馈,输出模式则不能用于内部反馈。缓冲模式的端口只能连接设计实体内部信号源,或者是其它实体的缓冲模式端口。 INOUT:定义端口为输入输出双向模式。在某些设计实体中,例如双向总线、RAM数据口、单片机的I/O口等,数据是双向的,既可以流入实体内部,也可以从实体流出,这是需设计为双向模式。实体内部的信号和外部输入实体的信号都可以经过双向模式端口,也允许引入内部反馈,所以双向模式是一个完备的端口模式。
3.2 VHDL程序的基本结构 3.2.2 结构体描述 结构体具体描述了设计实体行为,定义了实体的逻辑功能或内部电路结构关系,规定了该实体的数据流程,建立了实体输出与输入之间的关系。 结构体的一般格式如下: ARCHITECTURE 结构体名 OF 实体名 IS [定义语句]内部信号,常数,数据类型,函数定义; ——说明语句 BEGIN [进程语句]; ——功能描述语句 [并行处理语句]; END [ARCHITECTURE] [结构体名];
3.2 VHDL程序的基本结构 “说明语句”用来说明和定义结构体内部使用的信号、常数、数据类型、函数、过程、元件调用声明等,这是结构体中必需的。 “功能描述语句”描述结构体的行为、功能、电路连接关系等,可以是并行语句、顺序语句或者它们的混合。其中并行语句是结构体描述的主要语句,并行语句间是并行的,没有顺序关系。进程语句是典型的并行语句,进程间是并行的,但进程内部的语句是有顺序的。
3.2 VHDL程序的基本结构 结构体功能可以用三种方式进行描述,即行为描述法、数据流描述法、结构描述法: 1. 行为描述法 行为描述表示输入与输出间转换的关系,是对设计实体按算法的路径来描述。采用进程语句,顺序描述设计实体的行为。这种描述方式通常是对整体设计功能的定义,不是对单一器件进行描述,是一种高层次的描述方法。
3.2 VHDL程序的基本结构 图3-3 半加器及其逻辑电路
3.2 VHDL程序的基本结构 ARCHITECTURE alg_ha OF half_adder IS BEGIN PROCESS(a,b) IF a = '0 ' AND b = '0 ' THEN c <= '0'; s <= '0'; ELSIF a = '1' AND b= '1' THEN c <= '1'; ELSE s <= '1'; END IF; END PROCESS END alg_ha;
3.2 VHDL程序的基本结构 2. 数据流描述法 数据流描述法描述了数据流程的运动路径、运动方向和运动结果,采用进程语句顺序描述数据流在控制流作用下被加工、处理、存储的全过程。 由半加器的真值表可推导信号间逻辑关系,用逻辑表达式描述如下: s = a ⊕ b c = a · b 基于上述逻辑表达式的数据流描述为:
3.2 VHDL程序的基本结构 ARCHITECTURE dataflow_ha OF half_adder IS BEGIN s <= a XOR b; c <=n LDd a AND b; END dataflow_ha; 可见,结构体内的两条信号赋值语句之间是并行关系,每一赋值语句均相当于一个省略了“说明”的进程,描述了信号从输入到输出的路径。而行为描述中进程内的信号赋值语句是顺序语句。
3.2 VHDL程序的基本结构 3. 结构化描述法 结构化描述给出了实体内部结构组织,所包含的模块或元件及其互连关系。 结构化描述通常用于层次化结构设计。对于一个复杂的电子系统,将其分解成许多子系统,子系统再分解成各个功能模块。多层次设计的每个层次都可以作为一个元件,再构成一个模块或构成一个系统,每个元件分别仿真,然后再整体调试。 图3-3(a)所示的半加器可以用图3-3(b)所示的逻辑电路加以实现。对该电路结构采用结构化描述法的程序如下:
3.2 VHDL程序的基本结构 ARCHITECTURE struct_ha OF half_adder IS COMPONENT and_gate PORT (a1,a2:IN BIT; a3:OUT BIT ); END COMPONENT; COMPONENT xor_gate PORT (x1,x2:IN BIT; x3:OUT BIT ); BEGIN gl:and_gate PORT MAP (a,b,c ); g2:xor_gate PORT MAP (a,b,s ); END struct_ha;
3.3 数据对象、数据类型、运算符和表达式 在VHDL语言中可以赋值的客体叫做数据对象。每一种数据对象代表的物理含义和使用规则,允许赋值的数据类型,可以参与的运算等都有严格的规定。 3.3.1 数据对象 VHDL语言的基本数据对象有3种:常量、变量和信号。变量、常量和其它高级语言中相应类型类似,信号则是硬件描述语言中特有的,它带有硬件特征。从硬件电路的角度来看,信号和变量相当于电路之间的连线或连线上的信号值,常量则相当于电源(VCC)、地(GND)等。
3.3 数据对象、数据类型、运算符和表达式 1. 常量(Constant) 3.3 数据对象、数据类型、运算符和表达式 1. 常量(Constant) 常量是设计者在实体中给某一常量名定义数据类型和赋值,在程序中试图多次给常量赋值是错误的。常量定义的一般格式如下: CONSTANT 常量名 :数据类型 := 表达式 ; 其中表达式的数据类型必须和定义的常量数据类型一致。 常量定义一般包含在实体、结构体、程序包、进程、函数、过程等设计单元中。 例如: CONSTANT VCC :REAL : = 3.3 ;
3.3 数据对象、数据类型、运算符和表达式 该例子中,常量VCC被赋值为实型类型数据,在程序中该常量的值将不能再改变,并保持到程序结束。 3.3 数据对象、数据类型、运算符和表达式 该例子中,常量VCC被赋值为实型类型数据,在程序中该常量的值将不能再改变,并保持到程序结束。 CONSTANT ABUS :BIT_VECTOR : = "11000101" ; 常量ABUS的数据类型是BIT_VECTOR,被赋初值为"11000101",在程序中被做为某一器件的固定地址。
3.3 数据对象、数据类型、运算符和表达式 2. 变量(Variable) 3.3 数据对象、数据类型、运算符和表达式 2. 变量(Variable) 变量是个局部量,做为一个临时的数据存储单元,只能在进程、函数、过程等结构中使用,不能将信息带出它定义所在的当前结构。变量赋值是立即生效的,不存在延时。变量定义的一般格式如下: VARIABLE 变量名 :数据类型 : = 表达式; 其中表达式的数据类型必须和定义的变量数据类型一致。 例如: VARIABLE a :STD_LOGIC : = ' 1 ' ; ——定义标准逻辑位类型变量a,初始值为' 1 ' VARIABLE count:INTEGER RANGE 0 TO 255 ; ——定义整数类型变量count,取值范围为0到255
3.3 数据对象、数据类型、运算符和表达式 在变量定义语句中可以给出和变量相同数据类型的初始值,但这不是必须的。由于硬件电路上电后的随机性,很多综合器并不支持初始值设定,这样可以在程序中通过赋值语句来赋予变量一个值。变量赋值的方式如下: 变量名 : = 表达式; 变量在赋值时不能产生附加延时。例如,tmp1、tmp2是变量,那么下式产生延时的方式是不合法的: Tmp1 : = tmp2 AFTER 10 ns;
3.3 数据对象、数据类型、运算符和表达式 3. 信号(Signal) 3.3 数据对象、数据类型、运算符和表达式 3. 信号(Signal) 信号硬件系统描述中的基本数据类型,类似电路内部的连接线,实现实体和实体间、元件和元件间的连接。信号具有全局性特征,不但可以在一个设计实体内部各个单元间传递数据,还可以做为实体中并行语句模块间的信息通道,不需注明信息的流动方向。信号通常在实体、结构体、包集合中定义说明。注意不允许在进程和过程的顺序语句中定义信号。信号定义的格式如下: SIGNAL 信号名 :数据类型 : = 表达式; 例如: SIGNAL bus_enable :BIT : = ' 1 ' ; ——定义BIT类型信号,初始值为' 1 ' SIGNAL data_bus :STD_LOGIC_VECTOR [ 7 DOWNTO 0 ] ; ——定义8位宽度的数据总线
3.3 数据对象、数据类型、运算符和表达式 在给出信号的完整定义后,就可对信号赋值。信号赋值语句如下: 3.3 数据对象、数据类型、运算符和表达式 在给出信号的完整定义后,就可对信号赋值。信号赋值语句如下: 信号名 <= 表达式 AFTER 时间量; AFTER 时间量,表示数据信号的传入需延时给定的时间量,这与实际器件的硬件特征是吻合的。
3.3 数据对象、数据类型、运算符和表达式 3.3.2 数据类型 3.3 数据对象、数据类型、运算符和表达式 3.3.2 数据类型 VHDL语言对参与运算的各个量的数据类型有严格要求,相同类型的量之间才能互相传递。VHDL语言要求设计实体中的常量、变量、信号都要指定数据类型,而且数据类型相同,而位长不同时也不能直接代入。这样就使得VHDL编译或综合工具能很容易地找出设计中的错误。
3.3 数据对象、数据类型、运算符和表达式 1. 标准定义数据类型 VHDL语言的标准数据类型共有10种,如表3-1所示。
3.3 数据对象、数据类型、运算符和表达式 VARIABLE a :INTEGER RANGER -128 TO 128 ; 3.3 数据对象、数据类型、运算符和表达式 整数(INTEGER) 在VHDL语言中,整数与数学中的整数的定义相同。整数的表示范围为-2 147 483 647~2 147 483 647(-(231-1)~(231-1)),即32位有符号二进制整数。整数的例子如:+12456,+13,-457,… 尽管整数值是用一系列二进制位值来表示的,但是整数不能看作是位矢量,也不能按位来进行操作,对整数不能用逻辑操作符。当需要进行位操作时,可以用转换函数,将整数转换成位矢量。 在电子系统的开发过程中,整数也可以作为对信号总线状态的一种抽象手段,用来准确地表示总线的某一种状态。 在使用整数时,VHDL综合器要求用RANGER对定义的数限定范围,根据限定的范围决定此变量或信号的二进制位数。例如: VARIABLE a :INTEGER RANGER -128 TO 128 ;
3.3 数据对象、数据类型、运算符和表达式 实数(REAL) VHDL语言的实数类似于数学上的实数,实数值的范围为-1.0E+38~+1.0E+38。实数有正负数,书写时一定要有小数点。例如:-1.0,+2.5,-1.0E38,… 有些数可以用整数表示也可以用实数表示。例如,数字1的整数表示为1,而用实数表示则为1.0。两个数的值是一样的,但数据类型却不一样。 大多数EDA工具只能在仿真器中使用实数类型数据,综合器则不支持实数,这是因为VHDL语言适用于硬件系统设计与开发的语言,实数类型的实现太复杂,电路规模上难以承受。
3.3 数据对象、数据类型、运算符和表达式 实数常量的书写方式举例如下: 2#11001011# 二进制浮点数 1.0 十进制浮点数 3.3 数据对象、数据类型、运算符和表达式 实数常量的书写方式举例如下: 2#11001011# 二进制浮点数 1.0 十进制浮点数 0.0 十进制浮点数 65971.333333 十进制浮点数 65_971.333_3333 与上一行等价 8#43.6#e+4 八进制浮点数 43.6E 4 十进制浮点数
3.3 数据对象、数据类型、运算符和表达式 位(BIT) 在数字系统中,信号值通常用一个位来表示。位值的表示方法是用字符'0'或者'1' (将值放在单引号中)来表示。位与整数中的1和0不同,'1'和'0'仅仅表示一个位的两种取值。 在程序包STANDARD中的定义源代码是: TYPE BIT IS ( '0' , '1' );
3.3 数据对象、数据类型、运算符和表达式 位矢量基于BIT数据类型的数字,在程序包STANDARD中的定义源代码是: 3.3 数据对象、数据类型、运算符和表达式 位矢量(BIT_VECTOR) 位矢量基于BIT数据类型的数字,在程序包STANDARD中的定义源代码是: TYPE BIT_VECTOR IS ARRAY (Natural Ranger <> ) OF BIT; 使用位矢量时必须指明数据宽度,即数组元素个数和排列顺序,赋值是双引号括起来的一组位数据。例如: VARIABLE a :BIT_VECTOR( 7 DOWNTO 0 ) : = "00110011"
3.3 数据对象、数据类型、运算符和表达式 布尔量(BOOLEAN) 布尔量是二值枚举量,具有两种状态:“真”或者“假”。布尔量位不同,没有数值的含义,也不能进行算术运算,只能进行关系运算。例如在IF语句中被测试,测试结果产生一个布尔量值,TRUE或者FALSE。 如果某个信号或者变量被定义为布尔量,那么在仿真中将自动地对其赋值进行核查。一般这布尔量数据的初始值为FALSE。 在程序包STANDARD中的定义源代码是: TYPE BOOLEAN IS (FALSE , TRUE );
3.3 数据对象、数据类型、运算符和表达式 字符(CHARACTER) 字符类型数据通常用单引号括起来,如'A'。VHDL语言对大小写不敏感,但是对字符类型数据中的大、小写是不同的,例如,'A'不同于'a'。 字符类型数据可以是a~z和A~Z中的任一个字母,0~9中的任一个数以及一些特殊字符,如$,@,%等等。在程序包STANDARD中给出了预定义的128个ASCII码字符类型。 注意:字符'1'与整数1和实数1.0都是不相同的。当需要明确指出1是字符类型数据时,则可写为 CHARACTER('1') 。
3.3 数据对象、数据类型、运算符和表达式 字符串(STRING) 字符串是用双引号括起来的一个字符序列,也称字符矢量或字符串数组,。例如:"integer range",字符串一般用于提示和说明。 例如: VARIABLE string_var :STRING( 1 to 7 ); …… string_var : = "a b c d"
3.3 数据对象、数据类型、运算符和表达式 错误等级(SEVERITY LEVEL) 3.3 数据对象、数据类型、运算符和表达式 错误等级(SEVERITY LEVEL) 错误等级类型数据通常用来表征电子系统的状态,分为NOTE(注意)、WARNING(警告)、ERROR(出错)、FAILURE(失败)4个等级。 在系统仿真过程中用这4种状态来表示系统当前的工作情况,使开发者随时了解当前系统工作的情况,以采取相应的对策。 自然数(NATURAL)和正整数(POSITIVE) 这两类数据都是整数的子类,自然数类类数据取值0和0以上的正整数,正整数类型数据为大于0的整数。
3.3 数据对象、数据类型、运算符和表达式 时间(TIME) 3.3 数据对象、数据类型、运算符和表达式 时间(TIME) 时间类型也称为物理类型(PHYSICAL TYPE)。时间类型数据的范围是整数定义的范围,完整的时间量数据包含整数和单位两部分,整数和单位之间至少留一个空格,例如: 16 ns,25 ms,3 sec,162 min … 在程序包STANDARD中给出了时间的类型定义: TYPE TIME IS RANGER -2147483647 TO 2147483647 units fs ; ——飞秒,VHDL语言中的最小时间单位 ps = 1000 fs ; ——皮秒 ns = 1000 ps ; ——纳秒 us = 1000 ns ; ——微妙 ms = 1000 us ; ——毫秒 sec = 1000 ms ; ——秒 min = 60 sec ; ——分 hr = 60 min ; ——时 END units ;
3.3 数据对象、数据类型、运算符和表达式 事件类型一般用于仿真,VHDL综合器不支持时间类型。在系统仿真时,用时间类型数据可以表示信号的延时,从而使模型系统能更接近实际的硬件特性。 2. IEEE标准数据类型 在IEEE库的程序包 STD_LOGIC_1164中定义了两个非常重要的数据类型:标准逻辑位STD_LOGIC和标准逻辑矢量 STD_LOGIC_VECTOR。 在程序中使用这两类数据类型时,必须在程序的开始部分加入下面的语句: LIBRARY IEEE; USE IEEE.STD_LOIGC_1164.ALL;
3.3 数据对象、数据类型、运算符和表达式 标准逻辑位STD_LOGIC 3.3 数据对象、数据类型、运算符和表达式 标准逻辑位STD_LOGIC IEEE的STD_LOGIC标准逻辑位数据类型是设计中常用的数据类型,在STD_LOGIC_1164程序包中定义了该数据类型的9种取值。而传统的BIT类型只有'0'和'1'两种取值,因此较少使用。在IEEE库程序包STD_LOGIC_1164中STD_LOGIC数据类型的定义如下所示: TYPE STD_LOGIC IS ( ' U ' -- 未初始化的 ' X ' -- 强未知的 ' 0 ' -- 强0 ' 1 ' -- 强1 ' Z ' -- 高阻态 ' W ' -- 弱未知的 ' L ' -- 弱0 ' H ' -- 弱1 ' - ' -- 忽略 ) ;
3.3 数据对象、数据类型、运算符和表达式 标准逻辑矢量 STD_LOGIC_VECTOR 3.3 数据对象、数据类型、运算符和表达式 标准逻辑矢量 STD_LOGIC_VECTOR 在IEEE库程序包STD_LOGIC_1164中STD_LOGIC_VECTOR类型的定义如下所 TYPE STD_LOGIC_VECTOR IS ARRAY ( NATURAL RANGE <> ) OF STD_LOGIC; 可见,STD_LOGIC_VECTOR类型是在STD_LOGIC_1164程序包中定义的标准一维数组,数组中的每一个元素的数据类型都是标准逻辑位STD_LOGIC类型。向标准逻辑矢量 STD_LOGIC_VECTOR类型的数据对象赋值的方式与普通的一维数组 ARRAY数据对象赋值的方式是一样的,同位宽、同数据类型的矢量间才能进行赋值。
3.3 数据对象、数据类型、运算符和表达式 3. 用户定义数据类型 3.3 数据对象、数据类型、运算符和表达式 3. 用户定义数据类型 VHDL语言也允许用户根据自己设计的需要自己定义数据类型,用户定义数据类型的一般格式为: TYPE 数据类型名{,数据类型名} 数据类型定义; 用户定义数据类型可以有多种类型,如枚举(Enumerated)类型、整数(Integer)类型、实数(Real)类型、浮点数(Floating)类型、数组(Array)类型、记录(Recode)类型、存取(Access)类型、文件(File)类型等,下面介绍几种常用的用户定义数据类型。
3.3 数据对象、数据类型、运算符和表达式 枚举(Enumerated)类型 枚举类型就是将类型中的所有元素都列出来。枚举类型定义的格式为: 3.3 数据对象、数据类型、运算符和表达式 枚举(Enumerated)类型 枚举类型就是将类型中的所有元素都列出来。枚举类型定义的格式为: TYPE 数据类型名 Is (元素,元素,……); TYPE my_state IS ( state1 state2 state3 state4 state5 ) ;
3.3 数据对象、数据类型、运算符和表达式 但是,在逻辑电路中所有的数据都只能用0和1来表示,所以在综合过程中用符号名表示的元素都将被转化为二进制编码。枚举类型元素的编码是自动的,编码顺序是默认的,一般第一个枚举元素编码为0,以后的依次加1。 综合器在编码过程中自动将每一枚举元素转变成位矢量,位矢的长度将取所需表达的所有枚举元素的最小值。 在前面地举例中,用于表达五个状态的位矢长度应该为3,编码默认值为如下方式: state1 = '000'; state2 = '001'; state3 = '010'; state4 = '011'; state5 = '100';
3.3 数据对象、数据类型、运算符和表达式 整数(Integer)类型、实数(Real)类型 在前面的基本类型中可以看到,整数和实数类型在标准程序包中已经定义过。但在实际应用中,这两种数据类型的取值范围太大,综合其无法进行综合。因此,用户经常根据实际的需要重新定义,限定其取值范围。例如: TYPE my_num IS RANGE -100 TO 100 ; 可见,整数或实数用户定义数据类型的格式如下: TYPE 数据类型名 IS 数据类型定义约束范围;
3.3 数据对象、数据类型、运算符和表达式 数组(Array)类型 3.3 数据对象、数据类型、运算符和表达式 数组(Array)类型 数组类型就是将相同类型的数据集合在一起所形成的新的数据类型。数组可以是一维的,也可以是二维或多维。 数组类型定义的格式如下: TYPE 数据类型名 IS 范围 OF 原数据类型名; “范围”这一项默认是整数,例如: TYPE word IS ARRAY (INTEGER 1 TO 8) OF STD_LOGIC; TYPE d_bus IS ARRAY ( 0 TO 9) OF STD_LOGIC; TYPE instruction IS (ADD,SUB,INC,SRL,SRF,LDA,LDB,XFR); SUBTYPE digit IS INTEGER 0 TO 9; TYPE insflag IS ARRAY (instruction ADD TO SRF) OF digit;
3.3 数据对象、数据类型、运算符和表达式 多维数组需要用两个以上的范围来描述,而且多维数组不能生成逻辑电路,因此只能用于生成仿真图形及硬件的抽象模型。例如: TYPE memarray IS ARRAY (0 TO 5,7 DOWNTO 0) OF STD_LOGIC; CONSTANT romdata :memarray : = (( '0','0','0','0','0','0','0','0' ), ( '0','1','1','0','0','0','0','1' ), ( '0','0','0','0','0','0','0','0' ), ( '1','0','1','0','1','0','1','0' ), ( '1','1','0','1','1','1','1','0' ), ( '1','1','1','1','1','1','1','1' )); SIGNAL data_bit :STD_LOGIC; …… data_bit <= romdata( 3,7);
3.3 数据对象、数据类型、运算符和表达式 在代入初值时,各范围最左边所说明的值为数组的初始位脚标。在上例中(0,7)是起始位,接下去右侧范围向右移一位变为(0,6),以后顺序为(0,5),(0,4)直至(0,0)。然后,左侧范围向右移一位变为(1,7),此后按此规律移动得到最后一位(5,0)。
3.3 数据对象、数据类型、运算符和表达式 记录(Recode)类型 3.3 数据对象、数据类型、运算符和表达式 记录(Recode)类型 由不同类型的数据组织在一起形成的数据类型叫记录类型。记录类型定义的格式如下: TYPE 数据类型名 IS RECODE 元素名:数据类型名; … END RECORD; 从记录类型中提取元素的数据类型时应使用“.”。举例如下:
3.3 数据对象、数据类型、运算符和表达式 TYPE bank IS RECORD 3.3 数据对象、数据类型、运算符和表达式 TYPE bank IS RECORD addr0:STD_LOGIC_VECTOR(7 DOWNTO O); addrl:STD_LOGIC_VECTOR(7 DOWNTO O); r0:INTEGER; inat:instruction; END RECORD; SIGNAL addbusl,addbus2:STD_LOGIC VECTOR(31 DOWNTO 0); SIGNAL result:1NTEGER; SIGNAL alu_code:instruction; SIGNAL r_bank:bank : = ( "00000000","00000000",0,add); addbusl <= r_bank. addrl; r_bank. inst <= alu_code;
3.3 数据对象、数据类型、运算符和表达式 SUBTYPE a_bus IS STD_LOGIC_VECTOR ( 7 DOWNTO 0); 3.3 数据对象、数据类型、运算符和表达式 用户定义的子类型 用户定义的子类型是用户对已定义的数据类型,做一些范围限制而形成的一种新的数据类型。子类型的名称通常采用用户较易理解的名字。子类型定义的格式为: SUBTYPE 子类型名 数据类型名 [范围]; 例如,在“STD_LOGIC_VECTOR”基础上所形成的子类: SUBTYPE a_bus IS STD_LOGIC_VECTOR ( 7 DOWNTO 0); SUBTYPE digit IS INTEGER RANGE 0 TO 9;
3.3 数据对象、数据类型、运算符和表达式 子类型可以对原数据类型指定范围而形成,也可以完全和原数据类型范围一致。例如: 3.3 数据对象、数据类型、运算符和表达式 子类型可以对原数据类型指定范围而形成,也可以完全和原数据类型范围一致。例如: SUBTYPE abus IS STD_LOGIC_VECTOR (7 DOWNTO 0); SIGNAL aio:STD_LOGIC_VECTOR (7 DOWNTO 0); SIGNAL bio:STD_LOGIC_VECTOR (15 DOWNTO O); SIGNAL cio:abvs; aio <= cio;正确操作 bio <= cio;错误操作 新构造的数据类型及子类型通常在包集合中定义,再由USE语句装载到描述语句中。
3.3 数据对象、数据类型、运算符和表达式 3.3.3 VHDL运算符与表达式 3.3 数据对象、数据类型、运算符和表达式 3.3.3 VHDL运算符与表达式 1. 常用运算符 VHDL语言有4种运算符,分别是逻辑运算符、算术运算符、关系运算符和并置运算符。对于VHDL中的操作符与操作数间的运算有两点需要特别注意: 严格遵循在基本操作符间操作数是同数据类型的规则; 严格遵循操作数的数据类型必须与操作符所要求的数据类型完全一致
3.3 数据对象、数据类型、运算符和表达式 逻辑运算符 VHDL中共有7种逻辑运算,它们分别是: NOT —— 逻辑非; 3.3 数据对象、数据类型、运算符和表达式 逻辑运算符 VHDL中共有7种逻辑运算,它们分别是: NOT —— 逻辑非; AND —— 逻辑与; NAND —— 逻辑与非; OR —— 逻辑或; NOR —— 或非; XOR —— 异或; XNOR —— 异或非。 逻辑运算符适用的数据类型是BIT、STD_LOGIC、BOOLEAN、BIT_VECTOR以及STD_LOGIC_VECTOR。如果运算符两边的值的类型是数组,则数组的尺寸,即位宽要相等。
3.3 数据对象、数据类型、运算符和表达式 在7种逻辑运算符中,运算符NOT的优先级最高,而其他6种运算符则具有相同的优先级。在高级编程语言中的逻辑运算符有自左向右或是自右向左的优先级顺序,但是VHDL语言的逻辑运算符则没有左右优先级差别,设计人员经常采用加括号的方法来解决逻辑表达式中没有左右优先级差别的问题。例如: q <= a AND b OR NOT c AND d;
3.3 数据对象、数据类型、运算符和表达式 在编译时将会给出语法出错的信息。对于这种情况,这里可以采用加括号的方法来解决,可以将其修改为: 3.3 数据对象、数据类型、运算符和表达式 在编译时将会给出语法出错的信息。对于这种情况,这里可以采用加括号的方法来解决,可以将其修改为: q <= (a AND b) OR (NOT c AND d); 通常情况下,对于逻辑表达式中只有AND或OR或XOR的情况下可以不加括号,因为对于这三种逻辑运算来说,改变运算顺序并不会改变结果的逻辑。例如: q <= a AND b AND c AND d; q <= a OR b OR c OR d; q <= a XOR b XOR c XOR d;
3.3 数据对象、数据类型、运算符和表达式 算术运算符 3.3 数据对象、数据类型、运算符和表达式 算术运算符 在VHDL语言中,算术运算符主要包括16种,其中只有+(加)、一(减)和*(乘)能够被EDA开发工具综合为对应的逻辑电路,其余算术运算综合成逻辑电路将会很困难,甚至是完全不可能的。VHDL中的16种算术运算符如下所示: + —— 加; - —— 减; * —— 乘; / —— 除; MOD —— 取模; REM —— 取余; ** —— 乘方; ABS —— 取绝对值; + —— 正号; - —— 负号; SLL —— 逻辑左移; SRL —— 逻辑右移; SLA —— 算术左移; SRA —— 算术右移; ROL —— 逻辑循环左移; ROR —— 逻辑循环右移。
3.3 数据对象、数据类型、运算符和表达式 算术运算符的使用规则如下: 3.3 数据对象、数据类型、运算符和表达式 算术运算符的使用规则如下: +(加)、-(减)、+(正号)、-(负号)四种运算符的操作与日常数值运算相同,可以用于整数、实数和物理类型。 *(乘)、/(除)运算符的操作数可以为整数、实数。同时,物理类型可以被整数或实数相乘或相除,结果仍为物理类型;物理类型除以物理类型的结果是一个整数。 MOD(取模)和REM(取余)运算符只能用于整数类型。 ABS(取绝对值)运算符可以用于任何数据类型。 **(乘方)运算符的左操作数可以是整数或是实数,而右操作数必须是整数。只有在左操作数为实数时,其右操作数才可以是负整数。 SLL(逻辑左移)、SRL(逻辑右移)、SLA(算术左移)、SRA(算术右移)、ROL(逻辑循环左移)和ROR(逻辑循环右移)六种算术运算符为二元运算符,它们只能定义在一维数组上,并且其元素必须是bit和boolean型,左操作数必须为这种类型,而右操作数必须是整数类型。
3.3 数据对象、数据类型、运算符和表达式 下面是几个整数REM(取余)和MOD(取模)的小例子。通过这些例子,读者能够掌握REM和MOD运算的规则: 7 REM 2 = 1, 7 REM (-2 ) = 1, -7 REM 2 = -1, -7 REM (2) = -1 7 MOD 2 = 1, 7 MOD (-2) = -1, -7 MOD 2 = 1, -7 MOD (2) = -1
3.3 数据对象、数据类型、运算符和表达式 VHDL语言的关系运算符如下所示: = —— 相等; /= —— 不等于; 3.3 数据对象、数据类型、运算符和表达式 关系运算符 VHDL语言的关系运算符如下所示: = —— 相等; /= —— 不等于; < —— 小于; > —— 大于; <= —— 小于等于; >= —— 大于等于。
3.3 数据对象、数据类型、运算符和表达式 关系运算符有如下规则: 关系运算符两边数据类型必须一致(除=、/=); 3.3 数据对象、数据类型、运算符和表达式 关系运算符有如下规则: 关系运算符两边数据类型必须一致(除=、/=); =(等于)、/=(不等于)适用于所有数据类型对象之间的比较; <=符号有两种含义,赋值或小于等于,要根据上下文判断; >(大于)、<(小于)、>=(大于等于)和<=(小于等于)适用于整数、实数、位矢量及数组之间的比较; 两个矢量比较时,自左向右,按位比较。
3.3 数据对象、数据类型、运算符和表达式 关系运算符应用举例: PROCESS(num1,num2) 3.3 数据对象、数据类型、运算符和表达式 关系运算符应用举例: PROCESS(num1,num2) SIGNAL a:STD_LOGIC_VECTOR(4 DOWNTO 0); SIGNAL b:STD_LOGIC_VECTOR(4 DOWNTO O); SIGNAL c:STD_LOGIC; BEGIN a <= num 1; ——赋值运算 b <= num 2; ——赋值运算 IF (a <= b) THEN ——关系运算 c <= '1'; ——赋值运算 ELSE c <= '0'; ——赋值运算 ENDIF; END PROCESS;
3.3 数据对象、数据类型、运算符和表达式 SIGNAL a,b :STD_LOGIC_VECTOR( 3 DOWNTO 0); 3.3 数据对象、数据类型、运算符和表达式 并置运算符 并置运算符“&”用于位的连接。并置运算的规则如下: 并置运算可用于位的连接,形成位适量; 并置运算可用于矢量的连接,形成新的矢量,例如,两个4位的矢量并置运算后,可以构成8位矢量; 并置运算可用于位和矢量的连接,形成新的矢量,例如,位和一个4位的矢量并置运算后,可以构成5位矢量。 下面是一个使用了并置运算符的小例子,可见并置运算符的使用方法: SIGNAL a,b :STD_LOGIC_VECTOR( 3 DOWNTO 0); SIGNAL q :STD_LOGIC_VECTOR( 7 DOWNTO 0); q <= a&b;
3.3 数据对象、数据类型、运算符和表达式 SIGNAL a,b,c,d :STD_LOGIC; 3.3 数据对象、数据类型、运算符和表达式 在上面的小例子中,3条程序语句的作用是将两个4位长度的位矢量a和b连接成一个8位长度的位矢量并将其赋给信号q。 位的连接可以有不同的表示方法,下面进行介绍。先来看用并置运算符来连接4个STD_LOGIC类型的信号a、b、c、d,然看将连接后形成的位矢量赋给位矢量q。 SIGNAL a,b,c,d :STD_LOGIC; SIGNAL q :STD_LOGIC_VECTOR( 3 DOWNTO 0); q <= a&b&c&d;
3.3 数据对象、数据类型、运算符和表达式 2. 运算符优先级 3.3 数据对象、数据类型、运算符和表达式 2. 运算符优先级 在VHDL语言中中,逻辑运算、关系运算、算术运算、并置运算优先级是不相同的,各种运算的操作不可能放在一个程序语句中,所以把各种运算符排成统一的优先顺序表意义不明显。其次,VHDL语言的结构化描述,在综合过程中,程序是并行的,没有先后顺序之分,写在不同程序行的硬件描述程序同时并行工作。VHDL语言程序设计者不要理解程序是逐行执行,运算是有先后顺序的,这样是不利于VHDL程序的设计。运算符的优先顺序仅在同一行的情况下有顺序、有优先,不同行的程序是同时的。各个运算符的优先级别见下表3-2:
3.3 数据对象、数据类型、运算符和表达式
3.3 数据对象、数据类型、运算符和表达式 3. 表达式 3.3 数据对象、数据类型、运算符和表达式 3. 表达式 VHDL语言中的表达式与其他高级程序设计语言非常相似,同样是由运算符将基本元素连接起来的式子。要称为一个表达式,需要有两个要素:运算符和基本元素。基本元素包括对象名、文字、函数调用及括起来的表达式。例如:a&b,b (6)&b (5)&b (4),(A/B)*B+(A REM B),7 REM (-2)等。
3.3 数据对象、数据类型、运算符和表达式 3.3.4 基本顺序描述语句 3.3 数据对象、数据类型、运算符和表达式 3.3.4 基本顺序描述语句 并行语句和顺序语句是VHDL语言中的两种基本描述语句,它们完整描述了数字系统的硬件结构和逻辑功能。 顺序语句的特点是按照书写的先后次序来执行,它们只能出现在进程、块和子程序中,子程序包括函数(FUNCTION)和过程(PROCEDURE)。
3.3 数据对象、数据类型、运算符和表达式 顺序语句有两类:一类是只能作为顺序语句使用;另一类是既可以做顺序语句也可以做并行语句,这类语句放在进程、子程序以外是并行语句,放在进程、子程序内是顺序语句。 VHDL语言有6类基本顺序语句:赋值语句、流程控制语句、等待语句、子程序调用语句、返回语句和空操作语句。
3.3 数据对象、数据类型、运算符和表达式 1. 信号和变量赋值语句 a <= b; 3.3 数据对象、数据类型、运算符和表达式 1. 信号和变量赋值语句 VHDL语言中的赋值语句是指将一个值或一个表达式的运算结果传递给某一个数据对象,如信号或变量。在设计实体中,数据的传输以及对端口外部数据的读取都是通过赋值语句实现的。 信号赋值语句 信号的说明要放在VHDL语言程序的并行部分进行,但可以在VHDL语言程序的并行部分和顺序部分同时使用。信号赋值语句的书写格式如下: 目标信号 <= 表达式; 该语句表示,将右边信号量表达式的值赋予左边的目的信号量。例如: a <= b;
3.3 数据对象、数据类型、运算符和表达式 变量赋值语句 3.3 数据对象、数据类型、运算符和表达式 变量赋值语句 在VHDL语言中,变量的说明和赋值语句只能在VHDL语言程序的顺序部分进行说明和使用,即只能出现在进程、过程和函数中。变量赋值语句的书写格式为: 目标变量 := 表达式; 例如: count : = 10; ——向变量count赋值10
3.3 数据对象、数据类型、运算符和表达式 2. IF语句 IF语句是根据指定的条件确定执行哪条语句。IF语句的条件判断输出是布尔量,即是“真”(TRUE)或“假”(FALSE)。因此在IF语句的条件表达式中只能使用关系运算操作(=,/一,<,>,<=,>=)及逻辑运算操作的组合表达式。 IF语句共有3种类型:
3.3 数据对象、数据类型、运算符和表达式 IF_THEN语句 这种类型的语句书写格式如下: IF 条件 THEN 顺序处理语句 3.3 数据对象、数据类型、运算符和表达式 IF_THEN语句 这种类型的语句书写格式如下: IF 条件 THEN 顺序处理语句 END IF; 执行到IF语句时,判断指定条件是否成立。如果条件成立,则执行顺序处理语句部分;如果条件不成立,则跳过顺序处理语句部分,向下执行IF语句后面的语句。
3.3 数据对象、数据类型、运算符和表达式 IF_THEN_ELSE语句 这种类型的语句书写格式如下: IF 条件 THEN 3.3 数据对象、数据类型、运算符和表达式 IF_THEN_ELSE语句 这种类型的语句书写格式如下: IF 条件 THEN 顺序处理语句1; ELSE 顺序处理语句2; END IF;
3.3 数据对象、数据类型、运算符和表达式 在这种格式的IF语句中,当IF语句所指定的条件成立时,将执行顺序处理语句1部分;当IF语句所指定的条件不成立时,将执行顺序处理语句2部分。也就是说,用条件来选择两条不同程序执行的路径。 这种描述的典型逻辑电路实例是二选一电路。例如,二选一电路的输入为a和b,选择控制端为sel,输出端为c。用IF语句来描述该电路行为的程序如例3-2所示。
3.3 数据对象、数据类型、运算符和表达式 【例3-2】 IF_THEN_ELSE语句示例 3.3 数据对象、数据类型、运算符和表达式 【例3-2】 IF_THEN_ELSE语句示例 ARCHITECTURE rtl OF mux2 IS BEGIN PROCESS( a,b,sel) IF ( sel = '1') THEN c <= a; ELSE c <= b; END IF; END PROCESS; END rtl;
3.3 数据对象、数据类型、运算符和表达式 IF_THEN_ELSEIF_ELSE语句 3.3 数据对象、数据类型、运算符和表达式 IF_THEN_ELSEIF_ELSE语句 IF语句的多选择控制又称IF语句的嵌套,在这种情况下,它的书写格式为: IF 条件1 THEN 顺序处理语句1; ELSIF 条件2 THEN 顺序处理语句2; … ELSIE 条件n THEN 顺序处理语句n; ELSE 顺序处理语句n+1; END IF;
USE IEEE.STD_LOGIC_1164.ALL; ENTITY mux4 IS 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 mux4; ARCHITECTURE rtl OF mux4 IS BEGIN PROCESS(input,sel) IF(sel = "00" ) THEN y <= input(0 ); ELSIF( sel = "01" ) THEN y <= input(1 ); ELSIF(sel="10") THEN y <= input(2 ); ELSE y <= input(3 ); END IF; END PROCESS; END rtl; 【例3-3】 四选一电路示例
3.3 数据对象、数据类型、运算符和表达式 3. CASE语句 3.3 数据对象、数据类型、运算符和表达式 3. CASE语句 CASE语句常被用来描述总线或编码、译码的行为,用来从多个不同语句的序列中选择一个执行。虽然IF语句也有类似的功能,但是CASE语句的可读性比IF语句要强得多,程序的阅读者很容易找出条件式和动作的对应关系,但CASE语句比IF语句要耗费更多的硬件资源。CASE语句的书写格式如下所示: CASE 表达式 IS WHEN 条件表达式 => 顺序处理语句; END CASE;
3.3 数据对象、数据类型、运算符和表达式 当CASE和IS之间的表达式的取值满足指定的条件表达式的值时,程序将执行紧接着的,由符号=>所指的顺序处理语句。条件表达式的值有如下的4种不同的表示形式: WHEN值 => 顺序处理语句; WHEN值|值|值|…|值 => 顺序处理语句; WHEN值 TO 值 => 顺序处理语句; WHEN OTHERS => 顺序处理语句;
3.3 数据对象、数据类型、运算符和表达式 4. LOOP语句 3.3 数据对象、数据类型、运算符和表达式 4. LOOP语句 LOOP语句与其它高级语句中的循环语句一样,使它所包含的顺序处理语句被循环执行,循环执行的次数由循环变量的取值范围决定。在VHDL语言中常用来描述位片逻辑及迭代电路的行为。 LOOP语句的书写格式一般有两种:
3.3 数据对象、数据类型、运算符和表达式 ASUM:FOR i IN 1 TO 9 LOOP 3.3 数据对象、数据类型、运算符和表达式 FOR_LOOP语句 这种LOOP语句的书写格式如下: [标号]:FOR 循环变量 IN 离散范围 LOOP 顺序处理语句; END LOOP[标号]; LOOP语句中的循环变量的值在每次循环中都将发生变化,而IN后跟的离散范围则表示循环变量在循环过程中依次取值的范围。例如: ASUM:FOR i IN 1 TO 9 LOOP sum = i + sum; ——sum初始值为0 END LOOP ASUM;
3.3 数据对象、数据类型、运算符和表达式 【例3-5】 8位的奇偶校验电路的实例: LIBRARY IEEE; 3.3 数据对象、数据类型、运算符和表达式 【例3-5】 8位的奇偶校验电路的实例: 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 parity_check IS ARCHITECTURE rtl OF parity_check IS BEGIN PROCESS (a) VARIABLE tmp:STD_LOGIC; BEGlN tmp : = '0'; FOR i IN 0 TO 7 LOOP tmp : = tmp XOR a(i ); END LOOP; y <= tmp; END PROCESS, END rtl;
3.3 数据对象、数据类型、运算符和表达式 WHILE_LOOP语句 这种LOOP语句的书写格式如下: [标号]:WHILE 条件 LOOP 3.3 数据对象、数据类型、运算符和表达式 WHILE_LOOP语句 这种LOOP语句的书写格式如下: [标号]:WHILE 条件 LOOP 顺序处理语句; END LOOP[标号]; 在该LOOP语句中,如果条件为“真”,则进行循环;如果条件为“假”,则结束循环。 例如: i : = 1; sum : = 0; sbcd:WHILE (i<10) LOOP sum : = i + sum; i : = i + 1; END LOOP sbcd;
3.3 数据对象、数据类型、运算符和表达式 5. NEXT语句 3.3 数据对象、数据类型、运算符和表达式 5. NEXT语句 NEXT语句是LOOP语句的内部循环控制语句,用来在LOOP语句中进行有条件或无条件的跳转控制。它的3种书写格式为: NEXT; NEXT [LOOP] 标号; NEXT [LOOP] 标号 WHEN 条件表达式;
3.3 数据对象、数据类型、运算符和表达式 第一种格式,当LOOP语句内部的顺序处理语句执行到NEXT处时,无条件终止本次循环,跳回到本次循环LOOP语句处,开始下一次循环操作。 第二种格式,该格式的功能和第一种类似,只是当有多重LOOP语句嵌套时,跳转到指定标号的LOOP语句处,开始执行下一次循环操作。 第三种格式,当WHEN后的条件表达式的值为TRUE时,执行NEXT语句,否则继续向下执行。
3.3 数据对象、数据类型、运算符和表达式 L1:WHILE i<10 LOOP L2:WHILE j<20 LOOP …… 3.3 数据对象、数据类型、运算符和表达式 L1:WHILE i<10 LOOP L2:WHILE j<20 LOOP …… NEXT L1 WHEN i = j; END LOOP L2; END LOOP L1; 当i = j时,NEXT语句被执行,程序将从内循环跳出,跳转到标号L1处,开始下一次循环操作。
3.3 数据对象、数据类型、运算符和表达式 6. EXIT语句 3.3 数据对象、数据类型、运算符和表达式 6. EXIT语句 EXIT语句也是LOOP语句的内部循环控制语句,书写格式和跳转功能都和NEXT语句很类似。不过EXIT语句是跳转到LOOP标号指定的LOOP循环语句的结束处,即跳出LOOP循环语句。EXIT语句的书写格式也有3种: EXIT; EXIT [LOOP] 标号; EXIT [LOOP] 标号 WHEN 条件表达式;
3.3 数据对象、数据类型、运算符和表达式 【例3-6】 EXIT语句实示例: 3.3 数据对象、数据类型、运算符和表达式 【例3-6】 EXIT语句实示例: 在该例中int_a通常代入大于0的正数值。如果int_a的取值为负值或零将出现错误状态,算式就不能计算。也就是说int_a小于或等于0时,IF语句将返回“真”值,EXIT语句得到执行,LOOP语句执行结束。程序将向下执行LOOP语句后继的语句。 PROCESS(n) 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); ENDIF; END LOOP; y <= q; END PROCESS;
3.3 数据对象、数据类型、运算符和表达式 7. WAIT语句 3.3 数据对象、数据类型、运算符和表达式 7. WAIT语句 进程在仿真运行中总是处于两种状态之一:执行状态和等待状态。进程状态的变化受WAIT语句的控制,当进程执行到WAIT语句时,就将被挂起,直到满足此语句设置的结束条件后,才能重新开始执行。WAIT语句可以设置4种不同的条件:无限等待、时间到、条件满足以及敏感信号量变化,这几类条件可以混用,其书写格式为: WAIT ——无限等待 WAIT ON 信号表 ——敏感信号量变化 WAIT UNTIL 条件表达式 ——条件满足 WAIT FOR 时间表达式 ——时间到
3.3 数据对象、数据类型、运算符和表达式 例如: WAIT ON a,b; WAIT UNTIL ((x*10)<100); 3.3 数据对象、数据类型、运算符和表达式 例如: WAIT ON a,b; 信号a或信号b发生变化,进程将结束挂起状态,而继续执行WAIT ON语句后继的语句。 WAIT UNTIL ((x*10)<100); 当信号量x的值大于或等于10时,进程执行到该语句将被挂起;当x的值小于10时进程再次被启动,继续执行WAIT语句的后继语句。 WAIT FOR 20 ms; 时间表达式是一个常数20ms,当进程执行到该语句时将等待20ms。一旦20ms时间到,进程将执行WAIT FOR语句的后继语句。
3.3 数据对象、数据类型、运算符和表达式 8. RETURN语句 3.3 数据对象、数据类型、运算符和表达式 8. RETURN语句 返回语句RETURN用于子程序体中(包括过程和子函数),执行返回语句将结束子程序的执行,无条件地跳转到子程序的END处。RETURN语句有两种语句格式: RETURN; RETURN 表达式; 第一种格式用于结束过程,不返回任何值;第二种格式用于结束函数,并返回表达式的值。
3.3 数据对象、数据类型、运算符和表达式 9. NULL语句 CASE decode IS 3.3 数据对象、数据类型、运算符和表达式 9. NULL语句 空操作语句NULL不执行任何操作,唯一的功能就是使逻辑运行流程跨入下一步语句的执行。NULL语句常见于CASE语句中,利用NULL来表示剩下的不满足条件下的操作。例如下面的译码器的示例: CASE decode IS WHEN "001" => tmp : = rega AND regb; WHEN "101" => tmp : = rega OR regb; WHEN "110" => tmp : = NOT rega; WHEN OTHERS => NULL; END CASE;
3.3 数据对象、数据类型、运算符和表达式 3.3.5 基本并行描述语句 3.3 数据对象、数据类型、运算符和表达式 3.3.5 基本并行描述语句 VHDL语言是并行执行的语言,这是和其它高级程序设计语言不同的地方,也是符合硬件运行特点的。 在VHDL语言中有多种并行语句,各种并行语句间的执行是同步的,它们的执行顺序与书写顺序无关。在并行语句执行时,并行语句间可以有信息的往来,也可以是独立、互不相关的。每一种并行语句内部的语句运行方式也是有并行执行方式(如块语句)和顺序执行方式(如进程语句)两种。 在VHDL语言中能进行并行处理的语句有:进程语句、并行赋值语句、块语句、条件信号赋值语句、元件例化语句、生成语句、并行过程调用语句、参数传递映射语句、端口说明语句、并行断言语句等。下面介绍一些常用的并行语句,其它并行语句请读者查阅其它相关资料。
3.3 数据对象、数据类型、运算符和表达式 1. 进程语句 3.3 数据对象、数据类型、运算符和表达式 1. 进程语句 进程语句在前面的例子中已多次见到,它是VHDL语言程序中使用最频繁,最能体现VHDL语言特点的语句。在一个结构体中可以有多个进程语句同时并行执行,而进程语句内部则是由顺序语句组成的。进程语句与结构体中其它并行语句通过信号进行信息交流。进程语句有敏感信号表,当敏感信号表中任意信号发生变化时将启动进程语句执行内部的顺序语句。
3.3 数据对象、数据类型、运算符和表达式 进程语句PROCESS的一般书写格式如下: [进程名:] PROCESS [敏感信号表] 3.3 数据对象、数据类型、运算符和表达式 进程语句PROCESS的一般书写格式如下: [进程名:] PROCESS [敏感信号表] 变量说明语句; BEGIN …… 顺序处理语句; END PROCESS [进程名]; 可见,进程语句实际上是一段程序,在这段程序中从进程标识符开始,到END PROCESS结束。这段程序描述了硬件模块的功能或工作流程,靠敏感信号触发硬件模块的反复执行。
3.3 数据对象、数据类型、运算符和表达式 2. 并行信号赋值语句 3.3 数据对象、数据类型、运算符和表达式 2. 并行信号赋值语句 并行信号赋值语句有3种形式:简单信号赋值语句,条件信号赋值语句,选择信号赋值语句。 信号赋值语句可以在进程内部使用,此时它作为顺序语句形式出现;并行赋值语句也可以在构造体的进程之外使用,此时它作为并行语句形式出现。在这里所讲的信号赋值语句,冠以“并行”的词句,主要是强调该语句的并发性。
3.3 数据对象、数据类型、运算符和表达式 ARCHlTECTURE behav OF var IS BEGIN 3.3 数据对象、数据类型、运算符和表达式 简单信号赋值语句 简单信号赋值语句是在VHDL语言中最基本的语句,它的格式为: 信号 <= 表达式; 赋值号右边的表达式的数据类型必须与左边信号的数据类型一致。 一个并行信号赋值语句实际上是一个进程的缩写。例如 ARCHlTECTURE behav OF var IS BEGIN output t <= a + i; END behav;
3.3 数据对象、数据类型、运算符和表达式 可以等效于: ARCHITECTURE behav OF var IS BEGIN 3.3 数据对象、数据类型、运算符和表达式 可以等效于: ARCHITECTURE behav OF var IS BEGIN PROCESS (a,i) output t <= a + i; END PROCESS; END behave;
3.3 数据对象、数据类型、运算符和表达式 当赋值符号“<=”右边的表达式值发生任何变化时,赋值操作就会立即发生,新的值将赋予赋值符号“<=”左边的信号。从进程语句描述来看,在PROCESS语句的括号中列出了敏感信号量表,例中是a和i。由PROCESS语句的功能可知,在仿真时进程一直在监视敏感信号量表中的敏感信号量a和i。一旦任何一个敏感信号量发生新的变化,使其值有了一个新的改变,进程将得到启动,赋值语句将被执行,新的值将从output信号量输出。所以在这种情况下,并行赋值语句和进程语句是等效的。
3.3 数据对象、数据类型、运算符和表达式 条件信号赋值语句根据不同条件将不同的多个表达式之的值代入信号量,其书写格式为: 3.3 数据对象、数据类型、运算符和表达式 条件信号赋值语句 条件信号赋值语句根据不同条件将不同的多个表达式之的值代入信号量,其书写格式为: 目的信号量 <= 表达式1 WHEN 条件1 ELSE 表达式2 WHEN 条件2 ELSE 表达式3 WHEN 条件3 ELSE … … 表达式n;
3.3 数据对象、数据类型、运算符和表达式 在每个表达式后面都跟有用“WHEN”所指定的条件,如果满足该条件,则该表达式值赋值给目的信号量;如果不满足条件,则再判别下一个表达式所指定的条件。最后一个表达式可以不跟条件,它表明在上述表达式所指明的条件都不满足时,则将该表达式的值代入目标信号量。例3-6就是利用条件赋值语句来描述的四选一逻辑电路。
3.3 数据对象、数据类型、运算符和表达式 【例3-6】 四选一逻辑电路示例 ENTITY mux4 lS 3.3 数据对象、数据类型、运算符和表达式 【例3-6】 四选一逻辑电路示例 ENTITY mux4 lS PORT (i0,i1,i2,i3,a,b:IN STD_LOGIC; q:OUT STD_LOGIC); END mux4; ARCHITECTURE rtl OF mux4 IS SIGNAL sel:STD_LOGIC_VECTOR(1 DOWNTO 0); BEGIN ssel <= 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 rtl;
3.3 数据对象、数据类型、运算符和表达式 选择信号赋值语句 选择赋值语句类似于CASE语句,它对表达式进行测试,当表达式取值不同时将使不同的值赋值给目的信号量。选择赋值语句的书写格式如下 WITH 表达式 SELECT 目的信号量 <= 表达式1 WHEN 条件1, 表达式2 WHEN条件2, … … 表达式n WHEN条件n;
3.3 数据对象、数据类型、运算符和表达式 【例3-7】 四选一逻辑电路示例 LIBRARY IEEE; 3.3 数据对象、数据类型、运算符和表达式 【例3-7】 四选一逻辑电路示例 LIBRARY IEEE; USE IEEE.STD_LDGIC_1164.ALL; ENTITY mux IS PORT (i0,i1,i2,i3,a,b:IN STD_LOGIC; q:OUT STD_LOOIC); END mux: ARCHITECTURE behav OF mux IS SIGNAL sel:INTEGER; BEGIN 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; WITH sel SELECT q <= i0 WHEN 0, i1 WHEN l, i2 WHEN 2, i3 WHEN 3. 'X' WHEN OTHERS; END behav;
3.3 数据对象、数据类型、运算符和表达式 3. 端口说明语句 3.3 数据对象、数据类型、运算符和表达式 3. 端口说明语句 PORT端口说明语句是对设计实体和外部电路的接口通道的说明,包含对每一个接口通道的名称、模式和数据类型的说明。在3.2.1实体说明中已对端口说明语句做过详细说明,这里不再累述。
3.3 数据对象、数据类型、运算符和表达式 4. 元件例化语句 3.3 数据对象、数据类型、运算符和表达式 4. 元件例化语句 在3.2.2结构体描述中,介绍结构化描述法时,程序中使用了元件例化语句。元件例化实际上是一种描述连接关系的方法。将已经设计好的实体定义为一个元件,利用特定的语句将该元件与当前设计实体的指定端口连接,以完成 一个高层的设计。类似于在一块电路板上插入一个芯片,设计实体中的指定端口就是连接芯片(元件)的插座。 在设计中被元件例化语句声明并调用的元件,可以时一个设计好的VHDL设计实体,也可以是元件库中的元件,甚至还可以是其他硬件设计语言设计的元件。
3.3 数据对象、数据类型、运算符和表达式 元件例化语句主要由两个部分组成,首先是将一个现成的设计实体定义成一个元件,它的格式如下: 3.3 数据对象、数据类型、运算符和表达式 元件例化语句主要由两个部分组成,首先是将一个现成的设计实体定义成一个元件,它的格式如下: COMPONENT 元件名 IS PORT (端口名表); END COMPONENT 元件名; 元件的定义语句必须放在结构体的ARCHITECTURE和BEGIN之间。这里相当于对现有的设计实体进行封装,PORT()语句定义出元件和外部通信的各个端口,就像封装的集成芯片的外部引脚一样。
3.3 数据对象、数据类型、运算符和表达式 元件例化语句的第二个部分是将此元件的端口信号映射成当前设计实体中的信号,各个元件间的连接关系就是利用信号映射来实现的。这种端口映射的格式如下: 元件名 PORT MAP([端口名 => ] 连接端口名,……); 这里的端口名是元件的端口信号,连接端口名是连接的当前设计实体的端口名。如果是依次顺序连接,则MAP()映射表中直接列出连接端口名即可。
3.4 VHDL的库和包 在利用VHDL语言进行设计时,为了提高设计效率,常将一些有用的数据类型、子程序等设计单元、设计单元的集合(程序包)等放在一个或几个库中以供调用。如果在某个设计中需要用到某个程序包,必须先在设计的开始位置调用对应的库,并打开这个程序包,这样就可以随时使用这个程序包中的内容
3.4 VHDL的库和包 3.4.1 VHDL库的使用和种类 1. 库的使用 在VHDL中,库主要存放已经编译过的实体说明、结构体、程序包和配置。在库中的各个设计单元,实体说明、结构体、程序包和配置可以用作进行其他设计的资源,一个设计可以使用多个库中的设计单元。当一个设计需要使用库中的已编译单元时,必须要在每个设计的VHDL程序的起始部分说明要引用的库,这样被声明的库和库中的元件对本设计才是“可见”的。
3.4 VHDL的库和包 在VHDL语言中,库说明语句的格式如下所示: LIBRARAY 库名; 在设计的VHDL程序的起始部分对库进行说明以后,还要说明要使用库中的哪一个设计单元,这时使用USE子句来进行说明: USE 库名.程序包名.ALL; 库名就是前面库说明语句中已经说明过的库,程序包名就是实际设计要使用的设计单元所在程序包的名,ALL表示使用程序包中的所有项目。
3.4 VHDL的库和包 2. 库的种类 在VHDL程序设计中常用的库有IEEE库、STD库、WORK库和用户定义的库等。 IEEE库 IEEE库是VHDL设计中最常用的库,包含IEEE标准的程序包和其它一些支持工业标准的程序包。在IEEE库中有一个叫“STD_LOGIC_1164”的程序包,它是IEEE正式认可的标准包集合,也是最重要和最常用的程序包。一些著名的EDA工具公司自有的资源包,如SYNOPSYS公司的包集合“STD_LOGIC_ARITH”、“STD_LOGIC_ UNSIGNED”、 “STD_LOGIC_ SIGNED”等,尽管它们没有得到IEEE的承认,但是仍汇集在IEEE库中。
3.4 VHDL的库和包 IEEE库及其程序包的使用举例: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_ARITH.ALL;
3.4 VHDL的库和包 STD库 STD库是VHDL的标准库,库中有两个VHDL语言标准程序包:STANDARD和TEXTIO。设计者可以随时调用这两个程序包中的所有内容,即在编译和综合过程中,都自动将该包包含进去了,在设计中不必再使用USE子句显式说明。
3.4 VHDL的库和包 WORK库 WORK库是VHDL设计的现行作业库,用于存放用户设计和定义的设计单元和程序包,用户设计项目中的成品、半成品和以设计好的原件存放在该库中。 在利用VHDL语言进行项目设计时,不允许在根目录下进行,必须建立一个文件夹,该项目中的所有设计文件都保存在该文件夹中。VHDL综合器将该文件夹默认为WORK库。WORK库对本设计项目是“可见”的,所以也不用显式说明。
3.4 VHDL的库和包 用户定义库 用户为自身设计需要所开发的共用程序包和实体等,也可以汇集在一起定义成一个库,这就是用户定义库或称用户库。在使用时同样要首先说明库名。
3.4 VHDL的库和包 3.4.2 程序包 1. 程序包定义 VHDL标准中的程序包包含两个部分:程序包首说明部分和程序包包体部分。程序包说明部分的书写格式为: PACKAGE 程序包名 IS 程序包首说明部分; END [PACKAGE] [程序包名]; 该部分以保留字“PACKAGE”开始,在保留字“IS”与“END”之间是一些VHDL设计所需的公共信息,包括数据类型说明、子程序说明、常量说明、元件说明、信号说明、文件说明、属性说明和属性指定等,最后以保留字“END”结束程序包说明部分。
3.4 VHDL的库和包 程序包包体部分的书写格式为: PACKAGE BODY 程序包名 IS 程序包体说明部分及包体内子程序定义体; END [EPACKAGE BODY] [程序包名]; 程序包包体部分以保留字“PACKAGE BODY”开始,在保留字“IS”与“END”之间主要是程序包首中已说明的子程序的子程序体,同时还可包括USE子句、数据类型说明、子类型说明和常数说明等,它们对外是不可见的,最后以保留字“END”结束程序包包体部分。
3.4 VHDL的库和包 程序包首说明部分是主设计单元,它可以独立进行编译并插入到设计库中;程序包包体部分是次级设计单元,它也可以在其对应的主设计单元编译并插入到设计库之后,独立进行编译并也插入到设计库中。 如果程序包首中仅仅包含定义数据类型或定义数据对象等内容,程序包体是不必要的,程序包首可以独立地被使用;但在程序包中若有子程序说明,则必须有对应的子程序包体,这时子程序体必须放在程序包体中。
3.4 VHDL的库和包 2. 常用程序包 VHDL语言提供的常见程序包主要包括STANDARD、TEXTIO、STD_LOGIC_1164、NUMERIC_STD和NUMERIC_BIT: 程序包STANDARD 程序包STANDARD预先在STD库中编译,它主要定义了布尔类型、BIT类型、CHARACTER类型、出错级别、实数类型、整数类型、时间类型、延迟长度子类型、自然数子类型、正整数子类型、STRING类型、BIT_VECTOR子类型、文件打开方式类型和文件打开状态类型。
3.4 VHDL的库和包 IEEE标准1076规定,程序包STANDARD对所有的设计实体均可见。也就是说,所有的VHDL程序的开始部分都隐含了下面的程序行: LIBRARAY STD; USE STD.STANDARD.ALL;
3.4 VHDL的库和包 程序包TEXTIO 程序包TEXTIO也是预先在STD库中进行了编译,它主要定义了与文本文件操作有关的数据类型和子程序,定义了LINE类型、TEXT类型、SIDE类型、操作宽度WIDTH子类型、文件INPUT、文件OUTPUT、READLINE过程、对应于不同数据类型的READ过程、WRITELINE过程和对应于不同数据类型的WRITE过程。程序包TEXTIO对所有设计实体都是不可见的,在使用该程序包的时候,应在VHDL程序的开始部分添加以下的程序行: LIBRARAY STD; USE STD.TEXTIO.ALL;
3.4 VHDL的库和包 程序包STD_LOGIC_1164 程序包STD_LOGIC_1164预先在IEEE库中进行了编译,它是使用最广泛的程序包,它定义了设计人员经常采用的一些数据类型和函数,定义STD_ULOGIC类型、STD_ULOGIE_VECTOR类型、STD_LOGIC子类型、STD_LOGIC_VECTOR类型、决断函数RE_SOLVED、X01子类型、X01Z子类型、UX01子类型、UX01Z子类型、对应于不同数据类型的AND、NAND、OR、NOR、XOR、XNOR、NOT函数、对应于不同数据类型的TO_BIT、TO_BITVECTOR、TO_STDULOGIC、TO_STDLOGICVECTOR、TO_STDULOGICVECTOR、T0_X01、TO_X01Z、TO_UXOL转换函数、上升沿函数RISING_EDGE、下降沿函数FALLING_EDGE和对应于不同类型的IS_X函数。
3.4 VHDL的库和包 由于程序包STD_LOGIC_1164对所有设计实体都是不可见的,因此在使用该程序包的时候,应在VHDL程序的开始部分添加以下的程序行: LIBRARAY IEEE; USE IEEE.std_logic_1164.ALL;
3.4 VHDL的库和包 程序包NUMERIC_STD 程序包NUMERIC_STD定义了用于综合的数据类型和算术函数。具体地说,程序包NUMERIC_STD中定义了两种数据类型:UNSIGNED和SIGNED,其中UNSIGNED表示无符号的位矢量,SIGNED表示带符号的位矢量,其最左端是最高位。此外,程序包NUMERIC_STD中含有所有UNSIGNED和SIGNED类型的重载算术运算,还含有一些有用的类型转换函数、时钟检测函数和其他一些实用的函数。
3.4 VHDL的库和包 程序包NUMERIC_BIT VHDL综合程序包NUMERLE_BIT与程序包NUMERIC_STD基本相同,不同之处在于它的基本元素类型是BIT类型,而不是STD_LOGIC类型。
3.4 VHDL的库和包 3.4.3 函数和过程 在VHDL语言中,子程序是一个VHDL程序模块,利用内部的顺序语句来定义和实现算法。子程序供主程序调用并将处理结果返回给主程序。因此VHDL语言中,子程序的含义与其他高级编程语言中的子程序相同,它同样具有高级编程语言中的子程序所具有的优点,如使用灵活,程序模块化清晰易懂,可反复调用执行等。 子程序的使用方式并不像进程那样,从同一结构体的其它块语句或进程语句中读取信号值或向信号赋值,它只能通过子程序调用及与子程序的界面端口进行通信。子程序可以在程序包、结构体和进程这三个位置定义。
3.4 VHDL的库和包 在VHDL语言中,子程序有两种类型:过程(PROCEDURE)和函数(FUNCTION),它们广泛应用于数值计算、数据类型转换、运算符重载或设计元件的高层设计中。过程与函数的主要区别体现在以下几个方面: 过程可以具有多个返回值;而函数只能有一个返回值。 过程通常用来定义一个算法;而函数往往用来产生一个特定的值。 过程中的参数可以具有三种端口模式:IN、OUT、INOUT;而函数中的参数只具有一种端口模式:IN。 过程中允许使用WAIT语句和顺序赋值语句;而函数则不能使用这两种语句。
3.4 VHDL的库和包 1. 过程 过程的定义 VHDL语言中的子程序包括子程序说明部分和子程序定义部分。其中,子程序说明部分主要定义了其他设计调用子程序时的接口,子程序定义部分描述该子程序具体功能的实现。 VHDL语言中,过程首说明部分的书写结构如下所示: PROCEDURE 过程名(参数表); 过程体定义部分的书写结构如下所示: PROCEDURE 过程名(参数表) IS [过程说明部分] BEGIN <过程语句部分;>; END [PROCEDURE] 过程名;
3.4 VHDL的库和包 过程首说明部分不是必需的,过程体定义部分可以独立存在和使用。在进程或结构体中可以省略过程首说明部分,但在程序包中必须有。 过程首说明部分和过程体定义部分的参数表中的每个参数的格式是: [对象类型]参数名[,参数名…]:[端口模式]数据类型; 其中对象类型、端口模式是可选项。参数的对象类型包括常量、信号和变量,参数名是用来表示参数的唯一标识,端口模式包括IN、OUT和INOUT三种模式,数据类型用来指明参数所属的数据类型。
3.4 VHDL的库和包 过程体定义部分保留字“IS”后的过程说明部分主要是对过程中要用到的变量、常量和数据类型进行说明,并且这些说明只对该过程可见。接下来跟着的是以保留字“BEGIN”开始的过程语句部分,用来描述该过程的具体功能,注意这部分都是顺序语句。
3.4 VHDL的库和包 VHDL语言中的过程调用与其他高级编程语言中的子程序调用十分类似。过程被调用时主程序先要对过程进行初始化,即需要将初始值传递给过程的输入参数,从而启动过程语句;过程启动后,将会按照语句的顺序自上而下执行过程中的各条语句。过程执行完毕后,输出值将会传递给调用主程序所定义的变量或者信号中。 过程调用语句有两种方式:并行过程调用语句和顺序过程调用语句。其中:并行过程调用语句主要用于结构体的并行处理语句部分,它位于其他子程序和进程语句的外部;而顺序过程调用语句则位于进程语句或者另一个子程序的内部。 过程调用的格式如下: 过程名 ([形参名1 =>] 实参表达式1,[形参名2 =>] 实参表达式2,……);
【例3-8】过程定义与调用完整示例: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; PACKAGE pro_exam IS ——程序包首说明 PROCEDURE nand4a(SIGNAL a,b,c,d:IN STD_LOGIC; SIGNAL y:OUT STD_LOGIC );——过程首说明 END pro_exam; PACKAGE BODY pro_exam IS ——程序包体定义 SIGNAL y:OUT STD_LOGIC ); ——过程体定义 BEGIN y <= NOT(a AND b AND c AND d); END nand4a; USE WORK. pro_exam.ALL; ENTITY example IS PORT(e,f,g,h:IN STD_LOGIC; x:OUT STD_LOGIC); END; ARCHITECTURE bhv OF example IS nand4a(e,f,g,h); ——并行过程调用语句
3.4 VHDL的库和包 2. 函数 函数的定义 与过程一样,函数也包括函数首说明和函数体定义两个部分:函数首说明部分定义了主程序调用函数时的接口,函数体定义部分则描述了该函数具体逻辑功能的实现。函数也是在程序包、结构体和进程中进行定义。 函数首说明部分的书写结构如下所示: FUNCTION 函数名(参数表)RETURN 数据类型; 函数体定义部分的书写结构如下所示: FUNCTION 函数名(参数表)RETURN 数据类型 IS [函数说明部分;] BEGlN <函数语句部分>; RETURN(表达式); END [FUNCTION]<函数名>;
3.4 VHDL的库和包 函数首说明部分和函数体定义部分的参数表中的每个参数的格式是: [对象类型]参数名[,参数名…].[IN]数据类型; 保留字“RETURN”返回函数的值,它后面的数据类型用来指明函数返回值的类型。 保留字“IS”开始的函数说明部分,主要是对函数中要用到的变量、常量和类型进行说明,而且这些说明只对该函数可见。与过程中的参数稍有不同,函数参数的数据类型只能包括常量和信号;参数的端口模式只能是IN,因此参数的端口模式可以省略。如果函数中的参数没有指明对象类型和端口模式,那么此时参数将被默认为端口模式为IN的常量。注意:与过程一样,函数说明部分中也不能定义新的信号。
本章小结 本章主要介绍了EDA技术的基本概念,VHDL语言技术特点和采用VHDL语言进行数字系统设计的流程,详细介绍了VHDL程序的基本结构,三种结构体描述方法,以及VHDL语言的数据对象、运算符及其优先级、常用的顺序描述语句和并行描述语句,VHDL语言中库、包的使用方法。 采用VHDL语言的数字系统设计完整流程要经过设计方案制定、设计输入、逻辑综合、布局布线、仿真测试、编程下载等步骤。 本章中详细介绍了VHDL语言的基本语法知识。一个VHDL程序的包括库、程序包、实体、结构体和配置,其中实体和结构体是基本组成部分。
本章小结 实体是基本VHDL程序的设计单元定义,定义了和外界通信的方式和引脚。实体内的一个或多个结构体实现实体的逻辑功能。结构体功能的描述采用行为描述、数据流描述和结构化描述三种方法。VHDL语言中可以赋值的数据对象。参与运算的数都有数据类型,可以使VHDL语言的标准数据类型,也可以是在IEEE库的程序包中定义的数据类型,用户还可以根据需要自定义相应的数据类型。VHDL语言的4种运算符、运算符的优先级别以及表达式。 VHDL程序是并发执行的设计语言,大部分是并行语句,但在进程、块、过程和函数中需要顺序语句。本章中详细介绍了常用的并行语句和顺序语句的书写格式和使用方法。 在程序开始位置声明库和库中的程序包,VHDL语言中就可以直接调用已经存在的设计单元。VHDL语言中的子程序包括过程和函数,常将过程体或函数体定义放在程序包中,声明程序包后,就可在程序中的任意位置进行调用。