Synthesijer と高位合成ツールの作り方 (1)

みなさんこんにちは。この「Synthesijer と高位合成ツールの作り方」のシリーズでは、全5回を通じて Synthesijer をベースに FPGA 向けの簡単な高位合成処理系を作る方法を紹介していきます。例は Java ですが、お気に入りの言語向けの処理系を開発する足がかりとして利用できるように書いていくもりです。

第1回の今回は、高位合成ツールとは何かについて説明し、また、このシリーズのベースとする Synthesijer を紹介します。

高位合成ツールとは?

FPGA を利用する時、多くの場合 VHDL や Verilog、SystemVerilog など RTL でハードウェア・ロジックを設計します。高位合成ツールは、RTL よりも抽象度の高いレイヤで設計されたロジックを FPGA 上に実装できるように変換するツールのことです。

抽象度の高さに絶対的な基準はありませんが、C や C++ を使ってロジックを設計できるようにするツールは高位合成ツールと呼ばれています。

他にもよく知られた既存のソフトウェア言語で書かれたプログラムをロジックに変換する高位合成ツールや、独自に設計された言語を用いるツールなどたくさんの種類があります。

高位合成ツールの特徴

RTL での設計よりも高い抽象度で設計できる高位合成ツールの多くは、次のような特徴を持っています。

明示的にクロックを管理しない

RTL での設計では常にクロックを意識する必要があります。単純に順序よく演算を実行したいという場合でもクロックに応じて有効な処理が実行できるようなステートマシンを作る必要があります。また、データ処理のロジックが大きくなったら遅延を考慮して複数クロックに分割したり、複数のロジック同士の処理を待ち合わせるために遅延を入れたり、と、設計に手間がかかります。

高位合成ツールを用いた設計では、基本的には明示的にクロックを意識する必要がありません。ソフトウェアプログラミングのように、言語のルールに従ってデータを処理するステートマシンが構築されます。また、連続する演算処理を回路規模を考慮して適切なところにレジスタを挟んだり、複数の演算が合流するタイミングで遅延の調整をしたりします。

一方で、高位合成ツールを使用しても明示的にクロック毎の動作を書きたいという場合もあります。そのため、必要に応じてクロック毎の動作を記述できるツールもあり、制御ロジックの実装にうまく活用されています。

生成するアーキテクチャを設定できる

RTL 設計では、「やりたい処理をどのようなロジックで実装するか」を設計者自身が具体的に記述する必要があります。処理に必要な演算をどう並べるか、どのような順序で演算結果を有効にするか、データをレジスタに置くのかメモリに置くのか、メモリのポート数をいくつにするのか、コンポーネント間のデータ授受のインターフェースをどうするか、などといった様々なことを設計者自身が具体的に RTL で実装しなければなりません。

一方で、高位合成ツールを用いた設計では、RTL より高い抽象度で設計者が記述した「やりたい処理」からツールが「具体的な実装」を生成します。

設計者が具体的な実装を作り込む必要がないため、ツールが幾つかの実装方法から最適なものを探索できます。たとえば、ターゲットの周波数を決めてそれに合うような実装を探索させる、といったことができます。

時には (あるいは多くの場合)、ツールが設計者の意図を汲んだ実装を生成できるとは限りません。そのため、多くのツールでは、ループ処理を書いた後で同時に実行するループ内の処理の数や、メモリポート数などを設計者が指示できるようになっています。

ソフトウェアとして実行

C、C++、Java、C#、あるいは、Python といった既存のソフトウェアプログラミングで利用される言語をベースとした高位合成ツールでは、記述した処理をソフトウェアとして実行することができます。ソフトウェアでアルゴリズムレベルの動作確認をすることで開発時間を短縮できます。

ツールによっては、ソフトウェアとして動作確認をするときに用いたテスト用のソフトウェアを、そのままハードウェア用のテストベンチとして利用できる機能を持つものもあります。

高位合成ツールの種類

