文章目录

  • VHDL的操作符号
    • VHDL赋值运算符
      • <=
      • :=
      • =>
      • 位置赋值
    • VHDL按位运算符
      • NOT
      • AND
      • OR
      • XOR
      • XNOR
    • VHDL算术运算符
      • +
      • -
      • *
      • /
      • MOD与REM
      • **
    • VHDL关系运算符
    • VHDL逻辑运算符
    • VHDL连接运算符
    • VHDL移位运算符

VHDL的操作符号

VHDL赋值运算符

VHDL语言中共有3种赋值符号——“<=”、“:=”和“=>”。

<=

“<=”符号为signal专用,对signal的赋值能且只能使用“<=”赋值符号。 举例如下:
signal a : std_logic;
a <= ‘1’;
“<=”符号是一种有延迟的赋值,即赋值操作不是立即生效的。编译器会根据你代码所描述的功能以及在程序中所处的位置,来决定产生这种延迟的硬件逻辑结构是组合逻辑还是时序逻辑。

:=

单独使用时,“:=”符号为variable专用,对variable的赋值能且只能使用“:=”赋值符号。举例如下:
variable b : std_logic;
b := ‘0’;
此时,“:=”符号是一种无延迟的赋值,即赋值操作立即生效,这点与软件编程的思路很像。
除此以外,“:=”符号还有一种用途,就是初始化赋值。这个时候“:=”是依附于声明语句而使用的,应用的对象可以是signal、variable以及constant中的任何一种。

=>

“=>”符号为映射赋值符号,从某种意义上来说,它不能算是一种赋值运算符,而应该是一种连线符号,因为它只是为赋值符号的左右两边建立起一种连接关系而已。 因此与“<=”和“:=”不同,“=>”本身并不明确表示赋值的方向关系,即到底是把左边的数据写到右边去还是把右边的数据写到左边去。而对于“<=”和“:=”来说,赋值的方向显然非常明显,为从右至左。Architecture中的例化语句就是映射赋值符号应用的一个典型例子,无论端口方向是in还是out,都一律使用“=>”来进行映射赋值,至于赋值的方向,编译器会根据上下文自己去推断。
“=>”符号应用的地方很多,刚刚提到的元件例化语句(instance)的赋值语法如下:

<instance_name> : <component_name>
port map (<port_name> => <signal_name>,<other ports>...
);

而信号量的映射赋值则稍有不同,例如:

signal a, b : std_logic_vector(3 downto 0);
a <= (0 => ‘1’, 2 => '1', others => '0'); -- a = ”0101”
b <= (others => ‘1’); -- b = “1111”;

注意这里面有一个新的语法others,这在对逻辑向量赋值时非常好用,能够帮我们方便的完成一些高位宽向量的赋值。并且,类似像b这样的信号赋值,即是以后代码修改、扩展或减少了b的bit位数,赋值语句也可以不需要做任何修改,非常便于程序的维护和变更。

位置赋值

VHDL语言中仅有的三种赋值符号都已经介绍完了,可是赋值操作的形式并没有就此终结。除了以上三种赋值形式外,VHDL语言还有一种基于位置赋值的方式,这种赋值方式其实也属于映射赋值的范畴,但它不需要任何操作符,编译器仅仅根据参数的位置就可以进行映射赋值。
在VHDL语言中,元件例化、过程及函数调用都可以这样赋值,这一点跟C语言的函数调用非常类似。不过对于凡是支持“=>”符号来进行映射赋值地方,建议大家还是不要使用位置赋值,因为映射赋值是位置无关的,这样会在很大程度上减少程序出错的可能性,并且方便日后的修改与维护。

VHDL按位运算符

按位运算符是一类最基本的运算符,可以认为它们直接对应数字逻辑中的与、或、非门等逻辑门,在VHDL中它们的描述如下:

NOT

NOT是取反运算符。它是一个单目运算符,即作用于一个操作数,对它进行按位取反。相当于数字逻辑电路中的“非门”,例如:

signal a : std_logic := '1';
signal b : std_logic_vector (3 downto 0) := "1000";
a <= not a; -- a = '0'
b <= not b; -- b = "0111"

AND

