デジタル信号処理の固定小数点設計方法

デジタル信号処理の固定小数点設計方法

 デジタル信号処理を設計したいのだけど、どうすれば良いのかわからない。という相談を受けることがあります。FPGAにADC(ADコンバータ)を接続してFPGAに取り込む設計はできるけど、フィルターの設計ができない。という話です。よく聞けば、フィルターの事もよく勉強していて、FIRとかIIRも教科書で読んで、設計の方法もツールも使いこなすことができる。FPGAにはメーカーが用意したIPでFIRフィルタがあるので、ツールを使えばフィルタ設計もできる。なのに、実際にFPGAに搭載する時にどうすれば良いのかわからない。というのです。

 デジタル信号処理の教科書には下図のようにADC(ADコンバータ)でアナログ信号をデジタル化(量子化)して、FPGAでフィルタをかけたり、増幅したりして、DAC(DAコンバータ)でアナログ信号に戻す。というブロック図で説明しています。

 実際には上のブロック図にあるようにADCの入力前には回路があります。この回路はAC接続なのか?DC接続なのかを決定する回路です。DC接続とは、その信号のDC値が直接信号に関係している場合に使用します。たとえば、温度データなどです。温度センサはDC値が温度に比例するようになっているので、DCの値そのものが重要になります。

 それに対して、AC接続は信号の振幅が重要な場合に使われます。例えば音声です。音のデータは振幅と周波数が重要なのでDC値はほとんどの場合必要ありません。極端な場合、20Hz以下の周波数はカットして扱います。

 たとえば、アナログ回路で考えた場合、以下のようなDC接続の回路を2倍した時、図では2.5Vの直流値(DC値)がSin信号に重畳したような波形になっていますので、その2.5Vも2倍してしまいます。結果、5Vよりも大きな信号になってしまい、どこかでクリップすることになります。アナログ回路に詳しい方は、単電源のオペアンプを使って2倍の増幅回路を組んだ場合。と考えるとわかりやすいでしょう。

 このようにアナログ回路で考えた場合、だいたいデバイスの電源でクリップすることになります。デジタルの場合には電源という事ではなく、ビット幅でクリップするようになります。考え方は同じです。

 このように処理したい信号の特性によって、アナログ回路の場合には単電源回路なのか、AC接続の2電源回路なのかを使い分ける事になります。当然2電源の場合には正電源と負電源が必要になります。デジタルでも同じで、AC接続の場合には正の値と負の値を扱う事になります。

 デジタル回路でこのように負の数値を扱う時に2の補数という表記方法を使用します。12bitなり16bitをそのまま正の数。負の数。と考えて演算してもFPGAに組み込む事ができます。しかし、整数演算だけではFIRフィルタの係数計算など、面倒になる事が多いので、小数点を扱いたくなります。小数点を扱うにはどこかに小数点を固定して計算する固定小数点と小数点が移動する浮動小数点という考え方があります。FPGAでデジタル回路を設計する際には固定小数点で設計するのが一般的です。

 固定小数点演算で、私が一番多く使うのはQフォーマットによる演算です。一言でいえば-1と1の間に正規化して演算します。
 ※Qフォーマット自体は-1~1以上の範囲で表現する書式です。

 Qフォーマットという言葉はDSPで一般的になりました。TI社のDSPの解説で良く使われているので、深く理解したい方はTI社の資料を検索してみてください。

 Qフォーマットは小数点の正規化の方法という事になります。たとえば、16ビットで-1と1の間に正規化した場合、Q14表記またはQ14フォーマットと呼びます。Q14の”14″というのは小数点以下を14ビットで表現する。という事を意味しています。以下に各ビットの意味付けを図示します。

 実データをデジタルのQ14フォーマットに変換するのは次の式を用います。そしてデータ”flortingdata”は-1~1に正規化した値を使います。

Q14 = 2^14 * flortingdata

 flortingdata=0.1の時、Q14フォーマットでは、計算途中で16384×0.1=1638.4というように小数点が生じます。この場合四捨五入します。つまり1638になります。このように固定小数点計算では誤差を生じます。この誤差は信号処理ではノイズになるので、注意してください。どのように注意するのかといえば、気にならない精度にする。という事に尽きます。

