\$clog2()は2を基数としたlogの値を整数に切り上げした値(Ceiling Log2)を返すSystemVerilogのシステムタスクである。
ある値を表現するのに必要なビット数を計算するのによく使われ、そのように解説しているページをよく見かける。その事自体は正しいが、その使用方法の解説の多くが必ずしも正しくはないので、正しい解説をしておこうと思う。
尚、頂いたコメントの情報では、Vivadoの古いバージョンで\$clog2()が切り下げの処理になってしまっており、正しく計算されない場合があるそうです。なので古いVivadoを使っていた人が記事を書いた場合そのような解説になっている可能性があるとの事です。Vivadoを使っていて\$clog2の値がどうもおかしい場合にはバージョンを確認してみて下さい。
例えば0~7までの値を取る信号の場合ビット幅は[2:0]の3bitである。
\$clog2(7)は3 (log2(7) = 2.???)なので[\$clog2(7)-1:0]のビット幅となる。最大値7をパラメータ化して
parameter MAX_VAL = 7;
parameter WIDTH = $clog2(MAX_VAL); // 3
logic[WIDTH - 1 : 0] val;
のように書ける。このような解説のページが多い。しかしこれは必ずしも正しくない。これが正しいのはMAX_VALが2のべき乗以外の場合のみである。
2のべき乗の時、例えば8の場合を見てみよう。
0~8の値を表すには0000~1000なので必要ビット幅は[3:0]の4bitとなる。それに対して\$clog2(8)は3となる。なので、
parameter MAX_VAL = 8;
parameter WIDTH = $clog2(MAX_VAL); // 3
logic[WIDTH - 1 : 0] val;
は間違っており、思わぬバグとなってしまう。じゃあどうすれば良いかと言うと
parameter MAX_VAL = 8;
parameter WIDTH = $clog2(MAX_VAL+1); // 4
logic[WIDTH - 1 : 0] val;
が正しい。
\$clog2(1) = 0 // log2(1) = 0
\$clog2(2) = 1 // log2(2) = 1
\$clog2(3) = 2 // log2(3) = 1.???
\$clog2(4) = 2 // log2(4) = 2
\$clog2(5) = 3 // log2(5) = 2.???
\$clog2(6) = 3 // log2(6) = 2.???
\$clog2(7) = 3 // log2(7) = 2.???
\$clog2(8) = 3 // log2(8) = 3
と3は一つ上の2のべき乗の数4の仲間、5,6,7は8の仲間なのである。4は3ではなくて5,6,7の仲間に入れたい。8は9~15の仲間に入れたい。なので一つずらす為に+1をすればいいのである。
\$clog2(1 + 1) = 1 // log(2) = 1
\$clog2(2 + 1) = 2 // log(3) = 1.???
\$clog2(3 + 1) = 2 // log(4) = 2
\$clog2(4 + 1) = 3 // log(5) = 2.???
\$clog2(5 + 1) = 3 // log(6) = 2.???
\$clog2(6 + 1) = 3 // log(7) = 2.???
\$clog2(7 + 1) = 3 // log(8) = 3
\$clog2(8 + 1) = 4 // log(9) = 3.???
上記の例は信号の最大値 MAX_VALをパラメータにして必要バス幅を求めたが、メモリのワード数や配列の要素数からアドレスに必要なビット幅を求めるはどうするか考えてみよう。ワード数が8のメモリ或いは要素数が8個の配列の場合、アドレスは0~7の8個で
parameter MEM_DEPTH = 8;
parameter ADD_WIDTH = $clog2(MEM_DEPTH); // 3
logic[ADD_WIDTH - 1 : 0] addr;
logic[31:0] mem[MEM_DEPTH];
となる。この場合にはMEM_DEPTHが他のどんな値でも正しい。なぜならアドレスが最大値7の0~7という8種類の値を取ると言うのをパラメータ MEM_DEPTHで使用したからである。つまりMEM_DEPTH = MAX_VAL + 1と最初から+1されてるからである。
要するに
\$clog2()を使って必要ビット幅を算出する場合、最大値で考えた場合+1が必要、個数で考えた場合はそのままの値でOK
となる。
「\$clog2()を使えば必要ビット幅が分かるんだ!」と慌てて飛び付くとこのような落とし穴にはまってしまうので十分気を付けてほしい。