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

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

Rocket ChipのGeneratorのソースの解析メモ(10) - サンプルに沿ってPWMモジュールをTileLinkバスに追加してみる(4)

スポンサーリンク

「Rocket Chipに自分のモジュールを追加してみる」の4回目&一旦ここまででお終い。 firesimのデータをそのままRocket Chipに持って行ってもエラーが出てビルドできなかったので、Rocket Chip上でビルドするにあたって行った修正についてをまとめておく。

自前のConfigのビルド方法

今回はエミュレータのビルドを行う。Rocket ChipのREADME.mdに載っているようにただmake runと実行するとDefaultConfigを使ったエミュレータが生成されるのだが、ビルド時の変数指定で自前で用意したConfigを使ったエミュレータの生成が可能となっている。
指定する必要のある変数は以下の2つだ。

  • PROJECT:プロジェクト名。実際に指定するのは自分のConfigが含まれているディレクトリへのsrc/からの相対パス
  • CONFIG:自前のConfig名

今回自前で用意したソースコードは以下のような構成になっている。

src/main/scala/
 |
 +- example/slave/
     |
     +- Configs.scala
     +- PWM.scala
     +- TestHarness.scala
     +- Top.scala

この構成を例にすると設定する値はそれぞれ以下のようになる。

  • PROJECT : example.slave
  • CONFIG : PWMConfig
cd emulator
# 波形上でもモジュールの確認をしたいのでdebugでエミュレータを生成
make PROJECT=example.slave CONFIG=PWMConfig debug

Rocket Chipで出たエラーと対策

とりあえずそのまま移植しただけの状態でビルドすると、以下のようなエラーが発生する。

