FIRRTLの動作を解析するためには、Scalaの総合開発環境としてIntelliJ IDEAをインストールしておくのが良い。
これまでインストールしていなかったので、これを機にインストールしておこう。
www.jetbrains.com
無料版のCommunity EditionのLinux版をダウンロードして、WSL上にインストールしておく。tar.gzファイルを展開するとインストールは不要なので、ダウンロードした場所にPATHを通して起動する。
プロジェクトの設定は、firrtlリポジトリの先頭ディレクトリを指定してプロジェクトを開くと自動的に行われるようだ。CTRLキーを押しながら変数や関数にマウスポインタを合わせると、自動的にジャンプできるモードに入る。これは便利。
前回のリグレッション実行で立ち上げたスクリプトutil/bin/firrtl
は実体は単なるシェルスクリプトで、中身は以下のようになっている。
path=`dirname "$0"`
cmd="java -cp ${path}/firrtl.jar firrtl.stage.FirrtlMain ${@:1}"
eval $cmd
firrtl.stage.FirrtlMain
が引数を含めて呼び出されているらしい。これを見てみる。
とりあえず、以下のサンプルコードを使ってみる。自力でFIRRTLのコードを書いてみた。
circuit SimpleCircuit :
module SimpleCircuit :
output io : {flip in : UInt<32>, out : UInt<32>}
io.out <= io.in
以下のようにしてFIRRTLを起動すると、しばらくしてsimple_circuit.v
が生成された。それにしても1秒以下とはいえちょっと時間がかかるな。
$ ./utils/bin/firrtl -td regress -i regress/simple_test.fir -o regress/simple_test.v -X verilog
Total FIRRTL Compile Time: 565.4 ms
module SimpleCircuit(
input [31:0] io_in,
output [31:0] io_out
);
assign io_out = io_in;
endmodule
具体的にどのように実行されているかという話だが、まずは、FirrtlStage.scala
から始まっているように見える。
object FirrtlMain extends StageMain(new FirrtlStage)
StageMain
内でmain()
が呼ばれ、stage.execute()
つまり、FirtlStage.execute()
が実行される。
firrtl/options/Stage.scala
class StageMain(val stage: Stage) {
...
final def main(args: Array[String]): Unit = try {
stage.execute(args, Seq.empty)
...
execute()
では、各ステージでのtransform
が呼ばれるらしい。実行されるステージは、FirrtlStage
で定義されている。
firrtl/stage/FirrtlStage.scala
private val phases: Seq[Phase] =
Seq( new firrtl.stage.phases.AddDefaults,
new firrtl.stage.phases.AddImplicitEmitter,
new firrtl.stage.phases.Checks,
new firrtl.stage.phases.AddCircuit,
new firrtl.stage.phases.AddImplicitOutputFile,
new firrtl.stage.phases.Compiler,
new firrtl.stage.phases.WriteEmitted )
.map(DeletedWrapper(_))
まずAddDefaults
だが、以下の3つのオプションを操作しているように見える。
firrtl/stage/phases/AddDefaults.scala
def transform(annotations: AnnotationSeq): AnnotationSeq = {
...
annotations.foreach {
case _: BlackBoxTargetDirAnno => bb = false
case _: CompilerAnnotation => c = false
case _: InfoModeAnnotation => im = false
case a =>
}
...
(if (bb) Seq(BlackBoxTargetDirAnno(targetDir)) else Seq() ) ++
(if (c) Seq(CompilerAnnotation(default.compiler)) else Seq() ) ++
(if (im) Seq(InfoModeAnnotation()) else Seq() ) ++
annotations
BlackBoxTargetDirAnnoは、ブラックボックスのVerilogファイルを見つけるためのターゲットディレクトリの指定か?
CompilerAnnotationは、コンパイラに指示するオプションのようにみえる。
InfoModeAnnotationは、良く分からない。
このアノテーションというのは何かというのを調べたのだが、どうやらコンパイラに伝えるべき情報をオプションの中から蓄えるために使用しているもので、最初はEmptyだった。
firrtl/options/Stage.scala
class StageMain(val stage: Stage) {
final def main(args: Array[String]): Unit = try {
stage.execute(args, Seq.empty)
firrtl/options/Stage.scala
final def execute(args: Array[String], annotations: AnnotationSeq): AnnotationSeq =
transform(shell.parse(args, annotations))
...
firrtl/options/Stage.scala
final def transform(annotations: AnnotationSeq): AnnotationSeq = {
val annotationsx =
Seq( new phases.GetIncludes,
new phases.ConvertLegacyAnnotations )
.map(phases.DeletedWrapper(_))
.foldLeft(annotations)((a, p) => p.transform(a))
...