AND是与运算符。它是一个双目运算符,即必须有两个操作数,对它们进行按位与运算,相当于数字逻辑电路中的“与门”。例如:

signal a0 : std_logic := '1';
signal b0 : std_logic := '1';
signal c0 : std_logic;
signal a1 : std_logic_vector (3 downto 0) := "1101";
signal b1 : std_logic_vector (3 downto 0) := "1011";
signal c1 : std_logic_vector (3 downto 0);
c0 <= a0 and b0; -- c0 = '1'
c1 <= a1 and b1; -- c1 = "1001"

OR

OR是或运算符。它是一个双目运算符,即必须有两个操作数,对它们进行按位或运算,相当于数字逻辑电路中的“或门”。例如:

signal a0 : std_logic := '1';
signal b0 : std_logic := '0';
signal c0 : std_logic;
signal a1 : std_logic_vector (3 downto 0) := "1101";
signal b1 : std_logic_vector (3 downto 0) := "1011";
signal c1 : std_logic_vector (3 downto 0);
c0 <= a0 or b0; -- c0 = '1'
c1 <= a1 or b1; -- c1 = "1111"

XOR

XOR是异或运算符,它是一个双目运算符,即必须有两个操作数,对它们进行按位异或运算,相当于数字逻辑电路中的“异或门”。例如:

signal a0 : std_logic := '1';
signal b0 : std_logic := '0';
signal c0 : std_logic;
signal a1 : std_logic_vector (3 downto 0) := "1100";
signal b1 : std_logic_vector (3 downto 0) := "1010";
signal c1 : std_logic_vector (3 downto 0);
c0 <= a0 xor b0; -- c0 = '1'
c1 <= a1 xor b1; -- c1 = "0110"

XNOR

XNOR是同或运算符,它是一个双目运算符,即必须有两个操作数,对它们进行按位同或运算,相当于数字逻辑电路中的“同或门”。例如:

signal a0 : std_logic := '1';
signal b0 : std_logic := '0';
signal c0 : std_logic;
signal a1 : std_logic_vector (3 downto 0) := "1100";
signal b1 : std_logic_vector (3 downto 0) := "1010";
signal c1 : std_logic_vector (3 downto 0);
c0 <= a0 xnor b0; -- c0 = '0'
c1 <= a1 xnor b1; -- c1 = "1001"

VHDL算术运算符

算数运算符,即我们常说的加、减、乘、除等,这类运算符的抽象层级较高,从数字逻辑电路的实现上来看,它们都是基于与、或、非等基础逻辑门来组合实现的。算数运算符主要有两种用途:一种是作用于能对应到FPGA硬件的信号上,完成具体的硬件功能;另一种是作用于VHDL程序的某些参数或者语句结构上,仅仅起到帮助用户更方便的编写代码和帮助编译器更好的理解代码的作用。
它们在VHDL中的描述如下:

+

“+”是加法运算符,它是一个双目运算符,即必须有两个操作数,对它们进行加法运算。例如:

 signal a0, b0, c0 : integer;
signal a1 : std_logic_vector (3 downto 0) := "0001";
signal b1 : std_logic_vector (3 downto 0) := "1010";
signal c1 : std_logic_vector (3 downto 0);
a0 <= 97;
b0 <= 103;
c0 <= a0 + b0; -- c0 = 200
c1 <= a1 + b1; -- c1 = "1011",按照二进制数值来进行加法
a1 <= "1000";
c1 <= a1 + b1; -- c1 = "0010",进位被丢掉了

-

“-”是减法运算符,它是一个双目运算符,即必须有两个操作数(两个操作数的地位不一样,一个是被减数,一个是减数,不能颠倒),对它们进行减法运算。例如:

 signal a0, b0, c0 : integer;
signal a1 : std_logic_vector (3 downto 0) := "1000";
signal b1 : std_logic_vector (3 downto 0) := "0111";
signal c1 : std_logic_vector (3 downto 0);
a0 <= 97;
b0 <= 103;
c0 <= b0 + a0; -- c0 = 200,对比上例可以发现颠倒操作数的位置结果不变
c0 <= a0 - b0; -- c0 = -6
c0 <= b0 - a0; -- c0 = 6,显然减数、被减数颠倒后结果不一样
c1 <= a1 - b1; -- c1 = "0001",按照二进制数值来进行减法
c1 <= b1 - a1; -- c1 = "1111",按照有符号数来理解,"1111"其实就是-1;

