同一个符号的两种角色
&、|、^ 以及它们的取反形式会以两种不同的形式出现:
- 二元形式(两个操作数):
a & b,在等宽向量之间进行按位运算。 - 一元形式(一个操作数):
&a,把a的所有位缩减成单个位。
编译器通过操作数的个数区分它们。习惯上,二元形式叫“按位运算符”,一元形式叫“缩减运算符”。
按位运算:一次一位
完整的一组:
| 运算符 | 名称 | 第 N 位的输出 |
|---|---|---|
a & b | 与(AND) | a[N] AND b[N] |
a | b | 或(OR) | a[N] OR b[N] |
a ^ b | 异或(XOR) | a[N] XOR b[N] |
a ~& b | 与非(NAND) | NOT (a[N] AND b[N]) |
a ~| b | 或非(NOR) | NOT (a[N] OR b[N]) |
a ~^ b | 同或(XNOR) | NOT (a[N] XOR b[N]) |
~a | 非(NOT) | NOT a[N] |
注意 ~&、~|、~^ 的写法是波浪号在前、运算符在后。它们是一个整体的记号,中间没有空格。
两个操作数宽度不同时,较窄的那个会在左边补零扩展到相同宽度。如果你需要符号扩展,要用 $signed() 显式地使用有符号操作数。
缩减运算:从多位到一位
同样这些运算符的一元形式会把向量压缩成单个位:
各自的含义:
- 如果
data的所有位都是 1,&data返回1,否则返回0。适合检查“是否全为 1”。 - 如果
data的任意一位是 1,|data返回1,否则返回0。适合检查“是否非零”。 ^data返回奇偶校验位,即所有位的异或。1 的个数为奇数时是1,为偶数时是0。~&data、~|data、~^data是上面这些的取反。
在实际代码中到处都能看到它们:
wire empty = ~|fifo_count; // empty iff count is zero
wire all_ones = &mask; // all bits set
wire parity_bit = ^data; // parity of a byte
wire any_request = |request_vector; // is anything requested?
缩减形式简洁,综合出的门电路结构也一目了然:& 缩减成一个多输入与门,| 缩减成一个多输入或门,^ 缩减成一棵异或树(它在硬件中也就是奇偶校验生成器)。
常见用法
置位和清零
wire [7:0] data;
wire [7:0] mask = 8'b0000_1000;
wire [7:0] set = data | mask; // forces bit 3 to 1
wire [7:0] cleared = data & ~mask; // forces bit 3 to 0
wire [7:0] toggled = data ^ mask; // flips bit 3
wire [7:0] tested = data & mask; // zero if bit 3 was 0, non-zero otherwise
这些和你在 C 语言中使用的位操作惯用法完全一样,综合出来都是单个门的运算。
检查值是否全为 1
wire is_max = &counter; // 1 if every bit of counter is 1
只用一个门的缩减与,等价于写 counter == 8'hFF(这样写也可以,综合工具通常会生成相同的硬件)。
生成奇偶校验位
检测是否有信号有效
wire any_pending = |request_vector;
如果 request_vector 很宽(比如有 64 个请求方),缩减或会把它压缩成一个信号,你可以把它送进优先级编码器或仲裁器。
移位运算符
既然讲到了位级运算符,顺便看看移位:
a << N把a左移N位,右边补零。a >> N把a右移N位,左边补零。a <<< N算术左移(对无符号数与<<相同)。a >>> N算术右移:当a是有符号数时,左边用符号位填充。
按常数移位在硬件中没有任何开销(只是重新连线)。按运行时变量移位会生成桶形移位器,面积更大,但仍然不贵。
接下来
到这里,你已经见过所有返回单个值的运算符。下一篇文档拼接与复制讲解 {} 和 {N{...}} 语法,它们用小块拼出更宽的向量。在不同宽度的模块之间做接口时,你会经常用到它们。
常见问题
Verilog 中的按位运算符是什么?
按位运算符把两个等宽的向量逐位组合。a & b 把 a 的第 0 位与 b 的第 0 位相与,第 1 位与第 1 位相与,依此类推,得到一个与输入同宽的向量。完整的一组是 &(与)、|(或)、^(异或)、~(非),以及取反形式 ~&、~|、~^(与非、或非、同或)。
Verilog 中的缩减运算符是什么?
缩减运算符是按位运算符的一元形式,它把整个向量压缩成单个位。只有当 data 的每一位都是 1 时,&data 才返回 1。只要有任意一位是 1,|data 就返回 1。^data 返回所有位的异或,也就是奇偶校验位。缩减形式左边没有操作数,只有右边有。
Verilog 中 & 和 && 有什么区别?
& 是按位与:它逐位配对,结果与操作数同宽。&& 是逻辑与:它把每个操作数当作布尔值(零或非零),返回 1 位结果。4'b1100 & 4'b0011 是 4'b0000;4'b1100 && 4'b0011 是 1。
在 Verilog 中如何计算奇偶校验?
使用缩减异或运算符:parity = ^data。它把 data 的每一位异或在一起。对于 8 位向量就是 data[7] ^ data[6] ^ ... ^ data[0]。置 1 的位数为奇数时结果是 1,为偶数时是 0。用 ~^ 取反则得到偶校验。
Verilog 中的 ~ 有什么作用?
~ 是按位非:它把操作数的每一位取反。~4'b1100 是 4'b0011。不要把它和 !(逻辑非)混淆,后者先把操作数压缩成 1 位布尔值再取反。!4'b1100 是 1'b0(操作数非零,对真值取反得到 0)。