BLOG

Record, summarize, and improve.

VHDL

以下是 VHDL 中的所有关键字,按照功能进行分类:

1. 数据类型相关的关键字:

  • bit:表示单一的二进制位。
  • bit_vector:表示一系列的 bit,通常用于表示位向量。
  • boolean:表示布尔类型。
  • integer:表示整数类型。
  • real:表示实数类型。
  • time:表示时间类型。
  • std_logic:表示 9 值的逻辑类型。
  • std_logic_vector:表示一系列 std_logic 类型的位向量。
  • signed:表示带符号的二进制数类型(在 numeric_std 包中)。
  • unsigned:表示无符号的二进制数类型(在 numeric_std 包中)。

2. 控制结构相关的关键字:

  • if:条件语句,用于根据条件选择执行的代码块。
  • elsif:用于 if 语句中的多分支判断。
  • elseif 语句中的其他条件。
  • case:选择语句,用于多条件判断。
  • whencase 语句中的分支。
  • others:在 case 语句中表示默认的条件。
  • loop:用于循环操作。
  • exit:退出循环。
  • next:跳过当前循环的剩余部分,开始下一次循环。
  • wait:等待语句,用于仿真中等待事件或时间。

3. 赋值和信号相关的关键字:

  • signal:声明信号,用于在设计中传递信息。
  • variable:声明变量,用于过程或函数中的局部存储。
  • constant:声明常量,表示不可改变的值。
  • alias:给一个已存在的对象(信号、变量等)起一个新的名字。
  • file:表示文件类型,用于文件I/O操作。
  • use:用于导入库或程序包中的内容。

4. 函数、过程和过程调用相关的关键字:

  • function:声明函数,表示一种返回值的过程。
  • procedure:声明过程,表示没有返回值的操作。
  • return:返回语句,用于从函数或过程返回。
  • process:声明一个进程,是行为描述的基本单位。

5. 数据结构和包相关的关键字:

  • type:定义新的数据类型。
  • subtype:定义一个现有类型的子类型。
  • attribute:定义类型或对象的属性。
  • file:文件类型,用于文件操作。
  • package:声明程序包,用于组织模块化的代码。
  • package body:程序包体,包含程序包声明部分的实现。
  • package:用来引用其他库或包中的内容。

6. 仿真相关的关键字:

  • time:表示时间,通常用于延时或时序控制。
  • after:指定时间延迟,在仿真中用于定义信号变化的延时。
  • wait:等待语句,用于在仿真中等待某个条件或时间。

7. 库和配置相关的关键字:

  • library:指定程序库,VHDL 设计中会引用库来组织不同的模块。
  • context:表示库或包的使用上下文。
  • configuration:用来将实体与架构或结构相匹配。

8. 其他关键字:

  • entity:声明实体,定义硬件模块的接口。
  • architecture:定义硬件模块的架构或实现。
  • begin:开始一个语句块或过程。

1.1 VHDL的数据类型

VHDL 的数据对象有信号(Signal)、变量(Variable)、常量(Constant)和文件(File),其中文件(File)是 VHDL-93 标准,不可以被综合。

1)信号(signal)

信号用于将元件的装配端口连在一起形成模块,可以理解成现实中能看见的”导线”,性质是一样的。信号是实体间动态数据交换的手段,信号申明格式如下:

signal < signal_name > : < signal_type > [ :=initial_value ] ;

signal PrSv_s_Cnt : std_logic_vector(3 downto 0) := x”0”; -- count

在关键字 signal后跟一个或者多个信号名,每个信号名将建立一个新信号,用冒号把信号名和信号的数据类型分隔开,信号数据类型规定信号包含的数据类型信息及初始化信号指定的初值。

建议:关键字 signal后跟一个信号名,代码的格式清晰便于修改。

2)变量(variable)

变量用于存储进程和子程序中的局部数据,变量的赋值是立即执行的,没有延时。变量的申明格式如下:

variable variable_name ,variable_name : variable_type[:= value];

关键字 VARIABLE 后跟着一个或多个变量名,每个变量名对应建立一个新变量。variable_type 字段定义了变量的数据类型,并且还可以指定一个可选的初值。只可以在进程说明部分和子程序说明部分声明变量。

和信号相比,变量有以下优点:

  • 变量处理起来更快,因为变量赋值是立即发生的,而信号却必须为此事件作相应的处理。
  • 变量用很少的存储器,相反为了做一个调度安排和处理信号属性,需要存储更多的信号信息。
  • 变量比信号更容易实现同步处理。

3)常量(constant)

常量是为特定的数据类型值所赋予的名称,如果需要在多个具体元件中存放一个固定值就使用常量。例如可以如下定义常量 PI(π ):

CONSTANT PI: REAL:= 3.14;

定义常量的格式如下:

CONSTANT constant_name,constant_name : type_name[:= value];

constant MMCM_LOCK_CNT_MAX : integer := 256;

