首页资源分类FPGA/CPLD > VHDL语言基础

VHDL语言基础

已有 445464个资源

下载专区

上传者其他资源

    FPGA/CPLD热门资源

    本周本月全部

    文档信息举报收藏

    标    签:硬件设计语言

    分    享:

    文档简介

    用于设计FPGA必不可少的硬件设计语言

    文档预览

    EDA 技术 ――VHDL 语言基础 概述 1. 常见英文缩写解释(按字母顺序排列): ASIC: Application Specific Integrated Circuit. 专用 IC CPLD: Complex Programmable Logic Device. 复杂可编程逻辑器件 EDA: Electronic Design Automation. 电子设计自动化 FPGA: Field Programmable Gate Array. 现场可编程门阵列 GAL: Generic Array Logic. 通用阵列逻辑 HDL: Hardware Description Language. 硬件描述语言 IP: Intelligent Property. 智能模块 PAL: Programmable Array Logic. 可编程阵列逻辑 RTL: Register Transfer Level. 寄存器传输级(描述) SOC: System On a Chip. 片上系统 SLIC: System Level IC. 系统级 IC VHDL: Very high speed integrated circuit Hardware Description Language. 超高速集成电路硬件描述语言 第一章 VHDL 基本结构 内容:1.1 实体 1.2 结构体 1.3 块、子程序和进程 1.4 库和程序包 1.5 配置 一个完整的 VHDL 程序,通常要求最低能为 VHDL 综合器所支持,并能作 为一个独立的设计单元,即以元件的形式存在的 VHDL 程序。在 VHDL 程序中, 通 常 包 含 实 体 ( ENTITY )、 结 构 体 ( ARCHITECTURE )、 配 置 (CONFIGURATION)、包集合(PACKAGE)和库(LIBRARY)5 个部分。其 中实体和结构体这两个基本结构是必需的,他们可以构成最简单的 VHDL 程序。 1.1 实体 实体是 VHDL 语言源代码的基本单元,实体说明是对所设计电路与外部电路 进行接口的描述,它规定了设计单元的输入输出接口信号或引脚,是设计实体对 外的一个通信界面。 实体语句结构如下: ENTITY 实体名 IS [GENERIC(类属表);] [PORT(端口表);] END ENTITY 实体名; 例: ENTITY or2 IS PORT(a,b:IN STD_LOGIC; c:OUT STD_LOGIC); END ENTITY or2; 1.1.1 类属参量(GENERIC) 类属参量是实体说明中的可选项,位置在端口说明之前,其一般格式为: GENERIC [CONSTANT] 名字表:[IN] 子类型标识 [:= 静态表达式],…] 类属参量是一种端口界面常数,常用来规定端口的大小、实体中子元件的数 目及实体的定时特性等。设计者可以从外面通过类属参量的重新设定而容易的改 变一个设计实体或一个元件的内部电路结构和规模。 例: GENERIC (trise,tfall:TIME:=1ns; Addrwidth:INTEGER:=16); PORT(a0, a1 : IN STD_LOGIC; Add_bus:OUT STD_LOGIC_VECTOR(addrwidth-1 DOWNTO 0); 这里类属参量中参数 trise 为上升沿宽度,tfall 为下降沿宽度,用于仿真模块的设 计;定义地址总线的宽度为 Addrwidth 位,类属值 Addrwidth 的改变将使结构体 中所有相关的总线定义同时改变,由此使整个设计实体的硬件结构发生变化。 1.1.2 端口说明(PORT) 端口为所设计电路与外部电路连接通道的说明,是对设计单元与外部接口的 描述,其功能相当电路图符号的外部引脚。端口可以被赋值,也可以当做逻辑变 量用在逻辑表达式中。 其一般书写格式为: PORT (端口名 :端口模式 数据类型; …… 端口名 :端口模式 数据类型); 其中端口名是设计者为实体的每一个对外通道所取的名字,通常为英文字母加数 字。端口模式是指这些通道上的数据流动的方式,如输入或输出等。 端口模式有以下几种类型: 输入(IN); 输出(OUT); 双向模式(INOUT);缓冲(BUFFER)。端口 模式可用图下说明,图中方框代表一个设计实体或模块。 IN OUT INOUT BUFFER 从端口的名称、模式就能一目了然地指导信号的用途、性质、来源和去向。 1.2 结构体 结构体也叫构造体,结构体描述了所设计电路的结构、行为、元件及内部连 接关系,也就是说它定义了电路的逻辑功能。结构体对其逻辑功能的描述可用以 下三种方式,即行为描述(设计单元的数学模型描述)、寄存器传输描述(数据 流描述)和结构描述(逻辑元件连接描述)。 结构体一般由两大部分组成: 1.对数据类型、常数、信号、子程序和元件等因素进行说明、定义的部分; 2.描述实体的逻辑行为、以各种不同的描述风格表达的功能描述语句,包括各 种顺序语句和并行语句。 结构体的语句格式为: ARCHITECTURE 结构体名 OF 实体名 IS [定义语句] BEGIN [功能描述语句] END 结构体名; 1.2.1 结构体名 结构体名由设计者自行定义,通常用 dataflow(数据流)、behave(行为)、 structural(结构)命名。这 3 个名称体现了 3 种不同结构体的描述方式,使得阅 读 VHDL 语言程序时,能直接了解设计者采用的描述方式。 1.2.2 结构体信号定义语句 结构体信号定义语句必须放在关键词 ARCHITECTURE 和 BEGIN 之间,用 于对结构体内部将要使用的信号、常数、数据类型、元件、函数和过程加以说明。 需要注意的是结构体定义的信号为该结构体的内部信号,它只能用于这个结 构体中。 例:结构体的信号定义实例。 ARCHITECTURE rtl OF muj IS SIGNAL s1:BIT; SIGNAL s2,s3:STD_LOGIC_VECTOR (0 TO 3); …… BEGIN …… END rtl; 1.2.3 结构体功能描述语句 结构体功能描述语句位于 BEGIN 和 END 之间,具体地描述了电路的行为、 功能或连接关系。结构体的功能描述语句可以含有 5 种不同类型的并行语句,如 下图所示。 图中 5 种功能描述语句的基本组成和功能分 别是: 块语句是由一系列并行语句构成的组合 体,它的功能是将结构体中的并行语句组成 一个或多个子模块,增加程序的可读性。 进程语句定义顺序语句模块,用以将从 外部获得的信号值或内部运算数据向其他信 号进行赋值。 信号赋值语句将设计实体内的处理结果 向定义的信号或界面端口进行赋值。 子程序调用语句可以调用进程或参数, 并将获得的结果赋值于信号。 元件例化语句对其他的设计成果做元件 结构体 ( ) ARCHITECTURE 说明语句 功能描述语句 块语句(BLOCK) 进程语句(PROCESS) 信号赋值语句 子程序调用语句 元件例化语句 调用说明,并将此元件的端口与其他元件、 信号或高层实体的界面端口进行连接。 各语句后面将介绍 例: 2 选 1 数据选择器 ENTTITY mux2 IS PORT (d0,d1:IN BIT; sel:IN BIT; s:OUT BIT); END mux2; ARCHITECTURE dataflow OF mux2 IS SIGNAL sig:BIT; BEGIN Sig <= (d0 AND sel) OR (NOT sel AND d1); S<=sig; END dataflow; 1.3 块、子程序和进程 1.3.1 块语句(BLOCK) 在较大规模的电子系统设计中,传统的硬件电路设计通常包括一张系统总电 路原理图和若干张子原理图。在 VHDL 程序设计中,结构体是由多个 BLOCK 块构成的,如果将结构体比做总电路原理图,那么,每个 BLOCK 块则相当于一 张子原理图。 BLOCK 块语句的结构: 块标号:BLOCK 接口说明 类属说明 BEGIN 并行块语句 END BLOCK(块标号); 例: B1: BLOCK SIGNAL s1:BIT; BEGIN s1 <= a AND b; B2: BLOCK SIGNAL s2:BIT; BEGIN s2<= c AND d; B3: BLOCK BEGIN Z <= s2; END BLOCK B3; END BLOCK B2; y<= s1; END BLOCK B1; 1.3.2 进程(PROCESS) PROCESS 结构是最能体现 VHDL 语言特色的语句。与 BLOCK 语句一样, 利用 PROCESS 语句结构可以描述一个功能独立的电路。与 BLOCK 语句不同之 处是,在系统仿真时,PROCESS 结构中的语句是按顺序逐条向下执行的,而不 像 BLOCK 语句那样并发执行。一个结构体中可以有多个并行运行的进程结构, 每一个进程内部是由一系列顺序语句来构成的。 PROCESS 语句的结构 [进程标号] PROCESS [进程说明语句] BEGIN [(敏感信号表)] [IS] 顺序描述语句 END PROCESS [进程标号]; 注意:在多个进程的结构体描述中,进程标号是区分各个进程的标志。但是进程 标号并不是必需的。单进程以 PROCESS 开始,以 END PROCESS 结束。 例:两输入或非门 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY nor2_v2 IS PORT(a,b: IN STD_LOGIC; y: OUT STD_LOGIC); END nor2_v2; ARCHITECTURE behave OF nor2_v2 IS BEGIN PROCESS (a,b) VARIABLE comb : STD_LOGIC_VECTOR(1 DOWNTO 0); BEGIN comb := a & b; CASE comb IS WHEN "00"=> y <='1'; WHEN "01"=> y <='0'; WHEN "10"=> y <='0'; WHEN "11"=> y <='0'; WHEN OTHERS => y <='X'; END CASE; END PROCESS; END behave; 1.3.3 子程序(SUBPROGRAM) VHDL 程序与其他软件语言程序中应用子程序的目的是相似的,即能够更有 效地完成重复性的工作。子程序模块是利用顺序语句定义和完成算法的,但子程 序不能像进程那样可以从本结构体的其他块或进程结构中读取信号值或向信号 赋值,只能通过子程序调用与子程序的界面端口进行通信。 子程序被调用时,首先要初始化,执行处理功能后,将处理结果传递给主程 序。子程序内部的值不能保持,子程序返回后才能被再次调用并初始化。 子程序有两种类型:过程(PROCEDURE)和函数(FUNCTION)。 1.过程(PROCEDURE) 过程语句的书写格式为: PROCEDURE 过程名(参数表)IS [说明部分] BEGIN 过程语句部分 END PROCEDURE 过程名; 例:用过程语句设计的子程序 PROCEDURE vector_to_int (a: IN STD_LOGIC_VECTOR; x_flag: OUT BOOLEAN; q: INOUT INTEGER) IS BEGIN q := 0; x_flag := FALSE; FOR i IN a’RANGE LOOP q := q*2; --*: 乘, **:乘方 IF (a (i) =1) THEN q := q+1; ELSE (a (i) /=1) THEN -- /=:不等 x_flag := TRUE; END IF; END LOOP; END vector_to_int; 这个过程名为 vector_to_int 实现将位矢量转换成整数的功能,在过程语句执 行结束后,将输入值拷贝到调用者的 OUT 和 INOUT 所定义的变量中,完成子 程序和主程序之间的数据传递。 2. 函数 函数的语言书写格式为: FUNCTION 函数名 (参数表) RETURN 数据类型 IS [说明部分]; BEGIN 顺序语句; RETURN [返回变量名]; END RETURN 函数名; 例: 用 VHDL 构造的选择最大值的函数程序。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; PACKAGE bpac IS FUNCTION max (a: STD_LOGIC_VECTOR; b: STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; END bpac; PACKAGE BODY bpac IS FUNCTION max (a: STD_LOGIC_VECTOR; b: STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS VARIABLE tmp: STD_LOGIC_VECTOR (a’RANGE); BEGIN IF (a>b) THEN tmp := a; ELSE tmp := b; END IF; RETURN tmp; END; END bpac; 1.4 库和程序包 库和程序包用来描述和保留元件、类型说明函数、子程序等,以便在其它设 计中可以随时引用这些信息,提高设计效率。 1.4.1 库(LIBRARY) 库是经编译后的数据的集合,它存放包集合定义、实体定义、结构定义和 配置定义。 库语句的格式为: LIBRARY 库名; USE 语句指明库中的程序包。一旦说明了库和程序包,整个设计实体都可 以进入访问或调用,但其作用范围仅限于所说明的设计实体。USE 语句的使用 将使所说明的程序包对本设计实体部分或全部开放。 USE 语句有以下两种常用的格式: USE 库名.程序包名.项目名; USE 库名.程序包名.ALL; 第一种语句格式的作用是向本设计实体开放指定库中的特定程序包内的所选定 的项目。 第二种语句格式的作用是向本设计实体开放指定库中的特定程序包内的所 有内容。 例如: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_1164.STD_ULOGIC; 此例中,第一个 USE 语句表明打开 IEEE 库中的 STD_LOGIC_1164 程序 包,并使程序包中的所有公共资源对本语句后面的 VHDL 设计实体程序全部开 放,关键词 ALL 代表程序包中的所有资源。第二个 USE 语句开放了程序包 STD_LOGIC_1164 中的 STD_ULOGIC 数据类型。 STD_ULOGIC :可枚举数据类型 注意:库说明语句的作用范围从一个实体说明开始到它所属的结构体、配置为止, 当一个源程序中出现两个以上实体时,两条作为使用库的说明语句应在每个设计 实体说明语句前重复书写。 例: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY and IS --库使用说明 ┇ END and; ARCHITECTURE dataflow OF and IS ┇ END dataflow; CONFIGURATION c1 OF and IS ┇ AND c1; LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY or IS CONFIGURATION c2 OF and IS ┇ AND c2; -- CONFIGURATION(配置) --库使用说明 1.4.2 程序包 程序包也叫包集合,主要用来存放各个设计都能共享的数据类型、子程 序说明、属性说明和元件说明等部分。设计者使用时只要用 USE 子句进行说明 即可。 程序包由两部分组成:程序包首和程序包体。 程序包的一般书写格式如下: PACKAGE 程序包名 IS END [PACKAGE] 程序包名; PACKAGE BODY 程序包名 IS --程序包首 ┇ --程序包体 END [PACKAGE BODY] [程序包名]; 例: USE STD.STD_LOGIC.ALL; PACKAGE math IS TYPE tw16 IS ARRAY(0 TO 15)OF T_WLOGIC; FUNCTION add (a,b:IN tw16)RETURN tw16; FUNCTION sub (a,b:IN tw16)RETURN tw16; END math; PACKAGE BODY math IS FUNCTION vect_to_int(s:tw16); RETURN INTEGER IS VARIBLE result:INTEGER :=0; BEGIN FOR i IN 0 TO 7 LOOP result := result*2; IF s(i)=’1’THEN result := result+1 END IF; END LOOP; RETURN result; END vect_to_int; FUNCTION int_to_tw16(s:INTEGER); RETURN tw16 IS VARIBLE result: tw16; VARIBLE digit: INTEGER:=2**15; VARIBLE local: INTEGER; BEGIN local := s; FOR i IN 15 DOWNTO 0 LOOP IF local/ digit>=1 THEN Local := local- digit; ELSE result (i) :=0; END IF; digit := digit/2; END LOOP; RETURN result; END int_to_tw16; FUNCTION add(a,b:IN tw16); RETURN tw16 IS VARIBLE result: INTEGER; BEGIN result := vect_to_int (a)+ vect_to_int (b); RETURN int_to_tw16(result); END add; FUNCTION sub(a,b:IN tw16); RETURN tw16 IS VARIBLE result: INTEGER; BEGIN result := vect_to_int (a)+ vect_to_int (b); RETURN int_to_tw16(result); END sub; END math; 此例的程序包是由程序包首和程序包体两部分组成。程序包首定义了数 据类型和函数的调用说明,程序包体中才具体描述实现该函数功能的语句和数据 的赋值。这种分开描述的好处是,当函数的功能需要做某些调整或数据赋值需要 变化时,只要改变程序包体的相关语句就可以了,而无需改变程序包首的说明, 这样就使得需要重新编译的单元数目尽可能的减少了。 1.5 配置 在用 VHDL 描述硬件电路时,常常采用结构描述方式和混合描述方式。在这两 种描述方式中,常常需要将其他设计实体作为元件进行引用,这时就需要将不同 元件通过配置安装到不同的设计实体中。VHDL 提供了配置语句用于描述各种设 计实体和元件之间连接关系以及设计实体和结构体之间的连接关系。 1.5.1 默认配置 默认配置语句的基本格式为: CONFIGURATION 配置名 OF 实体名 IS FOR 选配结构体名 END FOR END 配置名; 例:与、或、与非、或非、异或,5 个结构体共用一个实体。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY example_v IS PORT (a : IN STD_LOGIC; b : IN STD_LOGIC; y : OUT STD_LOGIC); END example_v; ARCHITECTURE and2_arc OF example_v IS BEGIN y <= a AND b; END and2_arc; ARCHITECTURE or2_arc OF example_v IS BEGIN y <= a OR b; END or2_arc; ARCHITECTURE nand2_arc OF example_v IS BEGIN y <= NOT(a AND b); END nand2_arc; ARCHITECTURE nor2_arc OF example_v IS BEGIN y <= NOT(a OR b); END nor2_arc; ARCHITECTURE xor2_arc OF example_v IS BEGIN y <= a XOR b; END xor2_arc; CONFIGURATION cfg1 OF example_v IS FOR and2_arc END FOR; END cfg1; CONFIGURATION cfg2 OF example_v IS FOR or2_arc END FOR; END cfg2; CONFIGURATION cfg3 OF example_v IS FOR nand2_arc END FOR; END cfg3; CONFIGURATION cfg4 OF example_v IS FOR nor2_arc END FOR; END cfg4; CONFIGURATION cfg5 OF example_v IS FOR xor2_arc END FOR; END cfg5; 在上例( example_v)中,有 5 个不同的结构体,分别用来完成二输入 的逻辑与、或、与非、或非和逻辑异或的运算操作。在程序中使用了 5 个默认配 置语句来指明设计实体 example_v 和哪个结构体一起组成一个完整的设计:配置 语句 cfg1 将与逻辑结构体配置给实体;cfg2 将或逻辑结构体配置给实体;cfg3 将与非逻辑结构体配置给实体;cfg4 将或非逻辑结构体配置给实体;cfg5 将异或 逻辑结构体配置给实体。在进行模拟的时候,将根据所编译的是上面的哪个配置 来决定要进行模拟的结构体,也就是说最后一个被编译的结构体(异或逻辑)将 被模拟,下图就是异或逻辑的仿真波形。 异或门仿真波形 1.5.2 结构体的配置 结构体的配置主要是用来对结构体中引用的元件进行配置。 结构体的配置的书写格式: FOR <元件例化标号>:<元件名>USE ENTITY <库名>.<实体名(结构体名)>; 以 1 位全加器的构成为例说明结构体的配置的用法: 将两输入与门、或门、异或门设置成通用例化元件由结构体引用。 二输入与门源代码: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY and2_v IS PORT(a: IN STD_LOGIC; b: IN STD_LOGIC; y: OUT STD_LOGIC); END and2_v; ARCHITECTURE and2_arc OF and2_v IS BEGIN y <= a AND b; END and2_arc; CONFIGURATION and2_cfg OF and2_v IS FOR and2_arc END for; END and2_cfg; 与门 and2_v 仿真波形 二输入或门源代码: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY or2_v IS PORT(a: IN STD_LOGIC; b: IN STD_LOGIC; y: OUT STD_LOGIC); END or2_v; ARCHITECTURE or2_arc OF or2_v IS BEGIN y <= a OR b; END or2_arc; CONFIGURATION or2_cfg OF or2_v IS FOR or2_arc END for; END or2_cfg; 或门 or2_v 仿真波形 异或门源代码: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY xor2_v IS PORT(a: IN STD_LOGIC; b: IN STD_LOGIC; y: OUT STD_LOGIC); END xor2_v; ARCHITECTURE xor2_arc OF xor2_v IS BEGIN y <= a XOR b; END xor2_arc; CONFIGURATION xor2_cfg OF xor2_v IS FOR xor2_arc END for; END xor2_cfg; 异或门 xor2_v 仿真波形 全加器源代码: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY add1_v IS PORT(A : IN STD_LOGIC; B : IN STD_LOGIC; Cin : IN STD_LOGIC; Co : OUT STD_LOGIC; S : OUT STD_LOGIC); END add1_v; ARCHITECTURE structure OF add1_v IS COMPONENT and2_v PORT(a : IN STD_LOGIC; b : IN STD_LOGIC; y : OUT STD_LOGIC); END COMPONENT; COMPONENT or2_v PORT(a : IN STD_LOGIC; b : IN STD_LOGIC; y : OUT STD_LOGIC); END COMPONENT; COMPONENT xor2_v PORT(a : IN STD_LOGIC; b : IN STD_LOGIC; y : OUT STD_LOGIC); END COMPONENT; SIGNAL tmp1,tmp2,tmp3 : STD_LOGIC; FOR U1,U2 : xor2_v USE ENTITY work.xor2_v( xor2_arc); FOR U3,U4 : and2_v USE ENTITY work.and2_v( and2_arc); FOR U5 : or2_v USE ENTITY work.or2_v( or2_arc); BEGIN U1 : xor2_v PORT MAP(A,B,tmp1); U2 : xor2_v PORT MAP(tmp1,Cin,S); U3 : and2_v PORT MAP(tmp1,Cin,tmp2); U4 : and2_v PORT MAP(A,B,tmp3); U5 : or2_v PORT MAP(tmp2,tmp3,Co); END structure; 全加器 add1_v 仿真波形 实际上如果仅仅要构成 1 位全加器,采用行为描述方式只需几条逻辑语句即可。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY add1_v IS PORT(A : IN STD_LOGIC; B : IN STD_LOGIC; Cin : IN STD_LOGIC; Co : OUT STD_LOGIC; S : OUT STD_LOGIC); END add1_v; ARCHITECTURE structure OF add1_v IS BEGIN S <= A XOR B XOR Cin; Co <= (A XOR B) AND Cin OR (A AND B); END structure; 全加器 add1_v 仿真波形 第二章 VHDL 语言元素 2.1 VHDL 语言的数据对象 2.2 VHDL 语言的数据类型 2.3 VHDL 数据类型转换 2.4 VHDL 词法规则与标识符 2.1 VHDL 语言的数据对象 VHDL 语言中,可以赋予一个质的对象就称为数据对象。数据对象主要包 括以下三种:变量(VARIABLE)、常量(CONSTANT)、信号(SIGNAL)。 2.1.1 常量(CONSTANT)(常数) 定义一个常数主要是为了使设计实体中的某些量易于阅读和修改。常数说明就是 对某一常数名赋予一个固定的值。通常在程序开始前进行赋值,该值的数据类型 在说明语句中说明。 常数说明语句格式为: CONSTANT 常数名:数据类型 := 表达式; 例如: CONSTANT Vcc : REAL := 5.0; CONSTANT Fbus : BIT_VECTOR := “1011”; CONSTANT Delay : TIME := 10ns; 注:常量是一个恒定不变的值,一旦做了数据类型和赋值定义,它在程序中就不 能再改变。 2.1.2 变量(VARIABLE) 变量只能在进程和子程序中用,是一个局部量,不能将信息带出对它做出定义的 当前设计单元。与信号不同,变量的赋值是理想化数据传输,其赋值是立即生效 的,不存在任何的延时行为。 变量定义语句的格式为: VARIABLE 变量名 : 数据类型 : 约束条件 := 初始值; 例如: VARIABLE n: INTEGER RANGE 0 TO 15 := 2; VARIABLE a: INTEGER; 变量赋值语句的格式为: 目标变量名 := 表达式; 赋值语句 “:=” 右边的表达式必须与目标变量具有相同的数据类型,这个表达式 可以是一个运算表达式也可以是一个数值。变量赋值语句左边的目标变量可以是 单值变量,也可以是变量的集合。 例如定义变量: VARIABLE a,b := REAL; VARIABLE x,y := BIT_VECTOR(0 TO 7); 2.1.3 信号(SIGNAL) 信号是电子电路内部硬件连接的抽象。它可以作为设计实体中的并行语句模块间 交流信息的通道。信号及其相关的延时语句明显地体现了硬件系统的特征。 信号定义语句的格式为: SIGNAL 信号名:数据类型:约束条件 := 表达式; 例如: SIGNAL gnd :BIT := ‘0’; SIGNAL data :STD_LOGIC_VECTOR (7 DOWNTO 0); 信号赋值语句表达式为: 目标信号名 <= 表达式; 符号 “<=” 表示赋值操作,即将数据信息传入。数据信息传入时可以设置延时过 程,这与器件的实际传播延时十分接近。因此信号值的代入采用“<=”代入符,而 不是像变量赋值时那样用“:=”。但信号定义时初始赋值符号“:=”,即仿真的时 间坐标是从赋初始值开始的。 信号赋值语句举例: x <= y; a <= ‘1’; s1 <= s2 AFTER 10 ns; 注意:变量和信号都必须先定义,后赋值。注意赋值符“<=”和 “:=”的差别。 信号与变量的区别: 信号和变量是 VHDL 中重要的数据对象,他们之间的主要区别有: ·信号赋值至少要有δ延时;而变量赋值没有。 ·信号除当前值外有许多相关的信息,如历史信息和投影波形;而变量只有当前 值。 ·进程对信号敏感而不对变量敏感。 ·信号可以是多个进程的全局信号;而变量只在定义他们的顺序域可见(共享变 量除外)。 ·信号是硬件中连线的抽象描述,他们的功能是保存变化的数据值和连接子元件, 信号在元件的端口连接元件。变量在硬件中没有类似的对应关系,他们用于硬件 特性的高层次建模所需要的计算中。 2.2 VHDL 语言的数据类型 在对 VHDL 的数据对象进行定义时,都要指定其数据类型。VHDL 有多种 标准的数据类型,并且允许用户自定义数据类型。在 VHDL 语言语义约束中, 对类型的要求反映在赋值语句的目标与源的一致,表达式中操作的一致,子类型 中约束与类型的一致等许多方面。 2.2.1 VHDL 中预定义的数据类型(编程者可直接使用) 预定义类型在 VHDL 标准程序包 STANDARD 中定义,在应用中自动包 含进 VHDL 的源文件,不需要 USE 语句显示调用。数据类型说明如下。 1.整数(INTEGER) 整数与数学中整数的定义相似,可以使用预定义运算操作符,如加“+”、 减“-”、乘“×”、除“÷”进行算术运算。在 VHDL 语言中,整数的表示范围为- 2147483647~2147483647,即从-(231-1)到(231-1)。 2.实数(REAL) 在进行算法研究或实验时,作为对硬件方案的抽象手段,常常采用实数四则运算。 实数的定义值范围为-1.0E+38~+1.0E+38。实数有正负数,书写时一定要有小 数点。例如:-1.0,+2.5,-1.0E+38 3.位(BIT) 用来表示数字系统中的信号值。位值用字符‘0’或者‘1’(将值放在引号中)表示。 与整数中的 1 和 0 不同,‘1’和‘0’仅仅表示一个位的两种取值。 位数据可以用来描述数字系统中总线的值。位数据不同于布尔数据,可以用转换 函数进行转换。 4.位矢量(BIT_VECTOR) 位矢量是用双引号括起来的一组数据。例如:“001100”,X“00bb”。在这里位矢 量前面的 X 表示是十六进制。用位矢量数据表示总线状态最形象也最方便,在 VHDL 程序中将会经常遇到。使用位矢量时必须注明位宽,即数组中元素个数和 排列,例如: SIGNAL s:BIT_VECTOR(15 DOWNTO 0); 5.布尔量(BOOLEAN) 一个布尔量具有两种状态,“真”或者“假”。虽然布尔量也是二值枚举量,但它和 位不同没有数值的含义,也不能进行算术运算。它能进行关系运算。例如,它可 以在 if 语句中被测试,测试结果产生一个布尔量 TRUE 或者 FALSE。 6.字符(CHARACTER) 字符也是一种数据类型,所定义的字符量通常用单引号括起来,如‘a’。一般 情况下 VHDL 对大小写不敏感,但对字符量中的大小写则认为是不一样的。例 如,‘B’不同于‘b’。字符量中的字符可以是从 a 到 z 中的任一个字母,从 0 到 9 中的任一个数以及空格或者特殊字符,如$,@,%等等。包集合 standard 中给 出了预定义的 128 个 ASCⅡ码字符,不能打印的用标识符给出。字符‘1’与整数 1 和实数 1.0 都是不相同的,当要明确指出 1 的字符数据 时,则可写为: CHARACTER( ‘ 1’)。 7.字符串(STRING) 字符串是由双引号括起来的一个字符序列,也称字符矢量或字符串组。字符串常 用于程序的提示和说明。字符串举例如下: VATIABLE string_1 : STRING (0TO 3); ┇ string_1:= “a b c d”; 8.时间(TIME) 时间是一个物理量数据。完整的时间量数据应包含整数和单位两部分,而且整数 和单位之间至少应留一个空格的位置。例如 55 sec,2 min 等。在包集合 STANDARD 中给出了时间的预定义,其单位为 fs,ps,ns,μs,ms,sec,min 和 hr。例如:20 μs,100 ns,3 sec。 在系统仿真时,时间数据特别有用,用它可以表示信号延时,从而使模型系统能 更逼近实际系统的运行环境。 9.错误等级(SEVERITY LEVEL) 错误等级类型数据用来表征系统的状态,共有 4 种:note(注意),warning(警 告),error(出错),failure(失败)。在系统仿真过程中可以用这 4 种状态来提示 系统当前的工作情况,从而使设计人员随时了解当前系统工作的情况,并根据系 统的不同状态采取相应的对策。 10.大于等于零的整数(自然数)(NATURAL),正整数(POSITIVE) 这两种数据是整数的子类,NATURAL 类数据为取 0 和 0 以上的正整数;而 POSITIVE 则只能为正整数。 上述 10 种数据类型是 VHDL 语言中标准的数据类型,在编程时可以直接引用。 如果用户需使用这 10 种以外的数据类型,则必须进行自定义。但大多数的 CAD 厂商已在包集合中对标准数据类型进行了扩展。例如,数组型数据等,请同学们 注意。 2.2.2 用户自定义的数据类型 可以由用户定义的数据类型有: · 枚举(ENUMERATED)类型; · 整数(INTEGER)类型; · 实数(REAL)、浮点数(FLOATING)类型; · 数组(ARRAY)类型; · 存取(ACCESS)类型; · 文件(FILE)类型; · 记录(RECORDE)类型; · 时间(TIME)类型(物理类型)。 2.2.3 IEEE 预定义标准 1. 标准化数据类型 IEEE‘93 增加了多值逻辑包 STD_LOGIC_1164,使得“STD_LOGIC”数据具有 9 种不同的值。其定义如下所示: TYPE STD_LOGIC IS ( ‘U’, - - 初始值 ‘X’, - - 不定 ‘0’, -- 0 ‘1’, -- 1 ‘Z’, - - 高阻 ‘W’, - - 弱信号不定 ‘L’, - - 弱信号 0 ‘H’, - - 弱信号 1 ‘—’ - - 不可能情况 ); 2.3 VHDL 数据类型转换 在 VHDL 程序设计中不同的数据类型的对象之间不能代入和运算。实现他们之 间数据类型的转换有 3 种方法: ·类型标记法; ·函数转换法; ·常数转换法。 2.3.1 用函数进行类型转换 VHDL 语言中,程序包中提供了变换函数,这些程序包有 3 种,每个程序包中的 变换函数不一样。现列表如下。 ·STD_LOGIC_1164 包集合函数 函数 TO_ STDLOGICVECTOR(A) 由 BIT_VECTOR 转换为 STD_LOGIC_VECTOR 函数 TO_ BITVECTOR(A) 由 STD_LOGIC_VECTOR 转换为 BIT_VECTOR 函数 TO_ STDLOGIC(A) 由 BIT 转换为 STD_LOGIC 函数 TO_ BIT(A) 由 STD_LOGIC 转换为 BIT .STD_LOGIC_ARITH 包集合函数 函数:CONV_STD_LOGIC_VECTOR (A,位长) 由 UNSINGED,SINGED 转换为 INTEGER STD_LOGIC_ UNSINGED 包集合 函数:CONV_INTEGER (A) 由 STD_LOGIC_VECTOR 转换为 INTEGER ·STD_LOGIC_ UNSINGED 包集合 函数:CONV_INTEGER (A) 由 INTEGER,UNSINGED,SINGED 转换为 STD_LOGIC_VECTOR 2.3.2 类型标记法实现类型转换 类型标记就是类型的名称。类型标记法适合那些关系密切的标量类型之间的类型 转换,即整数和实数的类型转换。 例如: VARIABLE I:INTEGER; VARIABLE R:REAL; I := INTEGER(R); R := REAL(I); 2.3.3 常数实现类型转换 就模拟效率而言,利用常数实现类型转换比利用类型转换函数的效率更高。 下面的例子使用常数把类型为 STD_LOGIC 的值转换为 BIT 型的值。 例: LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY typeconv IS END; ARCHITECTURE arch OF typeconv IS TYPE typeconv_type IS ARRAY(STD_ULOGIC)OF BIT; CONSTANT typecon_con : typeconv_type : = ( ‘0’/‘L’=>‘0’ , ‘1’/‘H’ =>1’ , OTHERS=>‘0’); SIGNAL b:BIT; SIGNAL b:BIT; SIGNAL s:STD_ULOGIC; BEGIN b<= typecon_con (s); 2.4 VHDL 操作符 与其他程序设计语言相似,VHDL 中的表达式也是由运算符将基本元素连接起来 形成。这里的基本元素包括对象名、文字、函数调用及用括号括起来的表达式。 在 VHDL 语言中共有 4 类操作符,可以分别进行逻辑运算(LOGICAL)、关系 运 算 ( RELATIONAL )、 算 术 运 算 ( ARITHMETIC ) 和 并 置 运 算 (CONCATENATION)。需要指出的是操作符操作的对象是操作数,且操作数的 类型应该和操作符所要求的类型相一致。另外,运算操作符是有优先级的,例如, 逻辑运算符 not,在所有操作符中优先级最高。 1.逻辑运算符 在 VHDL 语言中,共有 6 种逻辑运算符,他们分别是: NOT 取反; AND 与; OR 或; NAND 与非; NOR 或非; XOR 异或。 这 6 种逻辑运算符可以对“STD_LOGIC ”和“BIT”等逻辑型数据、“STD_LOGIC _VECTOR”逻辑型数组及布尔数据进行逻辑运算。必须注意,运算符的左边和右 边,以及代入的信号的数据类型必须是相同的。 2.算术运算符 VHDL 语言中有 5 类算术运算符,他们分别是: ·求和操作符: +(加)、-(减) ·求积操作符:*(乘)、/(除)、MOD(求模)、REM(取余) ·符号操作符: +(正)、-(负) ·混合操作符: **(指数)、ABS(取绝对值) ·移位操作符:SLL(逻辑左移)、SRL(逻辑右移)、SLA(算术左移)、SRA(算 术右移)、ROL(逻辑循环左移)、ROR 逻辑循环右移) 3.关系运算符 VHDL 语言中有 6 种关系运算符,他们分别是: = 等于; /= 不等于; < 小于; <= 小于等于; > 大于; >= 大于等于; 4.并置运算符 & 连接 SIGNAL g,h,i:STD_LOGIC; SIGNAL c,d,e:STD_LOGIC _VECTOR(1 TO 0); ┇ d <= i & NOT h;- -元素与元素并置,形成长度为 2 的数组 a <= c & d; - -数组与数组并置,形成长度为 4 的数组 5.VHDL 操作符的优先顺序 运算符 优先级 NOT,ABS,** 最高优先级 *,/,MOD,REM +(正号),-(负号) +,-,& SLL,SLA,SRL,SRA,ROL,ROR =,/=,<,>,<=,>= AND,OR,NAND,NOR,XOR,XNOR 最低优先级 2.4 VHDL 词法规则与标识符 2.4.1 词法规则 1.注释 为了提高 VHDL 源程序的可读性,在 VHDL 中可以写入注释。注释以- -开 头直到本行末尾的一段文字。在 MUX+PLUSⅡ中可以看见,敲入- -之后,后面 字体的颜色就发生改变。注释不是 VHDL 设计描述的一部分,编译后存入数据 库中的信息不包含注释。 注释举例: Q:OUT STD_LOGIC _VECTOR(11 DOWNTO 0);--A/D 转换数据输出显示(行 注释) --SRAM 数据写入控制状态机(段注释) WRIT_STATE:PROCESS(clk,rst)--SRAM 写入控制状态机时序电路进程 2.数字 数字型文字可以有多种表达方式:可以是十进制数,也可以表示为二进制、八进 制或十六进制等为基的数,可以是整数,也可以是含有小数点的浮点数。现举例 如下。 ·十进制整数表示法:如 012 5 78_456 (=78456) 2E6 在相邻数字之间插入下划线,对十进制数值不产生影响,仅仅是为了提高文字的 可读性。允许在数字之前冠以若干个 0,但不允许在数字之间存在空格。 ·以基表示的数:用这种方式表示的数由五个部分组成。第一部分,用十进制数 标明数值进位的基数;第二部分,数值隔离符号“#”;第三部分,表达的文字; 第四部分,指数隔离符号“#”;第五部分,用十进制表示的指数部分,这一部分 的数如果为 0 可以省去不写。如 2#111_1011# 8#1473# 16#A8#E1 016#F.01#E+4 对以基表示的数而言,相邻数字间插入下划线不影响数值。基的最小数为 2,最 大数为 16,以基表示的数中允许出现 A 至 F 的字母,大小写字母意义无区别。 ·实数:实数必须带有小数点。如 12.0 0.0 3.14 6_741_113.666 52.6 E-2 ·物理量文字:综合器不支持物理量文字的综合。如 60 s(秒) 100 m (米) 177 A(安培) 3.字符和字符串 字符是用单引号引起来的 ASCⅡ字符,可以是数值,也可以是符号或字母,如 ‘E’, ‘e’, ‘$’, ‘23’, ‘A’… 字符串是一维的字符数组,需放在双引号中。有两种类型的字符串:文字字符串 和位矢量字符串。 文字字符串是用双引号引起来的一串文字。如 “FALSE”, “X”, “THIS IS END” 位矢量字符串是被双引号引起来的扩展的数字序列,数字序列前冠以基数说明 符。基数符有“B”、“O”、“X”,他们的含义如下。 B:二进制基数符号,表示二进制位 0 或 1,在字符串中每一个位表示一个 BIT。 O:八进制基数符号,在字符串中每一个数代表一个八进制数,即代表一个 3 位 (BIT)的二进制数。 X:十六进制基数符号,代表一个十六进制数,即代表一个 4 位二进制数。 例如: B“1011_1111”, O“152”, 4.下标名 X“F821” 下标名用于指示数组型变量或信号的某一元素。 SIGNAL a,b:BIT _VECTOR(0 TO 3); SIGNAL s:INTEGER RANGE 0 TO 2; SIGNAL x,y:BIT; x <= a (s); y <= b (3); 上例中,a (s)为一下标语句,s 是不可计算的下标名,只能在特定情况下进行综 合;b (3)的下标为 3,可以进行综合。 2.4.2 标识符 标识符是最常用的操作符,可以是常数、变量、信号、端口、子程序或参 数的名字。标识符规则是 VHDL 语言中符号书写的一般规则,为 EDA 工具提供 了标准的书写规范。VHDL’93 对 VHDL’87 版本的标识符语法规则进行了扩展, 通常称 VHDL’87 版本标识符为短标识符,VHDL’93 版标识符为扩展标识符。 1.短标识符 VHDL 短标识符需遵守以下规则: (1)必须以英文字母开头; (2)英文字母、数字(0~9)和下划线都是有效的字符; (3)短标识符不区分大小写; (4)下划线(_)的前后都必须有英文字母或数字。 一般的,在书写程序时,应将 VHDL 的保留字大写或黑体,设计者自己定义的 字符小写,以使得程序便于阅读和检查。尽管 VHDL 仿真综合时不区分大小写, 但一个优秀的硬件程序设计师应该养成良好的习惯。 例: 一些合法的标识符: S_MACHINE,present_state,sig3 不合法的标识符: present-state,3states,cons_,_now 2.扩展标识符 扩展标识符的识别和书写有下面的规则: (1)用反斜杠来界定扩展标识符,如 \control_machine\,\s_block\ 等都是合法 的扩展标识符; (2)扩展标识符允许包含图形符号和空格,如 \s&33\,\legal$state\ 是合法的扩 展标识符; (3)两个反斜杠之间的字可以和保留字相同,如 \SIGNAL\,\ENTITY\ 是合法 的标识符,与 SIGNAL、ENTITY 是不同的; (4)两个反斜杠之间的标识符可以用数字开头,如 \15BIT\,\5ns\是合法的; (5)扩展标识符是区分大小写的,如 \a\ 与 \ A\ 是不同的标识符; (6)扩展标识符允许多个下划线相邻,如 \our_ _entity\ 是合法的扩展标识符(不 推荐这种方式); (7)扩展标识符的名字中如果含有一个反斜杠,则用相邻的两个反斜杠来代表 它,如 \te\\xe\ 表示该扩展标识符的名字为 te\xe (共 5 个字符); 第三章 VHDL 的描述风格 3.1 行为描述方式 3.2 数据流描述方式(RTL 描述方式) 3.3 结构化描述方式 3.4 混合描述风格 VHDL 语言是通过结构体具体描述整个设计实体的逻辑功能。通常结构体有四种 不同的描述方式:行为描述方式(behavior)、数据流描述方式(dataflow)或寄 存器 RTL 描述方式、结构化描述方式(structural)以及混合描述方式。VHDL 通过这四种不同的描述方式从不同的侧面描述结构体的功能。前三种是最基本的 描述方式,他们组合起来就成为混合描述方式。 下面结合一个全加器来说明这四种描述风格,全加器的端口示意图如图所 示,其输入输出关系如表所示。 全加器框图 全加器的输入输出关系 x sum y Full c_in Adder c_out 3.1 行为描述方式 行为描述输入与输出间转换的行为,不需包含任何结 构信息,它对设计实体按算法的路径来描述。行为描 述在 EDA 工程中通常被称为高层次描述,设计工程 师只需要注意正确的实体行为、准确的函数模型和精 确的输出结果就可以了,无需关注实体的电路组织和 门级实现。 例:基于全加器真值表采用行为描述方式设计的全加 器(1 位二进制数全加) 对照真值表解释程序 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY full_adder IS GENERIC(tpd : TIME := 10 ns); PORT(x,y,c_in : IN STD_LOGIC; Sum, c_out : OUT STD_LOGIC); END full_adder; ARCHITECTURE behav OF full_adder IS BEGIN PROCESS (x, y, c_in) VARIABLE n: INTEGER; 输入 输出 c_in x y c_ou sum t 0 000 0 0 010 1 0 100 1 0 111 0 1 000 1 1 011 0 1 101 0 1 111 1 CONSTANT sum_vector: STD_LOGIC_VECTOR (0 TO 3) := “0101”; CONSTANT carry_vector: STD_LOGIC_VECTOR (0 TO 3) := “0011”; BEGIN n := 0; IF x = ’1’ THEN n := n+1; END IF; IF y = ’1’ THEN n:=n+1; END IF; IF c_in = ’1’ THEN n:=n+1; END IF; -- (0 TO 3) sum <= sum_vector (n) AFTER 2*tpd; - - sum_vector 初值为“0101” c_out <= carry_vector (n) AFTER 3*tpd; - - carry_vector 初值为“0011” END PROCESS; -- (0 TO 3) END behav; 3.2 数据流描述方式 数据流描述方式表示行为,也隐含表示结构,它描述了数据流的运动 路线、运动方向和运动结果。 对于全加器,用布尔方程描述其逻辑功能如下: s = x XOR y sum = s XOR c_in c_out = (x AND y) OR( s AND c_in) 下面是基于上述布尔方程的数据流风格的描述: 例:采用数据流描述方式的全加器 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY full_adder IS GENERIC(tpd : TIME := 10 ns); PORT(x,y,c_in : IN STD_LOGIC; Sum, c_out : OUT STD_LOGIC); END full_adder; ARCHITECTURE dataflow OF full_adder IS BEGIN s <= x XOR y AFTER tpd; sum <= s XOR c_in AFTER tpd; c_out <= (x AND y) OR( s AND c_in) AFTER 2* tpd; END dataflow; 3.23 结构描述方式 结构化描述方式就是在多层次的设计中,高层次的设计可以调用低层次的设计模 块,或直接用门电路设计单元来构成一个复杂逻辑电路的方法。利用结构化描述 方法将已有的设计成果方便地用于新的设计中,能大大提高设计效率。在结构化 描述中,建模的焦点是端口及其互连关系。 结构化描述的建模步骤如下: (1)元件说明 (2)元件例化 (3)元件配置 元件说明用于描述局部接口;元件例化是要相对于其他元件来放置该元件;元件 配置用于指定元件所用的设计实体。 U2 c_in s Half x s b Adderc c Half y Adder c a sum OR c_out Gate U1 U3 对于上图给出的全加器端口结构,可以认为它是由两个半加器和一个或门组 成的 。 基于上图所示的结构,可以写出全加器的结构化描述设计程序如下。 例:全加器的结构化描述 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY half_adder IS GENERIC(tpd:TIME:=10 ns); PORT(in1, in2: IN STD_LOGIC; sum, carry: OUT STD_LOGIC); END half_adder; ARCHITECTURE behavioral OF half_adder IS BEGIN PROSESS (in1, in2) BEGIN sum <= in1 XOR in2 AFTER tpd; carry <= in1 AND in2 AFTER tpd; END PROCESS; END behavioral; - -半加器设计完毕 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY or_gate IS GENERIC(tpd:TIME:=10 ns); PORT(in1, in2: IN STD_LOGIC; out1: OUT STD_LOGIC); END or_gate; ARCHITECTURE structural OF or_gate IS BEGIN out1 <= in1 OR in2 AFTER tpd; END structural; - - 或门设计完毕 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY full_adder IS GENERIC(tpd:TIME:=10 ns); PORT(x,y,c_in: IN STD_LOGIC; Sum, c_out: OUT STD_LOGIC); END full_adder; ARCHITECTURE structural OF full_adder IS COMPONENT half_adder PORT(in1, in2: IN STD_LOGIC; sum, carry: OUT STD_LOGIC); END COMPONENT; COMPONENT or_gate PORT(in1, in2: IN STD_LOGIC; out1: OUT STD_LOGIC); END COMPONENT; SIGNAL a, b, c:STD_LOGIC; FOR u1,u2 : half_adder USE ENTITY WORK.half_adder (behavioral); FOR u3: or_gate USE ENTITY WORK. or_gate (structural); BEGIN u1: half_adder PORT MAP (x, y, b, a); u2: half_adder PORT MAP (c_in, b, sum, c); u3: or_gate PORT MAP (c, a, c_out); END structural; 由上例可见,对于一个复杂的电子系统,可以将其分解为若干个子系统,每个子 系统再分解成模块,形成多层次设计。这样,可以使更多的设计者同时进行合作。 在多层次设计中,每个层次都可以作为一个元件,再构成一个模块或系统,可以 先分别仿真每个元件,然后再整体调试。所以说结构化描述不仅是一种设计方法, 而且是一种设计思想,是大型电子系统高层次设计的重要手段。 3.4 混合描述风格 在实际设计工作中,可以采用上述三种描述方式的任意组合,这就是混合描述。 同样还是图所给出端口结构的全加器模型,其混合描述方式如下。 例:全加器的混合描述 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY xor_gate IS GENERIC(tpd:TIME:=10 ns); PORT(in1,in2: IN STD_LOGIC; out1: OUT STD_LOGIC); END xor_gate; ARCHITECTURE behavioral OF xor_gate IS BEGIN out1 <= in1 XOR in2 AFTER tpd; END behavioral; LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY full_adder IS GENERIC(tpd:TIME:=10 ns); PORT(x,y,c_in: IN STD_LOGIC; Sum, c_out: OUT STD_LOGIC); END full_adder; ARCHITECTURE mix OF full_adder IS COMPONENT xor_gate PORT(in1, in2: IN STD_LOGIC; out1: OUT STD_LOGIC); END COMPONENT; SIGNAL s :STD_LOGIC; FOR ALL: xor_gate USE ENTITY WORK. xor_gate (behavioral); BEGIN u1: xor_gate PORT MAP (x, y, s); u2: xor_gate PORT MAP (s, c_in, sum); c_out <= (x AND y) OR (s AND c_in) AFTER 2*tpd; END mix; 第四章 VHDL 的主要描述语句 4.1 VHDL 顺序语句 4.2 VHDL 并行语句 用 VHDL 语言进行设计时,按描述语句的执行顺序进行分类,可将 VHDL 语句分为顺序执行语句(Sequential)和并行执行语句(Parallel)。 4.1 VHDL 顺序语句 顺序语句是指完全按照程序中书写的顺序执行各语句,并且在结构层次中前 面的语句执行结果会直接影响后面各语句的执行结果。顺序描述语句只能出现在 进程或子程序中,用来定义进程或子程序的算法。顺序语句可以用来进行算术运 算、逻辑运算、信号和变量的赋值、子程序调用等,还可以进行条件控制和迭代。 注意,这里的顺序是从仿真软件的运行和顺应 VHDL 语法的编程逻辑思路而言 的,其相应的硬件逻辑工作方式未必如此。应该注意区分 VHDL 语言的软件行 为与描述综合后的硬件行为的差异。 VHDL 顺序语句主要包括: ·变量赋值语句(Variable Evaluate) ·信号赋值语句(Signal Evaluate) ·WAIT 语句 ·IF 语句 ·CASE 语句 ·LOOP 语句 ·NEXT 语句 ·EXIT 语句 ·RETURN 语句 ·NULL 语句 ·过程调用语句(Procedure Call) ·断言语句(Assert) ·REPORT 语句 4.1.1 变量赋值语句 变量赋值语句语法格式为: 变量赋值目标 := 赋值表达式 例: VARIABLE s:BIT := ‘0’; PROCESS(s) VARIABLE count:INTEGER := ‘0’ --变量说明 BEGIN count := s+1 END PROCESS; 4.1.2 信号赋值语句 在 VHDL 语言中,用符号“<=”为信号赋值。 --变量赋值 信号赋值语句的规范书写格式如下: 目的信号量 <= [TRANSPORT][INERTIAL]信号变量表达式; 其中[TRANSPORT]表示传输延迟,[INERTIAL]表示惯性延迟。要求“<=”两边的 信号变量类型和位长度应该一致。 例: s <=TRANSPORT t AFTER 10ns; d <= INERTIAL 2 AFTER 3ns, 1 AFTER 8ns; 例:s <= a NOR(b AND c); 3 个敏感量 a,b,c 中任何一个发生变化,该语句都将被执行。 4.1.3 WAIT 语句 WAIT 语句在进程中起到与敏感信号一样重要的作用,敏感信号触发进程的 执行,WAIT 语句同步进程的执行,同步条件由 WAIT 语句指明。进程在仿真运 行中处于执行或挂起两种状态之一。当进程执行到等待语句时,就将被挂起并设 置好再次执行的条件。WAIT 语句可以设置 4 种不同的条件:无限等待、时间到、 条件满足以及敏感信号量变化。这几类 WAIT 语句可以混合使用。现分别介绍如 下: (1)WAIT --无限等待语句 这种形式的 WAIT 语句在关键字“WAIT”后面不带任何信息,是无限等待的情况。 (2)WAIT ON 信号表 --敏感信号等待语句 这种形式的 WAIT 语句使进程暂停,直到敏感信号表中某个信号值发生 变化。WAIT ON 语句后面跟着的信号表,在敏感信号表中列出等待语句的敏感 信号。当进程处于等待状态时,其中敏感信号发生任何变化都将结束挂起,再次 启动进程。 例[A] PROCESS BEGIN y <= a AND b; WAIT ON a,b; END PROCESS; 例[B] PROCESS(a,b) BEGIN y <= a AND b; END PROCESS; 在例[A]中执行所有语句后,进程将在 WAIT 语句处被挂起,直到 a 或 b 中任何 一个信号发生变化,进程才重新开始。例[A]与例[B]是等价的。 需要注意的是,在使用 WAIT ON 语句的进程中,敏感信号量应写在进程 中的 WAIT ON 语句后面;而在不使用 WAIT ON 语句的进程中,敏感信号量应 在开头的关键词 PROCESS 后面的敏感信号表中列出。VHDL 规定,已列出敏感 信号表的进程不能使用任何形式的 WAIT 语句。 (3)WAIT UNTIL 条件 --条件等待语句 这种形式的 WAIT 语句使进程暂停,直到预期的条件为真。WAIT UNTIL 后面跟 的是布尔表达式,在布尔表达式中隐式地建立一个敏感信号量表,当表中任何一 个信号量发生变化时,就立即对表达式进行一次测评。如果其结果使表达式返回 一个“真”值,则进程脱离挂起状态,继续执行下面的语句。即 WAIT UNTIL 语 句需满足以下条件: ·在条件表达式中所含的信号发生了变化; ·此信号改变后,且满足 WAIT UNTIL 语句中表达式的条件。 这两个条件缺一不可,且必须按照上述顺序来完成。 WAIT UNTIL 语句有以下三种表达方式: WAIT UNTIL 信号 = VALUE; WAIT UNTIL 信号’EVENT AND 信号 = VALUE; WAIT UNTIL 信号’STABLE AND 信号 = VALUE; 例如: WAIT UNTIL clock = “1”; WAIT UNTIL rising_edge(clk); WAIT UNTIL clk =‘1’AND clk’ EVENT; WAIT UNTIL NOT clk’ STABLE AND clk= “1”; 一般的,在一个进程中使用了 WAIT 语句后,综合器会综合产生时序逻辑电路。 时序逻辑电路的运行依赖 WAIT UNTIL 表达式的条件,同时还具有数据存储的 功能。 (4)WAIT FOR 时间表达式 --超时等待语句 例如:WAIT FOR 40 ns; 在该语句中,时间表达式为常数 40ns,当进程执行到该语句时,将等待 40ns, 经过 40ns 之后,进程执行 WAIT FOR 的后继语句。 例如:WAIT FOR(a*(b+c)); 在此语句中,(a*(b+c))为时间表达式,WAIT FOR 语句在执行时,首先计算 表达式的值,然后将计算结果返回作为该语句的等待时间。 4.1.4 IF 语句 在 VHDL 语言中,IF 语句的作用是根据指定的条件来确定语句的执行顺 序。IF 语句可用于选择器、比较器、编码器、译码器、状态机等的设计,是 VHDL 语言中最常用的语句之一。IF 语句按其书写格式可分为以下 3 种。 1.门闩控制语句 这类语句书写格式为: IF 条件 THEN 顺序语句 END IF; 当程序执行到这种门闩控制型 IF 语句时,首先判断语句中所指定的条 件是否成立。如果条件成立,则程序继续执行 IF 语句中所含的顺序处理语句; 如果条件不成立,程序将跳过 IF 语句所包含的顺序处理语句,而向下执行 IF 的 后继语句。 例: 利用 IF 语句引入 D 触发器 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY dff IS PORT(clk,d:IN STD_LOGIC; q:OUT STD_LOGIC); END dff; ARCHITECTURE rtl OF dff IS BEGIN PROCESS (clk) BEGIN IF (clk’EVENT AND clk=’1’) THEN q <= d; END IF; END PROCESS; END rtl; 2.二选一控制语句 这种语句的书写格式为: TF 条件 THEN 顺序语句 ELSE 顺序语句 END IF; 当 IF 条件成立时,程序执行 THEN 和 ELSE 之间的顺序语句部分;当 IF 语句的条件得不到满足时,程序执行 ELSE 和 END IF 之间的顺序处理语句。 即依据 IF 所指定的条件是否满足,程序可以进行两条不同的执行路径。 例:二选一电路结构体的描述 ARCHITECTURE rtl OF mux2 IS BEGIN PROCESS (a,b,s) BEGIN IF (s =’1’) THEN c <= a; ELSE c <= b; END IF; END PROCESS; END rtl; 3.多选择控制语句 这种语句的书写格式为: IF 条件 THEN 顺序语句 ELSEIF 顺序语句 ELSEIF 顺序语句 ┇ ELSE 顺序语句 END IF; 这种多选择控制的 IF 语句,实际上就是条件嵌套。它设置了多个条件, 当满足所设置的多个条件之一时,就执行该条件后的顺序处理语句。当所有设置 的条件都不满足时,程序执行 ELSE 和 END IF 之间的顺序处理语句。 例: 利用多选控制语句设计的四选一多路选择器 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY mux4 IS PORT(input:IN STD_LOGIC_VECTOR (3 DOWNTO 0); sel:IN STD_LOGIC_VECTOR (1 DOWNTO 0); y:OUT STD_LOGIC); END mux4; ARCHITECTURE rtl OF mux4 IS BEGIN PROCESS (input,sel) BEGIN 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; 4.1.5 CASE 语句 CASE 语句根据满足的条件直接选择多项顺序语句中的一项执行,它常用来描述 总线行为、编码器、译码器等的结构。 CASE 语句的结构为: CASE 表达式 IS WHEN 条件选择值 => 顺序语句, ┇ WHEN 条件选择值 => 顺序语句, END CASE; 其中 WHEN 条件选择值可以有四种表达方式; (1)单个普通数值,形如 WHEN 选择值 => 顺序语句; (2)并列数值,形如 WHEN 值/值/值 => 顺序语句; (3)数值选择范围,形如 WHEN 值 TO 值 => 顺序语句; (4)WHEN OTHERS => 顺序语句; 当执行到 CASE 语句时,首先计算 CASE 和 IS 之间的表达式的值,然后根据条 件语句中与之相同的选择值,执行对应的顺序语句,最后结束 CASE 语句。 使用 CASE 语句需注意以下几点: ·CASE 语句中每一条语句的选择值只能出现一次,即不能有相同选择值的条件语 句出现。 ·CASE 语句执行中必须选中,且只能选中所列条件语句中的一条,即 CASE 语 句至少包含一个条件语句。 ·除非所有条件语句中的选择值能完全覆盖 CASE 语句中表达式的取值,否则最 末一个条件语句中的选择必须用“OTHERS”表示,它代表已给出的所有条件语句 中未能列出的其他可能的取值。关键词 OTHERS 只能出现一次,且只能作为最 后一种条件取值。使用 OTHERS 是为了使条件语句中的所有选择值能覆盖表达 式的所有取值,以免综合过程中插入不必要的锁存器。这一点对于定义为 STD_LOGIC 和 STD_LOGIC_VECTOR 数据类型的值尤为重要,因为这些数据 对象的取值除了 1、0 之外,还可能出现输入高阻态 Z,不定态 X 等取值。 例[1] CASE 语句使用 CASE command IS WHEN “00”=> c <= a; WHEN “01”=> c <= b; --适合 4 选 1 数据选择器 WHEN “10”=> c <= e; WHEN “11”=> c <= f; WHEN OTHERS => NULL; --无效 END CASE; 例[2] CASE 语句使用 CASE sel IS WHEN 1TO 9 => c <= 1; WHEN 11/12 => c <= 2; WHEN OTHERS => c <= 3; END CASE; 在例[2]中,第一个 WHEN 语句的意思是当 sel 的值是从 1 到 9 中任意一个数 值时,信号 c 的值取 1;第二个 WHEN 语句的意思是当 sel 的值为 11 或 12 两者 之一时,信号 c 的取值为 2;第三个 WHEN 语句的意思是当 sel 的值不为前面 两种情况时,信号 c 的取值为 3。 例[3] 3-8 译码器。 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY decoder3_8 IS PORT(a,b,c,g1,g2a,g2b:IN STD_LOGIC; y:OUT STD_LOGIC_VECTOR (7 DOWNTO 0); END decoder3_8; ARCHITECTURE rtl OF decoder3_8 IS SIGNAL indata:STD_LOGIC_VECTOR (2 DOWNTO 0); BEGIN indata <= c & b & a; PROCESS(indata,g1,g2a,g2b) BEGIN IF(g1=‘1’AND g2a=‘0’ AND g2b=‘0’)THEN CASE indata IS WHEN “000”=> y <=“11111110”; WHEN “001”=> y <=“11111101”; WHEN “010”=> y <=“11111011”; WHEN “011”=> y <=“11110111”; WHEN “100”=> y <=“11101111”; WHEN “101”=> y <=“11011111”; WHEN “110”=> y <=“10111111”; WHEN “111”=> y <=“01111111”; WHEN OTHERS => y <= “XXXXXXXX”; END CASE; ELSE --指不满足 g1=‘1’AND g2a=‘0’ AND g2b=‘0’情况 y <=“11111111”; END IF; END PROCESS; END rtl; 与 IF 语句相比,CASE 语句组的程序语句是没有先后顺序的,所有表达 式的值都并行处理。IF 语句是有序的,先处理最起始、最优先的条件,后处理 次优先的条件。 4.1.6 LOOP 语句 LOOP 语句就是循环语句,它可以使包含的一组顺序语句被循环执行,其执行的 次数受迭代算法控制。在 VHDL 中常用来描述迭代电路的行为。 1.单个 LOOP 语句 单个 LOOP 语句的书写格式如下: [标号:] LOOP 顺序语句 END LOOP[标号]; 这种循环语句需引入其他控制语句(如 EXIT)后才能确定,否则为无限循环。 其中的标号是可选的。 例如: loop1:LOOP WAIT UNTIL clk=‘1’; q <= d AFTER 2 ns; END LOOP loop1; 2.FOR_LOOP 语句 该语句语法格式为: [标号:] FOR 循环变量 IN 离散范围 LOOP 顺序处理语句 END LOOP[标号]; 例: 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 ; ARCHITECTURE rtl OF parity_check IS BEGIN PROCESS(a) VARIABLE tmp:STD_LOGIC BEGIN tmp:=‘0’; FOR i IN 0 TO 7 LOOP tmp:= tmp XOR a(i); END LOOP; y <= tmp; - -y=1,a 为奇数个‘1’。y=0, a 为偶 数个‘1’。 END PROCESS; END rtl; 3.WHILE_LOOP 语句 这种语句的书写格式为: [标号:] WHILE 条件 LOOP 顺序处理语句 END LOOP[标号]; 在该 LOOP 语句中,没有给出循环次数的范围,而是给出了循环执行 顺序语句的条件;没有自动递增循环变量的功能,而是在顺序处理语句中增加了 一条循环次数计算语句,用于循环语句的控制。循环控制条件为布尔表达式,当 条件为“真”时,则进行循环,如果条件为“假”,则结束循环。 例:8 位奇偶校验电路的 WHILE_LOOP 设计形式 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY parity_check IS PORT(a:IN STD_LOGIC_VECTOR (7 DOWNTO 0); y:OUT STD_LOGIC); END parity_check ; ARCHITECTURE behav OF parity_check IS BEGIN PROCESS(a) VARIABLE tmp:STD_LOGIC BEGIN tmp := ‘0’; i := 0; WHILE (i < 8)LOOP tmp := tmp XOR a(i); i := i+1; END LOOP; y <= tmp; END PROCESS; END behav; 4.1.7 NEXT 语句 NEXT 语句的书写格式为: NEXT[标号][WHEN 条件] 该语句主要用于 LOOP 语句内部的循环控制。当 NEXT 语句后不跟[标 号],NEXT 语句作用于当前最内层循环,即从 LOOP 语句的起始位置进入下一 个循环。若 NEXT 语句不跟[WHEN 条件],NEXT 语句立即无条件跳出循环。 例:NEXT 语句应用举例 ┇ WHILE data >1 LOOP data := data+1; NEXT WHEN data=3 - -条件成立而无标号,跳出循环 data := data* data; END LOOP; N1:FOR i IN 10 DOWNTO 1 LOOP N2:FOR j IN 0 TO i LOOP NEXT N1 WHEN i=j; - -条件成立,跳到 N1 处 matrix(i,j):= j*i+1; - -条件不成立,继续内层循环 N2 END LOOP N2; END LOOP N1; 4.1.8 EXIT 语句 EXIT 语句的书写格式为: EXIT[LOOP 标号][WHEN 条件]; EXIT 语句也是用来控制 LOOP 的内部循环,与 NEXT 语句不同的是 EXIT 语句 跳向 LOOP 终点,结束 LOOP 语句;而 NEXT 语句是跳向 LOOP 语句的起始点, 结束本次循环,开始下一次循环。当 EXIT 语句中含有标号时,表明跳到标号处 继续执行。含[WHEN 条件]时,如果条件为“真”,跳出 LOOP 语句;如果条件为 “假”,则继续执行 LOOP 循环。 EXIT 语句不含标号和条件时,表明无条件结束 LOOP 语句的执行,因此, 它为程序需要处理保护、出错和警告状态,提供了一种快捷、简便的调试方法。 例[]两个元素位矢量 a、b 进行比较,当发现 a 与 b 不同时,跳出循环比 较程序并报告比较结果。 SIGNAL a,b:STD_LOGIC_VECTOR (0 TO 1); SIGNAL a_less_than_b:BOOLEAN; ┇ a_less_than_b <= FALSE; FOR i IN 1TO 0 LOOP IF(a(i)=‘1’AND b(i)=‘0’)THEN a_less_than_b <= FALSE; EXIT; ELSEIF(a(i)=‘0’AND b(i)=‘1’)THEN a_less_than_b <= TRUE; EXIT; ELSE NULL END IF; END LOOP; 4.1.9 返回语句 RETURN RETURN 语句是一段子程序结束后,返回主程序的控制语句。它只能用 于函数与过程体内,并用来结束当前最内层函数或过程体的执行。 RETURN 语句的书写格式为: RETURN; RETURN 表达式; 例:在函数体中使用 RETURN 语句 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY example IS PORT(a,b:IN INTEGER; y:OUT INTEGER); END example ; ARCHITECTURE rtl OF example IS BEGIN PROCESS(a,b) FUNCTION maximum (a,b:INTEGER)RETURN INTEGER IS VARIABLE tmp:INTEGER; BEGIN IF(a > b)THEN tmp := a; ELSE tmp := b; END IF; END maximum; BEGIN y <= maximum(a,b); END PROCESS; END rtl; 上例是一个对两个输入整数取最大值的功能描述,在结构体的进程中定义了一个 取最大值的函数。在函数体中正是通过 RETURN 语句将比较得到的最大值返回 的,并结束该函数体的执行。 4.1.10 NULL 语句 NULL 语句是空操作语句,不完成任何操作,执行 NULL 语句只是让程序运行流 程走到下一个语句。 NULL 语句的书写格式为: NULL; NULL 语句常用于 CASE 语句中,利用 NULL 来表示所余的不用的条件下的操 作行为,以满足 CASE 语句对条件值全部列举的要求。 例:采用 NULL 语句的四选一数据选择器。 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY mux4 IS PORT(d0 :IN STD_LOGIC_VECTOR (7 DOWNTO 1); d1 :IN STD_LOGIC_VECTOR (7 DOWNTO 1); d2 :IN STD_LOGIC_VECTOR (7 DOWNTO 1); d3 :IN STD_LOGIC_VECTOR (7 DOWNTO 1); s0 :IN STD_LOGIC; s1 :IN STD_LOGIC; y :OUT STD_LOGIC_VECTOR (7 DOWNTO 1) ); END mux4 ; ARCHITECTURE behave OF mux4 IS BEGIN lable:PROCESS(d0,d1,d2,d3,s0, s1) VARIABLE tmp:INTEGER; BEGIN tmp := 0; IF(s0=‘1’)THEN tmp := tmp+1; END IF; IF(s1=‘1’)THEN tmp := tmp+2; END IF; CASE tmp IS WHEN 0 => y <= d0; WHEN 1 => y <= d1; WHEN 2 => y <= d2; WHEN 3 => y <= d3; WHEN OTHERS => NULL; END CASE; END PROCESS; END behave; 上例是通过对用于选通 8 位总线的四选一多路选择器进行功能描述,具体说明 NULL 语句的使用。 4.1.11 过程调用语句(Procedure Call) 与其他高级程序设计语言相似,VHDL 提供了子程序的概念。其中在进程、函数 和过程中,可以使用过程调用语句,此时它是一种顺序语句。一个过程被调用时 将去执行它的过程体。过程调用语句的书写格式为: 过程名(实参表); 例: LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; USE IEEE. STD_LOGIC_UNSIGNED.ALL; ENTITY max IS PORT(in1:IN STD_LOGIC_VECTOR (7 DOWNTO 0); in2:IN STD_LOGIC_VECTOR (7 DOWNTO 0); in3:IN STD_LOGIC_VECTOR (7 DOWNTO 0); q:OUT STD_LOGIC_VECTOR (7 DOWNTO 0) ); END max ; ARCHITECTURE rtl OF max IS PROCEDURE maximum(a,b:IN STD_LOGIC_VECTOR; c:OUT STD_LOGIC_VECTOR)IS VARIABLE temp:STD_LOGIC_VECTOR(a’RANGE); BEGIN IF(a > b)THEN temp := a; ELSE temp := b; END IF; c := temp; END maximum; BEGIN PROCESS(in1,in2,tmp1) VARIABLE tmp1,tmp2:STD_LOGIC_VECTOR (7 DOWNTO 0) ; BEGIN maximum(in1,in2,tmp1); - -过程调用 maximum(tmp1,in3,tmp2); q <= tmp2; END PROCESS; END rtl; 上例是一个取三个输入位矢量最大值的功能描述,它在结构体中的进程语句中使 用了两个过程调用语句。 4.1.12 断言语句(Assert) 断言语句分为顺序断言语句和并行断言语句,顺序断言语句主要用于进程、函数 和过程仿真、调试中的人机对话,它可以给出一个文字串作为警告和错误信息。 断言语句的书写格式如下: ASSERT 条件 [REPORT 报告信息] [SEVERITY 出错级别]; 在执行过程中,断言语句对条件(布尔表达式)的真假进行判断,如果条件为 “TURE”,则向下执行另外一条语句;如果条件为“FALSE”,则输出错误信息和 错误严重程度的级别。在 REPORT 后面跟着的是设计者写的字符串,通常是说 明错误的原因,字符串要用双引号括起来。SEVERITY 后面跟着的是错误严重程 度的级别,他们分别是: ·NOTE(注意) ·WARNING(警告) ·ERROR(错误) ·FAILURE(失败) 若 REPORT 子句缺省,则默认消息为“Assertion violation”;若 SEVERITY 子句 缺省,则出错级别的默认值为“ERROR”。 例: RS 触发器的 VHDL 描述中断言语句的使用 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY rsff IS PORT(s :IN BIT; r :IN BIT; q :OUT BIT; qb :OUT BIT); END rsff; ARCHITECTURE rtl OF rsff IS BEGIN PROCESS(s,r) VARIABLE last_state :BIT; BEGIN ASSERT(NOT(s =‘1’AND r =‘1’)) REPORT “Both s and r equal to‘1’.” SEVERITY ERROR; IF(s =‘0’AND r =‘0’)THEN last_state := last_state; ELSIF(s =‘0’AND r =‘1’)THEN last_state := 0; ELSE last_state := 1; END IF; q <= last_state; qb <= not(last_state); END PROCESS; END rtl; 上例中,如果 r 和 s 都为‘1’时,表示一种不定状态。在进程中先是设定 了一条断言语句,目的是:当判断 r 和 s 都为‘1’时,输出终端将显示字符串 “Both s and r equal to‘1’.”,同时可能终止模拟过程,并显示错误的严重程度。接 下来用 IF 语句判别触发器的其他三种情况,最后将值送到触发器的两个输出端 口上。 4.1.13 REPORT 语句 REPORT 语句不增加硬件任何功能,但提供顺序断言语句的短格式,在仿 真时使用 REPORT 语句可以提高程序的可读性。 REPORT 语句的书写格式为: REPORT 输出信息 [SEVERITY 出错级别]; 例: RS 触发器的 VHDL 描述中 REPORT 语句的使用 (本例中,用 REPORT 语句 代替上例进程中的断言语句。) LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY rsff IS PORT(s :IN BIT; r :IN BIT; q :OUT BIT; qb :OUT BIT); END rsff; ARCHITECTURE rtl OF rsff IS BEGIN PROCESS(s,r) VARIABLE last_state :BIT; BEGIN IF(s =‘1’AND r =‘1’)THEN REPORT “Both s and r equal to‘1’.”; ELSIF (s =‘0’AND r =‘0’)THEN last_state:= last_state; ELSIF (s =‘0’AND r =‘1’)THEN last_state := 0; ELSE last_state := 1; END IF; q <= last_state; qb <= not(last_state); END PROCESS; END rtl; 4.2 VHDL 并行语句 在 VHDL 中,并行语句在结构体中的执行是同时并发执行的,其书写次序与其 执行顺序并无关联,并行语句的执行顺序是由他们的触发事件来决定的。 我们知道,实际的硬件系统中很多操作都是并发的,因此在对系统进行模拟时就 要把这些并发性体现出来,并行语句正是用来表示这种并发行为的。 在结构体语句中,并行语句的位置是: ARCHITECTURE 结构体名 OF 实体名 IS 说明语句 BEGIN 并行语句 END 结构体名; 其中并行语句主要有以下几种: ·PROCESS --进程语句 ·BLOCK --块语句 ·CONCURRENT SIGNAL ASSIGNMENT --并行信号代入语句 ·CONDITIONAL SIGNAL ASSIGNMENT - -条件信号代入语句 ·SELECTIVE SIGNAL ASSIGNMENT - -选择信号代入语句 ·CONCURRENT PROCEDURE CALL --并行过程调用语句 ·ASSERT - -并行断言语句 ·GENERIC -- 参数传递语句 ·COMPONENT_INSTANT ·GENERATE --元件例化语句 --生 成语句 并行描述语句语句可以是结构性的,也可以是行为性的。下面对这些语句的应用 加以介绍。 4.2.1 进程语句(PROCESS) 进程语句是最主要的并行语句,它在 VHDL 程序设计中使用频率最高,也是最 能体现硬件描述语言特点的一条语句。进程语句的内部是是顺序语句,而进程语 句本身是一种并行语句。进程语句的综合是比较复杂的,主要涉及这样一些问题: 综合后的进程是用组合逻辑电路还是用时序逻辑电路来实现?进程中的对象是 否有必要用寄存器、触发器、锁存器或是 RAM 等存储器件来实现。 进程语句结构中至少需要一个敏感信号量,否则除了初始化阶段,进程永远不会 被再次激活。这个敏感量一般是一个同步控制信号,同步控制信号用在同步语句 中,同步语句可以是敏感信号表、WAIT UNTIL 语句或是 WAIT ON 语句。一般 来说,只有一个同步点或者是具有多个同步点但都使用完全相同的同步控制信号 的进程不需要“记忆”在哪一个同步点上被挂起时,不会形成存储器。如下例所示: --综合后不需要存储器的 VHDL 进程 label1:PROCESS(a,b,c) ┇ BEGIN ┇ --其中没有其他同步描述 AND PROCESS label1; --综合后需要存储器的 VHDL 进程 label2:PROCESS ┇ BEGIN WAIT UNTIL clk’EVENT AND clk=‘1’; s <=‘0’; WAIT UNTIL clk’EVENT AND clk=‘1’; s <=‘1’; AND PROCESS label2; --不会形成存储器的变量 label3:PROCESS(a,b,c) VARIABLE var:BIT; BEGIN var := a XOR b; s <= var AND c; AND PROCESS label3; --需要存储器的变量 label4:PROCESS TYPE state_table IS (stop,go); VARIABLE state:table_ state; BEGIN WAIT UNTIL clk’EVENT AND clk=‘1’; CASE state IS - - state 在赋值之前先被读 访问 WHEN stop => state := go; WHEN go => state := stop;- -这两个语句是并发关系 END CASE; AND PROCESS label4; --综合为触发器的进程 label5:PROCESS BEGIN WAIT UNTIL clk’EVENT AND clk=‘1’; q <= d; END PROCESS label5; 4.2.2 块语句(BLOCK) 块(BLOCK)语句可以看作是结构体中的子模块,块语句把许多并行语句组合 在一起形成一个子模块,而它本身也是一个并行语句。 块语句的基本结构如下: [块标号:] BLOCK [保护表达式] [类属子句 [类属接口表;]]; [端口子句 [端口接口表;]]; [块说明部分] BEGIN <并行语句 1> <并行语句 2> ┇ END BLOCK [块标号]; 例: 利用块语句描述的全加器 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY add IS PORT(A:IN STD_LOGIC; B:IN STD_LOGIC; Cin:IN STD_LOGIC; Co:OUT STD_LOGIC; S:OUT STD_LOGIC); END add; ARCHITECTURE dataflow OF add IS BEGIN ex : BLOCK PORT(a_A:IN STD_LOGIC; a_B:IN STD_LOGIC; a_Cin:IN STD_LOGIC; a_Co:OUT STD_LOGIC; a_S:OUT STD_LOGIC); PORT MAP(a_A=>A,a_B=>B,a_Cin=> Cin, a_Co=> Co,a_S=>S); SIGNAL tmp1,tmp2:STD_LOGIC; BEGIN label1:PROCESS(a_A,a_B) BEGIN tmp1<= a_A XOR a_B; END PROCESS label1; label2:PROCESS(tmp1,a_Cin) BEGIN tmp2<= tmp1AND a_Cin ; END PROCESS label2; label3:PROCESS(tmp1,a_Cin) BEGIN a_S <= tmp1XOR a_Cin ; END PROCESS label3; label4:PROCESS(a_A,a_B,tmp2) BEGIN a_Co <= tmp2 OR(a_A AND a_B); END PROCESS label4; END BLOCK ex; END dataflow; 在上面的例子中,结构体内含有 4 个进程语句,这 4 个进程语句是并行 关系,共同形成了一个块语句。 在实际应用中,一个块语句中又可以包含多个子块语句,这样循环嵌套以 形成一个大规模的硬件电路。 4.2.3 并行信号代入语句 信号代入语句有两种:一种是在结构体中的进程内使用,此时它作为一种顺序语 句出现;另一种是在结构体的进程之外使用,此时它是一种并行语句,因此称之 为并行信号代入语句。 并行信号代入语句的语法格式为: 信号量 <= 敏感信号量表达式; 需要注意的是,一条信号代入语句与一个信号代入的进程语句是等价的,我们可 以把一条信号代入语句改写成一个信号代入的进程语句。 例: LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY and_gat IS PORT(a:IN STD_LOGIC; b:IN STD_LOGIC; y:OUT STD_LOGIC); END and_gat; ARCHITECTURE behave OF and_gat IS BEGIN y <= a AND b; --并行信号代入语句(在结构体进程之外) AND behave; 本例是一个 2 输入与门的 VHDL 描述,在结构体中使用了并行信号代入语句。 下面是 2 输入与门的另一种 VHDL 描述,在描述的结构体中采用了与上述并行 信号代入语句等价的进程语句。 例: LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY and_gat IS PORT(a:IN STD_LOGIC; b:IN STD_LOGIC; y:OUT STD_LOGIC); END and_gat; ARCHITECTURE behave OF and_gat IS BEGIN PROCESS (a,b) BEGIN y <= a AND b; AND PROCESS; AND behave; --进程语句( 顺序语句) 通过对上述两个例子的分析可见:从并行信号代入语句描述来看,当代入符 号“<=”右边的值发生任何变化时,信号代入语句的操作立即执行,将信号代入符 号“<=”右边的表达式代入给左边的信号量;从进程语句的描述来看,当进程敏感 信号表中的敏感信号量发生变化时,进程将被启动,顺序信号代入语句将被执行 以完成信号的代入操作。 在 VHDL 中提供了三种并行信号代入语句: ·并发信号代入语句 ·条件信号代入语句 ·选择信号代入语句 (1) 并发信号代入语句 信号代入语句在进程内部执行时,它是一种顺序语句;信号代入语句在结构体的 进程之外出现时,它作为并发语句的形式出现。作为并发信号代入语句,在结构 体中他们是并行执行的,他们的执行顺序与书写无关。 并发信号代入语句是靠事件驱动的。对于并发信号代入语句来说,只有代入符号 “<=”右边的对象有事件发生时才会执行该语句。 在实际设计中,并发信号代入语句常用来进行加法器、乘法器、除法器和比较器 等多种逻辑电路的描述。下面是一个用 VHDL 并发语句描述的全加器的例子。 例: LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY add IS PORT(A:IN STD_LOGIC; B:IN STD_LOGIC; Cin:IN STD_LOGIC; Co:OUT STD_LOGIC; S:OUT STD_LOGIC); END add; ARCHITECTURE dataflow OF add IS SIGNAL tmp1,tmp2:STD_LOGIC; BEGIN tmp1 <= A XOR B; tmp2 <= tmp1 AND Cin; --4 条并发信号代入语 句 S <= tmp1 XOR Cin; Co <= tmp2 OR(A AND B); AND dataflow; 在上例的结构体中有 4 条并发信号代入语句,他们的执行顺序与书写顺序是无关 的,因此上面的 4 条并发信号代入语句可以任意颠倒书写顺序,不会对执行结果 产生任何影响。上面提到的并发信号代入语句是事件驱动的,例如: tmp2 <= tmp1 AND Cin; S <= tmp1 XOR Cin; 两条语句,只要 tmp1 和 Cin 中的值有一个发生变化,即有事件发生,那么这两 条语句就会立即并发执行。 (2)条件信号代入语句 条件信号代入语句也是一种并发描述语句,它是一种根据不同条件将不同的表达 式代入目的信号的语句。条件信号代入语句的书写格式为: 目的信号 <= 表达式 1 WHEN 条件 1 ELSE 表达式 2 WHEN 条件 2 ELSE 表达式 2 WHEN 条件 3 ELSE ┇ 表达式 n-1 WHEN 条件 ELSE 表达式; 条件信号代入语句执行时要先进行条件判断,如果条件满足,就将条件前面的那 个表达式的值代入目的信号;如果不满足条件,就去判断下一个条件;最后一个 表达式没有条件,也就是说在前面的条件都不满足时,就将该表达式的值代入目 的信号。 下面的例子是用条件信号代入语句来描述的七段显示译码器 例: 采用条件代入语句描述的七段显示译码器 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY se7 IS PORT(input:IN STD_LOGIC_VECTOR (3 DOWNTO 0); output : OUT STD_LOGIC_VECTOR (6 DOWNTO 0)); END se7; ARCHITECTURE rtl OF se7 IS BEGIN output <=(‘0’,‘1’,‘1’,‘1’,‘1’,‘1’,‘1’) WHEN input =“0000”ELSE (‘0’,‘0’,‘0’,‘0’,‘1’,‘1’,‘0’)WHEN input =“0001”ELSE (‘1’,‘0’,‘1’,‘1’,‘0’,‘1’,‘1’)WHEN input =“0010”ELSE (‘1’,‘0’,‘0’,‘1’,‘1’,‘1’,‘1’)WHEN input =“0011”ELSE (‘1’,‘1’,‘0’,‘0’,‘1’,‘1’,‘0’)WHEN input =“0100”ELSE (‘1’,‘1’,‘0’,‘1’,‘1’,‘0’,‘1’)WHEN input =“0101”ELSE (‘1’,‘1’,‘1’,‘1’,‘1’,‘0’,‘1’)WHEN input =“0110”ELSE (‘0’,‘0’,‘0’,‘0’,‘1’,‘1’,‘1’)WHEN input =“0111”ELSE (‘1’,‘1’,‘1’,‘1’,‘1’,‘1’,‘1’)WHEN input =“1000”ELSE (‘1’,‘1’,‘0’,‘1’,‘1’,‘1’,‘1’)WHEN input =“1001”ELSE (‘1’,‘1’,‘1’,‘0’,‘1’,‘1’,‘1’)WHEN input =“1010”ELSE (‘1’,‘1’,‘1’,‘1’,‘1’,‘0’,‘0’)WHEN input =“1011”ELSE (‘0’,‘1’,‘1’,‘1’,‘0’,‘0’,‘1’)WHEN input =“1100”ELSE (‘1’,‘0’,‘1’,‘1’,‘1’,‘1’,‘0’)WHEN input =“1101”ELSE (‘1’,‘1’,‘1’,‘1’,‘0’,‘0’,‘1’)WHEN input =“1110”ELSE (‘1’,‘1’,‘1’,‘0’,‘0’,‘0’,‘1’)WHEN input =“1111”ELSE (‘0’,‘0’,‘0’,‘0’,‘0’,‘0’,‘0’); - -灭灯 END rtl; 在上例中,七段显示译码器有一个输入端口 input 和一个输出端口 output。 输入端口 input 是一个四位总线,表示 3 到 0 的四位逻辑向量,表示输入是一个 四位二进制数。输出端口 output 也以总线形式表示,它表示 6 到 0 的 7 位逻辑向 量,表示输出是一个七位二进制数,以驱动共阴极显示七段数码管。 在上例的结构体中,用一个条件代入语句来完成所有状态的显示译码。在 保留字 WHEN 的前面是驱动显示数码管的七位位矢量,WHEN 的后面是译码的 条件。需要说明的是条件信号代入语句中的书写顺序不是固定的,位置是可以任 意颠倒的,他们并不表示执行的先后顺序,实际上他们是并发执行的。 (3) 选择信号代入语句 选择信号代入语句的书写格式为: WITH 表达式 SELECT 目的信号 <= 表达式 1 WHEN 条件 1; 表达式 2 WHEN 条件 2; 表达式 3 WHEN 条件 3; ┇ 表达式 n WHEN 条件 n; VHDL 在执行选择信号代入语句时,目的信号是根据表达式的当前值来进行表达 式代入的。当表达式的值符合某个条件时,就把该条件前的表达式代入目的信号; 当表达式的值不符合条件时,语句就继续向下判断,直到找到满足的条件为止。 选择信号代入语句与 case 语句相类似,都是对表达式进行测试,当表达式的值 不同时,将把不同的表达式代入目的信号。需要注意的是,选择信号代入语句与 case 语句一样,必须把表达式的值在条件中都列出来,否则编译将会出错。 下面的例子是一个采用选择信号代入语句描述的选通 8 位总线的四选一多路选 择器。 例: LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY mux4 IS PORT(d0:IN STD_LOGIC_VECTOR (7 DOWNTO 0); d1:IN STD_LOGIC_VECTOR (7 DOWNTO 0); d2:IN STD_LOGIC_VECTOR (7 DOWNTO 0); d3:IN STD_LOGIC_VECTOR (7 DOWNTO 0); s0:IN STD_LOGIC; s1:IN STD_LOGIC; q:OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END mux4; ARCHITECTURE rtl OF mux4 IS SIGNAL comb:STD_LOGIC_VECTOR (1 DOWNTO 0);; BEGIN comb <= s1 & s0; WITH comb SELECT - -用 comb 进行选择 q <= d0 WHEN “00”; d1 WHEN “01”; d2 WHEN “10” d3 WHEN OTHERS; - -上面 4 条语句是并行执行的 END rtl; 4.2.4 并行过程调用语句 过程调用语句在进程内部执行时,它是一种顺序语句;过程调用语句在结构 体的进程之外出现时,它作为并发语句的形式出现。作为并行过程调用语句,在 结构体中他们是并行执行的,其执行顺序与书写顺序无关。 并行过程调用语句的一般书写格式如下: PROCEDURE 过程名(参数 1;参数 2;┄)IS [定义语句]; --变量定义 BEGIN [顺序处理语句] END 过程名; 下例是一个取三个输入位矢量最大值的功能描述,在它的结构体中使用了两个并 行过程调用语句。 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; USE IEEE. STD_LOGIC_UNSIGNED.ALL; ENTITY max IS PORT(in1:IN STD_LOGIC_VECTOR (7 DOWNTO 0); in2:IN STD_LOGIC_VECTOR (7 DOWNTO 0); in3:IN STD_LOGIC_VECTOR (7 DOWNTO 0); q:OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END max; ARCHITECTURE rtl OF max IS PROCEDURE maximun(a,b:IN STD_LOGIC_VECTOR; SIGNAL c:OUT STD_LOGIC_VECTOR)IS VARIABLE temp:STD_LOGIC_VECTOR (a’RANGE); BEGIN - -temp 矢量长度与 a 相同 IF (a > b) THEN temp := a; ELSE temp := b; END IF; c <= temp; END maximun; SIGNAL tmp1,tmp2:OUT STD_LOGIC_VECTOR(7 DOWNTO 0); BEG maximun(in1,in2,tmp1); maximun(tmp1,in3,tmp2); q <= tmp2; END rtl; 4.2.5 并行断言语句 并行断言语句的书写格式为: ASSERT 条件 [REPORT 报告信息] [SEVERITY 出错级别] 并行断言语句的书写格式与顺序断言语句的书写格式相同。顺序断言语句只能用 在进程、函数和过程中,而并行断言语句用在结构体中。任何并行断言语句都对 应着一个等价的被动进程语句,被动进程语句没有输出,因此并行断言语句的执 行不会引起任何事件的发生,只是在断言条件为“false”时给出一条信息报告。 例: LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; USE IEEE. STD_LOGIC_UNSIGNED.ALL; ENTITY example IS END example; ARCHITECTURE behave OF example IS SIGNAL comb: STD_LOGIC_VECTOR(1 DOWNTO 0); BEGIN ASSERT FALSE REPORT“This entity is a example to descript assert statement” SEVERITY NOTE; END behave; 4.2.6 参数传递语句 参数传递语句(GENERIC)主要用来传递信息给设计实体的某个具体元件,如 用来定义端口宽度、器件延迟时间等参数后并将这些参数传递给设计实体。使用 参数传递语句易于使设计具有通用性,例如,在设计中有一些参数不能确定,为 了简化设计和减少 VHDL 程序的书写,我们通常编写通用的 VHDL 程序。在设 计程序中,这些参数是待定的,在模拟时,只要用 GENERIC 语句将待定参数初 始化即可。 参数传递语句的书写格式为: GENERIC(类属表); 例: LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY and2 IS GENERIC(DELAY:TIME:= 10 ns); PORT(a:IN STD_LOGIC; b:IN STD_LOGIC; c:OUT STD_LOGIC); END and2; ARCHITECTURE behave OF and2 IS BEGIN c <= a AND b AFTER(DELAY); END behave; 4.2.7 元件例化语句 元件例化就是将预先设计好的设计实体定义为一个元件,然后利用映射语句将此 元件与当前设计实体中的制定端口相连,从而为当前设计实体引入一个低一级的 设计层次。在结构体中,描述只表示元件(或模块)和元件(或模块)之间的互 连,就像网表一样。当引用库中不存在的元件时,必须首先进行元件的创建,然 后将其放在工作库中,统购调用工作库来引用元件。在引用元件时,要先在结构 体中说明部分进行元件的说明,然后在使用原始时进行元件例化。 元件例化语句也是一种并行语句,各个例化语句的执行顺序与例化语句的书写顺 序无关,而是按照驱动的事件并行执行的。 在进行元件例化时,首先要进行例化元件的说明,元件说明部分使用 COMPONENT 语句,COMPONENT 语句用来说明在结构体中所要调用的模块。 如果所调用的模块在元件库中并不存在时,设计人员必须首先进行元件的创建, 然后将其放在工作库中统购调用工作库来引用该元件。 COMPONENT 语句的一般书写格式如下: COMPONENT <引用元件名> [GENERIC <参数说明>;] PORT <端口说明>; END COMPONENT; - -元件说明语句 在上面的书写结构中,保留字 COMPONENT 后面的“引用元件名”用来指定要在 结构体中例化的元件,该元件必须已经存在于调用的工作库中;如果在结构体中 要进行参数传递,在 COMPONENT 语句中,就要有传递参数的说明,传递参数 的说明语句以保留字 GENERIC 开始;然后是端口说明,用来对引用元件的端口 进行说明;最后以保留字 END COMPONENT 来结束 COMPONENT 语句。 如果在结构体中要引用上例中所定义的带延迟的二输入与门,首先在结构体中要 用 COMPONENT 语句对该元件进行说明,说明如下: COMPONENT and2 GENERIC(DELAY:TIME); PORT(a:IN STD_LOGIC; b:IN STD_LOGIC; c:OUT STD_LOGIC); END COMPONENT; 用 COMPONENT 语句对要引用的元件进行说明之后,就可以在结构体中对元件 进行例化以使用该元件。 元件例化语句的书写格式为: <标号名:> <元件名> [GENERIC MAP(参数映射)] PORT MAP(端口映射); 标号名是此元件例化的唯一标志,在结构体中标号名应该是唯一的,否则编译时 将会给出错误信息;接下来就是映射语句,映射语句就是把元件的参数和端口与 实际连接的信号对应起来,以进行元件的引用。 VHDL 提供了两种映射方法:位置映射和名称映射。 位置映射就是 PORT MAP 语句中实际信号的书写顺序与 COMPONENT 语句中 端口说明中的信号书写顺序保持一致,如下例所示: 位置映射示例: LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY example IS PORT(in1,in2:IN STD_LOGIC; out:OUT STD_LOGIC); END example; ARCHITECTURE structure OF example IS COMPONENT and2 GENERIC(DELAY:TIME); PORT(a:IN STD_LOGIC; b:IN STD_LOGIC; c:OUT STD_LOGIC); END COMPONENT; BEGIN U1 : and2 GENERIC MAP(10 ns) - -参数映射 PORT MAP(in1,in2,out); - -端口映射 END structure; 在上例中,元件 U1 的端口 a 映射到信号 in1,端口 b 映射到信号 in2,端口 c 映射到信号 out。 名称映射就是在 PORT MAP 语句中将引用的元件的端口信号名称赋给结构体中 要使用的例化元件的信号,如下例所示: 例:名称映射 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY example IS PORT(in1,in2:IN STD_LOGIC; out:OUT STD_LOGIC); END example; ARCHITECTURE structure OF example IS COMPONENT and2 GENERIC(DELAY:TIME); PORT(a:IN STD_LOGIC; b:IN STD_LOGIC; c:OUT STD_LOGIC); END COMPONENT; BEGIN U1:and2 GENERIC MAP(10 ns) PORT MAP(a => in1,b=> in2,c => out); END structure; 注意:名称映射的书写顺序要求并不是很严格,只要把要映射的对应信号连接 起来就可以了,顺序是可以颠倒的 . 4.2.9 生成语句 生成语句(GENERATE)是一种可以建立重复结构或者是在多个模块的表示形 式之间进行选择的语句。由于生成语句可以用来产生多个相同的结构,因此使用 生成语句就可以避免多段相同结构的 VHDL 程序的重复书写(相当于‘复制’)。 生成语句有两种形式:FOR- GENERATE 模式和 IF- GENERATE 模式。 FOR- GENERATE 模式的生成语句 FOR- GENERATE 模式生成语句的书写格式为: [标号:]FOR 循环变量 IN 离散范围 GENERATE <并行处理语句>; END GENERATE [标号]; 其中循环变量的值在每次的循环中都将发生变化;离散范围用来指定循环 变量的取值范围,循环变量的取值将从取值范围最左边的值开始并且递增到取值 范围最右边的值,实际上也就限制了循环的次数;循环变量每取一个值就要执行 一次 GENERATE 语句体中的并行处理语句;最后 FOR- GENERATE 模式生成语 句以保留字 END GENERATE [标号:];来结束 GENERATE 语句的循环。 生成语句的典型应用是存储器阵列和寄存器。下面以四位移位寄存器为 例,说明 FOR- GENERATE 模式生成语句的优点和使用方法。 下图所示电路是由边沿 D 触发器组成的四位移位寄存器,其中第一个触发 器的输入端用来接收四位移位寄存器的输入信号,其余的每一个触发器的输入端 均与左面一个触发器的 Q 端相连 dff1 dff2 dff3 dff4 输入 输出 di q(0) D Q q(1) D Q q(2) D Q q(3) D Q q(4) do CLK CLK CLK CLK cp 图用 D 触发器构成的四位移位寄存器 根据上面的电路原理图,写出四位移位寄存器的 VHDL 描述如下。 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY shift_reg IS PORT(di:IN STD_LOGIC; cp:IN STD_LOGIC; do:OUT STD_LOGIC); END shift_reg; ARCHITECTURE structure OF shift_reg IS COMPONENT dff PORT(d:IN STD_LOGIC; clk:IN STD_LOGIC; q:OUT STD_LOGIC); END COMPONENT; SIGNAL q:STD_LOGIC_VECTOR(4 DOWNTO 0); BEGIN dff1:dff PORT MAP (d1,cp,q(1)); dff2:dff PORT MAP (q(1),cp,q(2)); dff3:dff PORT MAP (q(2),cp,q(3)); dff4:dff PORT MAP (q(3),cp,do); END structure; 在上例的结构体中有四条元件例化语句,这四条语句的结构十分相似。我们对上 例再做适当修改,使结构体中这四条元件例化语句具有相同的结构,如下例所示: 例[] LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY shift_reg IS 元件说明 PORT(di:IN STD_LOGIC; cp:IN STD_LOGIC; do:OUT STD_LOGIC); END shift_reg; ARCHITECTURE structure OF shift_reg IS COMPONENT dff PORT(d:IN STD_LOGIC; clk:IN STD_LOGIC; q:OUT STD_LOGIC); END COMPONENT; SIGNAL q:STD_LOGIC_VECTOR(4 DOWNTO 0); BEGIN q(0)<= di dff1:dff PORT MAP (q(0),cp,q(1)); ――元件例化 dff2:dff PORT MAP (q(1),cp,q(2)); dff3:dff PORT MAP (q(2),cp,q(3)); dff4:dff PORT MAP (q(3),cp,q(4)); do<= q(4) END structure; 这样便可以使用 FOR- GENERATE 模式生成语句对上例中的规则体进行描述, 如例[]所示。 例: FOR- GENERATE 模式生成语句应用 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY shift_reg IS PORT(di:IN STD_LOGIC; cp:IN STD_LOGIC; do:OUT STD_LOGIC); END shift_reg; ARCHITECTURE structure OF shift_reg IS COMPONENT dff PORT(d:IN STD_LOGIC; clk:IN STD_LOGIC; q:OUT STD_LOGIC); END COMPONENT; SIGNAL q:STD_LOGIC_VECTOR(4 DOWNTO 0); BEGIN q(0)<= di label1:FOR i IN 0 TO 3 GENERATE dffx:dff PORT MAP (q(i),cp,q(i+1)); END GENERATE label1; do <= q(4) END structure; 可以看出用 FOR- GENERATE 模式生成语句替代例[]中的四条元件例化语 句,使 VHDL 程序变的更加简洁明了。在例[]的结构体中用了两条并发的信号代 入语句和一条 FOR- GENERATE 模式生成语句,两条并发的信号代入语句用来 将内部信号 q 和输入端口 di、输出端口 do 连接起来,一条 FOR- GENERATE 模 式生成语句用来产生具有相同结构的四个触发器。 IF- GENERATE 模式生成语句 IF- GENERATE 模式生成语句的书写格式如下: [标号:]IF 条件 GENERATE <并行处理语句>; END GENERATE [标号]; IF- GENERATE 模式生成语句主要用来描述一个结构中的例外情况,例如,某些 边界条件的特殊性。当执行到该语句时首先进行条件判断,如果条件为“TRUE” 才会执行生成语句中的并行处理语句;如果条件为“FALSE”,则不执行该语句。 例: IF- GENERATE 模式生成语句应用 LIBRARY IEEE; USE IEEE. STD_LOGIC_1164.ALL; ENTITY shift_reg IS PORT(di:IN STD_LOGIC; cp:IN STD_LOGIC; do:OUT STD_LOGIC); END shift_reg; ARCHITECTURE structure OF shift_reg IS COMPONENT dff PORT(d:IN STD_LOGIC; clk:IN STD_LOGIC; q:OUT STD_LOGIC); END COMPONENT; SIGNAL q:STD_LOGIC_VECTOR(3 DOWNTO 1); BEGIN label1: FOR i IN 0 TO 3 GENERATE IF(i=0)GENERATE dffx:dff PORT MAP (di,cp,q(i+1)); END GENERATE; IF(i=3)GENERATE dffx:dff PORT MAP (q(i),cp,do); END GENERATE; IF((i /=0)AND(i /=3))GENERATE dffx:dff PORT MAP (q(i),cp,q(i+1)); END GENERATE; END GENERATE label1; END structure; 在例[]的结构体中,FOR- GENERATE 模式生成语句中使用了 IF- GENERATE 模 式生成语句。IF- GENERATE 模式生成语句首先进行条件 i = 0 和 i = 3 的判断, 即判断所产生的 D 触发器是移位寄存器的第一级还是最后一级;如果是第一级 触发器,就将寄存器的输入信号 di 代入到 PORT MAP 语句中;如果是最后一级 触发器,就将寄存器的输出信号 do 代入到 PORT MAP 语句中。这样就解决了硬 件电路中输入输出端口具有不规则性所带来的问题。 第五章 组合逻辑电路设计 5.1 门电路 5.2 编码器 5.3 优先编码器 5.4 译码器 5.5 多路选择器 5.6 数值比较器 5.7 加法器 在前面的各章里,分别介绍了 VHDL 语言的语句、语法以及利用 VHDL 语言设计硬件电路的基本方法,本章重点介绍利用 VHDL 语言设计基本组合逻 辑模块的方法。 5.1 门电路 二输入异或门 y = ab + ab 二输入异或门的逻辑表达式如下所示: 例:采用行为描述方式设计的异或门 (依据逻辑表达式) LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY xor2_v1 IS PORT(a,b: IN STD_LOGIC; y: OUT STD_LOGIC); END xor2_v1; ARCHITECTURE behave OF xor2_v1 IS BEGIN y <= a XOR b; END behave; 例:采用数据流描述方式设计的异或门 (依据真值表) LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY xor2_v2 IS PORT(a,b: IN STD_LOGIC; y: OUT STD_LOGIC); END xor2_v2; ARCHITECTURE dataflow OF xor2_v2 IS BEGIN PROCESS (a,b) VARIABLE comb : STD_LOGIC_VECTOR(1 DOWNTO 0); BEGIN comb := a & b; CASE comb IS WHEN "00"=> y <='0'; WHEN "01"=> y <='1'; WHEN "10"=> y <='1'; WHEN "11"=> y <='0'; WHEN OTHERS => y <='X'; END CASE; END PROCESS; END dataflow; 5.2 编码器 用一组二进制代码按一定规则表示给定字母、数字、符号等信息的方法称 为编码,能够实现这种编码功能的逻辑电路称为编码器。 8 线—3 线编码器逻辑表达式: A2=I4+I5+I6+I7 A1=I2+I3+I6+I7 A0=I1+I3+I5+I7 例:采用行为描述方式的 8 线—3 线编码器 VHDL 源代码 (依据逻辑表达式) LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY coder83_v1 IS PORT(I0,I1,I2,I3,I4,I5,I6,I7: IN STD_LOGIC; A0,A1,A2: OUT STD_LOGIC); END coder83_v1; ARCHITECTURE behave OF coder83_v1 IS BEGIN A2 <= I4 OR I5 OR I6 OR I7; A1 <= I2 OR I3 OR I6 OR I7; A0 <= I1 OR I3 OR I5 OR I7; END behave; 例:采用数据流描述方式的 8 线—3 线编码器 VHDL 源代码(依据真值表) LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY coder83_v2 IS PORT( I: IN STD_LOGIC_VECTOR(7 DOWNTO 0); A: OUT STD_LOGIC_VECTOR(2 DOWNTO 0)); END coder83_v2; ARCHITECTURE dataflow OF coder83_v2 IS BEGIN PROCESS (I) BEGIN CASE I IS WHEN "10000000"=> A <="111"; WHEN "01000000"=> A <="110"; WHEN "00100000"=> A <="101"; WHEN "00010000"=> A <="100"; WHEN "00001000"=> A <="011"; WHEN "00000100"=> A <="010"; WHEN "00000010"=> A <="001"; WHEN OTHERS => A <="000"; END CASE; END PROCESS; END dataflow; 5.3 优先编码器 各输出端的逻辑方程 A2 = EI + EI (I7I6I5I4I3I2I1I0 + I7I6I5I4 I3 + I7I6I5I4I3 I2 + I7I6I5I4I3I2 I1 + I7I6I5I4I3I2I1 I0 ) = EI + I7I6I5I4 A1 = EI + EI (I7I6I5I4I3I2I1I0 + I7I6 I5 + I7I6I5 I4 + I7I6I5I4I3I2 I1 + I7I6I5I4I3I2I1 I0 ) = EI + I7I6I3I2 + I7I6 I5 + I7I6 I4 A0 = EI + EI (I7I6I5I4I3I2I1I0 + I7 I6 + I7I6I5 I4 + I7I6I5I4I3 I2 + I7I6I5I4I3I2I1 I0 ) = EI + I7 I 6 + I7I5I3 I 2 + I7I5 I4 + I7I5I3I1 GS = EI + EI I7I6I5I4I3I2I1I0 = EI + I7I6I5I4I3I2I1I0 EO = EI + I7I6I5I4I3I2I1I0 以 74148 逻辑表达式为依据,按行为描述方式编写的 VHDL 源代码如下: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY prioritycoder83_v1 IS PORT(I7,I6,I5,I4,I3,I2,I1,I0 : IN STD_LOGIC; EI:IN STD_LOGIC; A2,A1,A0: OUT STD_LOGIC; GS,EO:OUT STD_LOGIC); END prioritycoder83_v1; ARCHITECTURE behave OF prioritycoder83_v1 IS BEGIN A2 <= EI OR (I7 AND I6 AND I5 AND I4); A1 <= EI OR (I7 AND I6 AND I3 AND I2) OR (I7 AND I6 AND NOT I5) OR (I7 AND I6 AND NOT I4) ; A0 <= EI OR (I7 AND NOT I6) OR (I7 AND I5 AND NOT I4) OR (I7 AND I5 AND I3 AND I1) OR (I7 AND I5 AND I3 AND NOT I2); GS <= EI OR (I7 AND I6 AND I5 AND I4 AND I3 AND I2 AND I1 AND I0); EO <= EI OR NOT(I7 AND I6 AND I5 AND I4 AND I3 AND I2 AND I1 AND I0); END behave; 注意:采用数据流编写优先编码器时,因为 VHDL 语言目前还不能描述任意项, 即下面的语句形式是非法的: WHEN “0XXXXXXX”=>A<=“000”; 因此不能用 CASE 语句来描述 74148 。采用 IF 语句对 74148 进行了逻辑描述 如 下: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY prioritycoder83_v2 IS PORT ( I: IN STD_LOGIC_VECTOR(7 DOWNTO 0); EI:IN STD_LOGIC; A: OUT STD_LOGIC_VECTOR(2 DOWNTO 0); GS,EO:OUT STD_LOGIC); END prioritycoder83_v2; ARCHITECTURE dataflow OF prioritycoder83_v2 IS BEGIN PROCESS(EI,I) BEGIN IF(EI='1')THEN A <= "111"; GS <= '1'; EO <= '1'; ELSIF (I="11111111" AND EI='0')THEN A <= "111"; GS <= '1'; EO <= '0'; ELSIF (I(7)='0' AND EI='0')THEN A <= "000"; GS <= '0'; EO <= '1'; ELSIF (I(6)='0' AND EI='0')THEN A <= "001"; GS <= '0'; EO <= '1'; ELSIF (I(5)='0' AND EI='0')THEN A <= "010"; GS <= '0'; EO <= '1'; ELSIF (I(4)='0' AND EI='0')THEN A <= "011"; GS <= '0'; EO <= '1'; ELSIF (I(3)='0' AND EI='0')THEN A <= "100"; GS <= '0'; EO <= '1'; ELSIF (I(2)='0' AND EI='0')THEN A <= "101"; GS <= '0'; EO <= '1'; ELSIF (I(1)='0' AND EI='0')THEN A <= "110"; GS <= '0'; EO <= '1'; ELSE (I(0)='0' AND EI='0')THEN A <= "111"; GS <= '0'; EO <= '1'; END IF; END PROCESS; END dataflow; 5.4 译码器 按数据流描述方式编写的 3 线—8 线译码器 74138VHDL 源代码 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY decoder138_v2 IS PORT(G1,G2A,G2B: IN STD_LOGIC; A: IN STD_LOGIC_VECTOR(2 DOWNTO 0); Y: OUT STD_LOGIC_VECTOR(7 DOWNTO 0)); END decoder138_v2; ARCHITECTURE dataflow OF decoder138_v2 IS BEGIN PROCESS (G1,G2A,G2B,A) BEGIN IF(G1='1' AND G2A='0' AND G2B='0')THEN CASE A IS WHEN "000" => Y <="11111110"; WHEN "001" => Y <="11111101"; WHEN "010" => Y <="11111011"; WHEN "011" => Y <="11110111"; WHEN "100" => Y <="11101111"; WHEN "101" => Y <="11011111"; WHEN "110" => Y <="10111111"; WHEN OTHERS => Y <="01111111"; END CASE; ELSE Y <="11111111"; END IF; END PROCESS; ENDdataflow; 5.5 多路选择器 参考 74151 的真值表,采用 IF 语句结构编写的 VHDL 源代码如下: LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY mux8_v2 IS PORT(A: IN STD_LOGIC_VECTOR (2 DOWNTO 0); D0,D1,D2,D3,D4,D5,D6,D7:IN STD_LOGIC; G:IN STD_LOGIC; Y: OUT STD_LOGIC; YB:OUT STD_LOGIC); END mux8_v2; ARCHITECTURE dataflow OF mux8_v2 IS BEGIN PROCESS (A,D0,D1,D2,D3,D4,D5,D6,D7,G) BEGIN IF (G ='1') THEN Y <='0'; YB <='1'; ELSIF(G='0'AND A="000")THEN Y <= D0; YB <= NOT D0; ELSIF(G='0'AND A="001")THEN Y <= D1; YB <= NOT D1; ELSIF(G='0'AND A="010")THEN Y <= D2; YB <= NOT D2; ELSIF(G='0'AND A="011")THEN Y <= D3; YB <= NOT D3; ELSIF(G='0'AND A="100")THEN Y <= D4; YB <= NOT D4; ELSIF(G='0'AND A="101")THEN Y <= D5; YB <= NOT D5; ELSIF(G='0'AND A="110")THEN Y <= D6; YB <= NOT D6; ELSE Y <= D7; YB <= NOT D7; END IF; END PROCESS; END dataflow; 参考 74151 的真值表,采用 CASE 语句结构编写的 VHDL 源代码如下 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY mux8_v3 IS PORT(A2,A1,A0: IN STD_LOGIC; D0,D1,D2,D3,D4,D5,D6,D7:IN STD_LOGIC; G:IN STD_LOGIC; Y: OUT STD_LOGIC; YB:OUT STD_LOGIC); END mux8_v3; ARCHITECTURE dataflow OF mux8_v3 IS SIGNAL comb: STD_LOGIC_VECTOR(3 DOWNTO 0); BEGIN comb <= G & A2 & A1 & A0; PROCESS (comb,D0,D1,D2,D3,D4,D5,D6,D7,G) BEGIN CASE comb IS WHEN "0000" => Y <= D0; YB <= NOT D0; WHEN "0001" => Y <= D1; YB <= NOT D1; WHEN "0010" => Y <= D2; YB <= NOT D2; WHEN "0011" => Y <= D3; YB <= NOT D3; WHEN "0100" => Y <= D4; YB <= NOT D4; WHEN "0101" => Y <= D5; YB <= NOT D5; WHEN "0110" => Y <= D6; YB <= NOT D6; WHEN "0111" => Y <= D7; YB <= NOT D7; WHEN OTHERS => Y <='0'; YB <='1'; END CASE; END PROCESS; END dataflow; 5.6 数值比较器 数值比较器是对两个位数相同的二进制数进行比较并判定其大小关系 的算术运算电路。 下例是一个采用 IF 语句编制的对两个 4 位二进制数进行比较例子,其中 A 和 B 分别是参与比较的两个 4 位二进制数,YA、YB 和 YC 是用来分别表示 A>B、A B) THEN YA <='1'; YB <='0'; YC <='0'; ELSIF(A < B) THEN YA <='0'; YB <='1'; YC <='0'; ELSE YA <='0'; YB <='0'; YC <='1'; END IF; END PROCESS; END behave; 5.7 加法器 加法器是数字电路中的基本运算单元,下例是直接利用 VHDL 运算符“+”实 现加法运算的 8 位加法器源代码。其中 A 和 B 是两个相加的 8 位二进制数,Cin 是低位进位位,S 是 A、B 相加之和,Co 是 A、B 相加之后的进位位。 LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; ENTITY adder8_v IS PORT(A :IN STD_LOGIC_VECTOR(7 DOWNTO 0); B :IN STD_LOGIC_VECTOR(7 DOWNTO 0); Cin:IN STD_LOGIC; Co : OUT STD_LOGIC; S :OUT STD_LOGIC_VECTOR(7 DOWNTO 0)); END adder8_v; ARCHITECTURE behave OF adder8_v IS SIGNAL Sint : STD_LOGIC_VECTOR(8 DOWNTO 0); SIGNAL AA,BB: STD_LOGIC_VECTOR(8 DOWNTO 0); BEGIN AA <='0'& A(7 DOWNTO 0); --将 8 位加数矢量扩展为 9 位,为进位提供空间 BB <='0'& B(7 DOWNTO 0); --将 8 位被加数矢量扩展为 9 位,为进位提供空间 Sint <= AA + BB + Cin; S(7 DOWNTO 0) <= Sint(7 DOWNTO 0); Co <= Sint(8); END behave; 第六章 时序逻辑电路设计 在时序电路中,是以时钟信号作为驱动信号的,也就是说时序电路是在时钟 信号的边沿到来时,它的状态才会发生改变。因此,在时序电路中时钟信号是非 常重要的,它是时序电路的执行条件和同步信号。 在用 VHDL 描述时序逻辑电路时,通常采用时钟进程的形式来描述,也就是 说,时序逻辑电路中进程的敏感信号是时钟信号。时钟作为敏感信号的描述方式 有两种: 1.时钟信号显示地出现在 PROCESS 语句后面的敏感信号表中。 2. 时钟信号没有显示地出现在 PROCESS 语句后面的敏感信号表中,而是出现在 WAIT 语句的后面。 在时序逻辑电路中,时钟是采用边沿来触发的,时钟边沿分为上升沿和下降 沿。以下是这两种边沿的描述方式。 对于上升沿,其物理意义是指时钟信号的逻辑值是从‘0’跳变到‘1’。下面是时 钟上升沿的几种描述形式: 上升沿描述: 描述 1: label1:PROCESS(clk) BEGIN IF(clk’EVENT AND clk = ‘1’)THEN ┇ AND PROCESS; 描述 2: label2:PROCESS(clk) BEGIN WAIT UNTIL clk = ‘1’; ┇ AND PROCESS; 下降沿描述: 描述 1: label1:PROCESS(clk) BEGIN IF(clk’EVENT AND clk = ‘0’)THEN ┇ AND PROCESS; 描述 2: label2:PROCESS(clk) BEGIN WAIT UNTIL clk = ‘0’; ┇ AND PROCESS; 6.1 JK 触发器 例[6-1] LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY JKff_v1 IS PORT(J,K: IN STD_LOGIC; clk: IN STD_LOGIC; set: IN STD_LOGIC; reset: IN STD_LOGIC; Q,QB: OUT STD_LOGIC); END JKff_v1; ARCHITECTURE behave OF JKff_v1 IS SIGNAL Q_temp,QB_temp: STD_LOGIC; BEGIN PROCESS (clk,set,reset) BEGIN IF (set ='0' AND reset ='1')THEN Q_temp <= '1'; QB_temp <= '0'; ELSIF (set ='1' AND reset ='0')THEN Q_temp <= '0'; QB_temp <= '1'; ELSIF (clk'EVENT AND clk = '1')THEN IF(J='0' AND K='1')THEN Q_temp <= '0'; QB_temp <= '1'; ELSIF(J='1' AND K='0')THEN Q_temp <= '1'; QB_temp <= '0'; ELSIF(J='1' AND K='1')THEN Q_temp <= NOT Q_temp ; QB_temp <= NOT QB_temp; END IF; END IF; Q <= Q_temp; QB <= QB_temp; END PROCESS; END behave; 6.2 60 进制递增计数器 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; ENTITY cntm60v IS PORT( en : IN std_logic; clear : IN std_logic; load : IN std_logic; dl,dh : IN std_logic_vector(3 downto 0); clk : IN std_logic; cout : out std_logic; ql : buffer std_logic_vector(3 downto 0); qh : buffer std_logic_vector(3 downto 0) ); END cntm60v; ARCHITECTURE behave OF cntm60v IS signal ent2 : std_logic; BEGIN PROCESS (clk) VARIABLE tmpl,tmph :std_logic_vector(3 downto 0); BEGIN IF(clear='0') THEN tmpl:="0000"; tmph:=“0000”; - -异步清零 ELSIF(clk'EVENT AND clk = '1') THEN IF load='0' THEN tmpl:=dl; tmph:=dh; - - 同步置数 elsif(en='1') then if (tmpl="1001") then tmpl:=“0000”; if(tmph="0101") then tmph:=“0000”; else tmph:=tmph+1; end if; else tmpl:=tmpl+1; end if; end IF; END IF; ql <= tmpl; ent2 <= tmpl(3) and tmpl(0) and en; qh<=tmph; cout<=tmph(2) and tmph(0) and ent2; END PROCESS; END behave ; - -个位计数器 9+1=0 - -十位计数器 5+1=0 59+1=0 --end lf (load) --end if clear - -计数器为 59 时进位信号 cout 输出‘1’。 END

    Top_arrow
    回到顶部
    EEWORLD下载中心所有资源均来自网友分享,如有侵权,请发送举报邮件到客服邮箱bbs_service@eeworld.com.cn 或通过站内短信息或QQ:273568022联系管理员 高进,我们会尽快处理。