浮動小数点演算器をFPGA実装した場合の回路リソースと誤差の測定


## FPGAへの固定小数点実装の課題
[FPGA](https://jp.mathworks.com/discovery/fpga.html)に除算、Exp、Log、積分（フィードバック演算）などを固定小数点演算で実装しようとすると、ダイナミックレンジが非常に大きいためオーバーフローの危険性と演算精度のトレードオフに悩むことになり、実装がとても厄介です。

そんなダイナミックレンジが広いアルゴリズムの実装は浮動小数点で実装してしまえば、オーバーフローのことで悩む必要が無くなり、設計時間を短縮することが出来そうです。

## FPGAへの浮動小数点実装

Intel(Altera)のデバイスArria10には浮動小数点DSP（Hard Floating Point）が搭載されており、IntelのマクロALTERA FP Functionを使って、浮動小数点演算を少ない回路リソースで実現できます。

もう一方で、MATLAB/SimulinkのオプションHDL Coderを使えば、ターゲットデバイス依存しない浮動小数点のHDLコード（こちらはNative Floating Pointと呼んでいるよう。）を手軽に生成できるようです。


そこでALTERA FP FunctionとNative Floating Pointでの各種演算器の実装時における回路リソースの消費および浮動小数点演算における誤差を比較してみました。

## コード生成およびコンパイル
IntelのALTERA FP Function（以降AFP）もNative Floating Point（以降NFP）も、下図のようなSimulinkモデルを作成し、HDL Coderで自動生成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/f70a869b-8123-d9ae-5127-7e607f3b6ddf.png)

サブシステムには対象となるdouble/single/halfデータ型の乗算、加算、除算、Exp、Logの演算ブロックが1つだけ入っており、自動的に順にコード生成とコンパイルを行って、結果を取得します。

様々なパラメータで自動コード生成～コンパイルする方法については[こちら](https://qiita.com/SacredTubes/items/ba32de9a8e69bcb6d249)に詳しく書きましたが、パラメータを設定して実行すれば、各種パラメータでのコンパイル結果が得られます。


## AFP, NFPそれぞれの各種演算器の回路面積
以下が回路リソースの結果です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/497d3b30-754f-0a36-e639-d1879f062bf3.png)

なお、結果が「-」となっているところは機能が対応していないことを示しています。また、half精度はNFPのみで対応しています。
参考データとしてのexp, logの18bitでの実装は、LUTを使用しています。

※ NFPの設定はLatencyStrategy=MIN, FloatingPointTargetConfiguration=Full Multiplyer
※ Pre-routeの結果
※ Target Device = Arria10  10AS066H1F34E1SG

## FPGA実装時の演算誤差の測定方法
浮動小数点データは[IEEE 754](https://ja.wikipedia.org/wiki/%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0)で標準化されていますが、その演算は丸め等により微小な演算誤差が発生する場合があります（ULPエラーと言います）。誤差はフィードバックループなどにより、どんどん蓄積していって大きくなる場合があるので注意が必要です。

FPGA-In-the-Loop[^1]を使用して、PC上のSimulinkのシミュレーションとFPGA実機の間でどの程度の演算誤差があるのかを測定してみました。この機能を使うと、下図のようにSimulinkモデルをFPGAボードと通信させて、演算結果を比較することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/58c8deeb-20b6-163f-5a77-daab91c4e38d.png)

[^1]: FPGA-In-the-Loopは、MATLAB/SimulinkとFPGA実機の等価性検証を行うためのMathWorks社製品の機能名です。

浮動小数点演算の誤差は、通常何%といった大きな誤差は発生せず、LSB側の何ビットにエラーが発生しているかを示すULP（Units in the Last Place）で評価されます。そのため、誤差は以下のコードをMATLAB Functionブロックに書いて求めました。上図内のULP Error ***と書かれたブロックがそれです。

```matlab: 
function ULPErr = fcn(u0, u1)

u0_uint64 = typecast(u0, 'uint64');    % doubleを64bit整数に変換
u1_uint64 = typecast(u1, 'uint64');

ULPErr = nextpow2(u0_uint64 - u1_uint64+1);

```

誤差は下図のようにScope上に桁数で表示されます。図では値が0または1なので、1桁のULPエラーがあることを示しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/bca546c9-bf16-694e-1b6f-41d5471561b8.png)

## FPGA実装時の演算誤差の測定結果
以下が測定結果サマリーと詳細データです。

* 乗算、加算ではAFP, NFPの各データ型全てにおいて誤差はゼロ
* Exp、Logでは1～2桁の誤差
* 除算では、AFP, NFPのdouble, singleの誤差はゼロ
* NFPのhalfでは10桁ほどのULPエラーが検出(※)
* NFPのhalfでHandleDenormalオプション（表ではdenormal onと表記）を有効にすると誤差はゼロ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/825eecd6-4cb9-f91c-5bd4-9afa59b25007.png)

## まとめ
IntelのHard Floating Pointは、その名の通り浮動小数点乗算器を搭載しているので、singleの乗算器の実装は非常に少ない回路リソースで実現できることに驚きました。

ただ、double型で乗算器を実装しようと思うと、AFPはNFPよりは効率が高いものの、それなりにロジックもDSPも消費します。

Divide, Exp, Logなどの算術演算は、AFPはNFPに比べてLUTやRegisterの消費は少ない一方、MLABやBlock Memoryをかなり消費します。回路面積の観点だけでは単純にどちらが良いというのは難しいと思いました。ULPエラーも若干異なるので、誤差の観点でも評価は必要だと思います。

NFPのhalfの除算では、HandleDenormalオプションを有効にしないと10桁もの誤差が発生することがありるため、注意が必要です。



おわり