一般情况下,VHDL 中的常量是在程序包申明中进行申明,而在程序包体中指定具体的值。使用常量需要注意以下几个问题:

  • 在程序包中说明的常量被全局化。
  • 在实体说明部分的常量被那个实体中任何结构体引用。
  • 在结构体中的常量能被其结构体内部任何语句采用,包括为进程语句采用。
  • 在进程说明中说明的常量只能在进程中使用。
  • 在数组和一些线性运算中经常用常量表,VHDL 的设计描述用常量表特别适于实现 ROM 网络的电路与函数设计。
1.2 VHDL 语言的数据类型

根据VHDL使用的目的和场合,VHDL数据类型可以分为标准数据类型和用户自定义数据类型

1)标准数据类型

Image in a image block

VHDL 中的标准数据类型

其中,在数据类型后面,可以加上约束区间,比如:

Integer range 100 downto 20;

Bit_vector(7 downto 0) real range 2.0 to 100.0;

STD_LOGIC 和 STD_LOGIC_VECTOR 的逻辑数据取值,可以有 9 种状态,如下所示。

Image in a image block

标准逻辑(向量)取值表

2)用户自定义的数据类型

用户自定义数据类型的格式如下:

Type 数据类型名{,数据类型名} 数据类型定义;

数据类型定义放在语句的定义部分中,定义范围为从本定义行开始到本语句作用的最后。

一般用户自定义的数据类型分为以下几种:

1)枚举类型(enumeration)

枚举类型的格式如下:

Type 数据类型名 is (元素, 元素, ……)

例如,将一星期七天作为一个枚举,可以如下定义:

Type week is (mon, tue, wed, thu, fri, sat, sun);

在枚举类型中,元素是有序列性的,第 1 个元素对应逻辑电路状态 000,第 2 个为状态 001,第 3 个为状态 010……后一个逻辑状态为前一个元素逻辑状态加 1。所以,上面的例子中,mon对应逻辑状态 000,tue 对应逻辑状态 001,……,sun 对应逻辑状态 110。

2)整数类型、实数类型(INTEGER,REAL)

这里的整数类型和实数类型其实是前面所述的标准整数类型和实数类型的子类,定义的格式如下:

TYPE 数据类型名 IS 数据类型定义 约束范围

例如:

TYPE current IS REAL RANGE -1E4 TO 1E4

定义了 current 类型实数的范围是-104到 104。

3)数组(ARRAY)

数组定义的格式如下:

TYPE 数据类型名 IS ARRAY 范围 OF 原数据类型名;

注意:如果 范围 这一项没有被指定,则使用整数数据类型。

下面通过例子说明数组的定义方法。

TYPE word IS ARRAY (1 TO 8) OF STD_LOGIC;

TYPE tmem IS ARRAY (0 TO 2, 3 DOWNTO 0) OF STD_LOGIC;

以上定义了 tmem 一种数组类型,可以定义一个此类新的常数,如下:

CONSTANT mem:tmem:= ( ('0', '0', '0', '0'), ('0', '0', '1', '0'), ('1', '1', '0', '0') );

当范围这一项需用整数类型以外的其他数据类型时(如枚举类型),则应在指定数据范围前加数据类型名。例如:

TYPE week IS (sun, mon, tue, wed, thu, fri, sat);

TYPE workday IS ARRAY (week mon TO fri) OF STD_LOGIC;

如果要取得数组内的一个元素,格式如下:

数组名(下标)

例如,word(1)区的 word 数组序号为 1 的元素。

当数组类型定义中的范围用“(Natural Range <>)”或“(Positive Range <>)”代替时,表示本数组类型为非限定的类型,下标范围在信号或变量定义时再具体指定。

library IEEE;
use IEEE.std_logic_1164.all;

entity tmyarray is
port (
a : in std_logic ;
c : out std_logic
);
end ;

architecture myarray of tmyarray is

type array0 is array (natural range <>) of std_logic;

begin
process(a)
variable item:array0(7 downto 0);--定义一个变量 item
begin
item(7):=a;
c<=item(7);
end process;
end;

4)时间(TIME)

时间的格式和例子如下:
type <数据类型名> is <范围>
units <基本单位>;
<单位描述>;
end units

type time is range -1E18 TO 1E18
units fs;
ps=1000fs;
ns=1000ps;
us=1000ns;
ms=1000us;
sec=1000ms;
min=60sec;
hr=60min;
end units;

5)记录(Record)

TYPE 数据类型名 IS RECORD
元素名:数据类型名;
元素名:数据类型名;

元素名:数据类型名;
END RECORD;

注意:引用记录数据类型中的元素应使用“.”,而不是数组的括号。
例如,定义一个窗口尺寸的记录,如下:
TYPE window IS RECORD
length:INTEGER;
width:INTEGER;
END RECORD;

当需要使用 window 类型记录的元素时,方法如下:
signal win: window;
win.length<=10;

6)用户自定义的子类型

用户定义的子类型是用户对已定义的数据类型做一些范围限制而形成的一种数据类型。子类型的名称通常采用用户较容易理解的名字。

子类型的定义格式为:

SUBTYPE 子类型名 IS 数据类型名[范围];

