FPGA開発日記

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

Rocket-Chipのブートシーケンスと内部構造の解析(1)

RISC-V の実装の一つである Rocket-Chip の解析を行いたいのだが、Chiselの実装が複雑で詳細の解析に阻まれており、なかなか進まない。

やりたいこととしては、現在のRocket-Chip Generatorの環境を利用せずに、Rocket-Chipの生成されたVerilogファイルだけを使用してプログラムを動作させたい。

この環境が構築できれば、Rocket-ChipのRTLだけカスタマイズして自分のオリジナルの環境が構築できるようになるだろう。 そのために、Rocket-Chipがどのように動いているのかを解析して、少なくともLinuxがブートできるような環境を構築する必要がある。

Rocket-Chipがブートするブートシーケンスの仕組みを解析する

Rocket-Chipでプログラムを動かす、例えばDhrystoneを動作させたいときはrocket-chipのリポジトリで以下のように入力する。

cd rocket-chip/emulator
make CONFIG=DefaultConfig output/dhrystone.riscv.out

これでRocket-ChipのVerilogコードが生成され、この時の実行結果を解析するためには、output/dhrystone.riscv.out を見る必要がある。 このログファイルは実際に実行されていない命令の情報も入っているので、その辺りはgrepで削除していく。

grep " \[1\] pc=" output/dhrystone.riscv.out  | less -S
C0:         21 [1] pc=[0000010040] W[r10=0000000000000000][1] R[r 0=0000000000000000] R[r20=0163790d47bd1fab] inst=[f1402573] csrr    a0, mhartid
C0:         22 [1] pc=[0000010044] W[r11=0000000000010044][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[00000597] auipc   a1, 0x0
C0:         23 [1] pc=[0000010048] W[r11=0000000000010080][1] R[r11=0000000000010044] R[r28=0163790d47bd1fab] inst=[03c58593] addi    a1, a1, 60
C0:         24 [1] pc=[000001004c] W[r 0=ffffffd0053e315d][0] R[r 0=0000000000000000] R[r 5=0163790d47bd1fab] inst=[10500073] wfi (args unknown)
C0:         29 [1] pc=[0000010050] W[r 0=0000000000010052][1] R[r31=150ae1ddcefdcd8f] R[r29=0163790d47bd1fab] inst=[0000bff5] j       pc - 4
C0:         30 [1] pc=[000001004c] W[r 0=ffffffd0053e315d][0] R[r 0=0000000000000000] R[r 5=0163790d47bd1fab] inst=[10500073] wfi (args unknown)
C0:         35 [1] pc=[0000010050] W[r 0=0000000000010052][1] R[r31=150ae1ddcefdcd8f] R[r29=0163790d47bd1fab] inst=[0000bff5] j       pc - 4
C0:         36 [1] pc=[000001004c] W[r 0=ffffffd0053e315d][0] R[r 0=0000000000000000] R[r 5=0163790d47bd1fab] inst=[10500073] wfi (args unknown)
C0:         41 [1] pc=[0000010050] W[r 0=0000000000010052][1] R[r31=150ae1ddcefdcd8f] R[r29=0163790d47bd1fab] inst=[0000bff5] j       pc - 4
C0:         42 [1] pc=[000001004c] W[r 0=ffffffd0053e315d][0] R[r 0=0000000000000000] R[r 5=0163790d47bd1fab] inst=[10500073] wfi (args unknown)
C0:         47 [1] pc=[0000010050] W[r 0=0000000000010052][1] R[r31=150ae1ddcefdcd8f] R[r29=0163790d47bd1fab] inst=[0000bff5] j       pc - 4
... (以下略)

デバッグ用のVCDファイルを出力しながらシミュレーションを行う

VCDファイルの出力方法は以下だ。さらに生成されたvcdファイルをwlfに変換してQuestaSimで解析する。

make CONFIG=DefaultConfig output/dhrystone.riscv.vcd
vcd2wlf output/dhrystone.riscv.vcd output/dhrystone.riscv.wlf
f:id:msyksphinz:20180108144422p:plain

これを見ていくと、Rocket-Chipのブートシーケンスは以下のように分かれているように思われる。 1. BootROMからブートコードを実行する 2. デバッグモジュールからデバッグコードをフェッチする(おそらく実行バイナリファイルから情報を引き出して、IcacheとDCacheに格納しているものと思われる)。 3. メインプログラムを実行する

ちなみに、Rocket-Chipのアドレスマップは以下のようになっている。このアドレスマップはChisel上でどのように決められているのだ?

Generated Address Map
               0 -     1000 ARWX  debug-controller@0
            3000 -     4000 ARWX  error-device@3000
           10000 -    20000  R XC rom@10000
         2000000 -  2010000 ARW   clint@2000000
         c000000 - 10000000 ARW   interrupt-controller@c000000
        60000000 - 80000000  RWX  mmio@60000000
        80000000 - 90000000  RWXC memory@80000000
f:id:msyksphinz:20180108144610p:plain

まず1.のBootROMだが、これはrocket-chipリポジトリのbootromディレクトリにコードが格納されている。

cd bootrom
...
.section .text.hang, "ax", @progbits
.globl _hang
_hang:
  csrr a0, mhartid
  la a1, _dtb
1:
  wfi
  j 1b
...

このプログラムを見ればわかるとおり、ブートシーケンスはwfi(wait for interrupt)命令によってこの割り込みが入るのを待っている。この割り込みの挿入によってデバッグコードに移っていくものと思われる。

  1. デバッグモジュールは、おそらくは実行バイナリファイルを解析してDPIと接続し、ICacheとDCacheに転送しているのではないかと思われるので、ここも要解析だ。

  2. のメインプログラムは実行しているのはすぐにわかるのだが、DCacech/ICacheの位置を見つけて解析する必要がある。

f:id:msyksphinz:20180108155301p:plain

Rocket-ChipのBootROMを改造してプログラムを差し替えてみる

Rocket-ChipのBootROMはどのような構成になっているのだろうか? まずはVerilogの実装を見て確認してみよう。emulator/generated-src/freechips.rocketchip.system.DefaultConfig.v を確認してみると、BootROMと言いながら思いっきりハードワイヤードで実装してあった。

  • emulator/generated-src/freechips.rocketchip.system.DefaultConfig.v
module TLROM_bootrom( // @[:freechips.rocketchip.system.DefaultConfig.fir@194725.2]
  input         clock, // @[:freechips.rocketchip.system.DefaultConfig.fir@194726.4]
  input         reset, // @[:freechips.rocketchip.system.DefaultConfig.fir@194727.4]
  output        auto_in_a_ready, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  input         auto_in_a_valid, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  input  [2:0]  auto_in_a_bits_opcode, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  input  [2:0]  auto_in_a_bits_param, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  input  [1:0]  auto_in_a_bits_size, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  input  [8:0]  auto_in_a_bits_source, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  input  [16:0] auto_in_a_bits_address, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  input  [7:0]  auto_in_a_bits_mask, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  input         auto_in_d_ready, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  output        auto_in_d_valid, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  output [1:0]  auto_in_d_bits_size, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  output [8:0]  auto_in_d_bits_source, // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
  output [63:0] auto_in_d_bits_data // @[:freechips.rocketchip.system.DefaultConfig.fir@194728.4]
);
  wire  TLMonitor_clock; // @[Nodes.scala 25:25:freechips.rocketchip.system.DefaultConfig.fir@194735.4]
  wire  TLMonitor_reset; // @[Nodes.scala 25:25:freechips.rocketchip.system.DefaultConfig.fir@194735.4]
  wire  TLMonitor_io_in_a_ready; // @[Nodes.scala 25:25:freechips.rocketchip.system.DefaultConfig.fir@194735.4]
...
    .io_in_d_bits_size(TLMonitor_io_in_d_bits_size),
    .io_in_d_bits_source(TLMonitor_io_in_d_bits_source)
  );
  assign index = auto_in_a_bits_address[11:3]; // @[BootROM.scala 50:34:freechips.rocketchip.system.DefaultConfig.fir@195289.4]
  assign high = auto_in_a_bits_address[15:12]; // @[BootROM.scala 51:68:freechips.rocketchip.system.DefaultConfig.fir@195290.4]
  assign _T_1167 = high != 4'h0; // @[BootROM.scala 52:53:freechips.rocketchip.system.DefaultConfig.fir@195291.4]
  assign _GEN_1 = 9'h1 == index ? 64'h597f1402573 : 64'h1f414130010041b; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]
  assign _GEN_2 = 9'h2 == index ? 64'h840207458593 : _GEN_1; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]
  assign _GEN_3 = 9'h3 == index ? 64'h0 : _GEN_2; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]
  assign _GEN_4 = 9'h4 == index ? 64'h0 : _GEN_3; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]
  assign _GEN_5 = 9'h5 == index ? 64'h0 : _GEN_4; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]
  assign _GEN_6 = 9'h6 == index ? 64'h0 : _GEN_5; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]
  assign _GEN_7 = 9'h7 == index ? 64'h0 : _GEN_6; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]
  assign _GEN_8 = 9'h8 == index ? 64'hbcd5051b0000b537 : _GEN_7; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]
  assign _GEN_9 = 9'h9 == index ? 64'h2345859b000015b7 : _GEN_8; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]
  assign _GEN_10 = 9'ha == index ? 64'h1050007300b50633 : _GEN_9; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]
  assign _GEN_11 = 9'hb == index ? 64'hbff5 : _GEN_10; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]
  assign _GEN_12 = 9'hc == index ? 64'h0 : _GEN_11; // @[BootROM.scala 52:47:freechips.rocketchip.system.DefaultConfig.fir@195292.4]

