So-net無料ブログ作成

VHDLで飽和演算 [IPコア]

前にVHDLのリダクション演算を書いたので、それを使った飽和演算処理を紹介。
HDLで飽和演算をしたい場面はちょくちょくでてくるものの、完全にパラメタラブルに書くのは以外と面倒くさい。

ざっくりと処理条件を書くとこうなる。
入力データをd、出力データをyとし、それぞれのビット幅をn,m、n>mとする。

(1) 正の最大値への飽和
d[n-1](MSB:符号ビット)が'0'、かつd[n-2:m-1]の全てのビットが'0'でない場合。
y[m-1] <= '0', y[m-2:0] <= 全て'1'

(2) 負の最大値への飽和
d[n-1]が'1'、かつd[n-2:m-1]の全てのビットが1でない場合。
y[m-1] <= '1', y[m-2:0] <= 全て'0'

(3) 飽和なし
y <= d[m-1:0]

VHDLでは比較演算子でオーバーロードされるので、任意長ベクタ型がゼロかどうかの比較は簡単にできるものの、即値で任意長のビット集合を生成するのにかなり鬱陶しいことをやらないといけない。
故に比較するビット幅がパラメタラブルなものは鬼門なのだけど、ここでリダクション演算を使うとすっきり記述できる。
例えば

or_reduce(A) = '0'

とするとAの全てのビットが'0'かどうかを判別できる。同様に、

and_reduce(B) = '1'

とするとBの全てのビットが'1'かどうかを判別できる。
そうすると、まとめてこんな感じで書くことができる。

y <=
  (y'left=>'0',others=>'1') when(d(d'left) = '0' and or_reduce(d(d'left-1 downto y'left)) /= '0') else
  (y'left=>'1',others=>'0') when(d(d'left) = '1' and and_reduce(d(d'left-1 downto y'left)) /= '1') else
  d(y'range);

結構長ったらしい記述だけども、注目すべきはマジックナンバーが出てこない点。つまり、これはdとyのビット長を自動的に参照して飽和演算回路を生成してくれる。
こんな感じてVHDLのアトリビュート演算子はめっちゃ便利だから皆使おう。


ちなみに飽和演算のキモは出力部以外のビットが符号値のみで構成されているかをどう判断するかである。
なので飽和条件は以下のようにもうすこし詰めることができる。

(1) d[n-1:m-1]の全てのビットが同値の場合は飽和無し
y <= d[m-1:0]

(2) それ以外の場合は符号ビットから最大値を生成
y[m-1] <= d[n-1], y[m-2:0] <= not d[n-1]

任意長ベクタの全ビット値が同じかどうかは、リダクションORとリダクションANDの一致をとれば判別できるので

y <=
  d(y'range) when(or_reduce(d(d'left downto y'left)) = and_reduce(d(d'left downto y'left)) else
  (y'left=>d(d'left),others=>not d(d'left)); 

と書ける。こちらの方がソースリストはすっきりする反面、比較条件のパスが複雑になるので必ずしもロジックが減るわけではない、という点には注意。