FPGA開発日記

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

TileLinkのDiplomacyを使った実際のデザインを試してみる (2. オリジナルのデザインを作り込む)

TileLinkを使ったデザインを作っていきたい。Diplomacyについて勉強を進めていくなかで、やはり自分でデザインを作ってどの程度コントロールできるのか確認していく。オリジナルのデザインを作り上げていこう。

まず理解しておかなければならないのは、DiplomacyはChiselそのものの機能ではないということ。DiplomacyはChiselを使ったうえで構築されるプラットフォームであり、そのライブラリ群はRocketChipの一部として提供されている。したがって、オリジナルのDiplomacyを使ったデザインを構築したいとなると、RocketChipの力を借りなければならない。

まずはその環境を整えていく。

RocketChipをサブモジュールとして使用するデザイン環境の構築

実現したい環境は以下のようになる。実際のDiplomacyを使ったデザインはsrc/main/scala上に存在しており、そこではDiplomacyを使用している。このDiplomacyを活用するために、RocketChipをサブモジュールとして使用したい。

.
├── src
│   ├── main
│   └── test
├── rocketchip
│   ├── LICENSE.Berkeley
│   ├── LICENSE.SiFive
│   ├── LICENSE.jtag
...

このために、Rocket-Chipをサブモジュールとして押し込んだ。この時にディレクトリ名はrocketchipとしている。何故だかわからないが、ディレクトリ名がrocket-chiprocket_chipなどだと上手く動かない。これは少し調べてみる必要がありそう。

そして、ルートに存在するbuild.sbtに以下の記述を追加した。これはつまりrocketchipのデザインをルートディレクトリのプロジェクトに挿入することを意味する。

  • build.sbt
lazy val rocketchip = project in file("rocketchip")
lazy val root = (project in file("."))
  .dependsOn(other_resource)
  .dependsOn(rocketchip)

まずはテストだ。以下のようなコードを作ってみる。

  • src/main/scala/TLUnitTest/TLUnitTest.scala
// package tlunittest
package freechips.rocketchip.unittest

import Chisel._
// import other_resource._
import freechips.rocketchip.config._
import freechips.rocketchip.amba.ahb._
import freechips.rocketchip.amba.apb._
import freechips.rocketchip.amba.axi4._
import freechips.rocketchip.subsystem.{BaseSubsystemConfig}
import freechips.rocketchip.devices.tilelink._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.util._

class WithTLOriginalUnitTest extends Config((site, here, up) => {
  case UnitTests => (q: Parameters) => {
    implicit val p = q
    val txns = 1 * site(TestDurationMultiplier)
    val timeout = 50000 * site(TestDurationMultiplier)
    Seq(
      Module(new TLRAMSimpleTest(1,        txns=15*txns, timeout=timeout))
    )}
})

class TLOriginalUnitTestConfig extends Config(new WithTLOriginalUnitTest ++ 
                                              new WithTestDuration(1) ++ 
                                              new BaseSubsystemConfig)

このTLOriginalUnitTestConfigではConfigとしてWithTLOriginalUnitTestクラスを使用している。ここではUnitTestsを使用し、TLRAMSimpleTestインスタンスしてテストを開始する。このTLRAMSimpleTestはどのような仕組みになっているのかというと、

  • rocketchip/src/main/scala/tilelink/SRAM.scala
class TLRAMSimple(ramBeatBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule {
  val fuzz = LazyModule(new TLFuzzer(txns))
  val model = LazyModule(new TLRAMModel("SRAMSimple"))
  val ram  = LazyModule(new TLRAM(AddressSet(0x0, 0x3ff), beatBytes = ramBeatBytes))

  ram.node := TLDelayer(0.25) := model.node := fuzz.node

  lazy val module = new LazyModuleImp(this) with UnitTestModule {
    io.finished := fuzz.module.io.finished
  }
}

class TLRAMSimpleTest(ramBeatBytes: Int, txns: Int = 5000, timeout: Int = 500000)(implicit p: Parameters) extends UnitTest(timeout) {
  val dut = Module(LazyModule(new TLRAMSimple(ramBeatBytes, txns)).module)
  io.finished := dut.io.finished
}

この構成はどのようになっているのかというと、FuzzerTLRAMModelTLDelayer(0.25)TLRAM の順に接続される。

f:id:msyksphinz:20191229003822p:plain
TLOriginalUnitTestConfigの構成

さて、ここからVerilogを生成していこう。Rocketの構成をそのまま真似ているので、Makefileをそのまま活用する。

sbt 'runMain freechips.rocketchip.unittest.Generator . freechips.rocketchip.unittest TestHarness freechips.rocketchip.unittest TLOriginalUnitTestConfig'
../../firrtl_origin/utils/bin/firrtl -td . -i freechips.rocketchip.unittest.TLOriginalUnitTestConfig.fir -X sverilog

最初のコマンドで、freechips.rocketchip.unittest.TLOriginalUnitTestConfig.firを生成する。次のfirrtlコマンドで、Verilogを生成する。

  • TestHarness.sv
module TestHarness(
  input logic   clock,
  input logic   reset,
  output logic  io_success
);
  logic  UnitTestSuite_clock; // @[TestHarness.scala 10:23]
  logic  UnitTestSuite_reset; // @[TestHarness.scala 10:23]
  logic  UnitTestSuite_io_finished; // @[TestHarness.scala 10:23]
  UnitTestSuite UnitTestSuite ( // @[TestHarness.scala 10:23]
    .clock(UnitTestSuite_clock),
    .reset(UnitTestSuite_reset),
    .io_finished(UnitTestSuite_io_finished)
  );
  assign io_success = UnitTestSuite_io_finished; // @[TestHarness.scala 10:14]
  assign UnitTestSuite_clock = clock;
  assign UnitTestSuite_reset = reset;
endmodule

上手くできたようだ。ただし生成フローとしてはもう少し何とかならないか?確認を進めていこう。