三、VHDL语言基础

正文:

1 VHDL简介

VHDL的全称为VHSIC硬件描述语言(VHSIC Hardware Description Language),VHSIC: Very High Speed Integrated Circuit

1.1 历史

1980 – 美国国防部设立一个基金,在VHSIC项目之下开设了一个子课题,研究标准的硬件描述语言,1982诞生VHDL。
1987 – IEEE 将其修正为 IEEE 标准:1076
1993 – 修正了VHDL语言,升级至IEEE 1076-1993

1.2 产生原因

为什么使用VHDL ?原理图图形化设计缺点?
答:
大型设计,原理图连接太复杂,检查错误太困难
通过使用高级语言对你的设计做描述,可以分析语法错误,综合后效率可能更高,时间成本更好。

2 VHDL的基本结构

VHDL的基本设计单元结构:程序包说明、实体说明、结构体说明三部分。

-- 库、程序包的说明调用
Library IEEE;
use IEEE.Std_Logic_1164.ALL;
-- 实体声明
Entity FreDevider is
port
(
    Clock: IN Std_logic;
    Clkout: OUT Std_logic
);
END;
-- 结构体定义
Architecture Behavior Of FreDevider is
signal Clk:Std_Logic; -- 中间临时变量
begin
    process(Clock)
    begin
        IF rising_edge(Clock) THEN
            Clk <= NOT Clk;    
        END IF;
    END process;
    Clkout <= Clk;
END
 

2.1 库和程序包

2.1.1 库

库是专门用于存放预先编译好的程序包的地方,对应一个文件目录,程序包的文件就放在此目录中,其功能相当于共享资源的仓库,所有已完成的设计资源只有存入某个“库”内才可以被其他实体共享。库的说明总是放在设计单元的最前面,表示该库资源对以下的设计单元开放。库语句格式如下:

LIBRARY 库名;

常用的库有IEEE库、STD库和WORK库:

  • IEEE库:是VHDL设计中最常用的资源库,包含IEEE标准的STD_LOGIC_1164、NUMERIC_BIT、NUMERIC_STD以及其他一些支持工业标准的程序包。其中最重要和最常用的是STD_LOGIC_1164程序包,大部分程序都是以此程序包中设定的标准为设计基础。
  • STD库:是VHDL的标准库,VHDL在编译过程中会自动调用这个库,所以使用时不需要用语句另外说明。
  • WORK库:是用户在进行VHDL设计时的现行工作库,用户的设计成果将自动保存在这个库中,是用户自己的仓库,同STD库一样,使用该库不需要任何说明。

库的作用范围:

  库说明语句的作用范围从一个实体说明开始到它所属的构造体、配置为止。当一个源程序中出现两个以上的实体时,两条作为使用库的说明语句应在每个实体说明语句前重复书写。

(1)示例1:

  自定义一个库:

  • decoder2to4.vhd文件

 

 

  • mylib.vhd文件

 

 

  •  expdecoder.vhd文件

 

 

 (2)示例2:

  • my_pack是一个包,里面声明了一个component myadd。myadd 在另外一个vhd文件中实现。
  • 实现了一个实体mycompadd。注意:实现package my_pack 和 entity mycompadd 时显示调用了两次库。

 

 

  •  实现component myadd

 

 

 

2.1.2 程序包

程序包是用VHDL语言编写的一段程序,可以供其他设计单元调用和共享,相当于公用的“工具箱”,各种数据类型、子程序等一旦放入了程序包,就成为共享的“工具”,类似于C语言的头文件,使用它可以减少代码的输入量,使程序结构清晰。在一个设计中,实体部分所定义的数据类型、常量和子程序可以在相应的结构体中使用,但在一个实体的声明部分和结构体部分中定义的数据类型、常量及子程序却不能被其他设计单元使用。因此,程序包的作用是可以使一组数据类型、常量和子程序能够被多个设计单元使用。
程序包分为包头和包体两部分。包头(也称程序包说明)是对包中使用的数据类型、元件、函数和子程序进行定义,其形式与实体定义类似。包体规定了程序包的实际功能,存放函数和过程的程序体,而且还允许建立内部的子程序、内部变量和数据类型。包头、包体均以关键字PACKAGE开头。程序包格式如下:

--包头格式:
PACKAGE 程序包名 IS
[包头说明语句]
END 程序包名;

--包体格式:
PACKAGE BODY 程序包名 IS
[包体说明语句]
END 程序包名;

 

调用程序包的通用模式为:

USE 库名.程序包名.ALL;

 

