FPGA之道(24)VHDL数据类型

举报
李锐博恩 发表于 2021/07/15 03:34:51 2021/07/15
【摘要】 文章目录 前言VHDL数据类型常用数据类型逻辑数据类型std_logicstd_logic_vectorbooleanbitbit_vector 数值数据类型integerrealsigned & unsigned 枚举数据类型enum 数组数据类型记录数据类型type 子类型 前言 Verilog中的常用的数据类型不过是reg以及wi...

前言

Verilog中的常用的数据类型不过是reg以及wire,当然还有很多其他的,例如tri,integer等等。
对应于Verilog,VHDL中的数据类型也有很多种,但常用的也只有几种,下面我们先详细地认知下这些数据类型,这是简化使用的前提。
本文内容摘自《FPGA之道》。

VHDL数据类型

FPGA之道(23)VHDL的signal、variable与constant介绍了VHDL中“唯三”可以接受赋值的赋值对象,那么,这些对象都支持什么样的数据类型呢?关于这一点,我们将在这一章节进行介绍。不过在开始介绍之前,需要提醒大家,所谓的数据类型,都是编程语言抽象出来的,其目的是为了方便开发人员理解、编程、建模等。例如,C语言中无论什么数据类型的变量,其实都对应到内存中的某个地址的部分、整个或多个存储字段。而VHDL中被赋予特定数据类型的赋值对象,其实也都对应到FPGA内部的寄存器、查找表、块存储等存储单元或者连线中去了。

常用数据类型

逻辑数据类型

std_logic

std_logic是VHDL中的标准逻辑类型,在ieee标准库中,它是这么定义的:

Type STD_LOGIC is (
'U',		--Uninialized;未初始化态
'X',		--Forcing unknown;亚稳不定态
'0', 		-- Forcing 0;低电平态
'1', 		-- Forcing 1;高电平态
'Z', 		-- High Impedance;高阻态
'W',		-- Weak Unknown;弱浮接不定态
 'L', 		-- Weak 0;弱低电平态
 'H', 		-- Weak 1;弱高电平态
 '-'		-- Don’t care;不关心态
 )
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

其中,最常用的是‘0’、‘1’两个状态,分别代表数字逻辑中的逻辑0和逻辑1。除此以外,‘Z’状态是高阻态,这在三态门和双向端口中大有用途。而不关心态‘-’,表示的是我们不关心这一位的状态,而不是编译器不关心,利用该状态可以用来适当的简化逻辑电路,当然这个状态最好不要乱用,否则会引起前、后仿不一致甚至更多的隐患问题。 而‘U’状态,一般出现在仿真波形的最开始部分,当某些signal没有赋初值时,那么在第一次对它赋值之前它就会一直处于该状态。‘X’状态是亚稳不定态,一般当双向端口的写操作冲突时,会出现这个状态。出现‘X’状态并不代表端口上一定没有可以识别的逻辑电平,只是无法确定罢了。例如两个芯片的输出管脚通过导线连接在一起,一个输出‘1’,一个输出‘0’,那导线上的逻辑电平到底是什么情况呢?这取决于这两个芯片的驱动能力,如果输出‘1’的驱动能力明显强,那么导线上电平为‘1’,反之为‘0’,若真是旗鼓相当,那么得到的也许真是‘X’。剩下三个状态可以忽略不管,因为它们的关注点偏向数字电路的模拟特性,类似电流驱动能力之类的,这在我们的FPGA设计中是用不到的。
从std_logic的定义,我们可以很清楚的看到,具体使用逻辑数据类型的信号或变量时,我们该怎么对它进行赋值。例如:

signal a : std_logic;
a <= '1'; 

  
 
  • 1
  • 2

std_logic_vector

std_logic_vector是标准逻辑数组类型,也叫逻辑向量类型,定义如下:
TYPE std_logic_vector IS ARRAY ( NATURAL RANGE <>) OF std_logic;
它是对std_logic类型的很好的扩充。例如,如果没有这个类型,那么我们需要做两个3bits的数据的按位与操作,得写成这样:

signal a0,a1,a2 : std_logic;
signal b0,b1,b2 : std_logic; 
signal c0,c1,c2 : std_logic;
c0 <= a0 and b0;
c1 <= a1 and b1;
c2 <= a2 and b2;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

可是有了std_logic_vector这个标准逻辑数组类型后,我们就可以把代码写成这样:

signal a,b,c : std_logic_vector(2 downto 0);
c <= a and b;

  
 
  • 1
  • 2

类似像std_logic_vector这样在括号中描述范围的语法在VHDL还有不少,需要注意的一点是,范围的描述方法有两种,downto和to,上面的代码也可以写成这样:

signal a,b,c : std_logic_vector(0 to 2);
c <= a and b;

  
 
  • 1
  • 2

不过,建议大家最好在自己的设计中选定一种范围表示法——最好是只使用downto关键字,因为它和二进制数据的编码形式是一致的——统一代码风格,千万不要混用!这不仅仅是出于代码整洁、可读性而考虑的,更重要的,这是一种避免错误和后患的手段。
下面,来看一下逻辑向量的数据表示形式。逻辑向量是和二进制数据形式最一致的数据类型,所以使用最为频繁。那么,当我们需要给逻辑向量赋于一些具体数值的时候,我们该怎么书写呢?有四种常用写法,见下列。注意,逻辑向量的常数要用双引号引起来,并且逻辑向量的位宽必须是大于1的。

signal a : std_logic_vector(3 downto 0);
a <= B"1100"; 	-- 二进制,1个数代表1bit 
a <= "1100"; 		-- 也是二进制,与上一条语句完全相同,不写明进制默认按B算
a <= '1' & O"4";	-- 八进制,1个数代表3bits,所以需要在前面补一个bit
a <= X"C"; 		-- 十六进制,1个数代表4bits

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

注意,逻辑向量不支持类似的十进制常数赋值,因为十进制不是2的整数次幂。如果想使用十进制常数对逻辑向量进行赋值,必须使用转换函数,例如:
a <= conv_std_logic_vector(11, 4);
不过,如果在运算表达式中使用十进制常数,一般编译器会帮你自动转换,但是会给出一个warning作为提醒。对比以下两个赋值表达式:

a <= 11; -- error!
a <= b + 11; -- OK! But will give out a warning!

  
 
  • 1
  • 2

逻辑数据类型中,其实有以上两种就足够用了,不过VHDL语言还提供了以下几种逻辑类型供大家使用,简要介绍如下:

boolean

boolean是布尔代数类型,定义如下:
type BOOLEAN is (FALSE, TRUE) ;

bit

bit是比特类型,也叫位类型,定义如下:
type BIT is (‘0’, ‘1’);

bit_vector

bit_vector是比特数组、位数组类型,也叫比特向量,定义如下:
type BIT_VECTOR is array (NATURAL range <>) of BIT;

从上面的描述可以看出bit、bit_vector只是std_logic、std_logic_vector的一个子集而已,而boolean其实也是std_logic的一个子集,所以对于常规的FPGA开发来说,VHDL提供这三个逻辑数据类型就是让大家解闷的。

数值数据类型

integer

integer是整数类型,定义如下:
Type INTEGER is range –2147483648 to 2147483647;
它有两个常用的子集,分别为正整数和自然数,即positive、natural,分别定义如下:
subtype POSITIVE is range 1 to INTEGER’High; – INTEGER’High表示INTEGER所能
subtype NATURAL is range 0 to INTEGER’High; – 取到的最大值,即2147483647
需要说明的一点是,虽然VHDL语法中有整数类型,但是真正的整数运算(更准确的说应该是数的运算)一般都是用std_logic_vector来实现的。因为整数类型抽象层级过高,不适合在FPGA中作为主要数据类型使用,例如,integer不能按位访问。但是VHDL毕竟是一种编程语言,它是在PC机上进行编程的,编译环境也跑在PC机上,所以有些时候,整数类型也是很必要的。例如,entity中generic语法中定义的参数,一般都用整数表示。还有,for语句中的循环变量,也都是整数类型的。

real

