Chiselでの非同期リセットについての現状整理、RocketChip事情を添えて

2016年8月23日火曜日

同期リセットと非同期リセット

HDLから回路の初期状態を設定するための主要な方法として、同期リセットと非同期リセットがあります。
これらの差について詳しくは述べませんが、FPGAの文脈ではXilinxが同期リセットを推奨し、Alteraが非同期リセットを推奨しているという事情があります*1
Chiselで回路を組むうえで、(特にASICの)レジスタ初期化に非同期リセットを表現する仕組みが欲しいと感じたことがある方も多いでしょう。今回はChiselの処理トリガ実装を簡単に紹介したうえで、非同期リセットに関する現状と議論を整理します。
[*1] 同期リセットと非同期リセットでの配置配線結果差については@marsee101さんのFPGAの部屋 FPGAの非同期リセットと同期リセットの比較シリーズが詳しいです。

Chisel 2.2.35時点の処理トリガ実装

Chiselの最終出力がVerilog HDLである以上、Chiselが持つ表現力の限界はVerilog HDLコードのエミッタ部分を読めばわかるはずです。
Chisel 2.2.35に含まれる、指定信号のエッジにあわせた処理をおこなうためのVerilog HDLコード生成部分は次のとおりです(https://github.com/ucb-bar/chisel/blob/7dd686d968/src/main/scala/Verilog.scala#L531-L536を抜粋*2)。
  for ((clock, dom) <- clkDomains ; if !dom.isEmpty) {
    if (res.isEmpty) res.append("\n")
    res.append("  always @(posedge " + emitRef(clock) + ") begin\n")
    res.append(dom)
    res.append("  end\n")
  }
各クロックドメインにロジックが含まれていれば、クロックの立ち上がりエッジにあわせてクロックドメインのコードを書き出すという具合ですね。
Chiselのソースコード上でposedgeというキーワードが登場するのは、この部分とVerilog HDL用のキーワード列挙部分のみです。ちなみにposedgeと対をなすnegedgeは、テストハーネスコードの生成部分(と、posedge同様Verilog HDL用のキーワード列挙部分)でしか登場しません。
つまり、ChiselのVerilog HDLコード生成系はposedgeでの駆動、しかも個別のクロック信号に基づく処理を記述する能力しかありません。
このため、Chiselで組んだ回路から次のようなVerilog HDLコードを直接生成することはできません。
always @(posedge clk or negedge RST) begin
...
end
BlackBoxを利用してゴリ押せば別ですが、BlackBox内のコードは当然Chiselの言語仕様もランタイムも一切文句をつけてくれない(=守ってくれない)エリアです。JavaScriptのevalと同程度には気を配って使うべきでしょう。
[*2] Chiselのコードはhttps://github.com/ucb-bar/chisel/blob/7dd686d96/src/main/scala/Verilog.scala#L1-L29に記載のライセンスに基づいて利用しています

Chiselでの非同期リセットサポートに関するissuesと議論

非同期リセット不要論

ChiselのMLアーカイブにAsync reset in generated Verilog always blocksというスレッドがあります(2015/02/17-2015/03/18)。
マクロを使えばなんとかなるよ!というたくましい解決方法などが提案されていますが、原則的にChisel側で非同期リセットをサポートするつもりはないという結論です。当時UCB内での議論はhttps://groups.google.com/d/msg/chisel-users/4cc4SyB5mk8/AVSI-GLjrmUJに集約されていたようです*3
この半年ほど後にGitHubのissueとしても似た話がSupport for asynchronous resets in Verilog backend #549(2015/09/27-引き続きopen)として出ていました。
[*3] これが公式見解かというと微妙なところですが、少なくとも返信しているのはUCBの博士課程(Architecture Researchではない?)に在籍されていた方なので、学内主流の意見を取り上げて投稿しているものと解釈しました

2016年8月の急展開

非同期リセットのサポートはChisel 2で決着がつきませんでしたが、Chisel 3のバックエンドであるFIRRTLのSupport for asynchronously-resettable registers #219というissue(2016/08/05-)によってサポートの見通しが立ちました。
事態が動くきっかけになったのはRocketChipのAdd asynchronous FIFOs as an option for clock domain crossings. #190というpull-reqです。これは、RocketChip内で利用している回路間接続ユーティリティであるjunctionsライブラリ(https://github.com/ucb-bar/junctions)をRocketChip側で独自に拡張してFIFO機能を持たせようというものです。
このpull-reqへレビュアーとしてアサインされた方が「すでにjunctions側でこの実装を書いていたけれど、MemとSeqMemをふたつのクロックドメインから安全に利用できない限りマージすべきでないためしなかった。同じ理由でこっちもダメ」と突っぱねたことで流れが変わってきました。科学と魔法が交差した瞬間ですね。
この事情説明に続いて非同期リセットが必要という話が展開され、同氏の「SiFiveの人たちに、とにかく全リセットを非同期にすべきとうるさく言っているけれど、今のところ効果なし」というコメントがダメ押しとなってついに前述のfirrtl#219へと至りました。enhancement扱いなので優先度はさほど高くありませんが、これまでよりもかなり前進しました。
なお、時系列がややこしいのですが、前述のRocketChipへのpull-reqと前後して、当初Gitのsubmoduleとして取り込まれていたjunctionsはRocketChip側リポジトリの中にソースコードレベルで取り込まれるようになりました。さらに、2016/08/20のコミットで置き場所がhttps://github.com/ucb-bar/rocket-chip/tree/7b20609d4/src/main/scala/junctionsへと移動しました。pull-reqやコード本体を追う際には注意しましょう。

進むChisel 3、置いていかれるChisel 2

FIRRTL/Chisel 3で非同期リセットをサポートする方針になったのは良いのですが、現在主流のChisel 2はどうなるのでしょうか。
Chisel 2に対する各種の修正/変更は最近あまり優先度高くおこなわれない状況にあります。
たとえば、Chisel 2で複数クロックドメインを生成して、それらの間でデータのやり取りをおこなうコードを書いた場合、C++バックエンドを生成するとErro in C++ backend : (error: ‘T54’ was not declared in this scope) #464のようなエラーへ頻繁に行き当たります。要はC++コード生成部分で中間状態(wireと同等のもの)のスコープを適切に処理できておらず、暗黙のクロックに依存するコードが生成されるためですが、このissueは長く放置されています。
Chisel 2の問題に対する取り組み指針を伺わせる別の例としては、Compilation Error when assigning from other clock #696というクロックドメインまたぎに関するissueへのコメントが挙げられます。
このなかに、
Then again, it may be worth punting on this issue (to chisel3). A clock domain check is particularly amenable to a FIRRTL pass and, further, it is a FIRRTL pass that users may want to customize.
という発言があります。不要不急の変更をChisel 2に加えるのはなるべく避け、問題解決はFIRRTL/Chisel 3でおこないたいということですね。
このようにChiselプロジェクトの進行を見ていると、全体的に「はやくFIRRTLベースのChisel 3へ移行して、つぎはぎだらけのC++/Verilog HDL生成系から解放されたい。つらい苦しい」という雰囲気が出ています。

結論

Chisel 3では非同期リセットがサポートされる見込みです。非同期リセットを書きたい用途ではChisel 3への移行をそろそろ考え始めたほうがいい(今すぐ非同期リセットを使えるとは言っていない)です。どうしてもすぐに使いたければ、yet another Scalaでデジタル回路を書く仕組み: Spinal HDLを参考にChiselインスパイア系言語であるSpinal HDLを試してみても良いかもしれません。

0 件のコメント:

コメントを投稿