Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
7
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

$clog2によるビット幅算出のよくある間違い

\$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()を使えば必要ビット幅が分かるんだ!」と慌てて飛び付くとこのような落とし穴にはまってしまうので十分気を付けてほしい。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
7
Help us understand the problem. What are the problem?