*

“*”是乘法运算符,它是一个双目运算符,即必须有两个操作数,对它们进行乘法运算。例如:

 signal a0, b0, c0 : integer;
signal a1 : std_logic_vector (3 downto 0) := "0011";
signal b1 : std_logic_vector (3 downto 0) := "0010";
signal c1 : std_logic_vector (3 downto 0);
a0 <= 25;
b0 <= 40;
c0 <= a0 * b0; -- c0 = 1000
c1 <= a1 * b1; -- c1 = "0110",按照二进制数值来进行乘法

/

“/”是除法运算符,它是一个双目运算符,即必须有两个操作数(两个操作数的地位不一样,一个是被除数,一个是除数,不能颠倒),对它们进行除法运算。例如:

signal a0, b0, c0 : integer;
signal a1 : std_logic_vector (3 downto 0) := "0110";
signal b1 : std_logic_vector (3 downto 0) := "0010";
signal c1 : std_logic_vector (3 downto 0);
a0 <= 660;
b0 <= 30;
c0 <= a0 / b0; -- c0 = 22
c1 <= a1 / b1; -- c1 = "0011",按照二进制数值来进行除法

MOD与REM

MOD是取模运算,而REM是求余运算,它们都是完成求余数的运算,都是双目运算符,且操作数不能任意颠倒,语法如下:

x = a mod b;
y = a rem b;

如果a,b都是正数的情况下,它们的运算结果一摸一样。实际上,只要a和b同号时,它们的运算结果就是一样的。不一样的情况产生于a与b异号时。即,mod的结果是要与b同号的,而rem的结果是要与a同号的(注意,0的符号可正可负)。那么求余数的通用公式如下:
Remainder = a - b * N;
对于mod来说,N取使remainder绝对值最小,且和b同号的那个结果;
对于rem来说,N取使remainder绝对值最小,且和a同号的那个结果;
以下给出几个例子供大家参考:
7 mod 5 = 2; 7 rem 5 = 2;
(-7) mod 5 = 3; (-7) rem 5 = -2;
7 mod (-5) = -3; 7 rem (-5) = 2;
(-7) mod (-5) = -2; (-7) rem (-5) = -2;
注意,不要轻易在能对应FPGA硬件的信号量上使用这些运算符,会非常浪费资源。如果一定要使用,可以使用一些现成的IP核,或者自动动手编写相关算法,这样实现起来会更加节省资源、更加可控一些。

**

”是乘方运算,完成求一个数的n次幂运算,它是双目运算符,且操作数不能任意颠倒,语法如下:
a <= 2
8; – a = 256
注意,不要轻易在能对应FPGA硬件的信号量上使用乘方运算符,如果一定要用,可以使用一些现成的IP核,或者自动动手编写相关算法。

VHDL关系运算符

关系运算符主要用来做一些条件判断使用,通用的语法为:
<expression_left> <relation_operator> <expression_right>
VHDL中的关系运算符包括如下几种:

=   -- 两边表达式相等
/= -- 两边表达式不相等
<   -- 左边表达式小于右边表达式
<=  -- 左边表达式小于等于右边表达式
>   -- 左边表达式大于右边表达式
>=  -- 左边表达式大于等于右边表达式

关系运算符一般不单独使用,都需要配合具体的语句来实现完整的意思。关系表达式会返回true或者false的结果。即,如果左右表达式满足相应关系运算符表示的关系时,返回true,否则返回false。
需要注意一点,VHDL语言中是不支持简化书写的,即:

if (a = ‘1’) then
不可以写成
if (a) then -- error! will report : a is not a boolean expression!

VHDL逻辑运算符

逻辑运算符是连接多个关系表达式用的,从而实现更加复杂的判断,一般也不单独使用,都需要配合具体语句来实现完整的意思。它的语法如下:
<logical_operator> < relation_expression_right>
或者
<relation_expression_left> <logical_operator> < relation_expression_right>
VHDL中的逻辑运算符包括如下几种:

