![Intel FPGA数字信号处理设计(基础版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/353/44819353/b_44819353.jpg)
2.2.5 运算符
VHDL的运算符可分为逻辑运算符(Logical Operators)、关系运算符(Relational Operators)、符号运算符(Sign Operators)、算术运算符(Arithmetic Operatorss)、移位运算符(Shift Operators)及连接运算符(Concatenation Operators),每种运算符均在VHDL的程序包中预先进行了定义。与其他编程语言一样,VHDL的运算符也有重载的特性,即不同类型数据之间的运算操作采用相同的运算符时,程序可自动调用相应的运算符操作函数。VHDL是一种类型检查十分严格的编程语言,所有不同数据类型的操作均需要有相应的重载运算符函数支撑,否则程序不能正确编译。
1.逻辑运算符
逻辑运算符有7种:and(与)、or(或)、nand(与非)、nor(或非)、xor(异或)、xnor(同或)、not(非),每种运算符的意义都与数字电路中的门电路符号相同。图2-1所示为几种逻辑运算符表达式综合后的RTL原理图。
![](https://epubservercos.yuewen.com/5C7EAE/23950062909693706/epubprivate/OEBPS/Images/43122_48_1.jpg?sign=1738888392-BkKxiuu1AmY8gpYbhOwseUtaiSroyK8d-0-49c03760d5646a4782eeed7e035c8d8a)
图2-1 逻辑运算符表达式综合后的RT L原理图
逻辑运算符除用于赋值语句外,还可以用于产生布尔型数值,用作条件语句中的判断条件,如
![](https://epubservercos.yuewen.com/5C7EAE/23950062909693706/epubprivate/OEBPS/Images/43122_48_2.jpg?sign=1738888392-vjpOvWNV8SvpVuoPkKHFtuSJ1KkPZT0z-0-b1ca2621831760dc010396657f2be078)
2.符号运算符
符号运算符有3种:负值符号(-)、正值符号(+)及取绝对值(abs)。由于正值符号用于变量或信号前不改变原值,所以正值符号几乎用不到。负值符号对原值取相反的值,绝对值运算即取原值的绝对值。由于负值符号及取绝对值均需用有符号数来表示,因此必须声明有符号数运算符号的程序包,如STD_LOGIC_SIGNED。图2-2所示为STD_LOGIC_VECTOR类型数据的负值运算及取绝对值运算表达式综合后的RTL原理图。
![](https://epubservercos.yuewen.com/5C7EAE/23950062909693706/epubprivate/OEBPS/Images/43122_48_3.jpg?sign=1738888392-wGDDN7oH04i5rlDjQirhm6Qb0HpSGazJ-0-15a9478b89c57c95a253a995d62427c8)
图2-2 负值运算及取绝对值运算表达式综合后的RT L原理图
从图2-2中可以看出,负值运算是由取反及加法器两种基本逻辑元件完成的;而取绝对值运算则是由取反、加法器及多路信号选择器三种基本逻辑元件完成的。
在应用符号运算符时,必须声明具有符号数运算符的程序包(STD_LOGIC_SIGNED),否则程序不能编译通过;用XST综合工具综合时,界面中会显示“abs(-)can not have such operands in this context.”(程序中不能有abs(-)运算符)提示信息。
3.关系运算符
VHDL提供了6种关系运算符:等于(=)、不等于(/=)、大于(>)、小于(<)、大于或等于(>=)、小于或等于(<=)。各种关系运算符的意义十分清楚,值得注意的是,STD_LOGIC_SIGNED和STD_LOGIC_UNSIGNED这两个设计中最常用的程序包中对6种关系运算符均进行了重载,使STD_LOGIC_VECTOR类型的数据可以直接与STD_LOGIC_VECTOR或INTEGER类型的数据进行比较,这给编写代码带来了不少方便,因为一长串二进制数据的十进制值读/写起来很不方便。下面是一些关系运算符的语句例子。
![](https://epubservercos.yuewen.com/5C7EAE/23950062909693706/epubprivate/OEBPS/Images/43122_49_1.jpg?sign=1738888392-QrPU70gR5YxwO0nUGIpijt18BCXXKT7p-0-331a057f038294e4969b3e2812463757)
读者可能已经发现,在VHDL语言中,符号“<=”、“>=”可表示信号赋值,也可表示关系运算符,在写程序时是否需要刻意注意同情况的明确意义呢?完全不必要,且VHDL语言也没有提供这项语法。在编译程序时,编译器会根据代码的上下文自动确定符号代表的含义。
4.算术运算符
VHDL提供了7种关系运算符:加法(+)、减法(-)、乘法(*)、除法(/)、指数(**)、求模(mod)、求余(rem),但只有加法、减法、乘法这3种运算符在程序包STD_LOGIC_UNSIGNED和STD_LOGIC_SIGNED中进行了定义,且这3种运算符的操作数及运算结果均可以是STD_LOGIC_VECTOR数据类型。之所以重点关心STDLOGIC_VECTOR类型的数据是否能直接使用各种运算符,是因为STD_LOGIC_VECTOR是VHDL设计中使用最为普遍的数据类型。
加法及减法运算在数字电路中的实现相对简单。在用综合工具综合设计时,RTL电路图中的加、减操作会被直接综合成加法器或减法器。乘法运算在其他软件编程语言中实现起来十分简单,但用门电路、加法器、触发器等基本数字电路元件实现却着实不是一件容易的事。
除法、指数、求模、求余运算均没有在STD_LOGIC_SIGNED和STD_LOGIC_UNSIGNED程序包中定义,其操作数及运算结果也没有STD_LOGIC_VECTOR数据类型,因此无法在VHDL程序中直接对STD_LOGIC_VECTOR类型的数据进行相关运算。实际上,用基本逻辑元件构建这4种运算本身是十分繁杂的工作,如果要用VHDL实现这些运算,一种方法是使用开发环境提供的IP核或使用商业IP核,另一种方法是将算法分解成加、减、乘等操作步骤来逐步实现。
FPGA器件一般都提供除法器IP核。VHDL语言中不仅STD_LOGIC_VECTOR的数据类型不能直接使用这些运算,且设计文件中的变量及信号均不能使用这些运算符。那么VHDL提供的这几种运算符在哪里使用呢?只在常量声明时使用,即这些运算符只能对常量类型的数据进行运算。前面说过,常量本身就是一些固定值,直接指定即可,何必多此一举在常量表达式里加运算符?这还是为了代码书写的方便,以及在一些情况下更易表达常量的构成及常量之间的关系,如下面这些例子。
![](https://epubservercos.yuewen.com/5C7EAE/23950062909693706/epubprivate/OEBPS/Images/43122_50_1.jpg?sign=1738888392-5mZ0KM2Syp9A78TvtMm2AAR6SaZYANOy-0-fe30d96bc0610b688c7c3a7b1e22b695)
5.移位运算符
NUMERIC_STD程序包对4个移位运算符进行重载定义:逻辑左移(SLL)、逻辑右移(SRL)、循环左移(ROL)和循环右移(ROR)。在进行SLL及SRL运算时,移位后的空位填“0”。图2-3所示是4种移位运算操作的动作示意图。
![](https://epubservercos.yuewen.com/5C7EAE/23950062909693706/epubprivate/OEBPS/Images/43122_50_2.jpg?sign=1738888392-ynziaFf2hYHtZTqEz9SL5HqtYpJRg5wd-0-50345a8906cef9befdc6f648fe73a0e6)
图2-3 移位运算操作的动作示意图
虽然NUMERIC_STD程序定义了移位运算符,但该运算符只支持BIT_VECTOR类型的数据,所以在实际设计中极少使用。那么在设计中要用到移位运算时怎么办呢?STD_LOGIC_SIGNED和STD_LOGIC_UNSIGNED程序包已定义好了两个移位函数:左移函数SHL及右移函数SHR。
![](https://epubservercos.yuewen.com/5C7EAE/23950062909693706/epubprivate/OEBPS/Images/43122_50_3.jpg?sign=1738888392-whlwGrf9uR2GO6YkK8eBbY3zok9UnlTp-0-3ebce40d8a5330a873d5987924e21d13)
上面两条函数定义语句在STD_LOGIC_SIGNED及STD_LOGIC__UNSIGNED程序包中定义。SHL与SHR是函数,使用时需采用函数调用方法。对于无符号数信号,SHL、SHR与SLL、SRL的作用相同。对于有符号数信号,SHL与SLL仍然相同;SHR运算时,右边的空位补原数据的最高位,即补符号位,也称符号扩展。
6.连接运算符
连接运算符“&”用于将两个操作数连接起来,连接后的数据长度为两个操作数长度之和。连接运算符有什么作用呢?最直接的应用实例是加法操作。我们知道,两个长为2比特的二进制数据相加,为了保证相加的结果不溢出,结果必须用长为3比特的数据保存,考查下面的例子。
![](https://epubservercos.yuewen.com/5C7EAE/23950062909693706/epubprivate/OEBPS/Images/43122_51_1.jpg?sign=1738888392-eCHXHmb4bk636v5P1OLhM8REsq2rlmMu-0-2e37b722317385b5f06749edccab3a5f)
程序综合时没有出现问题,但在用ModelSim做仿真时,却出现了问题。
![](https://epubservercos.yuewen.com/5C7EAE/23950062909693706/epubprivate/OEBPS/Images/43122_51_2.jpg?sign=1738888392-TLBTbBgH3IjB29BOvxvEv0soLAdJ7o7v-0-ffbc8558bb34f13a4e3406e349c3a59b)
原因是VHDL语言具有严格的数据类型检查的特点,连接运算符正好可以解决这一问题。对于无符号数,可在操作数的高位再连接1比特的“0”即可;对于有符号数,在操作数的高位扩展1比特的符号位即可。在上面的例子中,将语句“c<=a+b”修改为“c<=('0'&a)+('0'&b)”后,程序可正常仿真。对于加法、减法操作来说,运算数据结果的长度与长度较长的操作数相同,也就是说,前面的语句也可修改为“c<=('0'&a)+b”。