「Rocket Chipに自分のモジュールを追加してみる」の4回目&一旦ここまででお終い。 firesimのデータをそのままRocket Chipに持って行ってもエラーが出てビルドできなかったので、Rocket Chip上でビルドするにあたって行った修正についてをまとめておく。
自前のConfigのビルド方法
今回はエミュレータのビルドを行う。Rocket ChipのREADME.mdに載っているようにただmake run
と実行するとDefaultConfig
を使ったエミュレータが生成されるのだが、ビルド時の変数指定で自前で用意したConfig
を使ったエミュレータの生成が可能となっている。
指定する必要のある変数は以下の2つだ。
今回自前で用意したソースコードは以下のような構成になっている。
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つのインポートとこれを参照している部分を削除した。
- Top.scala
//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
- Configs.scala
モジュールのインポートを修正してビルドすると以下のように端子の接続部分でアサインできない旨のエラーが発生する。
[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.debug
がOption
型になっているのが原因なのでget
を呼んでやればいい。
- TestHarness.scala
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の物に変更してみた。
- Configs.scala
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回目で掲載した以下の画像になる。
ということで、一通りPWMモジュールを追加するサンプルをRocket Chipで試せる様に修正を行ってみた。次はプログラム回りの部分をもう少し解析していくつもりだ。