[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:9:8: not found: object testchipip
[error] import testchipip._
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:10:8: not found: object icenet
[error] import icenet._
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Top.scala:10:8: not found: object testchipip
[error] import testchipip._
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Top.scala:11:8: not found: object icenet
[error] import icenet._
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Top.scala:18:8: not found: type HasNoDebug
[error]   with HasNoDebug
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Top.scala:19:8: not found: type HasPeripherySerial
[error]   with HasPeripherySerial {
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Top.scala:47:8: not found: type HasNoDebugModuleImp
[error]   with HasNoDebugModuleImp
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Top.scala:48:8: not found: type HasPeripherySerialModuleImp
[error]   with HasPeripherySerialModuleImp
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Top.scala:60:8: not found: type HasPeripheryBlockDevice
[error]   with HasPeripheryBlockDevice {
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Top.scala:66:10: not found: type HasPeripheryBlockDeviceModuleImp
[error]     with HasPeripheryBlockDeviceModuleImp
[error]          ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:36:9: value connectBlockDeviceModel is not a member of example.slave.ExampleTopWithBlockDeviceModule
[error]     top.connectBlockDeviceModel()
[error]         ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:44:9: value connectSimBlockDevice is not a member of example.slave.ExampleTopWithBlockDeviceModule
[error]     top.connectSimBlockDevice(clock, reset)
[error]         ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:50:8: not found: value NICKey
[error]   case NICKey => NICConfig(inBufFlits = 1800, usePauser = true)
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:50:18: not found: value NICConfig
[error]   case NICKey => NICConfig(inBufFlits = 1800, usePauser = true)
[error]                  ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:50:28: not found: value inBufFlits
[error]   case NICKey => NICConfig(inBufFlits = 1800, usePauser = true)
[error]                            ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:50:47: not found: value usePauser
[error]   case NICKey => NICConfig(inBufFlits = 1800, usePauser = true)
[error]                                               ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Top.scala:69:8: not found: type HasPeripheryIceNIC
[error]   with HasPeripheryIceNIC {
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Top.scala:75:10: not found: type HasPeripheryIceNICModuleImp
[error]     with HasPeripheryIceNICModuleImp
[error]          ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:53:9: value connectNicLoopback is not a member of example.slave.ExampleTopWithIceNICModule
[error]     top.connectNicLoopback()
[error]         ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:59:8: not found: value NICKey
[error]   case NICKey => NICConfig(inBufFlits = 1800)
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:59:18: not found: value NICConfig
[error]   case NICKey => NICConfig(inBufFlits = 1800)
[error]                  ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:59:28: not found: value inBufFlits
[error]   case NICKey => NICConfig(inBufFlits = 1800)
[error]                            ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:62:9: value connectSimNetwork is not a member of example.slave.ExampleTopWithIceNICModule
[error]     top.connectSimNetwork(clock, reset)
[error]         ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:80:7: not found: type WithBlockDevice
[error]   new WithBlockDevice ++ new WithSimBlockDevice ++ new BaseExampleConfig)
[error]       ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:83:7: not found: type WithBlockDevice
[error]   new WithBlockDevice ++ new WithBlockDeviceModel ++ new BaseExampleConfig)
[error]       ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:91:31: not found: type WithNBlockDeviceTrackers
[error] class WithTwoTrackers extends WithNBlockDeviceTrackers(2)
[error]                               ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:91:56: no arguments allowed for nullary constructor Object: ()Object
[error] class WithTwoTrackers extends WithNBlockDeviceTrackers(2)
[error]                                                        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:92:32: not found: type WithNBlockDeviceTrackers
[error] class WithFourTrackers extends WithNBlockDeviceTrackers(4)
[error]                                ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:92:57: no arguments allowed for nullary constructor Object: ()Object
[error] class WithFourTrackers extends WithNBlockDeviceTrackers(4)
[error]                                                         ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/TestHarness.scala:16:13: value := is not a member of Option[freechips.rocketchip.devices.debug.DebugIO]
[error]   Expression does not convert to assignment because receiver is not assignable.
[error]   dut.debug := DontCare
[error]             ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/TestHarness.scala:20:21: value connectSimSerial is not a member of example.slave.ExampleTopModuleImp[example.slave.ExampleTop]
[error]   io.success := dut.connectSimSerial()
[error]                     ^
[warn] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/util/DescribedSRAM.scala:7:34: imported `Annotated' is permanently hidden by definition of object Annotated in package util
[warn] import freechips.rocketchip.util.Annotated
[warn]                                  ^
[warn] one warning found
[error] 31 errors found
[error] (Compile / compileIncremental) Compilation failed

存在しないモジュールの削除

とりあえず以下のメッセージへの対応を行う。

[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:9:8: not found: object testchipip
[error] import testchipip._
[error]        ^
[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/Configs.scala:10:8: not found: object icenet
[error] import icenet._

上記の2つはfiresimのgithub環境でサブモジュールとして登録されているgithubリポジトリなのだが、Rocket Chipでは使用していないのでとりあえずこの2つのインポートとこれを参照している部分を削除した。

//import testchipip._
//import icenet._

class ExampleTop(implicit p: Parameters) extends RocketSubsystem
  with CanHaveMasterAXI4MemPort
  with HasPeripheryBootROM
  //    with HasSystemErrorSlave
  with HasSyncExtInterrupts
  //with HasNoDebug
  //with HasPeripherySerial
{

class ExampleTopModuleImp[+L <: ExampleTop](l: L) extends RocketSubsystemModuleImp(l)
  with HasRTCModuleImp
  with CanHaveMasterAXI4MemPortModuleImp
  with HasPeripheryBootROMModuleImp
  with HasExtInterruptsModuleImp
  //with HasNoDebugModuleImp
  //with HasPeripherySerialModuleImp
  with DontTouch

モジュールのインポートを修正してビルドすると以下のように端子の接続部分でアサインできない旨のエラーが発生する。

[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/TestHarness.scala:16:13: value := is not a member of Option[freechips.rocketchip.devices.debug.DebugIO]
[error]   Expression does not convert to assignment because receiver is not assignable.
[error]   dut.debug := DontCare
[error]             ^

これは今のRocket Chipを使用するとdut.debugOption型になっているのが原因なのでgetを呼んでやればいい。

class TestHarness(implicit val p: Parameters) extends Module {
  val io = IO(new Bundle {
    val success = Output(Bool())
  })

  val dut = p(BuildTop)(clock, reset.toBool, p)
  //dut.debug := DontCare
  dut.debug.get := DontCare
  dut.connectSimAXIMem()
  dut.dontTouchPorts()
  dut.tieOffInterrupts()
  io.success := true.B //dut.connectSimSerial()
}

longNameの修正

先ほどの修正を行うと以下のようにGeneratorのlongNameに関するエラーが発生した。

[error] /home/diningyo/prj/risc-v/rocket/prj/rocket-chip-diningyo/src/main/scala/example/slave/TestHarness.scala:25:76: overriding lazy value longName in trait GeneratorApp of type String;
[error]  value longName needs `override' modifier
[error]   val longName = names.topModuleProject + "." + names.topModuleClass + "." + names.configs
[error]

これは調べていくと、Rocket Chipのv1.2-073119-SNAPSHOTからlongNameの実装が抽象メンバからlazy valを使った実装に変更になっており、オーバーライドする際に明示的にoverrideを付ける必要がある。

object Generator extends GeneratorApp {
  //val longName = names.topModuleProject + "." + names.topModuleClass + "." + names.configs
  override lazy val longName = names.topModuleProject + "." + names.topModuleClass + "." + names.configs
  generateFirrtl
  generateAnno
}

bootイメージの変更

longNameまでの修正でChiselの文法的にはエラーがなくなるが、まだエミュレータの生成は出来なかった。

[error] Caused by: java.nio.file.NoSuchFileException: ./testchipip/bootrom/bootrom.rv64.img
[error]     at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86)
[error]     at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
[error]     at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
[error]     at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:214)
[error]     at java.nio.file.Files.newByteChannel(Files.java:361)
[error]     at java.nio.file.Files.newByteChannel(Files.java:407)
[error]     at java.nio.file.Files.readAllBytes(Files.java:3152)
[error]     at freechips.rocketchip.devices.tilelink.HasPeripheryBootROM.freechips$rocketchip$devices$tilelink$HasPeripheryBootROM$$contents(BootROM.scala:66)
[error]     at freechips.rocketchip.devices.tilelink.HasPeripheryBootROM.freechips$rocketchip$devices$tilelink$HasPeripheryBootROM$$contents$(BootROM.scala:65)
[error]     at example.slave.ExampleTop.freechips$rocketchip$devices$tilelink$HasPeripheryBootROM$$contents$lzycompute(Top.scala:13)
[error]     at example.slave.ExampleTop.freechips$rocketchip$devices$tilelink$HasPeripheryBootROM$$contents(Top.scala:13)
[error]     at freechips.rocketchip.devices.tilelink.HasPeripheryBootROM.$anonfun$bootrom$1(BootROM.scala:72)
[error]     at freechips.rocketchip.devices.tilelink.TLROM$$anon$1.<init>(BootROM.scala:37)
[error]     at freechips.rocketchip.devices.tilelink.TLROM.module$lzycompute(BootROM.scala:36)
[error] Nonzero exit code: 1
[error] (Compile / runMain) Nonzero exit code: 1

