Verilog基本文法(後編)
📌 このページについて
VerilogHDLにはいろいろな演算があります。C言語に似た演算が多いですが、HDL特有の演算(リダクション演算・連接演算)もあります。一つ一つ確実に理解しましょう。
1. 論理値と定数表現
ソフトウェアと違って、ハードウェアの信号には「不定値(X)」と「ハイインピーダンス(Z)」があるんだ🐾
この4つの状態と、ちょっと変わった「数字の書き方」を覚えよう!
VerilogHDLで扱える論理値は以下の4値のみです。
| 値 | 意味 | 用途 |
|---|---|---|
0 | 論理0(Low) | 通常の論理値 |
1 | 論理1(High) | 通常の論理値 |
x | 不定値 | 初期化前・競合状態 |
z | ハイインピーダンス | トライステートバッファ出力 |
定数の書式
定数は <ビット幅>'<基数><数値> の形式で表現します。
| 要素 | 内容 | 省略時のデフォルト |
|---|---|---|
| ビット幅 | 10進数でビット幅を指定 | 32ビット |
| 基数 | b/B=2進、o/O=8進、d/D=10進、h/H=16進 | 10進(d) |
| 数値 | 基数に対応した値。xとzも使用可(10進を除く) | — |
4'b1010 // 4ビット2進数: 1010
8'hFF // 8ビット16進数: 255
8'b1111_1111 // アンダースコアで区切り可(読みやすく)
8'hxx // 不定値
1'bz // 1ビットハイインピーダンス
255 // ビット幅指定なし→32ビット10進数
2. 演算子一覧
| 種類 | 演算子 | 説明 |
|---|---|---|
| 算術演算 | + - * / % | 加減乗除・余り |
| ビット演算 | & | ~ ^ ~^ | AND/OR/NOT/XOR/XNOR(ビット単位) |
| リダクション演算 | &A |A ^A | 全ビットに作用→1ビット出力 |
| 等号演算 | == != === !== | 等値・不等値比較(===はx/zも比較) |
| 関係演算 | < > <= >= | 大小比較 |
| 論理演算 | && || ! | AND/OR/NOT(条件判別・1ビット出力) |
| シフト演算 | << >> | 左シフト・右シフト |
| 条件演算 | 式1 ? 式2 : 式3 | 2方向分岐(セレクタ) |
| 連接演算 | {A, B} | 信号を結合して複数ビット信号を作成 |
3. 算術演算
assign SUM = A + B; // 8ビット加算
assign DIFF = A - B; // 減算
assign PROD = A * B; // 乗算
⚠️ 除算・余り演算の注意
/(除算)と %(余り)は多くの論理合成ツールが対応していません。シミュレーション専用と考えてください。
4. ビット演算
対応するビット同士で演算します。演算結果のビット幅は入力と同じです。
assign AND_out = A & B; // ビットAND(bit0同士、bit1同士…と演算)
assign OR_out = A | B; // ビットOR
assign NOT_out = ~A; // ビットNOT(反転)
assign XOR_out = A ^ B; // ビットXOR(排他的論理和)
assign XNOR_out= A ~^ B; // ビットXNOR
5. リダクション演算
複数ビット信号の全ビットに演算を作用させ、1ビットの結果を得る演算です。演算子を信号の前に置きます。
reg [7:0] cnt;
wire all_one = &cnt; // cntの全ビットがAND → 全部1のとき1
wire any_one = |cnt; // cntの全ビットがOR → 1つでも1なら1
wire parity = ^cnt; // cntの全ビットがXOR → パリティビット生成
💡 リダクション演算の利点
ビットごとに演算したものと等価ですが、簡潔に記述できるため誤りが少なくなります。パリティ生成・全ビット一致チェックに特に有効です。
6. 等号演算・関係演算
演算結果は常に1ビット(成立=1、不成立=0)です。
| 演算子 | 意味 | x/z の扱い |
|---|---|---|
== | 等値 | x/zが含まれると不成立(0) |
!= | 不等値 | x/zが含まれると不成立(0) |
=== | 完全等値(case等値) | x/zも比較対象(論理合成不可) |
!== | 完全不等値 | x/zも比較対象(論理合成不可) |
< > <= >= | 大小比較 | x/zが含まれると不成立(0) |
7. 論理演算
複数の条件判別を組み合わせる演算です。演算対象を1ビットの値として扱い、結果も1ビットです。主にif文などの条件判別に使います。
// 例:AがFFhでかつBが0でない場合に処理を実行
if ((A == 8'hFF) && (B != 8'h00)) begin
// 両方の条件が成立したときに実行
end else begin
// いずれか不成立のとき
end
⚠️ ビット演算(&)と論理演算(&&)を混同しない
&はビット単位演算(結果はNビット)、&&は条件判別(結果は1ビット)です。詳細は「ビット演算 vs 論理演算」の節を参照。
8. シフト演算
assign Q = A << SFT; // Aを SFT ビット分だけ左シフト
assign R = A >> SFT; // Aを SFT ビット分だけ右シフト
| 方向 | 空いたビット | 溢れたビット |
|---|---|---|
| 左シフト(<<) | LSB側に0を挿入 | MSB側が欠落 |
| 右シフト(>>) | MSB側に0を挿入 | LSB側が欠落 |
9. 条件演算(? :)
1行で2方向分岐を記述できる演算です。式1 ? 式2 : 式3 の形式で、式1が成立なら式2を、不成立なら式3を選びます。
条件演算(assign文1行)
// sel=1ならd1、0ならd0を選ぶ
assign dout = sel ? d1 : d0;
if文(always/function内)
reg dout;
always @(*) begin
if (sel) dout = d1;
else dout = d0;
end
💡 使い分けの目安
シンプルなセレクタなら条件演算が簡潔です。複雑な分岐や複数の代入が必要な場合はif文/case文を使います。
10. 連接演算({})
複数の信号を {} で囲んで結合し、複数ビットの信号を作る演算です。左側が上位ビットになります。
// 右辺での使用:2つの8ビット信号を連接して16ビットに
wire [15:0] addr_bus = {addr_hi, addr_lo}; // 上位8bit=addr_hi, 下位8bit=addr_lo
// 左辺での使用:16ビットの演算結果を2つの8ビット信号に分配
{addr_hi, addr_lo} = addr_bus + 1;
// 繰り返し連接:enable信号を8回繰り返して8ビット信号を生成
wire [7:0] dout = din & {8{enable}}; // dinの全ビットにenableをAND
⚠️ ビット幅の違う信号をANDするとき
1ビットの
enable 信号をそのまま8ビット信号にANDすると、下位1ビットにしか影響しません。{8{enable}} で8ビットに拡張してからANDすることで全ビットに作用します。
11. ビット演算 vs 論理演算
| 項目 | ビット演算(&) | 論理演算(&&) |
|---|---|---|
| 演算対象 | 対応するビット同士 | 値全体を1ビットとして扱う |
| 結果のビット幅 | 入力と同じNビット | 常に1ビット |
| 主な用途 | 回路記述(信号のマスク等) | 条件判別(if文等) |
wire [7:0] A = 8'hAA; // 1010_1010
wire [7:0] B = 8'h0F; // 0000_1111
wire [7:0] bit_and = A & B; // ビット演算 → 8'h0A (0000_1010)
wire log_and = A && B; // 論理演算 → 1'b1 (AもBも0でないため)
⚠️ 使い分けを誤ると誤動作
数値の演算にはビット演算(&)、条件判別には論理演算(&&)を使います。混同すると意図しない値が代入されます。
「&」と「&&」の使い間違いは、コンパイルエラーにならないからバグ探しが大変なんだ🐾
条件分岐(if)には『2つ重ねる(&&)』ってルールにしておくと安全だよ!
12. 演算の優先順位
| 優先度 | 演算子 | 種類 |
|---|---|---|
| 1(最高) | ! ~ & | ^(単項) | 論理NOT・ビットNOT・リダクション演算・符号 |
| 2 | * / % | 乗除算 |
| 3 | + - | 加減算 |
| 4 | << >> | シフト演算 |
| 5 | < > <= >= | 関係演算 |
| 6 | == != === !== | 等号演算 |
| 7 | & | ビットAND |
| 8 | ^ ~^ | ビットXOR/XNOR |
| 9 | | | ビットOR |
| 10 | && | 論理AND |
| 11 | || | 論理OR |
| 12(最低) | ? : | 条件演算 |
優先順位の例
// 例1:* は + より優先
assign Y = A * B + C; // (A*B) + C として演算
assign Y = A * (B + C); // +を優先させたければ括弧で囲む
// 例2:+ は << より優先
assign Y = A << B + C; // A << (B+C) として演算 ← 意図と異なる可能性
assign Y = (A << B) + C; // <<を優先させたければ括弧で囲む
// 例3:同優先度は左から
assign Y = a ^ b & c; // (a^b) & c (左側が優先)
assign Y = a ^ (b & c); // &を優先させたければ括弧で囲む
13. 演算の注意事項
📝 E5 演算の注意事項まとめ
| 注意点 | 内容 |
|---|---|
| ビット幅の不一致 | 足りない側は上位に0を補う。多い側は上位が欠落する |
| 定数のビット幅 | ビット幅を明示して記述すること(省略すると32ビット) |
| 符号付き演算 | VerilogHDLは符号を直接記述できない。符号処理回路を別途設計する |
| 演算優先順位 | 優先順位に頼らず括弧で明示する方が安全・可読性が高い |
| 除算・余り | 論理合成ツール非対応のことが多い。シミュレーション専用 |
| ===演算子 | x/zも比較可能だが論理合成不可。テストベンチ専用 |
