FPGA開発日記

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

RISC-V BOOMv2 で命令レイテンシ・スループットを測定する環境の構築 (1. 環境の構築)

Rocket-Chip で測定したベンチマークプログラムを、アウトオブオーダプロセッサであるBOOMでも測定してみたい。

BOOMはVersion1 (BOOMv1) と Version2 (BOOMv2) が存在しているのだが、とりあえずBOOMv2 で試してみよう。

結構試行錯誤しないと動作しなかったのでまとめておく。

f:id:msyksphinz:20180313224018p:plain
図. BOOMv1 と BOOMv2 のパイプライン構成の違い。BOOMv2 の方が動作周波数が高いが、性能は落ちる傾向がある。

BootROM の改造

BOOMv2のブートROMは、デフォルトでは./bootrom/bootrom.imgを使っている。

  • bootrom/bootrom.S
.text
.global _start
_start:
  // This boot ROM doesn't know about any boot devices, so it just spins,
  // waiting for the debugger to load a program and change the PC.
  j _start // reset vector
  .word 0 // reserved
  .word 0 // reserved
...

これでは、デフォルトではループしっぱなしである。別のブートROMを用意して、そちらからブートするように変更する。

  • program/boomv2_boot/bootrom.S
#define DRAM_BASE 0x80000000

.section .text.start, "ax", @progbits
.globl _start
_start:
        j   start_routine

        .rept   15
        nop
        .endr

start_routine:
        csrr    a0, mhartid
        la      a1, _dtb
        li              sp, 0x80010000
        li              a2, 0x80000000
    jr      a2

fin_loop:
        j       fin_loop

BootROMの設定は、以下のscala記述によって設定されている。

