ハードウェアの気になるあれこれ

技術的に興味のあることを調べて書いてくブログ。主にハードウェアがネタ。

Chiselの文法 - 入門編〜その1:モジュールの定義とエラボレート〜

スポンサーリンク

前回はGW中に作ったRISC-Vの紹介をしてみたが、その記事ではそのRISC-Vプロセッサの概要と作った所感についてをまとめただけでした。
まず最初の目標としていたのがこの取り組みだったため、ひとまず最初の目標は達成したというところ言う感じです。
そんなわけもあり一区切りという意味も込めて、ここまでに勉強してきたChiselの文法的な話をついてまとめてみようと思います。
ある意味Chiselの文法の入門編的な感じになる、、はず。

Chisel入門編〜その1:モジュールの定義とエラボレート〜

”文法まとめ”の位置づけなので、基本的な部分から。
なお、Chiselの開発環境は既に構築されている前提で書きます。 Chiselの環境についてはgithubに公開されている"chisel-template"を使うのがオススメ。

github.com

この辺の開発環境構築については以下の記事でまとめているのでこちらも良ければ参考になさってください。

www.tech-diningyo.info

Chiselのモジュール

Chiselのモジュールは以下の形で記載するのが基本形となります。

import chisel3._

class <モジュール名> extends Module {
  // 以下のval ioは必須
  val io = IO(<IOの宣言>)

  // モジュールの実装
}

上記に記載したとおり、io変数は実装必須。これは継承したModule内で以下のように抽象メソッドとして宣言されているからです。
因みにScalaのメソッドを()なしで宣言すると変数でのオーバーライドが可能になるので、上記のようにval ioでの宣言が可能になっているからです。

  // IO for this Module. At the Scala level (pre-FIRRTL transformations),
  // connections in and out of a Module may only go through `io` elements.
  def io: Record

上記のモジュール宣言を実際に書いてみると以下のようになります。

  • src/main/scala/TestChiselModule.scala
    • 注:なお、本記事ではsbtの標準のディレクトリ構成に従い、ファイルを配置していることを前提にします
import chisel3._
/**
  * Chiselのモジュール基本形
  * このモジュールは入力"in"を出力"out"に接続しスルーするだけ
  */
class TestChiselModule extends Module {
  val io = IO(new Bundle {
    val in = Input(Bool())
    val out = Output(Bool())
    })

  // 信号の接続
    io.out := io.in
  }

しれっとIO宣言部分にnew Bundle{}という記述が出てきてますが、今はこういうものだとお考えください。
IOポートの宣言自体はこのBundleを使って以下のような形で書けばOKです。

val io = IO(new Bundle {
  val <ポート名1> = [Input or Output](<Chiselの型>)
  val <ポート名2> = [Input or Output](<Chiselの型>)
  ...
  val <ポート名N> = [Input or Output](<Chiselの型>)
  })

エラボレート

Chiselのモジュールを書いたのでとりあえずエラボレートしてみたい!!となると思うので、先にエラボレートの方法を。

これも以前にまとめているので、こちらも合わせてどうぞ。

www.tech-diningyo.info

エラボレートするには以下のようにChiselで定義されているオブジェクトDriverに定義されているexecuteメソッドに作ったモジュールのインスタンスを渡せばOKです。

Driver.execute(Array[String](), () => new <モジュール名>)

第一引数はエラボレート時に指定できるオプションをArray[String]で渡す感じになります。
例えば以下のようにすることで出力先のディレクトリを指定すること出来ます。

Driver.execute(Array("--target_dir=<出力先ディレクトリ>"))

TestChiselModuleのエラボレート用オブジェクト

では先ほどのTestChiselModuleをエラボレートしてみます。
まずは上記のDriver.executeを実行コードに含むScalaのメインオブジェクトを作成します。
以下の例では2つのエラボレート用のmain関数を作成しています。どちらを使ってもOKです。
まあAppトレイト使ったほうが楽ですよね。

import chisel3._

/**
  * TestChiselModuleのエラボレート
  */
object ElaborateTestChiselModule {

  /**
    * Scalaのメイン関数
    * @param args プログラム引数
    */
  def main(args: Array[String]) {
    Driver.execute(Array(
      "--target-dir=rtl/chisel-basic"
    ),
    () => new TestChiselModule
    )
  }
}

/**
  * TestChiselModuleのエラボレートのAppトレイト使用版
  * Appトレイトを継承することでmainメソッドを作らなくて済む
  */
object ElaborateTestChiselModuleApp extends App{