NOT -- 右边表达式的逻辑结果取逻辑反,这是一个单目的操作符;
AND -- 左右两边表达式的逻辑结果取逻辑与,即同为true才返回true,否则返回false;
OR  -- 左右两边表达式的逻辑结果取逻辑或,即同为false才返回false,否则返回true;

结合关系运算符和逻辑运算符,给出以下几个例子:

if (a > b) then
max <= a;
else
max <= b;
end if;
if(a > 90 and a < 110)thenstable <= ‘1’;
elsestable <= ‘0’;
end if;

我们可以看出,VHDL逻辑运算符和按位运算符的前三个长得真是一模一样,只不过它们所处理的数据类型不一样。但是,如果我们把true看成’1’,把false看成’0’,其实也可以近似把它们看成是通用的,而且如果从硬件实现角度上来说,它们是没有区别的,例如:

if (a = '1' and b = '1')thenc <= '1';
elsec <= '0';
end if;
等价于
c <= a and b;

VHDL连接运算符

连接运算符不完成任何运算,它只是可以将若干个标准逻辑类型或者逻辑向量首尾相接,描述成为一个新的逻辑向量。它的语法如下:
<signal 1> & <signal 2> &
例如,

signal a, b : std_logic;
signal c : std_logic_vector(1 downto 0);
signal d : std_logic_vector(3 downto 0);
d(3) <= a;
d(2 downto 1) <= c;
d(0) <= b;
或者
d <= a & c & b;
该运算符尤其是在描述串行移位寄存器的时候最为方便,例如:
d <= d(2 downto 0) & a;

VHDL移位运算符

VHDL中共有3类移位运算符,分别介绍如下:

循环移位运算符,包括rol、ror,分别对应循环左移和循环右移;
算数移位运算符,包括sla、sra,分别对应算数左移和算数右移;
逻辑移位运算符,包括sll、srl,分别对应逻辑左移和逻辑右移。

它们的通用语法如下:
<signal_name> <shift_operator> <shift_amount_in_integer>;
循环移位的意思是在移位的时候,高(低)位的移出补到低(高)位上;
算数移位的意思是在移位的时候空出来的位保留原值;
逻辑移位的意思即是在移位的时候空出来的位用’0’填充。
举例如下:

signal a : std_logic_vector(3 downto 0) := "0111";
a <= a rol 2; -- a = "1101"
a <= a ror 1; -- a = "1011"
a <= a sla 1; -- a = "1111"
a <= a sra 2; -- a = "0001"
a <= a sll 2; -- a = "1100"
a <= a srl 1; -- a = "0011"

以上的6个移位操作符,需要调用IEEE.numeric_std库才能使用它们。我建议大家通常情况下不要使用这6个移位操作符,因为使用上一小节中介绍的连接运算符可以非常方便的实现移位操作,同时还不易出错,并且更加形象、更加接近底层硬件描述,而且通过连接运算符,我们还能定义更多样化的移位方式,例如用’1’填充空位的逻辑移位。 下面把上例的所有操作用连接运算符依次对比描述如下:

a <= a(1 downto 0) & a(3 downto 2);
a <= a(0) & a(3 downto 1);
a <= a(2 downto 0) & a(0);
a <= a(3) & a(3) & a(3 downto 2);
a <= a(1 downto 0) & "00";
a <= '0' & a(2 downto 0);

顺便说一下,移位操作是乘法和除法的实现基础。