これは、ブートプログラムをコピーして編集し、ロードする場所をConfig.scalaで変更した。

  • program/simple/simple.S (一部抜粋)
.section .text.hang, "ax", @progbits
.globl _hang
_hang:
        /* 新規追加部分 */
        li      a0, 0x0abcd
        li      a1, 0x01234
        add     a2, a0, a1
1:
  wfi
  j 1b
diff --git a/src/main/scala/coreplex/Configs.scala b/src/main/scala/coreplex/Configs.scala
index ab6d6fa..a6d6068 100644
--- a/src/main/scala/coreplex/Configs.scala
+++ b/src/main/scala/coreplex/Configs.scala
@@ -25,7 +25,7 @@ class BaseCoreplexConfig extends Config ((site, here, up) => {
   case MemoryBusKey => MemoryBusParams(beatBytes = site(XLen)/8, blockBytes = site(CacheBlockBytes))
   // Additional device Parameters
   case ErrorParams => ErrorParams(Seq(AddressSet(0x3000, 0xfff)), maxAtomic=site(XLen)/8, maxTransfer=4096)
-  case BootROMParams => BootROMParams(contentFileName = "./bootrom/bootrom.img")
+  case BootROMParams => BootROMParams(contentFileName = "./program/simple/simple.img")
   case DebugModuleParams => DefaultDebugModuleParams(site(XLen))
 })

