FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

FIRRTLに入門する (2. FIRRTL解析のためのIntelliJ IDEAの立ち上げと最初の解析)

https://raw.githubusercontent.com/freechipsproject/firrtl/master/doc/images/firrtl_logo.svg?sanitize=true

FIRRTLの動作を解析するためには、Scalaの総合開発環境としてIntelliJ IDEAをインストールしておくのが良い。

これまでインストールしていなかったので、これを機にインストールしておこう。

www.jetbrains.com

無料版のCommunity EditionのLinux版をダウンロードして、WSL上にインストールしておく。tar.gzファイルを展開するとインストールは不要なので、ダウンロードした場所にPATHを通して起動する。

プロジェクトの設定は、firrtlリポジトリの先頭ディレクトリを指定してプロジェクトを開くと自動的に行われるようだ。CTRLキーを押しながら変数や関数にマウスポインタを合わせると、自動的にジャンプできるモードに入る。これは便利。

f:id:msyksphinz:20191106005135p:plain
IntelliJ IDEAを起動してFIRRTLプロジェクトを開いたところ。

前回のリグレッション実行で立ち上げたスクリプトutil/bin/firrtlは実体は単なるシェルスクリプトで、中身は以下のようになっている。

#!/bin/bash

# This may be a brittle way to find $(root_dir)/utils/bin, is there a better way?
path=`dirname "$0"`
cmd="java -cp ${path}/firrtl.jar firrtl.stage.FirrtlMain ${@:1}"
eval $cmd

firrtl.stage.FirrtlMainが引数を含めて呼び出されているらしい。これを見てみる。

とりあえず、以下のサンプルコードを使ってみる。自力でFIRRTLのコードを書いてみた。

  • simple_circuit.fir
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
  • simple_circuit.v
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
  /** Append any missing default annotations to an annotation sequence */
  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) {

  /** The main function that serves as this stage's command line interface.
    * @param args command line arguments
    */
  final def main(args: Array[String]): Unit = try {
    stage.execute(args, Seq.empty)
  • firrtl/options/Stage.scala
  /** Run this stage on on a mix of arguments and annotations
    * @param args command line arguments
    * @param initialAnnotations annotation
    * @return output annotations
    * @throws OptionsException if command line or annotation validation fails
    */
  final def execute(args: Array[String], annotations: AnnotationSeq): AnnotationSeq =
    transform(shell.parse(args, annotations))
...
  • firrtl/options/Stage.scala
  /** Execute this stage on some input annotations. Annotations will be read from any input annotation files.
    * @param annotations input annotations
    * @return output annotations
    * @throws OptionsException if command line or annotation validation fails
    */
  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))
...