高位合成ツールの研究開発の歴史は長く、古今東西たくさんの処理系が実装されてきました。ここでは、そのほんの一部を紹介します。

汎用高位合成ツール

一般のソフトウェア用のプログラミング言語と同様に、特に用途を限定しないツールが、汎用の高位合成ツールです。既存のソフトウェアあるいや独自の言語を入力とする処理系があります。

既存のソフトウェア言語を入力とする高位合成ツールには、

などがあります。特に、FPGA ベンダが無償で提供している Vivado HLS と Intel HLS は広く使われています。

既存のソフトウェア言語を入力とするメリットの一つは学習コストを低く抑えられることです。ツールが入力とするプログラミング言語を修得している人であれば、新たな言語を覚えることなくハードウェア設計ができる、というのが、既存の言語を利用する高位合成ツールのうたい文句です。

一方で、既存のソフトウェア言語はプロセッサ上で実行されるプログラムの記述を想定しているため、ハードウェアならではの最適な設計を目指す場合に不向きな点もあります。そのため、最適なアーキテクチャをツールに生成させるために沢山の指示が必要になったり、ソフトウェアとしては不自然な記述を強いられることもあります。

はじめからハードウェア設計を考慮した言語を設計すれば、この問題を回避できます。独自の言語を入力とする高位合成ツールには、BluespecKaruta/Iroha があります。

特定用途向け高位合成ツール

ソフトウェアプログラミングでも、画像処理やパケット処理、最近だと AI 向けの処理など、特定のアプリケーションの記述を容易にし、また高い性能を実現するためのドメイン特化言語があります。同じように、特定の処理をロジックとして実装したいというモチベーションに合わせた、特定用途向け高位合成ツールがあります。

信号処理向けのロジックを Matlab から生成するツール (HDL Coder) や、画像処理を意識して設計された DSL の Halide で書かれた処理を FPGA 向けのハードウェアに変換するツール (Halide2FPGAHalide-HLS) 、パケット処理記述向けの P4 をハードウェアに変換するツール (Netcope P4 など)、また、音声ストリームを扱うのに特化した sigboost などがあります。

ソフトウェア由来の特定用途向け高位合成ツールだけがではなくハードウェアならではの特定用途向け高位合成ツールもあります。たとえば、データの場所を意識したロジック設計を容易にする Spatial や、制御レジスタの RTL やシミュレーション用モデルを自動生成する RgGen 、パイプライン演算ストリームを作るための FloPoCoSPGen などがあります。

最近の流行の一つ・HCL

高位合成ツールとは少し違いますが、RTL での設計コストを削減するための HCL (Hardware Construction Language; ハードウェア構成言語) にもたくさんの種類があります。これらは、ソフトウェア視点で見ると、ハードウェア設計というドメインに特化した DSL です。

HCL では、ロジックの設計レベルは RTL ですが、たとえば、繰り返し少しずつパラメタの違うロジックを生成する、とか、レインテンシの異なる複数の演算器をパイプライン動作させるために遅延を調整する、といった繁雑な処理をプログラムで記述できます。

高位合成ツールが抽象度の高い言語からハードウェアロジックを生成するのに対して、HCL は抽象度の高い言語を使ってハードウェアロジックを組み立てるツールです。

たとえば、MyHDLChiselVeriloggenVerugent などがあります。

高位合成ツールの動作

次の図は典型的な高位合成ツールの内部の動作を一枚にまとめたものです (参考: High-Level Synthesis)。

最初に、入力言語の字句解析・構文解析をおこなってデータフローグラフを作成します。次に、データフローグラフを解析して演算器の利用スケジューリングを割り当てます。この図では、+ と * を一つずつ持つハードウェアロジックを想定して利用スケジューリングを割り当てています。スケジューリング結果を元に、データを保存するレジスタを挿入したデータパスを生成し、演算を駆動するための状態遷移を決定します。最終的に対象のロジックデバイス (たとえば FPGA) 上のリソースの割当である物理マッピングを生成してツールの処理はおしまいです。