github.com

ログファイルを確認すると、正しく実行できているようだ。

cd rocket-chip/emulator
make CONFIG=DefaultConfig output/dhrystone.riscv.out
grep "\[1\] pc" output/dhrystone.riscv.out | less -S

C0:         21 [1] pc=[0000010040] W[r10=000000000000b000][1] R[r 1=62bef4b391e515e7] R[r 0=0000000000000000] inst=[0000b537] lui     a0, 0xb
C0:         22 [1] pc=[0000010044] W[r10=000000000000abcd][1] R[r10=000000000000b000] R[r13=17f201a91c7cccd7] inst=[bcd5051b] addiw   a0, a0, -1075
C0:         23 [1] pc=[0000010048] W[r11=0000000000001000][1] R[r 0=0000000000000000] R[r 0=0000000000000000] inst=[000015b7] lui     a1, 0x1
C0:         24 [1] pc=[000001004c] W[r11=0000000000001234][1] R[r11=0000000000001000] R[r20=17f201a91c7cccd7] inst=[2345859b] addiw   a1, a1, 564
C0:         25 [1] pc=[0000010050] W[r12=000000000000be01][1] R[r10=000000000000abcd] R[r11=0000000000001234] inst=[00b50633] add     a2, a0, a1
C0:         26 [1] pc=[0000010054] W[r 0=ffffffe484e63bc8][0] R[r 0=0000000000000000] R[r 5=17f201a91c7cccd7] inst=[10500073] wfi (args unknown)
C0:         31 [1] pc=[0000010058] W[r 0=000000000001005a][1] R[r31=000000000000abcf] R[r29=17f201a91c7cccd7] inst=[0000bff5] j       pc - 4
C0:         32 [1] pc=[0000010054] W[r 0=ffffffe484e63bc8][0] R[r 0=0000000000000000] R[r 5=17f201a91c7cccd7] inst=[10500073] wfi (args unknown)
C0:         37 [1] pc=[0000010058] W[r 0=000000000001005a][1] R[r31=000000000000abcf] R[r29=17f201a91c7cccd7] inst=[0000bff5] j       pc - 4

計算により、 0x0abcd + 0x01234 = 0x0be01 という計算が正しく行えていることを確認した。