前回の記事でChisel BootcampのModule1がやっと終わった。 徐ろに、題名につけてた”Chiselの勉強”を省いてスタート。
ということで今日からはModule2に入っていよいよChiselを使ったHW設計について学んでいく。
Module 2.1: 最初のChiselモジュール
モチベーション
とりあえずモチベーションを見てみる。
Module1でScalaにある程度なれたので、ついにハードウェアを掘り始める時だ!ChiselはConstructing Hardware In a Scala Embedded Languageの頭文字をとったものだ。これはChiselがScalaのDSLであり、あなたにScalaとChiselの良いところを同じコード内でどちらも享受することを認めることを意味する。どのコードが”Scala"のものでどのコードが”Chisel"のものであるかを理解することは大切だが、これについてはまた後ほど議論することにする。まず今はChiselとModule2のコードがVerilogを書くことよりも優れた方法であることについて考えていこう。このModuleではChisel
Module
全体とテスターについてをあなたに教えていく。この後に多くの例題を見ることになるので、まずはgistからコードを取ってこよう。
まあ、なんか全体的な話をするよーって感じでしょうかね。
セットアップ
以下のようなコードを実行することで、Chisel一式のダウンロードが行われる。
val path = System.getProperty("user.dir") + "/source/load-ivy.sc" interp.load.module(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))
上記のコードはJupyter notebook上で実行されるので、実行結果が出力される部分に様々なモジュールのダウンロードログが表示される。
ログはgistに置いてあるので、必要があれば参照してもらえれば。
ダウンロードが終わると以下のようなメッセージが出力されてセットアップが終わったぽい雰囲気になる。
Compiling chisel-bootcamp/Main.sc path: String = "chisel-bootcamp/source/load-ivy.sc"
この表示が出ると、import
が可能になる。
import chisel3._ import chisel3.util._ import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
上記の3つのimport
を実行すると、Chiselを使ったHW設計を行うためのセットアップが完了だ。
最初のモジュール
このセクションではいよいよChiselで最初のモジュールを作ることになるが、ここでは最初のモジュールも含めて以下のことを知ることが出来る。
- モジュールのコード
- テストケースのコード
- どのように実行するか
例題:モジュール
Verilogと同様にChiselでもモジュールの宣言を行うことが出来る。例題ではPassthrough
という名前のModule
が取り上げられている。ざっとまとめると以下のような仕様になる(まあ、何するモジュールなのかはお察しのとおりだ)
- モジュール名:
Passthrough
in
: 4-bit inputout
: 4-bit output- 処理:組み合わせ回路で
in
がout
に接続されているので、in
がout
を駆動する(パススルー)
// Chisel Code: Declare a new module definition class Passthrough extends Module { val io = IO(new Bundle { val in = Input(UInt(4.W)) val out = Output(UInt(4.W)) }) io.out := io.in }
これくらいの物だと、見ただけでもなんとなくわかるがChiselとしての基本的なことが色々詰まっているようなので、解説を読み進めていく。
モジュール宣言
まずはクラスを使ってChiselのModule宣言。
class Passthrough extends Module {
見てわかるようにModule
を継承して作られている。このModule
はChiselのビルトイン・クラスで全てのハードウェアモジュールはこれを継承しなければならない。
IO宣言
全ての入力/出力ポートは特別なio
val
の中に宣言する。
val io = IO(...)
このIO宣言については以下の制約が存在する。
- 変数名は
io
でなければならない IO
オブジェクト、またはIO(_instantiated_bundle_)
のような形式のインスタンスでなければならない
ポート用変数io
の中に新しいハードウェア構造の型(Bundle
)を宣言し、その中に名前のついた信号in
/out
をそれぞれ入力/出力として宣言する。
new Bundle { val in = Input(...) val out = Output(...) }
ビット幅指定
下記のコードで信号のハードウェアタイプを宣言する。
UInt(4.W)
この例ではin
/out
共に4-bit幅のunsinged intになる。
ハードウェアの処理 - 方向指示演算子(directioned operator)
val io
の下に続くのこのモジュールの処理になる。ここでは入力ポートの信号を出力ポートに接続しているのでパススルー処理が実装されていることになる。
io.out := io.in
- 右辺の信号で左辺の信号をドライブする
こと意味する。
この演算子を方向指示演算子(directioned operator)と呼ぶ。
エラボレーション
きちんとしたハードウェア構築言語ではその基礎となる言語をスクリプト言語のように扱うことが出来る。例を示すと、Chiselでモジュールを宣言した後、ScalaでChiselのコンパイラーを呼びだし、ChiselのPassthrough
モジュールをVerilogのPassthrough
モジュールに変換する。このプロセスをエラボレーションという。
// Scala Code: Elaborate our Chisel design by translating it to Verilog // Don't worry about understanding this code; it is very complicated Scala println(getVerilog(new Passthrough))
上記を実行すると以下の出力が得られる
[info] [0.001] Elaborating design... [info] [0.741] Done elaborating. Total FIRRTL Compile Time: 235.4 ms module cmd2HelperPassthrough( // @[:@3.2] input clock, // @[:@4.4] input reset, // @[:@5.4] input [3:0] io_in, // @[:@6.4] output [3:0] io_out // @[:@6.4] ); assign io_out = io_in; endmodule
なお、上記でモジュール名がcmd<#>HelperPassthrough
となっているのは、このチュートリアルをJupyter上で動かしたためのものだ。Chiselではモジュールの名前と他のコンポーネントのを可能な限り保持してくれるとのことだ。
上記の出力を見るとわかるが、printlnで出力された結果にVeirlogのPassthrough
モジュールが出力されている。
module cmd2HelperPassthrough( // @[:@3.2] input clock, // @[:@4.4] input reset, // @[:@5.4] input [3:0] io_in, // @[:@6.4] output [3:0] io_out // @[:@6.4] ); assign io_out = io_in; endmodule
処理もChiselで書いたものが確かに反映されている。
ということで、とりあえず最初のモジュールが出来たので今日はここまで。