ビット演算・関係演算はビット幅をそろえる、算術演算はそろえなくていい

[] [] []


2項演算 a op. bのビット幅の解釈

(1) abのビット幅は大きい方に合わせてゼロ拡張する。

 ただしa,b両方が符号付きであれば符号拡張する。

(2) 演算結果は(1)のビット幅に合わせる。

 ただし代入文では左辺の幅に合わせる。

 例) 演算結果自体

  2'b11+1'b1

  ゼロ拡張する 2'b11+2'b01

  結果3'b1002ビットに合わせる 2'b00

 例) 代入文

  d[3:0] = 2'b11+1'b1

  ゼロ拡張する 2'b11+2'b01

  結果3'b100を左辺に合わせる 4'b0100

「ビット演算(&,|,^)と関係演算(==,!=,<,>)はビット幅をそろえる」

ビット演算や関係演算では、オペランドのビット幅はそろっているのが普通であり、そろってなければ誤記の可能性が高い。

reg      en;

reg[3:0] a;

× en & a      // a[3:1]0になってしまう。誤記

{4{en}} & a

× if(a < 5'd20) // aは最大15にしかならない。誤記

if(a < 4'd10)

「算術演算(+,-,*)は必要最小限のビット幅で書けばよい」

一方、算術演算では、オペランドのビット幅が異なるのは普通であり、演算結果のビット幅もオペランドとは異なる。(加算・乗算結果のビット幅)

明示的にゼロ詰めして書いても間違いではないが、そもそも加算器や乗算器は入力がゼロの部分は回路自体が不要になるので、最小限のビット幅で書く方が回路を素直に表している。

reg[15:0] a_reg;

× a_reg = a_reg+16'h0001 // ゼロ詰めして書いても間違いではないが意味がない

a_reg = a_reg+1'b1  // 最小限のビット幅で書く方が回路を素直に表している

「符号つき演算の場合は両方のオペランドに$signed()をつける」

符号付きの数はゼロ拡張でなく符号拡張しなければならないので、明示的に c = $signed(a) + $signed(b) と書くとよい。(補足-符号付き変数)

両方が符号なし

  3'b001+2'b11

  ゼロ拡張する3'b001+3'b011

  結果3'b1003ビットに合わせる 3'b100

× 片方が符号なしだと、もう一方も符号なしと見なされる

  3'b001+$signed(2'b11)

  ゼロ拡張する3'b001+3'b011

  結果3'b1003ビットに合わせる 3'b100

両方が符号あり

  $signed(3'b001)+$signed(2'b11)

  符号拡張する3'b001+3'b111

  結果4'b10003ビットに合わせる 3'b000

「算術演算結果は、ビット幅を明確にするために中間変数に代入してから使う」

reg[7:0] a,b,c;

× if(a + b < 8'd10) // 文法上はa+bのキャリを捨ててから比較されるが

                     // れが意図どおりか曖昧

t[7:0]=a+b         // キャリを捨てることが明確になっていてよい

   if(t < 8'd10)

× c = (a + b) >> 1   // 文法上はa+bのキャリを捨ててからシフトされるが

                    // それが意図どおりか曖昧

t[8:0]=a+b        // キャリを保持することが明確になっていてよい

   c = t >> 1

「定数のビット幅は明示的に書く」

特に、定数のビット幅を省略すると32ビットと見なされる(例えば'd132'd1)ので誤動作につながることもある。

reg[7:0] a,b,c;

× if(b > a+1) // a=8'hFFのときa+1'b1=8'h00bより小さくなることを意図していても

         // a+1=32'h00000100bより大きくなり逆の結果になってしまう。

× if(b > a+1'b1) // 間違いではないが、中間変数に代入してから使う方が安全

「変数のビット範囲は部分選択するとき以外は書かない」

a[7:0]とビット範囲を明示的に書くと、コードを見ただけでビット幅の不一致に気づけるので一見よさそうだが、変数全部に対してこれをやると、1行が長くなって読みにくいし、書きまちがいや直しまちがいの確率が高くなるのでやめた方がよい。

reg[7:0] a,b,c;

× c[7:0] <= a[7:0] + b[7:0]; // ビット幅は明確だが...

c <= a + b;               // こっちの方が見やすい

 

reg[6:0] state_reg;

× if(state_reg[5:0]==IDLE) // 本当はstate[6:0]

if(state_reg==IDLE)


[] [] []