常用预定义程序包有以下四个:

  • 1.STD_LOGIC_1164程序包
    STD_LOGIC_1164程序包定义了一些数据类型、子类型和函数。数据类型包括:STD_ULOGIC、STD_ULOGIC _VECTOR、STD_LOGIC和STD_LOGIC _VECTOR,用的最多最广的是STD_LOGIC和STD_LOGIC_VECTOR数据类型。调用STD_LOGIC_1164程序包中的项目需要使用以下语句:
    LIBRARY IEEE;
    USE IEEE.STD_LOGIC_1164.ALL;
    该程序包预先在IEEE库中编译,是IEEE库中最常用的标准程序包,其数据类型能够满足工业标准,非常适合CPLD(或FPGA)器件的多值逻辑设计结构。
  • 2.STD_LOGIC_ARITH程序包
    该程序包是美国Synopsys公司的程序包,预先编译在IEEE库中。主要是在STD_LOGIC_1164程序包的基础上扩展了UNSIGNED(无符号)、SIGNED(符号)和SMALL_INT(短整型)三个数据类型,并定义了相关的算术运算符和转换函数。
  • 3.STD_LOGIC_SIGNED程序包
    该程序包预先编译在IEEE库中,也是Synopsys公司的程序包。主要定义有符号数的运算,重载后可用于INTEGER(整数)、STD_LOGIC(标准逻辑位)和STD_LOGIC _VECTOR(标准逻辑位向量)之间的混合运算,并且定义了STD_LOGIC _VECTOR到INTEGER的转换函数。还定义了STD_LOGIC _VECTOR类型的符号数算数运算子程序。
  • 4.STD_LOGIC_UNSIGNED程序包
    该程序包用来定义无符号数的运算,其他功能与STD_LOGIC_SIGNED相似。


自定义包的实现见2.1.1节库的示例。

 

 

 

2.2 实体(Entity)

实体描述了设计单元的输入输出接口信号或引脚,是设计实体经封装后对外的一个通信界面。

ENTITY 实体名 IS -- 引导语句
[GENERIC (常数名: 数据类型: 设定值)] -- 类属表
PORT -- 端口表
(
    端口名1: 端口方向 端口类型;
    端口名2: 端口方向 端口类型;
    端口名3: 端口方向 端口类型;
    ......
    端口名n: 端口方向 端口类型 -- 最后一个一定不能加";",不然会报next process的")" expect ";" or ","
);
END [实体名]; -- 结束语句

 注意:

  1. 端口n的声明最后一定不能加";",不然会报错误:next process的")" expect ";"
  2. port中端口默认类型为signal

2.2.1 类属:

  • 类属说明的书写格式是:

GENERIC(常数名:数据类型:设定值);
  • 其他
    类属 GENERIC 参量是一种端口界面常数,常以一种说明的形式放在实体或块结构体前的说明部分。比较常见的情况是利用类属来动态规定一个实体的端口的大小,或设计实体的物理特性,或结构体中的总线宽度,或设计实体中底层中同种元件的例化数量等等。一般在结构体中,类属的应用与常数是一样的,其中的常数名是由设计者确定的类属常数名,数据类型通常取 INTEGER 或TIME 等类型,设定值即为常数名所代表的数值,但需注意 VHDL 综合器仅支持数据类型为整数的类属值。例如:

ENTITY mcu1 IS
GENERIC (addrwidth : INTEGER := 16);
PORT(
    add_bus : OUT STD_LOGIC_VECTOR(addrwidth-1 DOWNTO 0) );
    ...

  在这里 GENERIC 语句对实体 mcu1 作为地址总线的端口 add_bus 的数据类型和宽度作了定义 即定义 add_bus 为一个 16 位的标准位矢量 定义 addrwidth 的数据类型是整数INTEGER 其中 常数名addrwidth减 1 即为 15 所以这类似于将上例端口表写成PORT (add_bus : OUT STD_LOGIC_VECTOR (15 DOWNTO 0));

2.2.2 端口

  • 端口语句的格式:
PORT(端口信号名:端口模式 数据类型;
端口信号名:端口模式 数据类型);

 

  • 端口模式有:
    • IN : 在实体中只读,接受外来数据. 只能出现在赋值语句的右侧。
    • OUT: 在实体中只能更新,不可读。只能出现在赋值语句的左侧。
    • INOUT: 在实体内部可更新、可读,可出现在赋值语句的两侧。 
    • BUFFER: 可用作内部赋值,可出现在赋值语句的两侧。在可综合代码中不推荐使用。

2.3 结构体

2.3.1 结构体的作用和特点

2.3.1.1 作用

结构体的任务是:定义结构体中的各项内部使用元素,如数据类型(TYPE),常数(CONSTAND),信号(SIGNAL),元件(COMPONENT),过程(POCEDURE),变量(VARIABLE)和进程(PROCESS)等。通过VHDL语句描述实体所要求的具体行为和逻辑功能。描述各元件之间的连接。

