Verilog 中的“运算符”意味着什么
运算符接收承载信号的表达式,产生新的承载信号的表达式。a + b 就是硬件:综合器会把它变成一个加法器。a == b 也是硬件:它构成一个比较器。这些都不是“运行的代码”,而是“运算符所描述的电路”。
全部运算符并不多,完全可以记住。本文介绍算术、比较、逻辑和条件运算符。按位与缩减运算符 介绍在软件中没有直接对应物的位级运算符。
算术运算符:+ - * / %
需要知道三点:
- 除法是整数除法。
7/2的结果是3,而不是3.5。Verilog 没有浮点类型(好吧,有real,但它只用于仿真)。 - 除法和取模在硬件中开销很大。
/4没问题(综合器会把它变成右移),但如果n是运行时才确定的变量,/n会生成一个多周期的除法器,而这几乎从来不是你想要的。 - 溢出会回绕。 两个 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 通常更清晰。