real是实数类型,定义如下:
type REAL is range –1.7E38 to 1.7E38;
这个数据类型的抽象级别比较高,不利于开发者对FPGA的资源掌控。并且在数字的世界里“0就是0,1就是1”,“1个就是1个,2个就是2个,没有1.5个这样的概念”,所以,数字的世界里本身不存在实数类型,即使是C语言中的float、double等类型,也不是真正的实数,而是以对数形式或位宽比较大的整数配合上描述小数点位置的数据罢了。

signed & unsigned

signed & unsigned是有符号数和无符号数类型,它们的定义如下:
type UNSIGNED is array (NATURAL range <>) of STD_LOGIC;
type SIGNED is array (NATURAL range <>) of STD_LOGIC;
从定义可以看出,它们两个除了名字不一样外,其他都是一样的。其实,不管是有符号数还是无符号数,都是数的二进制表示形式。如果你把数据最左边的bit当成符号位,那么这就是一个有符号数;如果你把数据最左边的bit当成MSB,那么这就是一个无符号数。一个逻辑向量的含义到底是有符号还是无符号,完全取决于你怎么看待它,因此也就没有必要非要给它命名为signed或者unsigned。之所以有这样两个定义,是仿真器可以根据你的定义来正确的理解和呈现出运算结果来而已。因此,这两个数据类型在FPGA中一般也都是直接用std_logic_vector来替代。

枚举数据类型

enum

enum是枚举数据类型,它在FPGA中是非常重要的,因为它是状态机状态的常用定义方式。当然,枚举的用处并不限于此,它的语法如下:
type <enum_type_name> is (, );
例如,如果你的状态机有两个状态:idle和work,那么可以定义如下:
type myStateType is (idle, work);
signal stateBuf : myStateType;
myStateType <= work; – 在适当的地方

数组数据类型

array
array是数组数据类型,它的语法如下:
type <array_type_name> is array(<index_range>) of <element_type>;
例如,要定义一个共有8个元素的数组,且元素类型是32位逻辑向量的,写法如下:
type arrayOfVector32 is array(7 downto 0) of std_logic_vector(31 downto 0);
signal myArray : arrayOfVector32;
注意,早期的版本一般是不支持对数组元素的按位访问的,例如,myArray(5)(3)是会报错的。这时候需要定义一个和数组元素同类型的变量,然后以它为中介来访问。写法如下:
signal tmp : std_logic_vector(31 downto 0);
tmp <= myArray(5);
<some_signal> <= tmp(3);
有时候,可以通过数组定义的嵌套来定义多维数组,例如:
type rt is array (7 downto 0) of std_logic_vector (7 downto 0);
signal r0: rt;
type rrt is array (7 downto 0) of rt;
signal rr0: rrt;
但是不推荐这么做。首先,这种嵌套形式并不是所有编译器都支持的;第二,由于FPGA是硬件描述语言,它本身已经面向的是比较底层的硬件电路,所以一般不要使用多维数组这么抽象的概念来让编译器去综合;第三,再多维的数组,在现实中都可以用一维数组来存储,c语言中的多维数组正是这样,所以任何需要多维数组来做的事情,一维数组也都可以胜任。

记录数据类型

type

type是记录数据类型,它的定义如下:

  
 
  • 1

type <record_name> is record
<element_name> : <element_type>;

end record;
type类似于c语言中的结构体struct,即将若干个元素封装成一个整体进行管理。举例如下:
type byteWord is record
byte : std_logic_vector(7 downto 0);
word : std_logic_vector(15 downto 0);
end record ;
signal tmp : byteWord;

tmp.byte <= X"F0";
tmp.word <= ‘1’& O"77007";
记录类型是一种抽象层级比较高的类型,对基本数据对象起到了封装的作用,便于程序的阅读和理解。不过通过把几个小逻辑向量连接起来成为一个大的逻辑向量,并用来传递和赋值能够实现和记录类型一样的效果,但是这种描述没有记录形象。

子类型

子类型的关键字是subtype,就是对已经存在的基本数据类型作一些范围限制,形成了新的一种数据类型。语法如下:
subtype <subtype_name> is <range_definition>;
例如,要定义一个大于等于9小于等于99的整数集合,可以这样描述:
subtype in9And99 is integer range 9 to 99;

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/104272249

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。