AIエッジコンテスト(実装コンテスト)のチュートリアル【4: FPGAに推論回路を設計するには】

前回まではUltra96のPS部分(ARMプロセッサ)を使った推論を実行するまでの手順を紹介しました。いよいよ、ハードウェア推論回路を実装してみましょう。
…といきたいところなのですが、最近のFPGAは複雑になりすぎて（しかも今回はGoogle ColabとかPyTorchとか、機械学習までやらないといけないんです。。）初心者の参入敷居が高くなっていますので、まずは何をしないといけないのか解説して、次回から設計に取り掛かりましょう。

### ハードウェアを使う意義
パレートの法則(2:8の法則ともいいます)によると、全体の大部分はごくわずかな部分が影響しているのですが、計算もほぼその法則が当てはまると言われています。つまり、今回の推論処理の大部分はわずかな部分、具体的に言えば畳み込み(Convolution)演算が９割以上を占めている、という事実です。

そこで、その部分だけを専用ハードウェアで処理することを目標にします。下図をイメージしてください。

<img width="547" alt="スクリーンショット 2020-02-27 15.34.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589480/638bd9f2-cb6f-aa59-b3f1-2a39743444d8.png">

もちろん、全てハードウェア化してもいいんですけど、たった１割未満のものを高速化するのに、１０倍以上と言われている（今回の連載の回数がそれを端的に表していますね）ハードウェア設計に時間をかけるのはコストが悪いです。コンテストの締め切りもありますし。限られた設計時間で最大の性能を引き出すために、ハード・ソフト協調システムを採用することが多くなってきました。そこで、多くのFPGAもARMプロセッサを搭載、またはPCI接続しているんですよね。

もう少し上の図を眺めていると、ハードソフト協調実行するには、

+ ホストPSがDDRメモリを読み出す
+ PLにデータを転送する
+ PLがデータを処理する
+ PSにデータを戻す

という手順で処理が行われます。PSとPLで処理を分担するには<font color="red">データ通信</font>という避けて通れない問題があって、多くのAIエッジコンピュータはこの問題を解決するために工夫されています。データ通信をなんとかしないと、ハードウェアを使う意義がないとも言えます。大変なんです。。

<img width="520" alt="スクリーンショット 2020-02-27 15.35.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589480/f1f13c93-5eda-1283-12b0-4be2c7c1a8ca.png">

### Ultra96での処理(主にデータ転送)
Ultra96での処理を上図に詳しく書いてみました。２：８の法則に従って重たい処理（＝高速化したい処理）を見つけます。大抵の場合は<font color="red">プロファイル解析</font>を行います。そして、PSとPLで行う処理を決めます。
そうすると、上図のように、ソフト（PS）側とハード（PL）側でやりとりするデータ（入力値、出力値）をそれを跨ぐ境界というデータの流れが出来上がります。今回コンテストで使うFPGAにはARMプロセッサで使われているAXIバスが搭載されており、PSとPLはAXIバスのプロトコルを使ってデータをやりとりします。Xilinx社のFPGAは３つのAXIバス通信プロトコルを使うことができます。

+ AXI4 Lite
+ AXI4 Streaming
+ AXI4 Scatter Gather (今回は説明しません)

<img width="371" alt="スクリーンショット 2020-02-27 15.35.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589480/7b235dde-3071-c3af-e04e-0922c1149db2.png">

まずはAXI4 Liteプロトコルです。Liteというので、処理を記述するのは単純です。ただし非常に遅いです。。上図に示すように、たった一つのデータを送るために、PSがDRAMにデータを取りに行って（そのためにDRAMにコマンド発行して、それを待って、数サイクルかけて読み出して、途中でデータリフレッシュとか入って、バンクを切り替えて、、、）、それからAXIプロトコルの準備（これもコマンド発行とか待ちとか基準があってややこしい）をして、ようやくデータを送ります。データ読み出しも同様です。。

<img width="365" alt="スクリーンショット 2020-02-27 15.35.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589480/49b3e32d-de96-1de2-2006-843241d65475.png">

そこで、AXI4 Streamingプロトコルを使います。上図をみてください。AXI4 Streamingとは名前の通り、連続データ転送を行うプロトコルで、PSがDMAに転送発行すれば連続でデータがPLに送られます。通常はFPGAのオンチップメモリ(BRAM: Block RAM)に転送しておき、PL上に設計したハードウェアで処理を行い、再度ホストPSのDRAMに転送する仕組みです。<font color="red">連続アドレス</font>へのアクセスであれば、性能を十分に発揮できます(連続アドレスでない場合はScatter Gatherもありますが、今回は触れません)。

<img width="682" alt="スクリーンショット 2020-02-27 15.50.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589480/547244db-d6e8-b04d-a0bd-af3a4a1064af.png">

### 設計フロー
以上を踏まえて、今回解説する設計フローを上図に書きました。Xilinx社の高位合成ツールであるVivado HLSを使用して世界に１つだけのオリジナルIPコアを合成します。そのコアにはホストPSから転送されてきたデータをストアするオンチップメモリ、処理を行う回路、及び、AXI4 Streaming通信回路が必要ですが、Vivado HLSを使うことで自動生成できてしまします！高位合成を使うメリットの一つですね。
また、設計したIPコアが正しく動作するか検証するためのデータ（<font color="red">テストベンチ</font>と言います）、はGoogle Colabで学習したモデルから生成して使いましょう。

<img width="674" alt="スクリーンショット 2020-02-27 15.50.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589480/1bf361d1-38ca-1d2a-6757-853b40bdfb5f.png">

オリジナルIPを作った後、PS(ARMコア、DMA, DRAMなど)を含めてハードウェア全体を統合し、合成します。Xilinx社のVivadoというツール（Vivado HLSではありません！）を使います。その後、PYNQをインストールしたソフトウェアシステム上にオリジナルIPの制御を含めた処理前半をPythonで記述します。ここ直近のDeep Learningフレームワークはほぼ全てPythonベースなので、そのままシームレスに統合できるので、とても便利になりました。もちろん、PYNQはUbuntu18.04 LTSに組み込まれているフレームワークなので、Ubuntuの資産をそのまま使うことも可能です。例えば、apt-getで様々なソフトウェアを組み込むことも可能であり、scikit-learnとかOpenCVも簡単にインストールできます。

### 次回は
ハードウェア設計といいましたが、その事前準備である検証用のテストベンチを生成しましょう。PyTorchを直接ハックして内部のデータにアクセスします。テストベンチだけで１回分ですから、ハードウェア設計ってやっぱり手間と時間がかかるんですよねぇ。。。