  Driver.execute(Array(
      "--target-dir=rtl/chisel-basic"
    ),
    () => new TestChiselModule
    )
}

エラボレート時のsbtコマンド

先ほどの作成したElaborateTestChiselModuleを実行するには以下のコマンドを実行します。

sbt "runMain ElaborateTestChiselModule"

sbtのコマンドrunMainは"src/main/scala"の下にある特定にmainメソッドを実行するコマンドになります。

sbt "runMain <mainメソッド名>"

が一般形です。この際にパッケージで階層化している場合にはパッケージ名を含んだ完全なパスが必要になります。

実行すると以下のような感じでログが表示されてRTLが生成されるはずです。

[IJ]sbt:chiselBasic> runMain ElaborateTestChiselModule
[info] Updating ...
[info] Done updating.
[warn] There may be incompatibilities among your library dependencies; run 'evicted' to see detailed eviction warnings.
[info] Compiling 2 Scala sources to /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/subprj/chisel-basic/target/scala-2.11/classes ...
[warn] /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/subprj/chisel-basic/src/main/scala/TestChiselModule.scala:16:6: reflective access of structural type member value out should be enabled
[warn] by making the implicit value scala.language.reflectiveCalls visible.
[warn] This can be achieved by adding the import clause 'import scala.language.reflectiveCalls'
[warn] or by setting the compiler option -language:reflectiveCalls.
[warn] See the Scaladoc for value scala.language.reflectiveCalls for a discussion
[warn] why the feature should be explicitly enabled.
[warn]   io.out := io.in
[warn]      ^
[warn] /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/subprj/chisel-basic/src/main/scala/TestChiselModule.scala:16:16: reflective access of structural type member value in should be enabled
[warn] by making the implicit value scala.language.reflectiveCalls visible.
[warn]   io.out := io.in
[warn]                ^
[warn] two warnings found
[info] Done compiling.
[warn] Multiple main classes detected.  Run 'show discoveredMainClasses' to see the list
[info] Packaging /home/diningyo/prj/study/2000_chisel/500_learning-chisel3/subprj/chisel-basic/target/scala-2.11/chiselbasic_2.11-2.0.jar ...
[info] Done packaging.
[info] Running ElaborateTestChiselModule
[info] [0.001] Elaborating design...
[info] [0.812] Done elaborating.
Total FIRRTL Compile Time: 276.1 ms
[success] Total time: 10 s, completed 2019/05/26 9:38:16
  • 生成されるRTL

エラボレートが終わると--target_dirで指定したディレクトリに以下の3のファイルが生成されます。

$ ls -l rtl/chisel-basic/
-rw-rw-r-- 1 diningyo diningyo   6  5月 26 09:42 TestChiselModule.anno.json
-rw-rw-r-- 1 diningyo diningyo 375  5月 26 09:42 TestChiselModule.fir
-rw-rw-r-- 1 diningyo diningyo 231  5月 26 09:42 TestChiselModule.v

この段階では"TestChiselModule.anno.json"は空のJSONファイルなので気にしなくて大丈夫です。
"TestChiselModule.fir"はFIRRTLといってChisel3で導入されたChiselとVerilog HDLの中間表現が記述されたファイルになります
ChiselでVerilog HDLのRTLを生成する際には

  • Chiselソース → FIRRTL
  • FIRRTL → Verilog HDL

という2段階の処理を経てVerilog HDLのRTLが生成されるようになっています。 このFIRRTLファイルは以下のようにほぼRTLと似たような形式のものになっているので、なんとなく読めるかと思います。

  • TestChiselModule.fir
;buildInfoPackage: chisel3, version: 3.1.7, scalaVersion: 2.11.12, sbtVersion: 1.1.1, builtAtString: 2019-03-20 22:15:13.399, builtAtMillis: 1553120113399
circuit TestChiselModule :
  module TestChiselModule :
    input clock : Clock
    input reset : UInt<1>
    output io : {flip in : UInt<1>, out : UInt<1>}

    io.out <= io.in @[TestChiselModule.scala 17:10]
  • TestChiselModule.v
module TestChiselModule( // @[:@3.2]
  input   clock, // @[:@4.4]
  input   reset, // @[:@5.4]
  input   io_in, // @[:@6.4]
  output  io_out // @[:@6.4]
);
  assign io_out = io_in; // @[TestChiselModule.scala 17:10:@8.4]
endmodule

FIRRTLについての細かな文法が知りたい場合は以下のFIRRTLの仕様書を読むと良いかと思います。

github.com

その2へ続く。

www.tech-diningyo.info