2.3.1.2 特点

  1. 用于描述模型的功能
  2. 必须和一个 Entity相关联
  3. 一个Entity可有多个 Architectures
  4. Architecture 语句并发执行.

2.3.2 结构体的格式:

Architecture 结构体名 OF 实体名 IS
定义语句法;
BEGIN
功能描述语句法;
END 结构体名称;

 

2.3.3 结构体的四种描述方式

结构化描述、数据流描述、行为描述、混合。

2.3.3.1 结构描述

原件和内联,通过低级器件的内联实现。

特点:

  1. 描述电路的功能和结构。
  2. 可调用专用硬件。
  3. 可综合。
  4. 结构化、模块化。
  5. 结构化描述易于理解,接近于原理图,使用简单的模块组成逻辑电路,实现逻辑功能。
  6. 组件以层次化相连接。
  7. 结构化描述可以将简单门、甚至更复杂的逻辑门连接在一起。
  8. 结构化描述在团队协作时非常有用。

2.3.3.2 RTL(寄存器传输级)描述, 又称数据流描述

描述数据在系统中的流动过程,数据流使用一系列的并发语句实现逻辑,数据流的描述是在控制逻辑函数已经获得的条件下实现,数据流代码亦称为并发代码。
注意:并发语句在编译中被同时评估,因此语句的顺序无关紧要。

特点:

  1. 直接使用基本门和扩展门实现,没有结构化。
  2. 逻辑表达式在编码之前需要写好。
  3. 没有特定的硬件约束。
  4. 可以综合。
  5. 数据流使用一系列的并发语句实现逻辑. 。
  6. 数据流的描述是在控制逻辑函数已经获得的条件下实现。
  7. 数据流代码亦称为并发代码。

举例:

  • input: x1, x2, x3, ... , xn;
  • output: y1, y2, y3, ..., yn;
  • 数据流描述:
y1 <= x1 AND x2; y2 = NOT x1; y3 <= x3 OR x2;

 

2.3.3.3 行为描述

顺序和并发语句,通过输入输出响应描述。

特点:

  1. 电路的功能,无关结构(用于数学模型或系统工作原理的仿真)。
  2. 不面向特定硬件。
  3. 不一定可综合。
  4. 易于理解,因为行为描述更像人类的语言。
  5. 它精确地表示了在“黑盒子”内部发生的输入-输出之间的关系。
  6. 在VHDL中,通常使用PROCESS(进程)实现。

举例:

  • input: x1, x2, x3, ... , xn;
  • output: y1, y2, y3, ..., yn;
  • 行为描述:
if x1 = '0' then
y1 <= x2 and x3; 
elsif ... 
esle ... 
end if;

 

  

2.3.3.4 举例说明,下面是一个两位相等比较器的例子

 

entity equ2 is
port(a,b:in std_logic_vector(1 downto 0);
equ:out std_logic);
end equ2;
--结构体结构描述:用元件例化,即网表形式来实现;
architecture netlist of equ2 is
-- nor 或非
component nor2 port(a,b :in std_logic; c :out std_logic); end component; component xor2
-- xor 异或
port(a,b :in std_logic; c :out std_logic); end component; signal x: std_logic_vector(1 downto 0); begin U1:xor2 port map(a(0),b(0),x(0)); U2:xor2 port map(a(1),b(1),x(1)); U3:nor2 port map(x(0),x(1),equ); end netlist; --结构体数据流描述:用布尔方程来实现: architecture equation of equ2 is begin equ<=(a(0) xor b(0)) nor(a(1) xor b(1)); end equation; --结构体行为描述:用顺序语句来实现: architecture con_behave of equ2 is begin process(a,b) begin if a=b then equ<='1'; else equ<='0'; end if; end procerss; end con_behave; --结构体行为描述:用并行语句来实现: architecture seq_behave of equ2 is begin equ<='1' when a=b else '0'; end sqq_behave;

 

用行为描述方式设计的全加器 

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;

ENTITY onebitadder IS
PORT(a,b,cin:IN BIT;
Sum,count:OUT BIT);
END onebitadder;

ARCHITECTURE behavior OF onebitadder IS
BEGIN
PROCESS(a,b,cin)
BEGIN
Sum<= a XOR b XOR cin;
Count<=(a AND b) OR ((a XOR b) AND cin);
END PROCESS;
END behavior;

 

2.3.4 块语句结构:

块结构名:
BLOCK 
端口说明 类属说明
BEGIN
并行语句
END BLOCK 块结构名;

 

2.3.5 进程语句结构:

进程名:
PROCESS(敏感信号表) IS
进程说明
BEGIN
顺序描述语句
END PROCESS 进程名;

 

2.4 配置(Configuration)

