Menu

Verilog 按位运算符与缩减运算符

Verilog 中的位级运算符:按位与、或、异或,它们的取反形式,以及把整个向量压缩成单个位的缩减运算符。

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

同一个符号的两种角色

&、|、^ 以及它们的取反形式会以两种不同的形式出现:

  • 二元形式(两个操作数):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)。

Coddy 编程语言插图

和 Coddy 一起学编程

开始