長いスタックトレースが出ているが、追っていくと「ファイルが見つからない」が原因で場所が「BootROM」っぽいというのがわかる。 Firesimから持ってきたBootROM指定部分は以下のコードでtestchipipのものがそのまま指定されているのでRocket Chipの物に変更してみた。

class WithBootROM extends Config((site, here, up) => {
  case BootROMParams => BootROMParams(
    //contentFileName = s"./testchipip/bootrom/bootrom.rv${site(XLen)}.img")
    contentFileName = s"./bootrom/bootrom.img")
})

PWMTLの修正

最後に以下のようにPWLモジュールからと思しきエラーが発生した。

[error] Caused by: java.lang.IllegalArgumentException: requirement failed
[error]     at scala.Predef$.require(Predef.scala:264)
[error]     at example.slave.PWMTLModule.$init$(PWM.scala:46)
[error]     at example.slave.PWMTL$$anonfun$2$$anon$2.<init>(PWM.scala:75)
[error]     at example.slave.PWMTL$$anonfun$2.apply(PWM.scala:75)
[error]     at example.slave.PWMTL$$anonfun$2.apply(PWM.scala:75)
[error]     at freechips.rocketchip.tilelink.TLRegisterRouter.module$lzycompute(RegisterRouter.scala:169)
[error]     at freechips.rocketchip.tilelink.TLRegisterRouter.module(RegisterRouter.scala:169)
[error]     at freechips.rocketchip.tilelink.TLRegisterRouter.module(RegisterRouter.scala:152)
[error]     at freechips.rocketchip.diplomacy.LazyModuleImpLike.$anonfun$instantiate$2(LazyModule.scala:158)
[error]     at chisel3.core.Module$.do_apply(Module.scala:52)
[error]     at freechips.rocketchip.diplomacy.LazyModuleImpLike.$anonfun$instantiate$1(LazyModule.scala:158)
[error]     at scala.collection.immutable.List.flatMap(List.scala:335)
[error]     at freechips.rocketchip.diplomacy.LazyModuleImpLike.instantiate(LazyModule.scala:156)
[error]     at freechips.rocketchip.diplomacy.LazyModuleImpLike.instantiate$(LazyModule.scala:155)
[error] Nonzero exit code: 1