FPGA之道(27)VHDL的操作符号相关推荐

  1. FPGA之道(34)Verilog初始化与操作符号

    文章目录 前言 Verilog初始化 Verilog的操作符号 Verilog赋值运算符 连续赋值符号 阻塞赋值符号 非阻塞赋值符号 映射赋值符号 位置赋值 Verilog按位运算符 ~ & ...

  2. FPGA之道(38)VHDL与Verilog的比较

    文章目录 前言 VHDL与Verilog的比较 语法比较 基本程序框架比较 端口定义比较 范围表示方法比较 元件调用与实例化比较 Process与always比较 标准逻辑类型比较 逻辑常量赋值比较 ...

  3. FPGA之道(24)VHDL数据类型

    文章目录 前言 VHDL数据类型 常用数据类型 逻辑数据类型 std_logic std_logic_vector boolean bit bit_vector 数值数据类型 integer real ...

  4. FPGA之道(22)VHDL基本程序框架

    文章目录 前言 VHDL基本程序框架 VHDL基本程序框架模板 Library Entity Architecture 声明与定义部分 语句部分 VHDL基本程序框架范例 VHDL注释语法 前言 VH ...

  5. FPGA之道(31)VHDL编写注意事项

    文章目录 前言 VHDL编写注意事项 大小写不敏感 VHDL中的关键字 多余的符号 纠结的downto 与to 数组范围混用 逻辑向量范围混用 范围中的变量 仿真雷区 进程敏感量表缺失 进程间语句顺序 ...

  6. FPGA之道(28)VHDL的并行语句

    文章目录 前言 VHDL的并行语句 VHDL直接信号赋值语句 VHDL条件式信号设置语句 VHDL选择式信号设置语句 VHDL进程语句 时钟事件表示方法 纯组合process 纯时序process 具 ...

  7. FPGA之道(30)编写自己的vhdl库文件

    文章目录 前言 编写自己的vhdl库文件 Work库 记录数据类型 子程序介绍 函数 过程 子程序使用总结 程序包 自定义程序包范例 前言 本文节选自<FPGA之道>来一起学习下高阶Ver ...

  8. FPGA之道(83)功能仿真之仿真语法(Graphic Waveform )

    文章目录 前言 仿真语法 Graphic Waveform 数字波形简介 从实际到仿真 实际系统检测 软件仿真模拟 "Hello world"之Graphic Waveform 待 ...

  9. FPGA之道(71)提高设计的综合性能(三)提高设计的重用性与易改性

    文章目录 前言 提高设计的重用性 构建自己的IP库 提高设计的易改性 常量参数化模块设计 结构参数化模块设计 总线参数化 规模参数化 功能参数化 参数化设计的参数管理与组织 参数相关性 可传递的模块参 ...

最新文章

  1. python学习第十节(yield表达式的应用+内置函数)
  2. 用python实现矩阵乘法
  3. 仿射密码介绍以及解题脚本
  4. 第十节 字符串指针变量与字符数组的区别(十一)
  5. python是面向过程的吗_Python开发是面向过程、函数还是对象?
  6. PCA主成分分析实战和可视化 | 附R代码和测试数据
  7. python 窗口函数_太好用了!Python中8种流行的SQL窗口函数了解一下!
  8. Windows、Linux 纷纷被爆漏洞,黑客可直取 root 权限!
  9. DAY37-Python入门学习-进程池与线程池、协程、gevent模块
  10. android学习日记20--连接组件之Intent和IntentFilter
  11. java调用opencv人脸识别 error insufficient out of memory
  12. fullscreen (JS 简易轻量化类原生的高兼容窗口全屏控件)
  13. Android BT STACK BTU 和 HCI之间的消息传递
  14. 电子电路学习笔记(2)——电容
  15. 【C#】WinForm 之 SQL Server 服务监控器(避免开机启动服务)
  16. 微信号开通检测软件的使用方法
  17. Saber 2016安装过程分享
  18. 有瓶颈设备的多级生产计划问题
  19. HDU 5128 The E-pang Palace 【暴力】
  20. php job框架,GitHub - zanphp/job-server

热门文章

  1. 客户需求分析8个维度_CRM准确数据分析,把控客户需求
  2. linux编程技术进程代码,Linux进程通信—信号量 代码实现
  3. Java不同场景加载不同类_[改善Java代码]不同的场景使用不同的泛型通配符
  4. python读写csv确定编码格式_Python使用utf8编码读写csv文件
  5. jar 工程我怎么在网页上url访问某一个方法_url及url参数与seo网站优化的关系
  6. 怎么用css控制border成为三角形
  7. tensorflow超参数优化,Tensorflow的超参数调整
  8. openssh漏洞_漏洞预警 | OpenSSH 命令注入漏洞
  9. anaconda安装yolov3_YOLOv3_图像识别_神经网络_人工智能
  10. scala mysql bit_Scala连接mysql数据库