例如:
SUBTYPE digit IS INTEGER RANGE 0 TO 9;
SUBTYPE abus IS STD_LOGIC_VECTOR(7 DOWNTO 0);
signal a: STD_LOGIC_VECTOR (7 downto 0);
signal b: STD_LOGIC_VECTOR (15 downto 0);
signal c: abus;
a<=c; --正确
b<=c; --错误

注意:在 VHDL 中,数据类型的定义是相当严格的,不同类型的数据是不能进行运算和直接代入的。为了实现正确的代入操作,必须将要代入的数据进行类型变换。

变换函数通常由 VHDL 语言的包集合提供。例如由 “ STD_LOGIC_1164 ”、“STD_LOGIC_ARITH”、STD_LOGIC_UNSIGNED”的包集合提供的变化函数如下所示:

Image in a image block

数据类型变换函数

有些数据,从数据本身是断定不出其类型的,如“01010001”,如果没有上下文,VHDL 编译器就无法知道它是字串型还是位数组类型。这时就要进行数据类型的限定。

类型限定的格式如下:
类型名'(数据)

例如:
a <= std_logic_vector'("01010001");
这样,编译器知道“01010001”肯定是矢量型,而不是别的类型。

1.3 VHDL 语言的运算符

在 VHDL 语言中,常用的运算符有逻辑运算(Logic)、关系运算(Relational)、算术运算(Arithmetic)和移位运算(Shift)。

1)逻辑运算符

逻辑运算符可以对 bit 和 boolean 类型的值进行运算,也可对这些类型的一维数组进行运算。对数组型的运算,运算施加于数组中的每个元素,结果与原来数组长度相同。

逻辑判断的运算为“短路运算”,也就是说,条件表达式的左边成立时,就不再进行右边的判断。比如,IF (a=0) AND (b/a>2) THEN…这个判断运算,当 a=0 时,后面的判断不再继续,避免出现除数为 0 的运算。

Image in a image block

VHDL逻辑运算符

2)关系运算符

关系运算符两边必须为相同的类型,其结果为 boolean 类型

等号(=)和不等号(/=)两边可以为任意类型的运算对象。其他关系运算符的运算对象必须为标量类型或离散类型的一维数组。对于复杂的运算对象,如数组,两个值相等意味着两个值的所有对应元素相等。

Image in a image block

VHDL关系运算符

3)算术运算符

算术运算符包括一些基本的算术运算,使用算术运算符需要注意的是乘方(**)运算的右边必须为整数。VHDL 的算术运算符如下图所示:

Image in a image block

VHDL 算术运算符

4)移位运算符

移位运算符为二元运算符,左边必须为一维数组,且元素类型为 bit 或 boolean 类型。右边运算数为整数,可以为负数,相当于反方向移位。一位移位与循环移位的语义示意如下图所示:

Image in a image block

一位移位与循环移位示意图

Image in a image block

VHDL 移位运算符

除了上面介绍的,VHDL 中运算符还包括正号“+”、负号“-” 以及“&”。其中,连接符号(&)用于一维数组,这个数组的元素个数可以为 1,运算结果为右边数组连接在左边数组之后形成新数组。

例如:sel <= a & b;

假设 a 信号为“0”,b 信号为“1”,那么得到的 sel 信号就是“01”。

所有的 VHDL 运算符之间都有优先级的关系,各运算符优先级从最高到最低,顺序如表 10 所示(同一行优先级相同)。

Image in a image block

运算符的优先顺序

附录:VHDL的代码格式

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

-- Uncomment the following library declaration if using-- arithmetic functions with Signed or Unsigned values--use IEEE.NUMERIC_STD.ALL;-- Uncomment the following library declaration if instantiating-- any Xilinx leaf cells in this code.library UNISIM;
use UNISIM.VComponents.all;


entity M_Top isport (
CpSl_i_Clk
: in std_logic ; -- ClockCpSl_i_Rst_n : in std_logic ; -- Reset_Active_lowCpSv_i_Data : in std_logic_vector(3 downto 0) ; -- DataCpSl_i_DataVld : in std_logic ; -- DataValidCpSl_o_DataVld : out std_logic ; -- DataVld_outCpSl_o_Data : out std_logic_vector(7 downto 0) -- Data_out);
end entity;

architecture arch_M_Top of M_Top is-------------------------------------- constant_describe------------------------------------constant PrSv_s_Cnt : std_logic_vector(3 downto 0) := x"F";

-------------------------------------- IP_describe------------------------------------

-------------------------------------- signal_describe------------------------------------signal PrSv_s_Cnt : std_logic_vector(3 downto 0); -- countbegin------------------------------------------------------------------------------ Map_describe----------------------------------------------------------------------------

process (CpSl_i_Rst_n, CpSl_i_Clk) beginif (CpSl_i_Rst_n = '0') then

elsif rising_edge(CpSl_i_Clk) thenif (PrSv_s_Cnt = PrSv_c_Cnt) thenPrSv_s_Cnt <= x"0";
elsePrSv_s_Cnt <= PrSv_s_Cnt + '1';
end if ;
end if;
end process;

......

------------------------------------------------------------------------------ End_coding----------------------------------------------------------------------------end arch_M_Top;

上面的格式,是VHDL的基本用法也是常用的用法。万变不离其宗,加油吧!