Menu

Verilog 运算符:算术、比较、逻辑与条件运算符

Verilog 的核心运算符:算术、比较、逻辑和条件运算符 ?:,以及混合位宽和有无符号时的规则与陷阱。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

Verilog 中的“运算符”意味着什么

运算符接收承载信号的表达式,产生新的承载信号的表达式。a + b 就是硬件:综合器会把它变成一个加法器。a == b 也是硬件:它构成一个比较器。这些都不是“运行的代码”,而是“运算符所描述的电路”。

全部运算符并不多,完全可以记住。本文介绍算术、比较、逻辑和条件运算符。按位与缩减运算符 介绍在软件中没有直接对应物的位级运算符。

算术运算符:+ - * / %

需要知道三点:

  1. 除法是整数除法。 7/2 的结果是 3,而不是 3.5。Verilog 没有浮点类型(好吧,有 real,但它只用于仿真)。
  2. 除法和取模在硬件中开销很大。 /4 没问题(综合器会把它变成右移),但如果 n 是运行时才确定的变量,/n 会生成一个多周期的除法器,而这几乎从来不是你想要的。
  3. 溢出会回绕。 两个 N 位无符号数相加,如果结果超过 N 位,会被截断为 N 位。如果想保留进位,就把结果声明得宽一位:
wire [7:0] a, b;
wire [8:0] sum = a + b;   // 9-bit sum captures carry out

比较运算符:== != < > <= >=

六个比较运算符都返回 1 位的结果:为真时是 1,为假时是 0,只要任一操作数中有 x 或 z 位,结果就是 x。要处理 x/z 的情况,请使用 === 和 !==(全等运算符),在 X 与 Z 值 中讲解。

这里的 <= 是比较运算符(小于等于)。同样的字符在过程块中也表示非阻塞赋值(q <= d)。靠上下文来区分:在表达式内部,它是比较;在被赋值对象的左侧,它是赋值。两者重叠容易让人困惑,但对解析器来说并不会产生歧义。

逻辑运算符:&& || !

逻辑运算符把操作数当作布尔值(非零为真,零为假),产生 1 位的结果:

必须牢牢记住的关键区别:逻辑 && 不是按位 &。

4'b1100 && 4'b0011   // 1 (both are non-zero, logical AND is true)
4'b1100 &  4'b0011   // 4'b0000 (no bit positions are 1 in both)

|| 和 | 也是一样。在 if 条件和 ?: 的判断条件中用逻辑运算符;希望每一位独立地做与/或运算时,用按位运算符。

&& 和 || 具有 C 语言意义上的短路特性:如果左侧已经能确定结果,右侧就不会被求值。这对检查指针或函数调用的测试平台有影响;对可综合的 RTL 来说几乎没有影响,因为那里的一切本来就是并行发生的。

条件运算符:?:

三目运算符是构建多路选择器和条件表达式的最佳帮手:

cond ? a : b 这种写法在硬件中就是一个以 1 位信号作选择端的二选一多路选择器。三选一、四选一时把它们串起来没问题,但再多的话,case 语句(在 case 语句 中讲解)的可读性会好得多。

?: 运算符也是给寄存器设置默认值、只在某些时候才覆盖它的标准写法:

next_value = update ? new_data : next_value;

位宽规则:初学者的陷阱

Verilog 算术运算符的位宽规则是出了名的出人意料。简单来说:

  • 结果的位宽是所有输入位宽中的最大值。
  • 如果结果被赋给更宽的对象,会做零扩展(有符号时做符号扩展)。
  • 中间计算的位宽也是结果的位宽,这意味着溢出会发生在中间计算中。

第一次乘法溢出了,因为两个操作数都是 8 位,所以结果的位宽也是 8 位,而 40000 放不下。第二次乘法没问题,因为我们在相乘之前显式地加宽了 a。

修复技巧:

  • 用拼接运算符 {8'b0, a} 做零扩展。
  • 用 $unsigned(a) 或 $signed(a) 来影响解读方式。
  • 拿不准时,把中间表达式转换为更宽的位宽。

运算符优先级

你不必记住整张表,但会经常用到几条规则:

  • *、/、% 的优先级高于 +、-。
  • 比较运算符的优先级高于逻辑运算符(&&、||)。
  • 条件运算符 ?: 的优先级非常低:在较大的表达式中,通常需要给它的分支加括号。
  • 拿不准时就加括号。仿真器又不按字符收费。
out = (mode == 2'd0) ? a + b : a - b;   // clearer with parens

接下来

你已经学完了所有和软件中长得相似的运算符。下一篇文档 按位与缩减运算符 介绍在软件中没有直接对应物的运算符:逐位的与/或/异或,以及把整个向量压缩成一位的缩减形式。

常见问题

Verilog 支持哪些运算符?

Verilog 的运算符大致分为四类:算术(+、-、*、/、%)、比较(==、!=、<、>、<=、>=)、逻辑(&&、||、!)以及按位/缩减(&、|、^、~、~&、~|、~^)。此外还有移位运算符(<<、>>、<<<、>>>)、拼接 {}、复制 {N{...}} 和条件运算符 ?:。

Verilog 中 && 和 & 有什么区别?

&& 是逻辑与:它把每个操作数当作布尔值(零或非零),返回 1 位的结果。& 是按位与:它把两个操作数逐位配对运算。4'b1100 && 4'b0011 的结果是 1(两者都非零);4'b1100 & 4'b0011 的结果是 4'b0000。条件判断中用 &&,位级运算用 &。

Verilog 中的除法是如何工作的?

是整数除法,小数部分会被舍弃。7 / 2 的结果是 3,而不是 3.5。% 运算符给出余数:7 % 2 的结果是 1。在仿真中,除以零会得到 x(未知)。这两个运算符严格来说都可综合,但在 FPGA 和 ASIC 上会生成非常庞大且缓慢的硬件:除非除数是 2 的幂,否则在可综合的路径中避免使用它们。

Verilog 中的条件运算符是什么?

?: 是三目运算符,也叫条件运算符,照搬自 C 语言。cond ? a : b 在 cond 为真(非零)时取值为 a,否则为 b。它是实现多路选择器类逻辑时用得最多的结构:out = sel ? in1 : in0 就构成一个二选一多路选择器。把多个 ?: 串起来可以构成更宽的多路选择器,不过输入超过两个时,case 通常更清晰。

Coddy 编程语言插图

和 Coddy 一起学编程

开始