ラベル SpinalHDL の投稿を表示しています。 すべての投稿を表示
ラベル SpinalHDL の投稿を表示しています。 すべての投稿を表示

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を試してみても良いかもしれません。

yet another Scalaでデジタル回路を書く仕組み: Spinal HDL

2016年8月22日月曜日

先日あれこれと調べている時にSpinal HDLを見つけて気になったので、簡単に調べた結果をまとめておきます。今のところは主に自分用の備忘録というか下調べ状態です*1
[*1] そもそもyet anotherというのは3番手以降に出てきたものについて付けられる修飾子な気がしますね

Spinal HDLとは何か

Spinal HDLは、Dolu1990氏が中心となって開発している、ハードウェア(デジタル回路)設計用のScala組み込み言語(DSL)です。Gitのコミット履歴をみる限りでは2015/01/25に始まったものです*2
[*2] これに続く書き溜め分コミットの内容から、実際には2014年8月に開発を開始し、2015年1月に一旦の区切りまで到達したらしいことがわかります
ハードウェア記述言語(HDL)としての立ち位置はChisel HDLと似ています。いわゆる高位合成系ではなく、HaskellのLavaと近い位置付けのDSL+ライブラリ群です。

Spinal HDLとChisel(Chisel HDL)の関係

Chisel → Spinal HDL