0.1 : Q14 = 16384 × 0.1 -> 1638 = 666(hex) = 0000011001100110(bin)

次に-0.1の場合には、

-0.1 : Q14 = 16384 × (-0.1) -> -1638 = f99a(hex) = 1111100110011010(bin)

MSBが1の時には負の値。0の時には正の値になっています。なぜ、-1~1の間に正規化するかというと、1×1=1だからです。なので乗算したときに整数の数が変化しないのです。加算の時には確かに注意する必要がありますが圧倒的に乗算の計算の方が多いので、-1~1の間の数に正規化して計算すると計算が楽になります。

 たとえば、FIRフィルタでは全部の係数を加算して”1”になるようにしますので、必然的に-1~1に正規化された係数を扱います。ゆえに、ADCで得られたデジタルデータもQフォーマットに一回変換して、FIRフィルタ等の演算を行い。DACに接続する直前でQフォーマットから所定のバイナリデータに戻してやる。という方法を取ります。

 たとえばFIRフィルタや2倍アンプなどをデジタルでFPGAに組み込む場合には上記のように入り口と出口でQフォーマット変換を挿入します。そうすることで、途中の計算ははるかに楽に考える事ができます。

 VerilogでもVHDLでも符号付きの演算をサポートしていますので、宣言時にsignedを付ければ、四則演算が使えます。

assign multi = $signed(indatA) * $signed(indatB); // verilog乗算
assign add = $signed(indatA) + $signed(indatB); // verilog加算
multi <= signed(indatA) * signed(indatB); – VHDL乗算
add <= signed(indatA) + signed(indatB); – VHDL加算

 このように記述すると、乗算になります。乗算はFPGAに専用のDSPが搭載されている事が多く、そのDSPに割り当てられるようにコンパイラが自動的に割り振ってくれます。逆に言えば、DSPに割り当てられるビット数を把握しておいて、その範囲内で演算するようにすれば効率の良い回路が設計できます。具体的には18bitのDSPを搭載したFPGAがあれば、18bit以内の四則演算を行うようにします。19bitや20bitでもコンパイラは設計してくれますが、その阿合、DSPを2段使う事になりますので、DSP消費量は2倍必要になります。

 さて、上記の説明では乗算と加算を紹介しましたが、除算や減算もできます。しかし、除算や減算は回路規模が大きくなることに注意が必要です。これはFPGAに搭載されているDSPは乗算と加算の専用設計になっているからです。どうしても除算や減算が必要な場合もありますので、それは回路規模を犠牲にする(つまり速度が犠牲になる)事を覚悟して設計します。もし、可能であれば、除算は乗算に、減算は加算に変換して設計する事をお勧めします

 加算の計算は次のようになります。

 普通に筆算しているのと同じ方法で考える事ができます。この時、小数点の位置がズレ無い事を確認してください。筆算でも小数点の位置を合わせてから上記のように計算するので同じです。

 次に乗算ですが、これも筆算をするときと同じように考える事ができます。

 どうでしょうか? 桁が多くなっていないでしょうか? 筆算をするときにも同じように計算してから、小数点以下の数を数えていたと思います。この時に-1~1に正規化しておけば、整数の部分を無視することができ、小数点以下の桁だけを考える事ができます。VHDLでは桁数のチェックが厳しいので、上記のように16bitどうしの乗算の場合には一旦32bitで受ける必要があります。その後、整数を上の図のように2bitにして小数点以下の桁数を決めましょう。

 小数点以下の桁数の決め方はVerilogでもVHDLでも同じです。上記の場合小数点以下は28bitになります。このまま、乗算を繰り返すとどんどん小数点以下の桁数が増えてしまいます。よって、通常は「丸め」を行います。10進数で言う所の四捨五入です。2進数なので零捨一入になります。

 「丸め」は10進数のように4以下の場合と5以上の場合に分ける必要はありません。必要な桁の下の桁を繰上げ加算するだけです。乗算前と同じ桁数にする場合には、上記のように13で示したビットを14ビット目に加算します。12ビット目以下はすべて捨てます。

 丸めは誤差になりますので、設計者の考えでどこで丸めをするのか。速度優先ですべて捨てるのか、など方法を考えます。