2.4.1 配置的作用

  • 用于实现模型的关联,可将一个 Entity 和一个Architecture关联起来,也可将一个 component 和一个 entity-architecture关联起来。
  • 目的是为了在一个实体中灵活的使用不同的Architecture。可以在仿真环境中大量使用,但是在综合环境中限制使用。

2.4.2 配置的格式

Configuration 配置名 of 实体名 IS
    for 结构体名
    end forend;-- 1076-1987 version
end Configuration;-- 1076-1993 version

Configuration <identifier> of <entity_name> IS
    for <architecture_name>
    end forend;-- 1076-1987 version
end Configuration;-- 1076-1993 version

 

2.4.3 配置举例

2.5 子程序模块

能被主程序反复调用并能将处理结果传送到主程序的程序模块,子程序分为过程语句(Procedure)和函数(Functure)两种。子程序中的参数说明是局部的,只能在子程序体内起作用。

2.5.1 过程语句(Procedure)

PROCEDURE 过程名(参数1;参数2;----) IS
定义语句;
BEGIN
顺序处理语句;
END 过程名;

 

2.5.2 函数(Functure)

FUNCTION 函数名(参数1;参数2;----RETURN 数据类型 IS
定义语句;
BEGIN
顺序处理语句;
RETURN 返回变量名;

 

3 VHDL基本语法

3.1 标识符

标识符用来定义常数、变量、信号、端口、子程序或者参数的名字。由字母(A~Z, a~z)、数组(0~9)和下划线(_)字符组成。

3.1.1 标识符规则:

  1. 首字符必须是字母
  2. 末字符不能为下划线
  3. 不允许出现两个连续的下划线
  4. 不区分大小写
  5. VHDL定义的保留字(关键字),不能用作标识符
  6. 标识符字符最长可以是32个字符
  7. 注释由两个连续的下划线(--)引导

3.1.2 关键字 (保留字)

关键字(keyboard)是VHDL中具有特别含义的单词,只能作为固定的用途,用户不能用其做为标识符。

3.2 数据对象

3.2.1 信号(SIGNAL)

为全局变量,在程序包说明、实体说明、结构体描述中使用,用于声明内部信号,而非外部信号(外部信号为IN、OUT、INOUT、BUFFER),其在元件之间起互联作用,可以赋值给外部信号。
定义格式:

SIGNAL 信号名: 数据类型[:= 初始值];

举例:
SIGANL brdy: BIT;
SIGANL output: INTEGER:= 2;

 

赋值格式:

目标信号名<=表达式

 

常在结构体中用赋值语句完成对信号赋初值的任务,因为综合器往往忽略信号声名时所赋的值。

3.2.2 变量(VARIABLE)

只在给定的进程(process)中用于声明局部值或用于子程序中,变量的赋值符号为“:=”,和信号不同,信号是实际的,是内部的一个存储元件(SIGNAL)或者是外部输入(IN、OUT、INOUT、BUFFER),而变量是虚的,仅是为了书写方便而引入的一个名称,常用在实现某种算法的赋值语句当中。
定义格式:

VARIABLE 变量名: 数据类型[:= 初始值];

举例:
VARIABLE opcode: BIT_VECTOR(3 downto 0):= "0000";
VARIABLE freq: INTEGER;

 

变量赋值符号":=",变量赋值立刻更新。 

3.2.3 常量(CONSTAT)

在结构体描述、程序包说明、实体说明、过程说明、函数调用说明和进程说明中使用,在设计中描述某一规定类型的特定值不变,如利用它可设计不同模值的计数器,模值存于一常量中,对不同的设计,改变模值仅需改变此常量即可,就如参数化元件。
定义格式:

CONSTANT 常数名:数据类型:= 初始值;

举例:
CONSTANT rise_fall_time: TIME:= 2ns;
CONSTANT data_bus: INTEGER:= 16;

 

3.2.4 信号与变量的区别

信号与变量最大的不同在于,如果在一个进程中多次为一个信号赋值,只有最后一个值会起作用,而当为变量赋值时,变量的值改变是立即发生的。

3.3 数据类型

VHDL是一种强类型语言,对于每一个常数、变量、信号、函数及设定的各种参量的数据类型(DATA TYPES)都有严格要求,相同数据类型的变量才能互相传递和作用,标准定义的数据类型都在VHDL标准程序表STD中定义,实际使用中,不需要用USE语句以显式调用。

VHDL常用的数据类型有三种:

  1. 标准定义的数据类型
  2. IEEE预定义标准逻辑位
  3. 矢量及用户自定义的数据类型。

3.3.1 标准定义的数据类型

  • 布尔:(Boolean)

Type BOOLEAN IS (FALSE, TRUE);

取值为FALSE和TRUE,不是数值,不能运算,一般用于关系运算符

  • 位:(Bit)

TYPE BIT IS ('0', '1');

取值为0和1,用于逻辑运算

  • 位矢量:(Bit_Vector)

TYPE BIT_VECTOR IS ARRAY(Natural range<>) OF BIT;

基于Bit类型的数组,用于逻辑运算

SIGNAL a: Bit_Vector (0 to 7); Signal a: Bit_Vector (7 to 0);

  • 字符:(character)

TYPE CHARACTER IS (NUL, SOH, STX, ..., '', '!', ...);

通常用''引号引起来,区分大小写

  • 字符串:(String)

通常用""双引号引起来,区分大小写

VARIABLE string_var: STING (1 to 7);

string_var := "ABCD"

  • 整数:(Integer)

取值范围-(2^{31}-1) to (2^{31}-1),可用32位的有符号的二进制数表示

VARIABLE a: INTEGER -63 to 63

在实际应用中,VHDL仿真器将ITEGER作为有符号数处理,而VHDL综合器将Integer作为无符号数处理

要求用range子句为所定义的数限定范围,以便根据范围来决定表示此信号或变量的二进制的位数。

  • 实数:(real)

实数类型仅能在VHDL仿真器中使用,综合器不支持

取值范围 -1.0E38 to +1.0E38

  • 时间:(Time)

物理量数据,包括整数和单位两个部分

时间类型仅能在VHDL仿真器中使用,综合器不支持

范围从-(2^{31} -1)to (2^{31}-1)

表达方法包含数字、(空格)单位两部分,如(10 PS)

常用单位:fs,ps,ns,us,ms,sec,min,hr

  • 错误等级:(severity level)

表示系统状态

该类型仅能在VHDL仿真器中使用,综合器不支持

TYPE severity_lever IS (NOTE, WARNING, ERROR, FAILURE)

3.3.2 IEEE预定义的标准逻辑位与矢量

  • STD_LOGIC:工业标准的逻辑类型,取值为‘0’(强制为高)、‘1’(强制为低)、‘Z’(高阻态)、‘X’(强未知)、‘W’(弱未知;弱信号不定)、‘L’(弱0;弱低)、‘H’(弱1;若高)、‘-’(忽略;不关心)、‘U’(未初始化),只有前四种具有实际物理意义,其他的是为了与模拟环境相容才保留的。
  • STD_LOGIC_VECTOR:工业标准的逻辑类型集,STD_LOGIC的组合。

3.3.3 用户自定义的数据类型

  • 枚举类型
    • 格式:
      TYPE 数据类型名 IS (枚举文字,枚举文字,. . . .)
  • 整数类型和实数类型
    • 整数类型与实数类型是标准包中预定义的整数类型的子集,由于综合器无法综合未限定范围的整数类型的信号或变量,故一定要用RANGE子句为所定义整数范围限定范围以使综合器能决定信号或变量的二进制的位数。
    • 格式:
      TYPE 数据类型名 IS RANGE 约束范围;(如-10到+10
  • 数组类型
    • 格式:
      TYPE 数据类型名 IS ARRAY(下限 TO 上限) OF 类型名称
  • 记录类型
    • 格式:
TYPE 记录类型名 IS RECODE
元素名: 数据类型名;
元素名: 数据类型名;
END RECODE

 

3.4 运算符

3.4.1 算术运算符

  1. +, -, *, /
  2. MOD: 取模,取模运算(a MOD b)的符号与b相同,其绝对值小于b的绝对值。如:-5 MOD 2 = 1,5 MOD -2 = -1。
  3. REM: 取余,取余运算(a REM b)的符号与a相同,其绝对值小于b的绝对值。如:-5 REW 2 = -1,5 MOD -2 = 1。
  4. 多数语言对取余和取整不区分,比如python都是用%符号。详情见https://blog.csdn.net/origin_lee/article/details/40541053
  5. SLL(Shift Left Logical): 逻辑左移,将位向量左移,右边移空位补零。
  6. SRL(Shift Right Logical): 逻辑右移,将位向量右移,左边移空位补零。
  7. SLA(Shift Left Arithmetic): 算数左移,将位向量左移,右边第一位数值保持不变。
  8. SRA(Shift Right Arithmetic): 算数右移,将位向量右移,左边第一位数值保持不变。
  9. ROL(rotate left): 循环左移。
  10. ROR(rotate right): 循环右移。
  11. **: 乘方。
  12. ABS(absolute): 取绝对值。

3.4.2 关系运算符

  1. =,/=,<,>,<=,>=

3.4.3 逻辑运算符

  1. AND, OR
  2. NAND, NOR, NOT
  3. XNOR: 同或, XOR: 异或

3.4.4 赋值运算符

  1. <=: 信号赋值,最后赋值。
  2. :=:逻辑赋值,即时赋值。

3.4.5 关联运算符

  1. =>

3.4.6 其他运算符

  1. +,-
  2. &:并置操作符

3.4.7 运算符优先级

3.4.8 运算符表格一览

关于运算符的分类暂不详究。。。

4 VHDL语句

顺序语句和并行语句是VHDL程序设计中两大基本描述语句系列。顺序语句(Sequential Statements)用来实现模型的算法描述;并行语句(Concurrent Statements)用来表示各模型算法描述之间的连接关系,这些语句从多侧面完整地描述数字系统的硬件结构和基本逻辑功能,其中包括通信的方式、信号的赋值、多层次的元件例化以及系统行为等。

4.1 并行语句

4.1.1 并行语句的特点

  • 相对于传统的软件描述语言,并行语句结构是最具VHDL特色的:并行运行。 
  • 物理意义:硬件连接关系
  • 每一并行语句内部的语句运行方式可以有两种不同的方式,即并行执行方式(如块语句)和顺序执行方式(如进程语句)。因此,VHDL并行语句勾画出了一幅充分表达硬件电路的真实的运行图景。

4.1.2 并行语句的格式

Architecture <architecture_name> of <entity_name> is
说明语句;
begin
    并行语句;
end architecture 结构体名;

 

4.1.3 并行语句的种类及详解

  1. 并行信号赋值语句(CONCURRENT SIGNAL  ASSIGNMENTS)
  2. 条件/选择信号赋值语句(CONDITIONAL/SELECTED SIGNAL  ASSIGNMENTS)
  3. 进程语句(PROCESS STATEMENTS)
  4. 块语句(BLOCK STATEMENTS)
  5. 元件例化语句(COMPONENT INSTANTIATIONS)
  6. 生成语句(GENERATE STATEMENTS)
  7. 并行过程调用语句(CONCURRENT PROCEDURE CALLS)

4.1.3.1 并行信号赋值语句

<signal_name> <= <expression>;

Example:
q <= input1 or input2;
q <= input1 and input2;
-- 最终执行 q <= input1 and input2; 

 

4.1.3.2 条件信号赋值语句

格式:

<signal_name> <= <signal/value> when <condition1> else
                              <signal/value> when <condition2> else
                                                   .
                                                   .
                                                   .
                              <signal/value> when <conditionN> else                                   
                              <signal/value>;                

 

示例:

 

 

 

4.1.3.3 选择信号赋值语句

with <expression> select
<signal_name> <= <signal/value> when <condition1>,
                 <signal/value> when <condition2>,
                 <signal/value> when others

 

 

 

4.1.3.4 进程语句

有人说:从本质上讲VHDL的所有语句都是并行语句。进程(process)语句是用来给并行的硬件提供顺序语句,一个结构体可以包含多个进程,多个进程之间并行运行。进程内部的语句顺序执行。

 

 

 (1)进程的工作原理:

 

 

 (2)进程与时钟:

  • 时钟的上下沿表示方法:

    

 

 

  •  示意图:

 

    

 

 

(3)敏感表和wait语句

  • 敏感表和WAIT语句等效, 但WAIT语句更多样化 
  • 一个进程不可既有敏感 表,又有WAIT语句,但 要有一种
  • 逻辑综合对WAIT有严格 的限制(只有wait until语句可以综合)

       

  • wait until语句
    • 语法:
      wait until condition;
    • 可综合
    • 示例:
      architecture arc2 of process_wait is 
      begin  
      process    
      begin   
          wait until clk'event and clk = '1';    
           q<=d;  
      end process;
      end architecture; 
  • wait on 语句
    • 语法:
      wait on sensitivity list;
      wait
      on (a,b,c); --敏感表
       
    • sensitivity list信号敏感表里任意一个发生变化才会执行 
    • 不太可能综合(特别是时序电路,实际电路不可能接受 上升沿和下降沿触发) 

(4)注意:

  • 进程内部,对同一信号多次赋值,只最后一次起作用,即最靠近end process;的,如:
    process(a,b,s)  
    begin   
        --final results: s<=a+b y<=a+b+1              
        y<=s+1;    
        s<=a;              
        s=a+b;  
    end process; 
  • 进程内部,对同一变量多次赋值,立即执行。如:
    process(a,b)  
    variable ss: std_logic_vector(3 downto 0); 
    begin  
        ss:=a;  
        y<=ss+1;
        ss:=a+b; 
    end process;
    --结果:由于y<=ss+1写在ss:=a之后,所以将a+1的值赋给y  

4.1.3.5 块语句

块(BLOCK)语句是一种将结构体中的并行描述语句进行组合的方法,它的主要目的是改善并行语句及其结构的可读性,或是利用BLOCK的保护表达式关闭某些信号。 

块标号:BLOCK [(块保护表达式)] [IS]
接口说明;
类属说明;
BEGIN
并行语句; 
END BLOCK [块标号]; 

 

 

4.1.3.6 元件例化语句 

  • 元件例化就是将预先设计好的设计实体定义为一个元件,然后利用特定的语句将此元件与当前的设计实体中的指定端口相连接,从而为当前设计实体引入一个新的低一级的设计层次。
  • 元件例化语句由两部分组成,前一部分是将一个现成的设计实体定义为一个元件的语句,第二部分则是此元件与当前设计实体中的连接说明。
COMPONENT  元件名 [IS] 
[GENERIC(类属表)说明;] 
[PORT (端口名表)说明;] 
END COMPONENT 元件名; 

4.1.3.7 生成语句 

  • 生成语句作用:复制
  • 根据某些条件设置好某一元件或设计单位,可以用生成语句复制一组完全相同的并行元件或设计单元电路 
[标号:]   for 循环变量 in 取值范围 generate 
[声明部分] 
begin       
并行语句; 
end generate [标号]; 

 

取值范围: 表达式 to 表达式   或者    表达式 downto 表达式 

 

4.1.3.8 并行过程调用语句  

  • 并行过程调用语句作为并行语句直接出现在结构体中
  • 调用语句可以为:过程(Procedure)或函数 (Function)
  • 调用的语法格式:过程名/函数名(关联参数表) 
  • 函数和过程统称子程序 

(1)过程

  • 特点:
    • 参数可以是 in, out, inout 模式

      • 输入参数(in)的默认数据类型是constant

      • 输出参数(out)或者inout参数默认数据类型variable

      • 参数对象可为 constant, variable and signal
      • 过程调用参数需要一一对应,形参为constant, 实参可以为signal、constant或variable;形参若 为signal或variable,则实参需对应一致的类型 

 

    • 其特征是过程中可以修改参数值(out,inout模式 参数)
    • 不需要RETURN语句 
  • 语法:
    PROCEDURE procedure_name (mode_parameters) IS   
    [声明部分] 
    BEGIN 
    顺序语句; 
    END [PROCEDURE ] [procedure_name];  
  • 函数一般定义在包中(声明及实现)

(2)函数

  • 特点:
    • 产生一个返回值。
    • 参数只能为in模式。
    • 传递的参数在函数内部只能使用不能修改(因为参数为in)。
    • 允许的参数数据类型为 constant或signal,默认constant。
    • 形参和实参必须匹配。形参为constant,则实参可以是variable、signal、 constant或表达式;形参为signal,实参要为 signal。
    • 需要一个RETURN 语句 。
  • 语法:
    FUNCTION function_name (input_parameters) 
    RETURN data_type IS   
    [声明部分]; 
    BEGIN   
    顺序语句; 
    RETURN 声明名; 
    END [FUNCTION] [function_name];
  • 函数一般定义在包中(声明及实现)

(3)过程与函数的区别:

  • 函数参数只能使用IN。过程中参数为IN、 INOUT、OUT三种。 
  • 函数只有一个返回值。过程可以通过参数返回 多个返回值。

4.2 顺序语句

4.2.1 顺序语句的特点

  顺序语句的特点是执行顺序和书写顺序基本一致 。

4.2.2 顺序语句的种类及详解

  1. 顺序信号/变量赋值语句
  2. IF-THEN 语句
  3. CASE 语句
  4. LOOP 语句
  5. RETURN语句
  6. NULL语句 

4.2.2.1 顺序 信号/变量赋值语句

(1)进程中的顺序信号赋值语句:

  • signal<=expression;
  • End process时更新
  • 多次赋值,以最后一次赋值为准

(2)进程中的顺序变量赋值语句:

  • variable:= expression;
  • 立刻更新
  • 多次赋值,以最后一次赋值为准 
  • 注意:当含有信号且信号需变量为其赋值时,改变量选用信号赋值语句前的。如该示例

4.2.2.2 if-then 语句

  • 从上到下按照顺序评估条件,存在优先级 。
  • 若第一个条件满足,可以执行对应的顺序语句。 其他的都不再执行。
  • 如果所有的条件都不满足,将执行ELSE之内的语句。
  • 可以嵌套使用。

示例:

4.2.2.3 case语句

  • 条件只被评估一次
  • 没有优先级必须包括所有的条件(可以没有when others,但是必须包含所有条件)
  • WHEN OTHERS 语句包括了没有指定的所有条件

示例:

 with 语句是不能在process 内的。

4.2.2.4 loop 语句

(1)无限 loop 。Loop 将无限执行,除非 EXIT 语句存在 。

(2)WHILE LOOP 。条件满足,则循环 。

(3)FOR LOOP。迭代 loop 。

 示例:

四位左移:

 

4.2.2.5 return 语句

Return语句是一段子程序结束后,返回主程序的语句。

格式:

return;--只能用于过程,它后面不要有表达式   

return 表达式;--只用于函数,它后面必须有表达式,函数结束必须用return语句

示例:

  • 过程:

  • 函数:

4.2.2.6 NULL语句

空操作。

格式:

NULL

示例:

4.2.3 异步复位与同步复位

(1)异步复位(清零)

process(clk, reset, a)
begin
    if clk'event and clk = '1' then
        q <= a + 1;
   -- 高位复位 elsif reset
= '1' then q <= '0'; end if; end process;

(2)同步复位(清零)

process(clk, reset, a)
begin
   -- 高位复位
   if clk'event and clk = '1' then
        if reset = '1' then
            q <= '0'
        else
            q <= a + 1;
    end if;
end process;        

示例

(1)使用异步reset的8-bit寄存器 

-- 使用异步reset的8-bit寄存器 
LIBRARY ieee;
USE ieee.std_logic_1164.all; 
 
ENTITY reg8 IS  PORT (D : IN  STD_LOGIC_VECTOR(7 DOWNTO 0);    
Resetn, Clock : IN  STD_LOGIC;   
Q : OUT  STD_LOGIC_VECTOR(7 DOWNTO 0) ); 
END reg8; 
 
ARCHITECTURE behavioral OF reg8 IS  
BEGIN  
PROCESS ( Resetn, Clock )  
BEGIN   
IF Resetn = '0' THEN    
    Q <= "00000000";   
ELSIF rising_edge(Clock) THEN    
    Q <= D;   
END IF;  
END PROCESS; 
END behavioral;` 

 

(2)使用异步reset的N-bit寄存器 

-- 使用异步reset的N-bit寄存器 
LIBRARY ieee; 
USE ieee.std_logic_1164.all; 
 
ENTITY regn IS  
GENERIC ( N : INTEGER := 16 );  
PORT ( D : IN STD_LOGIC_VECTOR(N-1 DOWNTO 0);    
Resetn, Clock : IN STD_LOGIC;    
Q : OUT STD_LOGIC_VECTOR(N-1 DOWNTO 0) 
); 
END regn; 
 
ARCHITECTURE behavioral OF regn IS  
BEGIN  
PROCESS ( Resetn, Clock )  
BEGIN   
IF Resetn = '0' THEN
    Q <= (OTHERS => '0'); -- 给Q的所有位赋0,方便用于多位信号的赋值操作  
ELSIF rising_edge(Clock) THEN    
    Q <= D;   
END IF;  
END PROCESS ; 
END behavioral ; 

 

4.2.4 计数器

(1)使用变量的计数器 

4.2.5 全加器

示例:

(1)数据流描述

 

 (2)4-bit 带进位全加器 (结构化描述)

 

   

 

 

 

 (3)4-bit 带进位全加器 (行为描述)

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;

entity adder4_full is
    port(
        a : in std_logic_vector(3 downto 0);
        b : in std_logic_vector(3 downto 0);
        ci : in std_logic;
        s : out std_logic_vector(3 downto 0);
        co : out std_logic;
    ); 
end adder4_full;
architecture RTL of adder4_full is
begin
    process(a, b, ci)
        variable a_t, b_t, c_t, s_t : std_logic_vector(4 downto 0);
    begin
    -- & 拼接运算符
        a_t:='0'&a;
        b_t:='0'&b;
        c_t:="0000"&ci;
        s_t:=a_t + b_t + c_t;
        co<=s_t(4);
        s<=s_t(3 downto 0);
    end process;
end RTL;

 

4.2.6 总结

在一个进程中,可以有多个语句

需要考虑的问题:

当敏感表中的信号发生变化,此进程将被激活。一旦被激活,在进程中的所有语句都将顺序执行一次。

进程中对信号的任何赋值在进程之外是不可见的,直到进程执行结束,才会更新信号。

如果有多个语句对同一个信号赋值,只有最后一个是可见的,或有效的 。 

5 Quartus-II 使用

 

6 有限状态机

  状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作,完成特定操作的控制中心。

状态机分为摩尔(Moore)型状态机和米莉(Mealy)型状态机。

  • 摩尔(Moore)型状态机:若输出只和状态有关,与输入无关,则称为Moore状态机。

  

 

 

 

  • 米莉(Mealy)型状态机:输出与状态和输入有关,则称为Mealy状态机。

 

 

 

 

 

 

 

 

6.1 摩尔型状态机

示例:

 

 

(1)单进程的Moore FSM 

 

 

 

(2)双进程的Moore FSM 

 

 

 

 

 

 

 

 

 

(3)三进程的Moore FSM 

 

 

6.2 米莉型状态机

示例1:

示例2:

 

 

 

 

 

 

 

7 常用VHDL

 

posted @ 2020-06-23 23:15  阿傥  阅读(3930)  评论(0编辑  收藏  举报