登場順序でいうとSpinal HDLはChiselの後発プロジェクトです。ソースコード考古学的な意味ではChisel(Chisel HDL)の派生物にはあたらず、私が把握している限りでのコード共有はありません。Chiselに触発されて生まれた新実装です。
ライセンスも両者で異なります。Chiselは修正BSD(いわゆる3条項BSD)ライセンスです*3。Spinal HDLはコア部分がLGPL 3、周辺ライブラリがMITライセンスです。
[*3] 完全に蛇足ですが、Chiselを開発しているUC BerkeleyはBSD/修正BSDライセンスの本家本元でもあります
もともとDolu1990氏はChiselを使っていて、行き当たった問題などをissuesとしていくつか登録しています(#207, #213, #265)*4
[*4] issueコメントで2014年3月頃からChiselを学び始めたと書いています

ChiselとSpinal HDLの違い

Spinal HDLのドキュメントには「Chiselとどう違うのか?」について詳細に述べたページがあるので、参照してください。かいつまんで紹介すると、次のような問題を指摘しています。

Chiselはいくつか構文上の問題や下回りの実装に起因する実用上の窮屈さを持つ

  • プログラマがミスを犯しやすい局面で警告を出してくれないパターンがいくつかある
  • 標準で用意されているライブラリが物足りない
  • 生成されるVerilog HDLコードが到底人に読めるものではない
  • クロックドメインを複数持つ回路の実装が大変(リセット信号の扱いが中途半端、非同期リセットを持たないなど)
  • Scalaの型とChisel用の型を暗黙変換すべき
これらに対して、issueやPRという形でおこなわれた改善提案があまり実を結んでいないこと、Chiselプロジェクトの立ち上がりから経過した時間の長さを考えると今後も取り込まれる見込みが薄いことからSpinal HDLを開発するに至ったと記されています。

Spinal HDLの機能と仕様に関するメモ

あまりまとまっていませんが、構文面はChiselとキーワード差程度で似ていて、実際に使うと勝手が異なる雰囲気です。
  • 最終出力先のHDLとしてVerilog HDLだけではなくVHDLもサポートする
  • ChiselでModuleクラスを拡張して回路を組むところでComponentクラスを拡張する
    • 出力先としてVHDLも選択できる事情によると考えられる
  • Bundleの扱いはChiselと似ている
    • 互換性のないBundle同士を<>でつなぐのはコンパイルか検証時に弾かれそう(未確認)
  • 論理回路を組んでいく部分は基本的にChiselと似た構文で書ける
    • 演算時のbit幅拡張仕様は異なる雰囲気(未確認)
  • 回路記述負荷を低減するための構文やライブラリがある程度存在する(後述)
  • ChiselのようなC++コード経由でシミュレータ(DUT)を生成してScalaコードとつないでテスト、の仕組みは持たない*5
[*5] Describe testbench from within Spinalで話題になっていますが、当面はRTL(コードの生成)側へ注力するようです。

Spinal HDLの良さそうな点(主観)

クロックとリセットの扱いが圧倒的にChiselよりもうまい

Spinal HDLは作者自身が前述のChiselに対する不満点で書いている、クロックとリセットの扱いに力を入れています。少なくともドキュメントを読む限りでは、これらは圧倒的にChiselよりも練られています。
  • 特にClockingAreaという、モジュール(Component)の一部を指定クロックによって駆動する仕組みがクール
  • Chiselで最近ようやく導入に向かってる非同期リセットも当然のように存在する
  • クロックドメインまたぎで面倒なメタステーブル現象の回避のために、ChiselのAsyncFifoよりも扱いやすいバッファ(BufferCC)を用意している
詳しくはクロックドメインに関するドキュメントを参照してください。

ステートマシン構築の苦労を減らす仕組みを用意している

Spinal HDLが(Chiselと同様)高位合成系ではない以上、アルゴリズム記述のうえで問題になるのがロジックのステートマシンへの分解です。
Spinal HDLの標準ライブラリには、ステートマシン(FSM)を構築して状態間の遷移記述を省力化するライブラリが含まれています(ステートマシンの説明)。
ここでは以下の要素が登場します。
  • 状態開始時の処理: onEntry
  • 状態中の処理: whenIsActive
  • 状態を抜ける際の処理: onExit
これら3種類の処理と、状態間を遷移するためのgoto呼び出しでロジックを構成します*6。ソフトウェア方面から来ると、キーワードを見た瞬間に「ヒッ、gotoだ!!」となるような気もしますが、つまりは必要悪です。
[*6] 2016/07/27のSFM: Added whenIsInactive.コミットで、なんとinactive時の処理も書けるようになりました。whenIsInactiveでステート間のgotoを発生させたらどうなってしまうのか…。
Chiselでenumを使ってシンプルな状態管理をしていくと、どうにもコードのネストが深くなりがちですが、この記法だとある程度コンパクトなコード群に分解できるのがメリットでしょうか。とはいえ、さすがに30-40ステートに及ぶものを書くならば諦めてC/C++からの高位合成系へ放り込んでBlackBoxとして組み込んだほうが良さそうですが。

せっせとComponentを切らなくともロジックの塊を記述できる仕組みがある

ロジックのまとまりを書いてローカルなスコープで利用したい場面は多いです。Spinal HDLでは、Componentを継承してioを定義せずともlightweightにロジック群を書く仕組みとしてAreaを用意しています(Areaの説明)。
実はすでに紹介した指定クロックドメイン用ロジックを書くためのClockingAreaはこの一種です。
Chiselでインライン定義モジュールを作るためには内部でio変数を定義して入出力接続をおこなう必要があります。Chiselは設計上の判断としてこのような仕組みを用意していない印象ですが、クロージャにどっぷり浸かった民としてはやはり欲しいところです。

標準ライブラリに各種バスが揃っていて脳汁が出る

Spinal HDLのライブラリを覗いてみると、AMBA3(AHB-Lite、APB)、AMBA4(AXI4、AXI4-Lite)にAvalonMMもあります。実際に試してみないと性能のほどはわかりませんが、少なくとも既存のIPと指定バスでつないで実験をするあたりまでは省力で進められることを期待できます。

少人数プロジェクトゆえの小回りの効く開発

StateMachineの「泥臭いけど必要」な設計といい、Dolu1990氏が取捨選択を決定できる圧倒的な身軽さがある(そして精力的に開発している)ため進化速度がはやいです。なお弱点はそっくり裏返しで、アクティブ開発者がDolu1990氏含めて2名のみというところですね。

まとめ

Chiselインスパイア系言語/環境のSpinal HDLは、Chiselへの不満を解消するように多くの工夫を凝らして作られています。Spinal HDL環境には、Chiselにも欲しいなーと思うようなものがいくつも存在し、Chiselへ輸入できそうなものも多いです。
Spinal HDL→Chiselの輸入のためにChiselをforkするよりは大人しくSpinal HDLを使っても良いのかも、と感じるところもありました。しかし私がChiselの仕組みで特に気に入っているのがテスト周りで、この仕組みを現状のSpinal HDLは持たないので、すぐにこれを実用してみるイメージは湧いていません。
個人的にはNode.js対io.jsの構図で、Chiselの新仕様をせっせと練る場としてSpinal HDLが機能して、最終的に統合されるシナリオであると安心して両方を掘っていけるのですが、その実現には両者のユーザ規模が1万倍ぐらい足りませんね…。
今回書いたのは実用した評価レポートなどではないため、今後実際に触ってみて印象が変わる可能性も大いにあります。これからもたまにSpinal HDL事情を調べて、時折触ってみようと思います。