もちろん、ここで示したスケジューリング以降の結果はこれに限ったものではありません。要求や仕様に応じて、よりよいハードウェア・ロジックを生成するのが高位合成ツールのがんばりどころです。

出力

一般に、高位合成ツールから FPGA 向けのビットストリームを直接生成することは無いようです。HDL やネットリストあるいは他の高位合成ツール用の入力言語を出力します。Xilinx や Intel のツールで取り込みやすいような IP コアとして生成物をアーカイブして出力するツールもあります。

+αの機能

高位合成ツールで本格的に大規模な処理を実装しようとすると、ループや条件分岐のような制御構造や、まとまったデータを扱うための配列が欲しくなります。また、ソフトウェアプログラミングで関数やクラスを作るように、処理を意味のあるまとまりごとに分割したくもなります。

高位合成ツールでは、それらへの対応が必要になります。ここに、いかに既存のプログラミング言語から自然にロジックを生成するか、あるいは、ロジックが生成しやすい言語を設計するか、の面白さがあります。

高速化にはループが必須!?

C/C++ のような主に逐次処理のソフトウェアを記述するプログラミング言語の入力からハードウェア・ロジックを生成する高位合成ツールの場合、主な最適化対象となり得るのがループです。ループは繰り返し実行されるのでこれを高速化できれば全体の処理時間を短くすることができます。また、ループ処理をパイプライン化できれば、ループの繰り返し回数の分の高速化ができます。

ループをどのように効率よく動作するロジックに変換するかについては、High-Level Synthesis Blue Book に詳しく書いてあります。高位合成ツールを作ってみたいかな?という人だけではなく、今後、高位合成ツールを使っていこうという人も一度は目を通しておくとよいでしょう。高位合成ツールの気持ちを理解して、高位合成ツールが扱いやすいようなループを記述できるようになります。

開発コストを下げる、とは

高位合成処理系を導入するメリットは、高い抽象度で設計することで開発コストを下げることです。開発コストを下げる、というのは基本的には、適切なロジックを短い時間で実装することです。と、一言でまとめることができれば単純なのですが、状況はもう少し複雑です。時には、比較対象 (たとえばソフトウェア実行) と比べて高い性能が出る必要がある場合もあるでしょうし、時には、ある程度大きくて遅い回路でも動けばいいから開発時間を極端に短かくしたい、ということもあるでしょう。

もちろん、学習コストが小さいという意味では既存のコードを入力として受け付けられる方がいいかもしれませんが、長い目でみると新しい言語を覚えた方が短い時間で高品質のロジックを生成できて開発コストが下がるかもしれません。

現時点では、高位合成ツールには選択肢があまり多くありません。いろんな人がいろんな視点で高位合成ツールを開発し、用途に応じて取捨選択できるような未来がくるといいですね。

Synthesijer とは?

Synthesijer は、筆者が開発している Java で記述されたプログラムをハードウェアロジックに変換する高位合成ツールです。ロジックに変換できるプログラムは、ソフトウェアとして実行できますので、アルゴリズムレベルのデバッグはソフトウェアとして実行することでデバッグサイクルを短くできます。

2020年6月時点で、特筆できるような最適化は実装していませんが、Java のオブジェクト指向を活用したロジック設計ができて、既存の RTL モジュールを Java のインタンスとして取り込むことができるのでグルーロジックの実装が容易にできます。

また、Java の Thread を並行動作するロジックに見立てて合成するため、インスタンスレベルで並列動作するモジュールを自然な形で並行動作するロジックとして実装できます。

RTL で実装するにはちょっと煩雑になりそうだけどプロセッサ (ソフトコア) を持ち出すのはちょっと面倒というような SoC のコントローラなどに便利に利用できます。

Synthesijer のインストール

Synthesijer は、GitHub で Apache ライセンスの元に公開されているオープンソースソフトウェアです。ビルド済みバイナリをここからダウンロードするか、Ubuntu など Snap の使える環境では Snap を使ってインストールすることで利用できます。なお、Synthesijer を使うには JDK11 が必要です。また、リソース一式を自分でビルドする場合には sbt が必要です。

