RTLを語る会(12)でChisel 3/FIRRTL+自作HDL試作のLTをしてきた

2016年9月28日水曜日

9/25(日)に秋葉原界隈でおこなわれた「RTLを語る会(12)」に参加し、ついでにLTをしてきたので補足などをまとめます。

RTLを語る会。

RTLを語る会(12) 〜Vivado HLS GO〜です。参加するのは2度めで、前回「どえらく濃い場に来てしまったぞ・・・」と感じた記憶があります。
今回もVivado HLS(Xilinxの高位合成系)を使い倒して得られた諸々の話が展開されていて「貴重な場だ・・・」という感想でした(KONMAI感)。資料が公開されている分についてはイベント資料一覧で確認できます。一部の資料は公開されていません(これも大変得るものが多いセッションで、現地参加して本当によかったと思いました)。

LTをしてきた

LTとしては長めの15分枠にて、RTLコードを生成するための(alt-)HDLであるChisel HDLの簡単な紹介とその新バージョン(Chisel 3)に関する話をしました。色々と端折りましたが、実際には18分ぐらい喋っていた気がします。

Chiselに関してはこのblogでも既に(告知) C90 3日目西a-05abでScala+Chisel入門記事の載った技術書が出ますChiselで開発を始める小冊子(MFT2016配布物)の小改訂版を100円で販売しますで紹介しているのでここでは触れません。Chisel 3とそのバックエンドであるFIRRTLについてこれまであまり言及していないので書きます。

Chisel 3のアーキテクチャ

