同じ記号、2つの役割
&、|、^とその反転兄弟は2つの異なる形で現れます:
- 二項形式(2オペランド):
a & b- 同じ幅のvector間のビット単位演算。 - 単項形式(1オペランド):
&a-aの全ビットを単一ビットに畳むreduction。
コンパイラはオペランド数で区別します。命名規則として、二項形式を「ビット単位演算子」、単項形式を「reduction演算子」と呼びます。
ビット単位:1ビットずつ
フルセット:
| Operator | 名前 | 位置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] |
~&、~|、~^はチルダが先、演算子が後で綴られることに注意してください。これらは1つのトークンで、間にスペースはありません。
2つのオペランドの幅が異なるとき、狭い方が一致するように 左側にゼロ拡張 されます。符号拡張が欲しい場合は、$signed()でsignedオペランドを明示的に使ってください。
Reduction:多ビットから1ビットへ
同じ演算子の単項形式は、vectorを1ビットに畳みます:
各々の意味:
&dataはdataの すべてのビット が1なら1を返し、そうでなければ0を返します。「これは全部1か?」のチェックに有用。|dataはdataの いずれかのビット が1なら1を返し、そうでなければ0を返します。「これは非0か?」のチェックに有用。^dataは パリティ (全ビットのXOR)を返します。1の数が奇数なら1、偶数なら0。~&data、~|data、~^dataは上記の逆。
実際のコードで至るところで見ます:
wire empty = ~|fifo_count; // countが0なら空
wire all_ones = &mask; // すべてのビットがセット
wire parity_bit = ^data; // バイトのパリティ
wire any_request = |request_vector; // 何かリクエストされている?
reduction形式は簡潔で、自明なgate treeに合成されます:&は多入力ANDに、|は多入力ORに、^はXOR tree(ハードウェアではパリティジェネレータでもあります)に。
一般的なパターン
ビットのセットとクリア
wire [7:0] data;
wire [7:0] mask = 8'b0000_1000;
wire [7:0] set = data | mask; // ビット3を1に強制
wire [7:0] cleared = data & ~mask; // ビット3を0に強制
wire [7:0] toggled = data ^ mask; // ビット3を反転
wire [7:0] tested = data & mask; // ビット3が0なら0、非0なら非0
これらはCで使うのと同じビット操作の慣用句です。単一gate演算に合成されます。
値が全1かチェック
wire is_max = &counter; // counterの各ビットが1なら1
1個のgateでのreduction AND vs counter == 8'hFFを書く方法(後者も動きます。合成ツールは通常同じハードウェアを生成します)。
パリティビットの生成
アクティブsignalの検出
wire any_pending = |request_vector;
request_vectorが幅広い場合(例えば64のリクエスタ)、reduction ORが優先エンコーダや調停器に供給できる1本のsignalに畳みます。
シフト演算子
ビットレベルの演算子の話のついでに、シフト:
a << NはaをNビット位置左にシフトし、右側をゼロで埋める。a >> NはaをNビット位置右にシフトし、左側をゼロで埋める。a <<< Nは算術左シフト(unsignedでは<<と同じ)。a >>> Nは算術右シフト、aがsignedなら符号ビットで埋める。
定数のべきによるシフトはハードウェアで無料(単なる配線変更)です。ランタイム変数によるシフトはバレルシフタを生成し、より大きいですが依然として安価です。
次に読むもの
これで1つの値を返すすべての演算子を見ました。次のドキュメント連結と繰り返しは、ピースから広いvectorを構築する{}と{N{...}}構文を扱います。異なる幅のmodule間のインターフェースで常に使うことになります。
よくある質問
Verilogのビット単位演算子とは?
ビット単位演算子は同じ幅の2つのvectorを位置ごとに組み合わせます。a & bはaのビット0とbのビット0をAND、ビット1とビット1、というふうにし、入力と同じ幅のvectorを生成します。フルセットは&(AND)、|(OR)、^(XOR)、~(NOT)、そして反転形~&、~|、~^(NAND、NOR、XNOR)です。
Verilogのreduction演算子とは?
reduction演算子はビット単位演算子の単項形式で、vector全体を1ビットに畳みます。&dataはdataの各ビットが1のときだけ1を返します。|dataはいずれかのビットが1なら1を返します。^dataはすべてのビットのXOR、つまりパリティを返します。reduction形式は左にオペランドがなく、右側だけです。
Verilogの&と&&の違いは?
&はビット単位ANDで、ビットを位置ごとにペアにし、結果はオペランドと同じ幅です。&&は論理ANDで、各オペランドをブール(0か非0)として扱い、1ビット結果を返します。4'b1100 & 4'b0011は4'b0000、4'b1100 && 4'b0011は1です。
Verilogでパリティを計算するには?
reduction XOR演算子を使います:parity = ^data。dataの各ビットをすべてXORします。8ビットvectorならdata[7] ^ data[6] ^ ... ^ data[0]です。立っているビット数が奇数なら結果は1、偶数なら0です。~^で反転すれば偶数パリティになります。
Verilogの~の働きは?
~はビット単位NOTで、オペランドの各ビットを反転します。~4'b1100は4'b0011です。オペランドを1ビットブールに畳んで反転する!(論理NOT)と混同しないでください。!4'b1100は1'b0です(オペランドが非0、その真理値をNOTすると0)。