Rocket Chip環境の仕組み解析メモの垂れ流し記事。とりあえずRTL生成時のざっくりした流れを追ったメモを元に少しだけ手直し。
Rocket ChipのRTL生成フロー(全体の流れ)
少し前に書いた記事(IntelliJ IDEA上でRocket Chipのエミュレータをビルドする)で記載したが、Rocket ChipのREADMEに従ってエミュレータを生成する際に実行するmake
コマンドの中では以下のようなsbt
コマンドが発行されていた。
runMain freechips.rocketchip.system.Generator ./emulator/generated-src freechips.rocketchip.system TestHarness freechips.rocketchip.system DefaultConfig
上記はrunMain
を使ってfreechips.rocketchip.system.Generator
のメインオブジェクトを実行する処理に相当するので"src"ディレクトリ中のデータから
- rocketchip/system/Generator.scala
の中にあるオブジェクトを見てやればいいことになる。 ということで早速中身を確認していく。
src/main/system/Generator.scala
そこそこ長いので不要な部分は端折りながら。
オブジェクトの宣言
冒頭は当たり前だけどGenerator
オブジェクトの宣言。ここではGeneratorApp
というクラスを継承することでScalaのメインオブジェクトとして宣言する形になっている。
GeneratorApp
についてはまた後ほど。
冒頭に実行するテストスイートについての情報が定義されている。
/** A Generator for platforms containing Rocket Subsystemes */ object Generator extends GeneratorApp { // 各アーキごとの追加テストリスト val rv64RegrTestNames = LinkedHashSet( "rv64ud-v-fcvt", // 略 "rv64si-p-dirty") val rv32RegrTestNames = LinkedHashSet( "rv32mi-p-ma_addr", // 略 "rv32ui-p-sll")
テストスイートの構築メソッド(addTestSuites)
以下はテストスイートの構築メソッド。 コンフィグで指定されたパラメータから
- xLen
- エクステンション
- モード
の情報を抽出しそれに対応するテストをTestGeneration.addSuites
を使って追加している。
override def addTestSuites { import DefaultTestSuites._ val xlen = params(XLen) // TODO: for now only generate tests for the first core in the first subsystem params(RocketTilesKey).headOption.map { tileParams => val coreParams = tileParams.core val vm = coreParams.useVM val env = if (vm) List("p","v") else List("p") // FPUのオプションに従ってテストを追加 coreParams.fpu foreach { case cfg => if (xlen == 32) { TestGeneration.addSuites(env.map(rv32uf)) if (cfg.fLen >= 64) TestGeneration.addSuites(env.map(rv32ud)) } else { TestGeneration.addSuite(rv32udBenchmarks) TestGeneration.addSuites(env.map(rv64uf)) if (cfg.fLen >= 64) TestGeneration.addSuites(env.map(rv64ud)) } } // A-extension(Atomimc)のテストに関する処理 if (coreParams.useAtomics) { if (tileParams.dcache.flatMap(_.scratch).isEmpty) TestGeneration.addSuites(env.map(if (xlen == 64) rv64ua else rv32ua)) else TestGeneration.addSuites(env.map(if (xlen == 64) rv64uaSansLRSC else rv32uaSansLRSC)) } // C-extensionのテストに関する処理 if (coreParams.useCompressed) TestGeneration.addSuites(env.map(if (xlen == 64) rv64uc else rv32uc)) val (rvi, rvu) = if (xlen == 64) ((if (vm) rv64i else rv64pi), rv64u) else ((if (vm) rv32i else rv32pi), rv32u) TestGeneration.addSuites(rvi.map(_("p"))) TestGeneration.addSuites((if (vm) List("v") else List()).flatMap(env => rvu.map(_(env)))) TestGeneration.addSuite(benchmarks) TestGeneration.addSuite(new RegressionTestSuite(if (xlen == 64) rv64RegrTestNames else rv32RegrTestNames)) } }
エミュレータの生成処理
以下がGenerator
のメイン処理。
longName
を構築した後、
を行う。
val longName = names.configProject + "." + names.configs generateFirrtl generateAnno generateTestSuiteMakefrags generateROMs generateArtefacts }
この中のgenerateFirrtl
を読み解ければ、どのようにしてRocket Chipが構築されていくのかがわかりそう。
残りの4つは中を見る限り、生成したRocket Chipの情報を元に各種ファイルを出力するだけの模様。
とりあえず以下にそのコードを載せておく。
def generateAnno { val annotationFile = new File(td, s"$longName.anno.json") val af = new FileWriter(annotationFile) af.write(JsonProtocol.serialize(circuit.annotations.map(_.toFirrtl))) af.close() }
- generateTestSuiteMakefrags
- ここで先ほど紹介したs
addTestSuites
が呼び出される。 - 出力されるのは"<出力先ディレクトリ>/*.d"で中身はmake時のターゲット情報が記載される
- ここで先ほど紹介したs
/** Output software test Makefrags, which provide targets for integration testing. */ def generateTestSuiteMakefrags { addTestSuites writeOutputFile(td, s"$longName.d", TestGeneration.generateMakefrag) // Subsystem-specific test suites }
- generateROMs
- *.rom.confが出力される
- エミュレータの生成時にはファイルの中身は空。
def generateROMs { writeOutputFile(td, s"$longName.rom.conf", enumerateROMs(circuit)) }
- generateArtefacts
- 先ほど書いた*.memmap.jsonが出力される
- 処理的には
foreach
の処理なので、他にも出力されそうだが詳細は未調査
/** Output files created as a side-effect of elaboration */ def generateArtefacts { ElaborationArtefacts.files.foreach { case (extension, contents) => writeOutputFile(td, s"$longName.$extension", contents ()) } }
Rocket Chip環境が凄いのはREADMEに書いてあるmake
一発で作成できるDefaultConfig
以外のコンフィグもこのジェネレータ・オブジェクトのみで生成できる点だと思う。
なのでメインの処理となるgenerateFirrtl
についての調査を継続中です。ただLazyModule
が。。。。やってることはわかってきたけど自分で使おうとすると。。。