Chisel 3はChisel 2とほぼ互換性を保った記法を採用していますが、中身の実装が大きく異なります。代表的な違いは、構造のフロントエンドとバックエンドが切り分けられた点です(図1)。
図1 Chisel 3の実行時アーキテクチャ
Chisel 2は実行時に回路情報に基づいた.v/.cppファイルを自前生成し、.cppの場合はそれをコンパイルして開発環境用のネイティブバイナリを生成します。そして生成されたバイナリをDUTとしてChisel側に用意したテストコードと接続してシミュレーション検証をおこないます。
これに対してChisel 3では、バックエンドがFIRRTLというプロジェクトへ切り出されました。Chisel本体が担うのはFIRRTLの生成と、FIRRTLの先で生成されたバイナリとの通信によるシミュレーション検証作業のみへと軽減されます。
Chisel 2でマルチクロック利用などの込み入ったコードを書くと、回避の面倒なバグを踏むことが多くあります(代表的なのが#696です)。また、Chisel 2から生成した.cppコードがそもそもコンパイルを通らないケースもあります(マルチクロック利用時にcpp側のスコープ切りがおかしいパターンほか)。
Chisel 3+FIRRTLでは.cppコードの生成がChisel内部からFIRRTL側へ移っただけでなく、生成にVerilator*1を利用するようになりました*2
[*1] Verilatorは高速なHDLシミュレータ兼、HDL→C++コードの生成ユーティリティです。10年以上に渡り粛々と開発が進んでいて脱帽する系です
[*2] このためVerilatorのバージョンへの依存が激しい状態にもなっていますが、そのうち落ち着くはずです

LTのネタパート

割と真面目にChisel 2/3とFIRRTLの話をしたところで本題です。
Chisel 3世代では、バギーでメンテしづらいC++コード生成系を切り離せたことで今後のChiselプロジェクトの見通しがよくなることを期待できる一方、Chiselと同列にあたるFIRRTLのフロントエンドを比較的簡単に作れるようになりました。

Chisel 3の基盤を使ってHDL生成用DSLを自作する話

実は最近、Kindleへ突っ込んでおいたFIRRTLの言語仕様書を空いた時間でせっせと読んでいました。
そして、今回は運良く会場へのまとまった移動時間がありました。そこで「FIRRTLのフロントエンドとなる自前DSLを作ってみよう。C#ターゲットで」と思い立ち、作ってみました。
ここでC#を選択したのは自分が使い慣れているのに加えて、IDEが充実しており「プログラマ負荷の低いHDLを構築できる下地がある」可能性があるためです。なおFIRRTLのフロントエンドとしては、FIRRTL言語仕様に従ったコードを生成する機能があれば最低限の要件を満たせます(これに加えてDUTとの通信・テスト機能などを適宜作り込むことになります)。

FIRRTL#

そして作ったのがFIRRTL#という処理系です。.NET向けのクラスとして書いており、「回路内の単一モジュール定義と、モジュールの入出力ポートでAND/XOR処理をできる」ものです。
つまり、次のコードを書くと・・・
using FirrtlSharp;

namespace FirrtlSharpHalfAdder
{
    class HalfAdder : Module
    {
        public HalfAdder()
        {
            var io = new
            {
                a = new UInt(isOut: false),
                b = new UInt(isOut: false),
                s = new UInt(isOut: true),
                c = new UInt(isOut: true)
            };
            this.io = io;

            io.s.Assign(io.a + io.b);
            io.c.Assign(io.a ^ io.b);
        }
    }

    public class AdderGenerator
    {
        public static void Main()
        {
            FirrtlSharp.Generator.GenerateHDL(new HalfAdder());
        }
    }
}
FIRRTL経由で次のようなVerilog HDLコードを生成できます。
...
`ifdef RANDOMIZE_MEM_INIT
`define RANDOMIZE
`endif

module HalfAdder(
  input   clk,
  input   reset,
  input   io_a,
  input   io_b,
  output  io_s,
  output  io_c
);
  assign io_s = io_a & io_b;
  assign io_c = io_a ^ io_b;
endmodule
module TopHalfAdder(
  input   clk,
  input   reset
);
  wire  halfadder_clk;
  wire  halfadder_reset;
  wire  halfadder_io_a;
...

FIRRTL#、おいしいの?

おいしくありません。
今回はC# 6時点の構文でどの程度直感的かつ柔軟なHDL記法を実現できるか、という検討を重視しました。パッと見で厳しそうだな、と思ったところは案の定厳しいものです。
I/OポートのIDEでのコード補完ぐらいは効きます。I/O内の信号宣言時に個別の型指定をおこなうことで、型検証の道を残しつつ簡便な記法を維持することもできました。
これを育てていけば形になりそうにも見えますが、実際は次のように大きな問題が複数あります。
まず致命的なのが、C#を利用している限り<=を信号の受け渡し用演算子として自然な記法で成立させられないことです。これは演算子のオーバーロードでは解決できない問題で、C#が1;のような記述を許さないためです。
無理やり
  if (io.s <= io.a ^ io.b);
のようなif+空ステートメントの組み合わせでコードを記述できますが、この構文を許容するぐらいなら圧倒的にScalaを学んだほうが生産的です。ちなみに=をオーバーライドすることもできないので、io.s = io.a ^ io.b;という微妙に嫌な構文で妥協することも許されません。delegateをうまく利用すればio.s => io.a ^ io.b;といった記法は可能かもしれませんが、許容しがたい記法なので深く検討していません。
また、C#は構文上の制約としてシンボルに指定できる文字種が少ないため、たとえば<==といったメソッドを作るのも無理筋です(さらに、中置記法もないので自然な形には収められません)。
このあたりを満たす言語を作って、そこからC#コードを生成するという手はなくもないのですがFIRRTLの性質を考えると本末転倒です。

FIRRTL#実装の裏側

名前は.NET向け各種バインディングの慣習に従ってFIRRTL#と付けましたが、大したことはやっていません。
firrtl-sharpにてコードを公開しておきます。C# 6時点の構文でどの程度直感的かつ柔軟な記法を実現できるか、という検討に時間を多く使った結果、FIRRTLコード生成部分は見るも無残な感じになりました。これを拡張してもどこにもたどり着きはしないと思います。他言語でFIRRTL対応DSLを組むために最低限必要な要素が何かを知りたい場合には多少参考になるかもしれません。
今回はC# embedded languageとしてのHDL生成系がコンセプトとしてアリか否かを検討し、可能な範囲で試作してみるというのがメイン目的だったので、目的は満たせました。
当然テストハーネスなどは作っていないので、生成されたHDLの自動テストができるわけでもありません。

まとめ

FIRRTLによってHDL生成系を分離できたのはChiselの将来にとって良さそうで、この先割と良いバランスの立ち位置に落ち着きそう。