diff --git a/src/main/scala/coreplex/Configs.scala b/src/main/scala/coreplex/Configs.scala
index 3f265d84..dcd0b0d3 100644
--- a/src/main/scala/coreplex/Configs.scala
+++ b/src/main/scala/coreplex/Configs.scala
@@ -26,7 +26,7 @@ class BaseCoreplexConfig extends Config ((site, here, up) => {
   case NTiles => site(RocketTilesKey).size
   case CBusConfig => TLBusConfig(beatBytes = site(XLen)/8)
   case L1toL2Config => TLBusConfig(beatBytes = site(XLen)/8) // increase for more PCIe bandwidth
-  case BootROMFile => "./bootrom/bootrom.img"
+  case BootROMFile => "../../program/boomv2_boot/bootrom.img"
   case BroadcastConfig => BroadcastConfig()
   case BankedL2Config => BankedL2Config()
   case CacheBlockBytes => 64

詳細は不明だが、どうやらBootROMの0x0cの場所は0でなければならないらしい?そんなルールは不要なので削除する。

これらをパッチとして用意し、Verilogを生成する際に適用する。

diff --git a/src/main/scala/rocketchip/Utils.scala b/src/main/scala/rocketchip/Utils.scala
index 78f4bb9c..23e98af8 100644
--- a/src/main/scala/rocketchip/Utils.scala
+++ b/src/main/scala/rocketchip/Utils.scala
@@ -93,8 +93,8 @@ object GenerateBootROM {

     require(address == address.toInt)
     val configStringAddr = address.toInt + rom.capacity
-    require(rom.getInt(12) == 0,
-      "Config string address position should not be occupied by code")
+    // require(rom.getInt(12) == 0,
+    //   "Config string address position should not be occupied by code")
     rom.putInt(12, configStringAddr)
     rom.array() ++ (configString.getBytes.toSeq)
   }

デバッグ情報を生成する

たとえシミュレーションができるようになっても、デバッグ情報が出てこないと正しく命令が実行できているかを解析できない。 そこで、scalaの実装の部分を少し変更し、コミットログを表示させて、加工しやすいように変更する。BOOMv2 の実装の、ログ生成部分に以下の変更を加える。

diff --git a/src/main/scala/consts.scala b/src/main/scala/consts.scala
index de697d3..38deea9 100755
--- a/src/main/scala/consts.scala
+++ b/src/main/scala/consts.scala
@@ -22,7 +22,7 @@ import util.Str
 trait BOOMDebugConstants
 {
    val DEBUG_PRINTF        = false // use the Chisel printf functionality
-   val COMMIT_LOG_PRINTF   = false // dump commit state, for comparision against ISA sim
+   val COMMIT_LOG_PRINTF   = true // dump commit state, for comparision against ISA sim
    val O3PIPEVIEW_PRINTF   = false // dump trace for O3PipeView from gem5
    val O3_CYCLE_TIME       = (1000)// "cycle" time expected by o3pipeview.py

diff --git a/src/main/scala/core.scala b/src/main/scala/core.scala
index ae238d5..de7a91f 100755
--- a/src/main/scala/core.scala
+++ b/src/main/scala/core.scala
@@ -1249,20 +1249,20 @@ class BoomCore(implicit p: Parameters, edge: uncore.tilelink2.TLEdgeOut) extends
          {
             when (rob.io.commit.uops(w).dst_rtype === RT_FIX && rob.io.commit.uops(w).ldst =/= UInt(0))
             {
-               printf("%d 0x%x (0x%x) x%d 0x%x\n",
-                  priv, Sext(rob.io.commit.uops(w).pc(vaddrBits,0), xLen), rob.io.commit.uops(w).inst,
-                  rob.io.commit.uops(w).inst(RD_MSB,RD_LSB), rob.io.commit.uops(w).debug_wdata)
+               printf("%d %d 0x%x DASM(%x) x%d 0x%x\n",
+                 debug_tsc_reg, priv, Sext(rob.io.commit.uops(w).pc(vaddrBits,0), xLen), rob.io.commit.uops(w).inst,
+                 rob.io.commit.uops(w).inst(RD_MSB,RD_LSB), rob.io.commit.uops(w).debug_wdata)
             }
             .elsewhen (rob.io.commit.uops(w).dst_rtype === RT_FLT)
             {
-               printf("%d 0x%x (0x%x) f%d 0x%x\n",
-                  priv, Sext(rob.io.commit.uops(w).pc(vaddrBits,0), xLen), rob.io.commit.uops(w).inst,
-                  rob.io.commit.uops(w).inst(RD_MSB,RD_LSB), rob.io.commit.uops(w).debug_wdata)
+               printf("%d %d 0x%x DASM(%x) f%d 0x%x\n",
+                 debug_tsc_reg, priv, Sext(rob.io.commit.uops(w).pc(vaddrBits,0), xLen), rob.io.commit.uops(w).inst,
+                 rob.io.commit.uops(w).inst(RD_MSB,RD_LSB), rob.io.commit.uops(w).debug_wdata)
             }
             .otherwise
             {
-               printf("%d 0x%x (0x%x)\n",
-                  priv, Sext(rob.io.commit.uops(w).pc(vaddrBits,0), xLen), rob.io.commit.uops(w).inst)
+               printf("%d %d 0x%x DASM(%x)\n",
+                 debug_tsc_reg, priv, Sext(rob.io.commit.uops(w).pc(vaddrBits,0), xLen), rob.io.commit.uops(w).inst)
             }
          }
       }

ベンチマークプログラムのhexファイル生成

上記のブートコードでは、プログラムの開始位置を0x8000_0000に設定してある。これはCoreplexの外部にあるAXIメモリの先頭にあたる。 このAXIメモリもScalaで記述されており、そこに$readmemhでプログラムのhexファイルを配置すれば良さそうだ。

と思って配置しようとしたら、大変なことに気がついた。8ビット毎にメモリが区切って実装してある!

  • rtl/rocketchip.BOOMConfig.v
module AXI4RAM(
...
  reg [7:0] mem_0 [0:33554432-1];
...
  reg [7:0] mem_1 [0:33554432-1];
...
  reg [7:0] mem_6 [0:33554432-1];
...
  reg [7:0] mem_7 [0:33554432-1];

AXIメモリを改造することもできるけど、面倒なのでhexファイルを8分割した。1バイト毎に8個のhexファイルに分割する。

  • tp_F_fadd_s.dmp
...
    80000020:   04000e13                li      t3,64
    80000024:   00000697                auipc   a3,0x0
    80000028:   1806a787                flw     fa5,384(a3) # 800001a4 <run_bench+0x14>
    8000002c:   00f7f753                fadd.s  fa4,fa5,fa5
    80000030:   00f7f6d3                fadd.s  fa3,fa5,fa5
    80000034:   00f7f653                fadd.s  fa2,fa5,fa5
    80000038:   00f7f5d3                fadd.s  fa1,fa5,fa5
    8000003c:   00f7f553                fadd.s  fa0,fa5,fa5
    80000040:   00f7f053                fadd.s  ft0,fa5,fa5
    80000044:   fffe039b                addiw   t2,t3,-1
    80000048:   00f7f753                fadd.s  fa4,fa5,fa5
    8000004c:   00f7f6d3                fadd.s  fa3,fa5,fa5
    80000050:   00f7f653                fadd.s  fa2,fa5,fa5
...
  • tp_F_fadd_s.hex
...
 13 0e 00 04 97 06 00 00
 87 a7 06 18 53 f7 f7 00
 d3 f6 f7 00 53 f6 f7 00
 d3 f5 f7 00 53 f5 f7 00
 53 f0 f7 00 9b 03 fe ff
 53 f7 f7 00 d3 f6 f7 00
  • tp_F_fadd_s.hex_0
89
30
00
23
13
87
d3
d3
53
53
53
53
1b
d3
...

こんな感じの細切れのhexファイルを生成するスクリプトを作って読み込ませた。

実行結果は以下だ。ここでは、tp_F_fadd_s.hex を実行した例だ。 fadd.s 命令のスループットを測定する。 依存関係のある命令の実行だ。命令がキャッシュに載っている状態で、単精度の加算命令は演算に7サイクル必要であることが分かる。

...
                3699 3 0x00000000800000bc fadd.s  ft0, fa0, fa5 f 0 0x53babba753babba7
                3706 3 0x00000000800000c0 fadd.s  ft3, ft0, fa5 f 3 0x35d9bb6b35d9bb6b
                3713 3 0x00000000800000c4 fadd.s  ft4, ft3, fa5 f 4 0x53babba753babba7
                3720 3 0x00000000800000c8 fadd.s  ft5, ft4, fa5 f 5 0x35d9bb6b35d9bb6b
                3727 3 0x00000000800000cc fadd.s  ft6, ft5, fa5 f 6 0x53babba753babba7
                3734 3 0x00000000800000d0 fadd.s  ft7, ft6, fa5 f 7 0x35d9bb6b35d9bb6b
                3741 3 0x00000000800000d4 fadd.s  ft0, ft7, fa5 f 0 0x53babba753babba7
                3748 3 0x00000000800000d8 fadd.s  fa6, ft0, fa5 f16 0x35d9bb6b35d9bb6b
                3755 3 0x00000000800000dc fadd.s  fa7, fa6, fa5 f17 0x53babba753babba7
                3762 3 0x00000000800000e0 fadd.s  ft8, fa7, fa5 f28 0x35d9bb6b35d9bb6b
                3769 3 0x00000000800000e4 fadd.s  ft9, ft8, fa5 f29 0x53babba753babba7
...