解析していくとPWMにアクセスに来る際のサイズに問題があるようなので、メモリマップを修正してレジスタの配置を8byteでアラインするようにした。

trait PWMTLModule extends HasRegMap {
  val io: PWMTLBundle
  implicit val p: Parameters
  def params: PWMParams

  val w = params.beatBytes * 8
  require(w <= 64)

  // How many clock cycles in a PWM cycle?
  val period = Reg(UInt(w.W))
  // For how many cycles should the clock be high?
  val duty = Reg(UInt(w.W))
  // Is the PWM even running at all?
  val enable = RegInit(false.B)

  val base = Module(new PWMBase(w))
  io.pwmout := base.io.pwmout
  base.io.period := period
  base.io.duty := duty
  base.io.enable := enable

  // BaseSubSystemをそのまま使うとwが64になるので
  // メモリマップを変更した。
  regmap(
    0x00 -> Seq(
      RegField(w, period)),
    0x08 -> Seq(
      RegField(w, duty)),
    0x10 -> Seq(
      RegField(1, enable)))
}

ここまでを修正すると以下のように正常にメモリマップが生成され、そこにPWMが追加されているのが確認できた。

Generated Address Map
           0 -     1000 ARWX  debug-controller@0
        2000 -     3000 ARW   pwm@2000 <-- 追加された
        3000 -     4000 ARWX  error-device@3000
       10000 -    20000  R X  rom@10000
     2000000 -  2010000 ARW   clint@2000000
     c000000 - 10000000 ARW   interrupt-controller@c000000
    80000000 - 90000000  RWXC memory@80000000

ビルド後に以下のようにエミュレータが生成されていることも確認できた。

$ ls
Makefile  Makefrag-verilator  emulator-example.slave-PWMConfig-debug  generated-src  generated-src-debug  output  verilator

シミュレータを実行してみる

riscv-testsの環境が正常にインストールされていると、以下のコマンドでriscv-testsの各テストを単体で実行可能だ。

./emulator-example.slave-PWMConfig-debug +max-cycles=100000000 +verbose -voutput/rv64ui-p-add.vcd output/rv64ui-p-add 3>&1 1>&2 2>&3 | /home/diningyo/prj/risc-v/rocket/tools/rv64gc/bin/spike-dasm  > output/rv64ui-p-add.out && [ $PIPESTATUS -eq 0 ]
$ ls output/
rv64ui-p-add.out  rv64ui-p-add.vcd

上記を実行して取得したのがこの「PWMモジュールを追加する」の1回目で掲載した以下の画像になる。

f:id:diningyo-kpuku-jougeki:20190909234138p:plain
Rocket Chipに追加されたPWMモジュール

ということで、一通りPWMモジュールを追加するサンプルをRocket Chipで試せる様に修正を行ってみた。次はプログラム回りの部分をもう少し解析していくつもりだ。