以下、典型的なセットアップ方法を紹介します。

Windows ユーザの場合

あらかじめ JDK11 をインストールし、DOSプロンプトから実行できるようにしておいてください。たとえば、JDK11 からダウンロードしたバイナリを C:¥jdk-11 に展開、次のスクリーンショットのように環境変数の PATH をセットします。

DOS プロンプトで java コマンドが実行できれば OK です。

ホームディレクトリの下に Synthesijer というフォルダを作成し、ダウンロードページ から実行バイナリ (2020年6月時点では synthesijer_20191116.bat) をダウンロード、synthesijer.bat という名前でコピーします。コピーしたファイルを実行するとヘルプが表示されます。

c:¥User¥miyo¥Synthesijer> synthesijer

Synthesijer: 3.1.0
...

Linux ユーザで Snap を使う場合

次のように、snap コマンドでインストールできます。

$ snap install synthesijer
$ synthesijer

Synthesijer: 3.1.0
...

Linux ユーザで Snap を使わない場合や Mac ユーザー

あらかじめ、JDK11 をインストールして実行できるようにしておいてください。ホームディレクトリの下に Synthesijer というフォルダを作成し、ダウンロードページから実行バイナリ (2020年6月時点では synthesijer_20191116 ) をダウンロードし、synthesijer という名前で保存します。実行権限を付与すれば利用できるようにになります。

$ mkdir -p $HOME/Sytnehsijer
$ wget -O synthesijer http://synthesijer.github.io/web/dl/20191116/synthesijer_20191116
$ chmod 755 synthesijer
$ ./synthesijer

Synthesijer: 3.1.0
...

$ export PATH=$HOME/Synthesijer:$PATH などとしてパスに追加しておけば、いつでも利用できるようになります。

Synthesijer の動作確認

以下の簡単な Java プログラムをホームディレクトリに作成した Synthesijer ディレクトリの下に Test.java という名前で保存します。

public class Test{
    public boolean flag;
    private int count;

    public void run(){
        while(true){
            count++;
            if(count > 5000000){
                count = 0;
                flag = !flag;
            }
        }
    }
}

以下のようにして Test.java をコンパイルします。

synthesijer --vhdl --verilog Test.java

これで、Test.java から VHDL ファイル Test.vhd と Verilog ファイル Test.v が 生成されます。

できあがった VHDL や Verilog ファイルを FPGA ベンダのツールを使って合成・配置配線すると FPGA 上で動作させることができます。また、RTL シミュレータを使えば動作の確認ができます。

Synthesijer のサンプルと応用事例

ダウンロードページからサンプル (2020年6月時点では synthesijer_sample_20191116.zip) をダウンロードできます。詳細は次回以降で説明します。

また、高位合成ツール「Synthesijer」を使うに Synthesijer を利用した面白い応用事例や、Fluentd 対応 MIDI キーボードを作ってみた、AHCI の実装 (e7achi_sample) などもあります。

HCL としての Synthesijer.Scala

Synthesijer は Java プログラムを内部の変換処理に従って RTL に組み立てます。この RTL に組み立てるための Java プログラムを Scala で操作できるようにしたものがSynthesijer.Scala です。

Synthesijer の RTL ビルディグブロックを Synthesijer.Scala では Scala の言語機能を使って組み立てることができます。複雑なロジックでも Scala を使って簡潔に記述したり、ビルディングブロック組み立てて作ったロジックを再利用したりできます。

まとめ

この記事では、高位合成ツールについて説明しました。また、この連載でベースとする Synthesijer を紹介しました。

Synthesijer は Java で書かれたプログラムをハードウェア・ロジックに変換することができます。次回は、Synthesijer のコンセプトおよび動作の詳細と Javaプログラムからハードウェア・ロジックに変換する処理のおおまかな流れを紹介します。

わさらぼ みよしたけふみ

タイトルとURLをコピーしました