第7章 VHDL结构与要素 主要内容 EDA技术实用教程 ■ 子程序 ■ VHDL库、程序包和配制 ■ VHDL文字规则 ■ 数据类型 ■ 实体和结构体 ■ 子程序 ■ VHDL库、程序包和配制 ■ VHDL文字规则 ■ 数据类型 ■ VHDL操作符
7.1 实体和结构体 实体(Entity) 构造体(Architecture) 配置(Configuration) 包集合(Package) 7.1 实体和结构体 一个完整的VHDL语言程序通常包含: 实体(Entity) 构造体(Architecture) 配置(Configuration) 包集合(Package) 库(Library)5个部分。 前四种是可分别编译的源设计单元。
实体:用于描述所设计的系统的外部接口信号; 构造体(或称结构体):用于描述系统内部的结构和行为; 包集合:存放各设计模块都能共享的数据类型、常数和子程序等; 配置:用于从库中选取所需单元来组成系统设计的不同版本; 库:存放已经编译的实体、构造体、包集合和配置。库可由用户生成或由ASIC芯片制造商提供。
一、 实体 1、实体语句结构 实体说明单元的一般语句结构: ENTITY 实体名 IS [GENERIC ( 类属表 );] 一、 实体 1、实体语句结构 实体说明单元的一般语句结构: ENTITY 实体名 IS [GENERIC ( 类属表 );] [PORT ( 端口表 );] END ENTITY 实体名;
2、 GENERIC类属说明语句 类属说明的一般书写格式如下: GENERIC([ 常数名 : 数据类型 [ : 设定值 ] { ;常数名 : 数据类型 [ : 设定值 ] } ) ;
类属参量以关键词GENERIC引导一个类属参量表,在表中提供时间参数或总线宽度等静态信息。 类属表说明用于设计实体和其外部环境通信的参数和传递信息。 类属在所定义的环境中的地位与常数相似,但却能从环境(如设计实体)外部动态地接受赋值,其行为又类似于端口PORT。因此,常如实体定义语句那样,将类属说明放在其中,且放在端口说明语句的前面。
【例7-1】 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY andn IS GENERIC ( n : INTEGER ); --定义类属参量及其数据类型 PORT(a : IN STD_LOGIC_VECTOR(n-1 DOWNTO 0); --用类属参量限制矢量长度 c : OUT STD_LOGIC); END; ARCHITECTURE behav OF andn IS BEGIN PROCESS (a) VARIABLE int : STD_LOGIC; int := '1'; FOR I IN a'LENGTH - 1 DOWNTO 0 LOOP IF a(i)='0' THEN int := '0'; END IF; END LOOP; c <=int ; END PROCESS;
【例7-2】 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY exn IS PORT(d1,d2,d3,d4,d5,d6,d7 : IN STD_LOGIC; q1,q2 : OUT STD_LOGIC); END; ARCHITECTURE exn_behav OF exn IS COMPONENT andn --元件调用声明 GENERIC ( n : INTEGER); PORT(a: IN STD_LOGIC_VECTOR(n-1 DOWNTO 0); c: OUT STD_LOGIC); END COMPONENT ; BEGIN -- 类属映射语句,定义类属变量,n赋值为2 u1: andn GENERIC MAP (n =>2) PORT MAP (a(0)=>d1,a(1)=>d2,c=>q1); u2: andn GENERIC MAP (n =>5) -- 定义类属变量,n赋值为5 PORT MAP (a(0)=>d3,a(1)=>d4,a(2)=>d5, a(3)=>d6,a(4)=>d7, c=>q2);
3、 类属映射语句 类属映射语句可用于设计从外部端口改变元件内部参数或结构规模的元件,或称类属元件,这些元件在例化中特别方便,在改变电路结构或元件升级方面显得尤为便捷。其语句格式是: GENERIC MAP(类属表)。 类属映射语句与端口映射语句PORT MAP()具有相似的功能和使用方法,它描述相应元件类属参数间的衔接和传送方式,它的类属参数衔接(连接)表达方式也相同。
USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_arith.ALL; 【例7-3】 LIBRARY IEEE; --待例化元件 USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_arith.ALL; USE IEEE.STD_LOGIC_unsigned.ALL; ENTITY addern IS PORT (a, b: IN STD_LOGIC_VECTOR; result: out STD_LOGIC_VECTOR); END addern; ARCHITECTURE behave OF addern IS BEGIN result <= a + b; END; LIBRARY IEEE; ENTITY adders IS GENERIC(msb_operand: INTEGER := 15; msb_sum: INTEGER :=15); PORT(b: IN STD_LOGIC_VECTOR (msb_operand DOWNTO 0); result: OUT STD_LOGIC_VECTOR (msb_sum DOWNTO 0)); END adders; 接下页
ARCHITECTURE behave OF adders IS COMPONENT addern 接上页 ARCHITECTURE behave OF adders IS COMPONENT addern PORT ( a, b: IN STD_LOGIC_VECTOR; result: OUT STD_LOGIC_VECTOR); END COMPONENT; SIGNAL a: STD_LOGIC_VECTOR (msb_sum /2 DOWNTO 0); SIGNAL twoa: STD_LOGIC_VECTOR (msb_operand DOWNTO 0); BEGIN twoa <= a & a; U1: addern PORT MAP (a => twoa, b => b, result => result); U2: addern PORT MAP (a=>b(msb_operand downto msb_operand/2 +1), b=>b(msb_operand/2 downto 0), result => a); END behave;
图7-1 例7-3的逻辑电路图
4、 PORT(端口)说明 由于PORT说明语句是对一个设计实体界面的说明及对设计实体与外部电路的接口通道的说明,其中包括对每一接口的输入输出模式和数据类型的定义。其格式如下: PORT ( 端口名 : 端口模式 数据类型 ; { 端口名 : 端口模式 数据类型} ) ; 其中的端口名是设计者为实体的每一个对外通道所取的名字,端口模式是指这些通道上的数据流动方式。数据类型是指端口上流动的数据的表达格式或取值类型,VHDL要求只有相同数据类型的端口信号和操作数才能相互作用。
二、结构体(构造体) 结构体是实体所定义的设计实体中的一个组成部分。 结构体描述设计实体的内部结构和外部设计实体端口间的逻辑关系。 结构体的组成部分是: 对数据类型、常数、信号、子程序和元件等元素的说明部分。 描述实体逻辑行为的、以各种不同的描述风格表达的功能描述语句。 以元件例化语句为特征的外部元件(设计实体)端口间的连接。
1. 结构体的一般语言格式 ARCHITECTURE 结构体名 OF 实体名 IS [说明语句] BEGIN [功能描述语句] END ARCHITECTURE 结构体名;
2. 结构体说明语句 结构体中的说明语句是对结构体的功能描述语句中将要用到的信号、常数、元件、函数和过程加以说明。(说明语句并非必须)。 在一个结构体中说明和定义的数据类型、常数、元件、函数和过程只能用于这个结构体中。如果希望这些定义也能用于其它的实体或结构体中,需要将其作为程序包来处理。
3. 功能描述语句结构 结构体中包含四类功能描述语句: 进程语句:定义顺序语句模块。 信号赋值语句:将设计实体内的处理结果向定义的信号 或界面端口进行赋值。 子程序调用语句:用以调用过程或函数,并将获得的结 果赋值于信号。 元件例化语句:对其它的设计实体作元件调用说明,并 将此元件的端口与其它的元件、信号或 高层次实体的界面端口进行连接。
7.2 子程序(SUBPROGRAM) 子程序可在程序包、结构体和进程中定义(一般放在程序包中)。 子程序是一个VHDL程序模块,它只能使用顺序语句,其应用目的是能更有效地完成重复性工作,使用方式只能通过子程序调用及与子程序的界面端口进行通信。 子程序可在程序包、结构体和进程中定义(一般放在程序包中)。 VHDL子程序允许有许多重名的子程序,但这些子程序的参数类型及返回值数据类型是不同的。 子程序有两种类型,即过程和函数。
一、 函数(FUNCTION) 在VHDL中有多种函数形式,如用于不同目的的用户自定义函数和在库中现成的具有专用功能的预定义函数,如决断函数、转换函数等。 转换函数用于从一种数据类型到另一种数据类型的转换,决断函数用于在多驱动信号时解决信号竞争问题。 1、函数的语句表达格式: FUNCTION 函数名(参数表) RETURN 数据类型 --函数首 FUNCTION 函数名(参数表)RETURN 数据类型 IS -- 函数体 [ 说明部分 ] BEGIN 顺序语句 ; END FUNCTION 函数名;
2、函数格式的含义 一般地,函数定义应有两部分组成,即函数首和函数体(在进程或结构体中不必定义函数首,而在程序包中必须定义函数首。 (1)函数首:是由函数名、参数表和返回值的数据类型三部分组成。 函数首的名称的名称即为函数的名称,需放在关键词FUNCTION之后,此名称可是普通的标志符,也可是运算符(运算符必须加上双引号。即所谓的运算符重载——是对VHDL中现存的运算符进行重新定义,以获得新的功能)。
函数的参量表是用来定义输出值(不必显示表示参数的方向),函数参量可以是信号或常数,参数名需放在关键词CONSTANT或SIGNAL之后(如果没有特别说明,则参数被默认为常数。) (2)函数体:可定义函数新功能,需放在程序包的包体内。它包含一个对数据类型、常数、变量等的局部说明,以及用以完成规定算法或转换的顺序语句部分,一旦函数被调用,就将执行这部分语句。它需以关键词END FUNCTION以及函数名结尾。 只是如果只是在一个结构体中定义并调用函数,则仅需函数体,函数首的作用只是作为程序包的有关此参数的一个接口界面。
USE IEEE.STD_LOGIC_1164.ALL; PACKAGE packexp IS --定义程序包 【例7-4】 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; PACKAGE packexp IS --定义程序包 FUNCTION max( a,b : IN STD_LOGIC_VECTOR) --定义函数首 RETURN STD_LOGIC_VECTOR ; FUNCTION func1 ( a,b,c : REAL ) --定义函数首 RETURN REAL ; FUNCTION "*" ( a ,b : INTEGER ) --定义函数首 RETURN INTEGER ; FUNCTION as2 (SIGNAL in1 ,in2 : REAL ) --定义函数首 RETURN REAL ; END ; PACKAGE BODY packexp IS FUNCTION max( a,b : IN STD_LOGIC_VECTOR) --定义函数体 RETURN STD_LOGIC_VECTOR IS BEGIN IF a > b THEN RETURN a; ELSE RETURN b; END IF; END FUNCTION max; --结束FUNCTION语句 END; --结束PACKAGE BODY语句 接下页
LIBRARY IEEE; -- 函数应用实例 USE IEEE.STD_LOGIC_1164.ALL; 接上页 LIBRARY IEEE; -- 函数应用实例 USE IEEE.STD_LOGIC_1164.ALL; USE WORK.packexp.ALL ; ENTITY axamp IS PORT(dat1,dat2 : IN STD_LOGIC_VECTOR(3 DOWNTO 0); dat3,dat4 : IN STD_LOGIC_VECTOR(3 DOWNTO 0); out1,out2 : OUT STD_LOGIC_VECTOR(3 DOWNTO 0) ); END; ARCHITECTURE bhv OF axamp IS BEGIN out1 <= max(dat1,dat2); --用在赋值语句中的并行函数调用语句 PROCESS(dat3,dat4) out2 <= max(dat3,dat4); --顺序函数调用语句 END PROCESS;
图7-2 例7-4的逻辑电路图
【例7-5】 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL ; ENTITY func IS PORT ( a : IN STD_LOGIC_VECTOR (0 to 2 ) ; m : OUT STD_LOGIC_VECTOR (0 to 2 ) ); END ENTITY func ; ARCHITECTURE demo OF func IS FUNCTION sam(x ,y ,z : STD_LOGIC) RETURN STD_LOGIC IS BEGIN RETURN ( x AND y ) OR y ; END FUNCTION sam ; PROCESS ( a ) m(0) = sam( a(0), a(1), a(2) ) ; m(1) = sam( a(2), a(0), a(1) ) ; m(2) = sam( a(1), a(2), a(0) ) ; END PROCESS ; END ARCHITECTURE demo ;
二、 重载函数(OVERLOADED FUNCTION) VHDL允许以相同的函数名定义函数,但要求函数中定义的操作数具有不同的数据类型,以便调用时用于分辨不同功能的同名函数。 同样名称的函数可以用不同的数据类型作为此函数的参数定义多次,以此定义的函数称为重载函数。函数还可以允许用任意位矢长度来调用。
VHDL不允许不同数据类型的操作数间进行直接操作或运算。为此,在具有不同的数据类型操作数构成的同名函数中,可定义以运算符重载式的重载函数。 VHDL的IEEE库中的STD-LOGIC-UNSIGNED程序包中预定义的操作符如+、-、*、=、>=、>、<、/=、AND和MOD等,对相应的数据类型INTEGER、STD-LOGIC和STD-LOGIC-VECTOR(标准逻辑位和矢量)的操作作了重载,赋予了新的数据类型操作功能,即通过重新定义运算符的方式,允许被重载的运算符能够对新的数据类型进行操作,或者允许不同的数据类型之间用此运算符进行运算。 例7-6是一个完整的重载函数max的定义和调用的实例。
USE IEEE.STD_LOGIC_1164.ALL ; PACKAGE packexp IS --定义程序包 【例7-6】 LIBRARY IEEE ; USE IEEE.STD_LOGIC_1164.ALL ; PACKAGE packexp IS --定义程序包 FUNCTION max( a,b :IN STD_LOGIC_VECTOR) --定义函数首 RETURN STD_LOGIC_VECTOR ; FUNCTION max( a,b :IN BIT_VECTOR) --定义函数首 RETURN BIT_VECTOR ; FUNCTION max( a,b :IN INTEGER ) --定义函数首 RETURN INTEGER ; END; PACKAGE BODY packexp IS FUNCTION max( a,b :IN STD_LOGIC_VECTOR) --定义函数体 RETURN STD_LOGIC_VECTOR IS BEGIN IF a > b THEN RETURN a; ELSE RETURN b; END IF; END FUNCTION max; --结束FUNCTION语句 FUNCTION max( a,b :IN INTEGER) --定义函数体 RETURN INTEGER IS 接下页
IF a > b THEN RETURN a; ELSE RETURN b; END IF; 接上页 BEGIN IF a > b THEN RETURN a; ELSE RETURN b; END IF; END FUNCTION max; --结束FUNCTION语句 FUNCTION max( a,b :IN BIT_VECTOR) --定义函数体 RETURN BIT_VECTOR IS END FUNCTION max; --结束FUNCTION语句 END; --结束PACKAGE BODY语句 -- 以下是调用重载函数max的程序:
LIBRARY IEEE ; USE IEEE.STD_LOGIC_1164.ALL ; USE WORK.packexp.ALL; ENTITY axamp IS PORT(a1,b1 : IN STD_LOGIC_VECTOR(3 DOWNTO 0); a2,b2 : IN BIT_VECTOR(4 DOWNTO 0); a3,b3 : IN INTEGER RANGE 0 TO 15; c1 : OUT STD_LOGIC_VECTOR(3 DOWNTO 0); c2 : OUT BIT_VECTOR(4 DOWNTO 0); c3 : OUT INTEGER RANGE 0 TO 15); END; ARCHITECTURE bhv OF axamp IS BEGIN c1 <= max(a1,b1); --对函数max( a,b :IN STD_LOGIC_VECTOR)的调用 c2 <= max(a2,b2); --对函数max( a,b :IN BIT_VECTOR) 的调用 c3 <= max(a3,b3); --对函数max( a,b :IN INTEGER) 的调用
【例7-7】 LIBRARY IEEE ; -- 程序包首 USE IEEE.std_logic_1164.all ; USE IEEE.std_logic_arith.all ; PACKAGE STD_LOGIC_UNSIGNED is function "+" (L : STD_LOGIC_VECTOR ; R : INTEGER) return STD_LOGIC_VECTOR ; function "+" (L : INTEGER; R : STD_LOGIC_VECTOR) function "+" (L : STD_LOGIC_VECTOR ; R : STD_LOGIC ) function SHR (ARG : STD_LOGIC_VECTOR ; COUNT : STD_LOGIC_VECTOR ) return STD_LOGIC_VECTOR ; ... end STD_LOGIC_UNSIGNED ;
use IEEE.std_logic_1164.all ; use IEEE.std_logic_arith.all ; 接上页 LIBRARY IEEE ; -- 程序包体 use IEEE.std_logic_1164.all ; use IEEE.std_logic_arith.all ; package body STD_LOGIC_UNSIGNED is function maximum (L, R : INTEGER) return INTEGER is begin if L R then return L; else return R; end if; end; function "+" (L : STD_LOGIC_VECTOR ; R : INTEGER) return STD_LOGIC_VECTOR is Variable result : STD_LOGIC_VECTOR (L’range) ; Begin result := UNSIGNED(L) + R ; return std_logic_vector(result) ; end ; ... end STD_LOGIC_UNSIGNED ;
三、 过程(PROCEDURE) 1、过程的语句格式是: PROCEDURE 过程名(参数表) -- 过程首 PROCEDURE 过程名(参数表) IS [说明部分] BIGIN -- 过程体,可独立存在和使用 顺序语句; END PROCEDURE 过程名; 2、 过程首:不是必须的,在进程或结构体中不必定义,在程序包中必须定义。 过程首由过程名和参数表组成。参数表 可以对常数、变量和信号三类数据对象目标作出说明,并用关键词IN、OUT和INOUT定义这些参数的工作模式,即信息的流向(如果没有指定模式,则默认为IN)。 以下是三个过程首的定义示例: PROCEDURE pro1 (VARIABLE a, b : INOUT REAL) ; PROCEDURE pro2 (CONSTANT a1 : IN INTEGER ; VARIABLE b1 : OUT INTEGER ) ; PROCEDURE pro3 (SIGNAL sig : INOUT BIT) ;
3、过程体 过程体是由顺序语句组成,过程的调用即启动了对过程体的顺序的执行。与函数一样,过程体中的说明部分只是局部的,其中的各种定义只能适用于过程体内部。过程体的顺序语句部分可以包含任何顺序执行的语句。 在不同的调用环境中,可以有两种不同的语句方式对过程进行调用,即顺序语句方式或并行语句方式。对于前者,在一般的顺序语句自然执行过程中,一个过程被执行,则属于顺序语句方式(因为这是它只相当于一条顺序语句的执行);对于后者,一个过程相当于一个小的进程,当这个过程处于并行语句环境中时,其过程体定义的任一IN或INOUT的目标参量(即数据对象:变量、常数、信号)发生改变时,则启动过程的调用,这时的调用是属于并行语句方式的。
【例7-8】 PROCEDURE prg1(VARIABLE value:INOUT BIT_VECTOR(0 TO 7)) IS BEGIN CASE value IS WHEN "0000" => value: "0101" ; WHEN "0101" => value: "0000" ; WHEN OTHERS => value: "1111" ; END CASE ; END PROCEDURE prg1 ; 这个过程对具有双向模式变量的值value作了一个数据转换运算。
PROCEDURE comp ( a, r : IN REAL; m : IN INTEGER ; v1, v2: OUT REAL) IS 【例7-9】 PROCEDURE comp ( a, r : IN REAL; m : IN INTEGER ; v1, v2: OUT REAL) IS VARIABLE cnt : INTEGER ; BEGIN v1 := 1.6 * a ; -- 赋初始值 v2 := 1.0 ; -- 赋初始值 Q1 : FOR cnt IN 1 TO m LOOP v2 := v2 * v1 ; EXIT Q1 WHEN v2 > v1; -- 当v2 > v1,跳出循环LOOP END LOOP Q1 ASSERT (v2 < v1 ) REPORT "OUT OF RANGE" -- 输出错误报告 SEVERITY ERROR ; END PROCEDURE comp ;
四、重载过程(OVERLOADED PROCEDURE) 两个或两个以上有相同的过程名和互不相同的参数数量及数据类型的过程称为重载过程。类似于重载函数,重载过程也是靠参量类型来辨别究竟调用哪一个过程。 过程结构中的语句是顺序执行的,过程的调用是将所定义的过程名作为一条语句来执行(而函数的调用是将所定义的函数作为语句中的一个因子,如一个操作符或一个赋值数据对象或信号等)。
【例7-10】 PROCEDURE calcu ( v1, v2 : IN REAL ; SIGNAL out1 : INOUT INTEGER) ; PROCEDURE calcu ( v1, v2 : IN INTEGER ; SIGNAL out1 : INOUT REAL) ; ... calcu (20.15, 1.42, signl) ; -- 调用第一个重载过程calcu calcu (23, 320,sign2 ) ; -- 调用第二个重载过程 calcu
7.3 VHDL库、程序包和配置 一、 库的种类——常用的库有5(或4)种 1. IEEE库 2. STD库 3. WORK库 一、 库的种类——常用的库有5(或4)种 1. IEEE库 2. STD库 3. WORK库 4. VITAL库 *5.用户定义库
库是存放已经编译的实体、构造体、包集合和配置的数据的集合。 IEEE库: 是VHDL设计中最常用的库,它含有IEEE标准的程序包和其它一 些支持工业标准的程序包,主要包括STD-LOGIC-1164、NUMERIC-BIT和NUMERIC-STD等程序包。使用时必须在VHDL设计实体前面显示表达出来。 STD库: 是VHDL的标准库,包含STANDARD和TEXTIO程序包 (文件输入-输出包),在编译和综合过程中, VHDL的每一项设计都自动地将其包含进去。
WORK库: 是用户的VHDL设计的现行工作库,用于存放用户设计和定义的一些设计单元和程序包。它自动满足VHDL语言标准,使用该库无需说明。 VITAL库: 只在VHDL仿真器中使用,可提高VHDL门级时序模拟的精度,库中包含时序程序包VITAL-TIMING和VITAL-PRIMITIVES。 用户定义库: 是用户为自身设计需要所开发的共用包集合和实体等。使用时要首先说明库名。
二、 库的用法 在VHDL语言中,库的说明语句总是放在实体单元前面, VHDL允许在一个设计实体中同时打开多个不同的库,但库之间必须相互独立。 库语句一般必须与USE语句同用。库语句的关键词LIBRARY指明所使用的库名;USE语句指明库中的程序包。 USE语句的使用有两种常用格式: USE 库名.程序包名.项目名 ; USE 库名.程序包名.ALL ; 如:LIBRARY IEEE ; USE IEEE.STD_LOGIC_1164.STD_ULOGIC ; USE IEEE.STD_LOGIC_1164.RISING_EDGE ; 数据类型 函数
三、 VHDL程序包 已在设计实体中定义的数据类型、子程序或数据对象对于其它设计实体是不可用的(或者说是不可见的),为了使已定义的常数、数据类型、元件调用说明以及子程序能被更多其它的设计实体方便地访问和共享,可以将它们收集在一个VHDL程序包中。(多个程序包可以并入一个VHDL库中)。它对于大系统开发、多个或多组开发人员同步工作显得尤为重要。
( 1 ) 常数说明:如定义系统数据总线通道的宽度。 1、程序包的组成 程序包主要有以下四种基本结构组成,一个程序包至少应包含以下结构中的一种。 ( 1 ) 常数说明:如定义系统数据总线通道的宽度。 ( 2) VHDL数据类型说明:主要用于在整个设计中通用的数据类型,例如通用的地址总线数据类型定义等。 (3)元件定义:主要规定在VHDL设计中参与文件例化的文件接口界面。 ( 4) 子程序:并入程序包的子程序有利于在设计中任一处进行方便地调用。
PACKAGE 程序包名 IS -- 程序包首(程序包的说明部分) 程序包首说明部分 END 程序包名; 通常程序包中的内容应具有更大的适用面和良好的独立性,以供各种不同设计需求的调用,,如STD-LOGIC-1164程序包定义的数据类型STD-LOGIC和STD-LOGIC-VECTOR。一旦定义了程序包,各种独立的设计就能方便地调用。 2、程序包的结构 定义程序包的一般语句结构如下: PACKAGE 程序包名 IS -- 程序包首(程序包的说明部分) 程序包首说明部分 END 程序包名; PACKAGE BODY 程序包名 IS -- 程序包体(程序包的内容部分) 程序包体说明部分以及包体内容
程序包的结构由程序包首和程序包体组成。 程序包首名和程序包体名是同一个名字。 程序包首的说明部分可收集多个不同的VHDL设计所需的公共信息,其中包括数据类型说明、信号说明、子程序说明及元件说明等。所有这些信息虽然也可以在每一个设计实体中进行逐一单独的定义和说明,但如果将这些经常用到的并具有一般性的说明定义放在程序包中供随时调用,可以提高设计的效率和程序的可读性。 程序包结构中,程序包体并非是必需的。程序包首可以独立定义和使用。
程序包体将包括在程序首中已定义的子程序的子程序体中。 程序包体说明部分的组成内容可以是USE语句(允许对其它程序包的调用)、子程序定义、子程序体、数据类型说明等。 对于没有具体子程序说明的程序包体可以省去。如仅仅是定义数据类型或定义数据对象等内容,程序包体是不必要的,程序包首可以独立地被使用;但在程序包中若有子程序说明时,则必须有对应的子程序包体,这时,子程序体必须放在程序包体中。程序包常用来封装属于多个设计单元分享的信息。
【例7-11】 PACKAGE pacl IS -- 程序包首开始 TYPE byte IS RANGE 0 TO 255 ; -- 定义数据类型byte SUBTYPE nibble IS byte RANGE 0 TO 15 ; -- 定义子类型nibble CONSTANT byte_ff : byte := 255 ; -- 定义常数byte_ff SIGNAL addend : nibble ; -- 定义信号addend COMPONENT byte_adder -- 定义元件 PORT( a, b : IN byte ; c : OUT byte ; overflow : OUT BOOLEAN ) ; END COMPONENT ; FUNCTION my_function (a : IN byte) Return byte ; -- 定义函数 END pacl ; -- 程序包首结束
【例7-12】 PACKAGE seven IS SUBTYPE segments is BIT_VECTOR(0 TO 6) ; TYPE bcd IS RANGE 0 TO 9 ; END seven ; USE WORK.seven.ALL ; -- WORK库默认是打开的, ENTITY decoder IS PORT (input: bcd; drive : out segments) ; END decoder ; ARCHITECTURE simple OF decoder IS BEGIN WITH input SELECT drive <= B"1111110" WHEN 0 , B"0110000" WHEN 1 , B"1101101" WHEN 2 , B"1111001" WHEN 3 , B"0110011" WHEN 4 , B"1011011" WHEN 5 , B"1011111" WHEN 6 , B"1110000" WHEN 7 , B"1111111" WHEN 8 , B"1111011" WHEN 9 , B"0000000" WHEN OTHERS ; END simple ;
STD_LOGIC_1164程序包 STD_LOGIC_ARITH程序包 3、常用的预定义的程序包 STD_LOGIC_UNSIGNED和STD_LOGIC_SIGNED程序包 STANDARD和TEXTIO程序包
(1 ) STD_LOGIC_1164程序包:这是IEEE库中最常用的程序包,是IEEE的标准程序包。其中包含了一些数据类型、子类型和函数的定义,这些定义将VHDL扩展为一个能描述多值逻辑(即除具有“0”和“1”以外还有其它的逻辑量,如高阻态“Z”(Z必须大写)、不定态“X”等)的硬件描述语言,很好地满足了实际数字系统的设计需求。 STD_LOGIC _ 1164程序包中用得最多和最广的是定义了满足工业标准的两个数据类型STD_LOGIC 和STD_LOGIC _ VECTOR。 (2)STD_LOGIC_ARITH程序包: STD_LOGIC_ARITH预先编译在IEEE库中,此程序包在STD_LOGIC _ 1164程序包的基础上扩展了三个数据类型UNSIGNED、SIGNED和SMALL _ INT,并为其定义了相关的算术运算符和转换函数。
(3)STD_LOGIC_UNSIGNED和STD_LOGIC_SIGNED程序包 它们都是synopsys公司的程序包,都预先编译在IEEE库中。这些程序包重载了可用于INTEGER型及STD_LOGIC 和STD_LOGIC _ VECTOR型混合运算的运算符,并定义了一个由STD_LOGIC _ VECTOR型到INTEGER型的转换函数。这两个程序包的区别是, STD_LOGIC_SIGNED中定义的运算符考虑了符号,是有符号数的运算。 (4)STANDARD和TEXTIO程序包:这是STD库中的预编译程序包。 STANDARD程序包中定义了许多基本的数据类型、子类型和函数。 TEXTIO程序包定义了支持文件操作的许多类型和子程序。在使用本程序包之前,需加语句USE STD.TEXTIO.ALL。 TEXTIO程序包主要仅供仿真器使用。
四、 配置 配置可以把特定的结构体 关联到(指定给)一个确定的实体。 配置语句的一般格式如下: 配置主要为顶层设计实体指定结构体,或为参与例化的元件实体指定所希望的结构体,以层次方式来对元件例化作结构配置。 配置语句的一般格式如下: CONFIGURATION 配置名 OF 实体名 IS 配置说明 END 配置名;
7.4 VHDL文字规则 一、 数字——有多种表达方式。 一、 数字——有多种表达方式。 整数:整数都是十进制的数,如: 5, 678, 0, 156E2(=15600), 45_234_287 (=45234287) 实数:实数也都是十进制的数,但必须带有小数点,如: 1.335, 88_670_551.453_909(=88670551.453909),1.0,44.99E-2(=0.4499)
60s (60秒), 100m (100米), k (千欧姆), 177A (177安培) 以数制基数表示的文字:用这种方式表示的数由五个部分组成。 SIGNAL d1,d2,d3,d4,d5, : INTEGER RANGE 0 TO 255; d1 <= 10#170# ; -- (十进制表示,等于 170) d2 <= 16#FE# ; -- (十六进制表示,等于 254) d3 <= 2#1111_1110#; -- (二进制表示,等于 254) d4 <= 8#376# ; -- (八进制表示,等于 254) d5 <= 16#E#E1 ; -- (十六进制表示,等于2#1110000#,等于224) 物理量文字(VHDL综合器不接受此类文字)。如: 60s (60秒), 100m (100米), k (千欧姆), 177A (177安培)
二、 字符串 (1)文字字符串——是用双引号括起的一串文字。如: 二、 字符串 (1)文字字符串——是用双引号括起的一串文字。如: "ERROR" , "Both S and Q equal to 1" , "X" , "BB$CC“ (2)数位字符串——也称位矢量,是预定义的数据类型BIT 的一位数组。其表示首先要有计算基数,然后将该基数表示的值放在双引号中,基数符以“B”、”O”和“X”表示,并放在字符串的前面。它们的含义是: B:二进制基数符号,表示二进制位0或1,在字符串中的每位表示一个BIT。 O:八进制基数符号,在字符串中的每一个数代表一个八进制数,即代表一个3位(BIT)的二进制数。 X:十六进制基数符号(0~F),代表一个十六进制数,即一个4位的二进制数。 NB:字符是单引号括起的ASCII字符,也可以是符号或字母.
例: data1 <= B"1_1101_1110" -- 二进制数数组,位矢数组长度是9 data2 <= O"15" -- 八进制数数组,位矢数组长度是6 data3 <= X"AD0" -- 十六进制数数组,位矢数组长度是12 data4 <= B"101_010_101_010" -- 二进制数数组,位矢数组长度是12 data5 <= "101_010_101_010" --表达错误,缺B。 data6 <= "0AD0" --表达错误,缺X。
标志符是最常用的操作符,可是常数、变量、信号、端口、子程序或参数的名字。 三、 标识符 标志符是最常用的操作符,可是常数、变量、信号、端口、子程序或参数的名字。 其规则为: l有效的字符:包括26个大小写英文字母,数字包括0~9 以及下划线“_”。 l任何标识符必须以英文字母开头。 l必须是单一下划线“_”,且其前后都必须有英文字母或数字。 l标识符中的英语字母不分大小写。 l允许包含图形符号(如回车符、换行符等),也允许包含空格符。
合法的标识符:Decoder_1 , FFT, Sig_N , Not_Ack , State0 , Idle 非法的标识符: _Decoder_1 --起始为非英文字母 2FFT --起始为数字 Sig_#N --符号”#”不能成为标识符的构成 Not-Ack --符号”-”不能成为标识符的构成 RyY_rst_ --标识符的最后不能是下划线”_” data_ _BUS --标识符中不能有双下划线”_” return --关键词
四、 下标名 下标段名用于指示数组型变量或信号的某一段元素。 下标名的语句格式: 标识符(表达式) 下标名用于指示数组型变量或信号的某一元素。 下标段名用于指示数组型变量或信号的某一段元素。 下标名的语句格式: 标识符(表达式) 其中,“标识符”必须是数组型的变量或信号的名字;“表达式”所代表的值必须是数组下标范围中的一个值,这个值将对应数组中的一个元素。 如果这个表达式是一个可计算值,则此操作数容易综合,反之,则只能在特定的情况下综合,且耗费资源较大。
例: SIGNAL a,b :BIT_VECTOR (0 TO 3); SIGNAL m :INTEGER RANGE 0 TO 3; SIGNAL y,z :BIT; y<=A(m); --不可计算型下标表示 z<=b(3) ; --可计算型下标表示
7.5 数据类型 一、VHDL中的数据类型——可分为四类: 7.5 数据类型 一、VHDL中的数据类型——可分为四类: 1、标量型(Scalar Type):包括实数类型、整数类型、枚举类型、时间类型。 2、复合类型(Composite Type):可由小的数据类型复合而成,如可由标量型复合而成; 主要有数组型(Array)和记录型(Record) 3、存取类型(Access Type):为给定的数据类型的数据对象提供存取方式。 4、文件类型(Files Type):用于提供多值存取类型。
二、 VHDL的预定义数据类型 VHDL的预定义数据类型都是在VHDL标准程序包STANDARD 中定义的。 布尔(BOOLEAN)数据类型:它是一个二值枚举型数据类型,布尔量不能用于运算,只能通过关系运算符获得。 源代码是:TYPE BOOLEAN IS (FALSE,TURE); 2. 位(BIT)数据类型:属于枚举型,取值只能是1或者0;可参与逻辑运算,运算结果仍为位数据类型。 源代码是:TYPE BIT IS (‘0’,’1’);
3. 位矢量(BIT_VECTOR)数据类型 它只是基于位数据类型的数组,使用位矢量必须注明位宽(数组中的元素个数和排列)。 源代码是: TYPE BIT-VECTOR IS ARRY(NATURAL RANGE <>)OF BIT; 4. 字符(CHARACTER)数据类型 通常用单引号引起来,如‘A’。字符类型区分大小写,如‘B’不同于’b’。 5. 整数(INTEGER)数据类型: 整数类型的数代表正整数、负整数和零,可使用预定义的运算操作符,如加“+”、减“-”、乘“*”除“/”等进行算术运算,其取值范围是-2147483647- +2147483647,即用32位有符号的二进制数表示。
6. 实数(REAL)数据类型: VHDL的实数类型类似与数学上的实数,或称浮点数,其取值范围是- 1.0E38- +1.0E38。通常情况下,它仅能在VHDL仿真器中使用,VHDL综合器不支持实数。实数常量的书写方式举例如下:
7. 字符串(STRING)数据类型:或称字符串数组,字符串必须用双引号标明,VHDL综合器支持字符串数据类型。 其示例如下: VARIABLE string_var : STRING (1 TO 7 ) ; string_var := "a b c d" ; 8. 时间(TIME)数据类型 VHDL中唯一的预定义物理类型是时间。完整的时间类型包括整数和物理量单位两部分,表达上整数和单位之间至少留一个空格,如55 ms,20 ns。
STANDARD程序包中也定义了时间。定义如下: TYPE time IS RANGE -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 ;
三、 IEEE预定义标准逻辑位与矢量 标准逻辑位STD_LOGIC数据类型:是标准BIT数据类型的扩展,共定义了九种值,而不是只有逻辑0和1两种取值。 2. 标准逻辑矢量(STD_LOGIC_VECTOR)数据类型:是定义在 STD_LOGIC_1164程序包中的标准一维数组,数组中的每一个元素的数据类型都是标准逻辑位STD_LOGIC。使用中,必须考虑位矢的宽度,同位宽、同数据类型的矢量间才能进行赋值。 STD_LOGIC_VECTOR类型定义如下: TYPE STD_LOGIC_VECTOR IS ARRAY ( NATURAL RANGE <> ) OF STD_LOGIC ;
四、 其他预定义标准数据类型 无符号数据类型(UNSIGNED TYPE):它代表一个无符号的数值,在综合器中,这个数值被解释为一个二进制数,这个二进制数的最左位是其最高位。如:十进制的8可表示为:UNSIGNED‘(“1000”)。如果要定义一个变量或信号的数据类型为UNSIGNED,则其位矢长度越长,所能代表的数值就越大;如一个4位变量的最大值为15,0是其最小值;不能用UNSIGNED定义负数。 两则无符号数据定义的示例: VARIABLE var : UNSIGNED(0 TO 10) ; SIGNAL sig : UNSIGNED(5 TO 0) ; 2. 有符号数据类型(SIGNED TYPE):表示一个有符号的数值,综合器将其解释为补码,此数的最高位是符号位。例如: SIGNED'("0101") 代表 +5,5 SIGNED'("1011") 代表 –5
五、 数组类型 数组类型属复合类型,是将一组具有相同数据类型的元素集合在一起,作为一个数据对象来处理的数据类型。VHDL仿真器支持多维数组,但综合器只支持一维数组。 VHDL允许定义两种不同类型的数组,即限定性数组和非限定性数组;其区别是:前者下标的取值范围在数组定义时就被确定了,而后者下标的取值范围留待随后确定。
TYPE 数组名IS ARRAY (数组范围)OF 数据类型 ; 限定性数组定义语句格式如下: TYPE 数组名IS ARRAY (数组范围)OF 数据类型 ; 其中:“数组名”是新定义的限定性数组类型的名称,可是任何标识符;“数组范围”明确指出数组元素的定义数量和排序方式,以整数来表示其数组的下标;“数据类型”指数组各元素的数据类型。 非限制性数组的定义语句格式如下: TYPE 数组名IS ARRAY (数组下标名RANGE )OF 数据类型 ; 其中:“数组名”是定义的非限定性数组类型的取名;“数组下标名”是以整数类型设定的一个数组下标名称,符号“〈〉”是下标范围待定符号,用到该数组类型时,再填入具体的取值范围;“数据类型”指数组各元素的数据类型。
【例7-13】——调用预定义的类型转换函数的示例 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY amp IS PORT ( a1,a2 : IN BIT_VECTOR(3 DOWNTO 0); c1,c2,c3 : IN STD_LOGIC_VECTOR (3 DOWNTO 0); b1,b2,b3 : INTEGER RANGE 0 TO 15; d2,d3,d4 : OUT STD_LOGIC_VECTOR(3 DOWNTO 0)); END amp; d1 <= TO_STDLOGICVECTOR(a1 AND a2); --(1) d2 < = CONV_STD_LOGIC_VECTOR(b1,4) WHEN CONV_INTEGER(b2)=9 else CONV_STD_LOGIC_VECTOR(b3,4); --(2) d3 < = c1 WHEN CONV_INTEGER(c2)= 8 ELSE c3; --(3) d4 < = c1 WHEN c2 = 8 else c3; --(4)
【例7-14】——利用转换函数CONV_INTEGER()设计3-8译码器 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY decoder3to8 IS PORT ( input: IN STD_LOGIC_VECTOR (2 DOWNTO 0); output: OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END decoder3to8; ARCHITECTURE behave OF decoder3to8 IS BEGIN PROCESS (input) output <= (OTHERS => '0'); output(CONV_INTEGER(input)) <= '1'; END PROCESS; END behave;
【例7-15】——几个预定义转换函数的函数首表达式 FUNCTION To_bit (s : std_ulogic; xmap : BIT := '0') RETURN BIT ; FUNCTION To_bitvector ( s : std_logic_vector ; xmap : BIT := '0') RETURN BIT_VECTOR ; FUNCTION To_bitvector ( s : std_ulogic_vector ;
下面是转换函数To_bitvector的函数体: FUNCTION To_bitvector ( s : std_logic_vector ; xmap : BIT := '0' ) RETURN BIT_VECTOR IS ALIAS sv : std_logic_vector(s'LENGTH-1 DOWNTO 0 ) IS s ; VARIABLE result : BIT_VECTOR(s'LENGTH-1 DOWNTO 0 ); BEGIN FOR i IN result'RANGE LOOP CASE sv(i) IS WHEN '0'|'L' => result(i) := '0'; WHEN '1'|'H' => result(i) := '1'; WHEN OTHERS => result(i) := xmap; END CASE ; END LOOP ; RETURN result ; END ;
7.6 VHDL操作符 与传统的程序设计语言一样, VHDL各种表达式中的基本元素也是由不同类型的运算符相连而成的。这里所说的基本元素称为操作数(Operands),运算符称为操作符(Operators)。操作数和操作符相结合就成了描述VHDL算术或逻辑运算的表达式;其中 ,操作数是各种运算的对象,而操作符规定运算的方式。 在VHDL中有四类操作符,即逻辑操作符、关系操作符、算术操作符和符号操作符,此外还有重载操作符。前三类操作符是完成逻辑和算术运算的最基本的操作符单元,重载操作符是对基本操作符作了重新定义的函数型操作符。
一、 逻辑操作符(Logical Operator) VHDL共有七种基本逻辑操作符。逻辑运算符AND、OR、NAND、NOR、XOR、XNOR、NOT对BIT或BOOLEAN型的值进行运算。 如果逻辑操作符左边和右边值的类型为数组,则这两个数组的尺寸(即位宽)要相等。通常,在一个表达式中有两个以上的算符时,需要使用括号将这些运算分组——如果一串运算中的算符相同,且是AND、OR、XOR这三个算符中的一种,则不需要使用括号;如果一串运算中的算符不同或除这三种算符之外的算符,则必须使用括号。 如: A and B and C and D (A or B ) xor C 表7-1列出了各种操作符功能及所要求的数据类型。 表7-2列出了VHDL操作符优先级。
表7-1 VHDL操作符列表
表7-1 VHDL操作符列表
表7-2 VHDL操作符优先级
【例7-16】 SIGNAL a ,b,c : STD_LOGIC_VECTOR (3 DOWNTO 0) ; SIGNAL d,e,f,g : STD_LOGIC_VECTOR (1 DOWNTO 0) ; SIGNAL h,I,j,k : STD_LOGIC ; SIGNAL l,m,n,o,p : BOOLEAN ; ... a<=b AND c; --b、c 相与后向a赋值,a、b、c的数据类型同属4位长的位矢量 d<=e OR f OR g ; -- 两个操作符OR相同,不需括号 h<=(i NAND j)NAND k ; -- NAND不属上述三种算符中的一种,必须加括号 l<=(m XOR n)AND(o XOR p); -- 操作符不同,必须加括号 h<=i AND j AND k ; -- 两个操作符都是AND,不必加括号 h<=i AND j OR k ; -- 两个操作符不同,未加括号,表达错误 a<=b AND e ; -- 操作数b 与 e的位矢长度不一致,表达错误 h<=i OR l ; -- i 的数据类型是位STD_LOGIC,而l的数据类型是 ... -- 布尔量BOOLEAN,因而不能相互作用,表达错误。
二、 关系操作符(Relational Operator) 关系操作符的作用: 是将相同数据类型的数据对象进行数值比较或关系排序判断,并将结果以布尔类型的数据表示出来,即TURE或FLASE两种。 六种关系运算操作符: “ = ”(等于)、“/=”(不等于)、“ >”(大于)、“< ”(小于)、“>=”(大于等于)“<=”(小于等于)
【例7-17】 ENTITY relational_ops_1 IS PORT ( a,b : IN BIT_VECTOR (0 TO 3) ; m : OUT BOOLEAN) ; END relational_ops_1 ; ARCHITECTURE example OF relational_ops_1 IS BEGIN output <= (a = b) ; END example ; 【例7-18】 ENTITY relational_ops_2 IS PORT (a,b : IN INTEGER RANGE 0 TO 3 ; m : OUT BOOLEAN) ; END relational_ops_2 ; ARCHITECTURE example OF relational_ops_2 IS BEGIN output <= (a >= b) ; END example ;
三、 算术操作符(Arithmetic Operator) 表7-3 算术操作符分类表
1. 求和操作符:包括加减操作符和并置操作符。 【例7-19】 VARIABLE a,b ,c ,d ,e ,f : INTEGER RANGE 0 TO 255 ; ... a := b + c ; d := e – f ; 2. 求积操作符 求积操作符包括 * (乘)、 / (除)、MOD(取模)和RED(取余)四种操作符。
乘与除的数据类型是整数和实数(包括浮点数)——乘除运算一般不使用,可用移位相加、查表、LPM专用模块等方法变通;MOD和RED的操作数数据类型只能是整数,运算操作结果也是整数。 注:10、乘法运算符的逻辑实现,要求它的操作数是常数或是2的乘方时才能被综合;对于除法,除数必须是底数为2 的幂(综合中可以通过右移来实现除法)。 20、MAX+PUSⅡ限制“*”、“/”号右边操作数必须为2的乘方,如x*8。如果使用MAX+PUSⅡ的LPM库中的子程序则无此限制,此外, MAX+PUSⅡ不支持MOD和REM算符。
3. 符号操作符 符号操作符“+”和“-”的操作数只有一个,操作数的数据类型是整数,操作符“+”对操作数不作任何改变,操作符“-”作用于操作数后的返回值是对原操作数取负,在实际使用中,取负操作数需加括号,如:z:=x*(-y)
注: MAX+PUSⅡ不支持混合操作符“**”和“ABS”。FUNDATION FPGA Express限定 **算符左边的操作数必须为2。 4. 混合操作符 混合操作符包括乘方“**”操作符和取绝对值“ABS”操作符两种。VHDL规定,它们的操作数数据类型一般为整数类型。乘方运算的左边可以是整数或浮点数,但右边必须为整数,而且只有在左边为浮点时,其右边才可以为负数。 注: MAX+PUSⅡ不支持混合操作符“**”和“ABS”。FUNDATION FPGA Express限定 **算符左边的操作数必须为2。 【例7-22】 SIGNAL a,b : INTEGER RANGE -8 to 7 ; SIGNAL c : INTEGER RANGE 0 to 15 ; SIGNAL d : INTEGER RANGE 0 to 3 ; a <= ABS(b) ; c <= 2 ** d ;
SLL是将位矢向左移,右边跟进的位补零。 SRL的功能与SLL相反。 5. 移位操作符 六种移位操作符SLL、SRL、SLA、SRA、ROL和ROR都是VHDL’93标准新增的运算符,在1987标准中没有,有的综合器尚不支持此类操作。 SLL是将位矢向左移,右边跟进的位补零。 SRL的功能与SLL相反。 ROL和ROR的移位方式不同,她们移出的位将用于依次填补移空的位,执行的是自循环式移位方式。 SLA和SRA是算术移位操作符,其移空位用最初的首位来填补。 移位操作符的语句格式是: 标识符 移位操作符 移位位数 ; 注:目前许多综合器不支持以上格式,除非其“标识符”该为常数位矢,如“1011”。
【例7-23】——利用移位操作符SLL和程序包STD_LOGIC_UNSIGNED中的数据类型转换函数CONV_INTEGER设计3-8译码器 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY decoder3to8 IS port ( input: IN STD_LOGIC_VECTOR (2 DOWNTO 0); output: OUT BIT_VECTOR (7 DOWNTO 0)); END decoder3to8; ARCHITECTURE behave OF decoder3to8 IS BEGIN output <= "00000001" SLL CONV_INTEGER(input); --被移位部分是常数! END behave;
作 业 P244 8-10,*8-15