FPGA開発日記

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

RISC-V BOOM2 を改造してSoC環境を構築する (1. BOOM2 のブートの仕組み解析)

BOOM (Berkeley Out-of-Order Machine) は、UCBが開発して管理(現在はRISC-VでHPC/AIの参入を発表しているEsperanto?)しているRISC-Vのアウトオブオーダプロセッサだ。

BOOM (Berkeley Out-of-Order Machine) について

これまで、BOOMについては以下のような記事を中心にいろいろと解析してきた。 簡単に言うとBOOMはRISC-Vのアウトオブオーダ実装であり、2-way(2命令同時発行)、4-way(4命令同時発行)が公開されている。

BOOMプロセッサを搭載したSoCシミュレーション環境を作る

これまでRocket-Core, SiFive Freedom SoCを使って作ってきたように、BOOMのコアのRTLを抜き出して、自分のオリジナルSoCを作る環境を構築したい。

BOOMはRocket-Coreと同様にChiselで記述されているので、ChiselからVerilog-hDLに変換し、そのRTLをテストベンチに搭載してRTLシミュレーション環境を構築する。

f:id:msyksphinz:20180221235434p:plain

BOOMのRTLファイルを生成する

Chiselで記述されているBOOMプロセッサのVerilog-HDLを生成するためには、Rocket-Chip GeneratorのリポジトリでBOOMのブランチに移動し、RTLファイルを生成する。

github.com

  • Top.BOOMConfig.v
module Top(
  input   clk,
  input   reset,
  input   io_mem_axi_0_aw_ready,
  output  io_mem_axi_0_aw_valid,
  output [31:0] io_mem_axi_0_aw_bits_addr,
  output [7:0] io_mem_axi_0_aw_bits_len,
  output [2:0] io_mem_axi_0_aw_bits_size,
  output [1:0] io_mem_axi_0_aw_bits_burst,
  output  io_mem_axi_0_aw_bits_lock,
  output [3:0] io_mem_axi_0_aw_bits_cache,
  output [2:0] io_mem_axi_0_aw_bits_prot,
  output [3:0] io_mem_axi_0_aw_bits_qos,
  output [3:0] io_mem_axi_0_aw_bits_region,
  output [4:0] io_mem_axi_0_aw_bits_id,
  output  io_mem_axi_0_aw_bits_user,
  input   io_mem_axi_0_w_ready,
  output  io_mem_axi_0_w_valid,
  output [63:0] io_mem_axi_0_w_bits_data,
...

クロック・リセット・あとはAXIのポートが生成されていることが確認できる。

  • clk : クロック
  • reset : リセット
  • io_mem_axi_0 : Master側のAXIバス. AR, AW, R, W, B のチャネルが用意されている。
  • io_interrupts : 例外入力
  • io_debug : デバッグポート

が生成されていることが確認できた。 とりあえずこれらのポートはすべて0に落として、動作を解析していくことにする。

BOOMでRTLシミュレーション時にデバッグ情報を生成するオプション

BOOMはオプションを何も指定しないと、CPUの動作トレース情報を何も出力してくれない。 これはRocket-Coreとは異なる部分だ。しかし、Chiselの一部分を書き換えるとデバッグ情報を生成してくれるようになる。

...
trait BOOMDebugConstants
{
   val DEBUG_PRINTF        = false // use the Chisel printf functionality
   val DEBUG_ENABLE_COLOR  = false // provide color to print outs. Requires a VIM plugin to work properly :(
   val COMMIT_LOG_PRINTF   = false // 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
...

この中の、DEBUG_PRINTFをTrueに設定することでデバッグ情報を出力できるようになる。

少し順番が前後するが、上記のオプションを追加することでRTLシミュレーション時にトレース情報を出力してくれるようになる。

--- Cyc=                 347 , ----------------- Ret:                   34 ----------------------------------                                                                                                                                                        [134/1297]
  Dec:  ([0x00820]                        [0x00824]                        ) ctate: (N: _ _ _     R) BMsk:00 Mode:M
(--)  DASM(00000000)  |  (--)  DASM(00000000)  |  )
   fin(0)  [ISA: 0, 0, 0,32] [Phs:  0(-)  0[R](X)  0[R](-) 32[R](-)]   [ISA: 0, 0, 0,32] [Phs:  0(-)  0[R](X)  0[R](-) 32[R](-)] Exct(-                   2) Commit(0) fl: 0x3ffffffffffffffffffffffffff2 (107) is: 0x0000000000000000000000000000 (  0)
                          Branch Unit:  , ,0 PC=0x00800, 0 Targ=0x00804 NPC=0,0x00824 00
 brob[ 0] (2) T=0 m=2 r=28 B
 brob[ 1] (0) T=2 m=0 r=30
 brob[ 2] (0) T=0 m=0 r= 0
 brob[ 3] (0) T=0 m=0 r= 0
 brob[ 4] (0) T=0 m=0 r= 0
 brob[ 5] (0) T=0 m=0 r= 0
 brob[ 6] (0) T=0 m=0 r= 0
 brob[ 7] (0) T=0 m=0 r= 0
 brob[ 8] (0) T=0 m=0 r= 0
 brob[ 9] (0) T=0 m=0 r= 0
 brob[10] (0) T=0 m=0 r= 0
 brob[11] (0) T=0 m=0 r= 0
 brob[12] (0) T=0 m=0 r= 0
 brob[13] (0) T=0 m=0 r= 0
 brob[14] (0) T=0 m=0 r= 0
 brob[15] (0) T=0 m=0 r= 0
 brob[16] (0) T=0 m=0 r= 0
 brob[17] (0) T=0 m=0 r= 0
 brob[18] (0) T=0 m=0 r= 0
 brob[19] (0) T=0 m=0 r= 0
 brob[20] (0) T=0 m=0 r= 0
 brob[21] (0) T=0 m=0 r= 0
 brob[22] (0) T=0 m=0 r= 0
 brob[23] (0) T=0 m=0 r= 0
 predictor: ghist: 0x0005, r_ghist: 0x0005 commit: 0x0005
wakeup_idx: 15, ld is head of ROB:0
         ldq[ 0]=(----_U_ 0) st_dep( 6,m=0000) 0x00438 H T   saq[ 0]=(-------) b:00 0x00438 -> 0x0000000000000000
         ldq[ 1]=(----___ 0) st_dep( 5,m=0000) 0x00000       saq[ 1]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[ 2]=(----___ 0) st_dep( 5,m=0000) 0x00000       saq[ 2]=(-------) b:00 0x0043c -> 0xffffffffffffffff
         ldq[ 3]=(----___ 0) st_dep( 5,m=0000) 0x00000       saq[ 3]=(-------) b:00 0x00100 -> 0x0000000000000000
         ldq[ 4]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 4]=(-------) b:00 0x00438 -> 0x0000000000000000
         ldq[ 5]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 5]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[ 6]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 6]=(-------) b:00 0x0043c -> 0xffffffffffffffff
         ldq[ 7]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 7]=(-------) b:00 0x00000 -> 0x0000000000000000 H E C T
         ldq[ 8]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 8]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[ 9]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[ 9]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[10]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[10]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[11]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[11]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[12]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[12]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[13]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[13]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[14]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[14]=(-------) b:00 0x00000 -> 0x0000000000000000
         ldq[15]=(----___ 0) st_dep( 0,m=0000) 0x00000       saq[15]=(-------) b:00 0x00000 -> 0x0000000000000000
BrPred1:    (IF1_PC= n/a- Predict:n/a) ------ PC: [--_-  for br_id:(n/a),     next: 0x000004782c ifst:0]
I$ Response: (-) IF2_PC= 0x0000000824 (mask:0x2) DASM(7b002473)DASM(00847413) ----BrPred2:(-,-,0) [btbtarg: 0x00000] jkilmsk:0x3 ->(0x2)
bp2_aligned_pc: 0x0000000820 BHT:(- 0x000004782c, 1) p:0 (1) b:0 j:0 (1) J -
  integer_issue_slot[ 0](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 1](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 2](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 3](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 4](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 5](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 6](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 7](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 8](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[ 9](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[10](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[11](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[12](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[13](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[14](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[15](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[16](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  0,  0) PDST:  0 - [ [DASM(00000000)]  0x00000000:   0] ri: 0 bm=  0 imm=0x00000
  integer_issue_slot[17](-)(Req:-):wen=  P:(!,!,!) OP:(  0,  1, 40) PDST:  4 - [ [DASM(42802e23)]  0x0000081c:   2] ri:37 bm=  0 imm=0x43c02
  integer_issue_slot[18](-)(Req:-):wen=  P:(!,!,!) OP:(  0, 20, 62) PDST:  2 X [ [DASM(f1402473)]  0x00000820:  31] ri:38 bm=  0 imm=0xf1402

SoC 構成でのRTLシミュレーション

上記のオプションでRTLシミュレーションを実行してみた。

CPUのフェッチポートを観測していると、どうやら0x1000を何度もフェッチしているようだ。 これはどういうことだろう?

f:id:msyksphinz:20180221232810p:plain

というわけでBootROMを確認してみると、成程、BOOMの場合は最初にプログラムのロードが入って デバッグ割り込みが挿入されないと起動しない仕組みになっているのか。

  • ./src/main/scala/RocketChip.scala
  def makeBootROM()(implicit p: Parameters) = {
    val rom = java.nio.ByteBuffer.allocate(32)
    rom.order(java.nio.ByteOrder.LITTLE_ENDIAN)

    // for now, have the reset vector jump straight to memory
    val resetToMemDist = p(GlobalAddrMap)("mem").start - p(ResetVector)
    require(resetToMemDist == (resetToMemDist.toInt >> 12 << 12))
    val configStringAddr = p(ResetVector).toInt + rom.capacity

    // 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.
    rom.putInt(0x0000006f)                        // loop forever
    rom.putInt(0)                                 // reserved
    rom.putInt(0)                                 // reserved
    rom.putInt(configStringAddr)                  // pointer to config string
    rom.putInt(0)                                 // default trap vector
    rom.putInt(0)                                 //   ...
    rom.putInt(0)                                 //   ...
    rom.putInt(0)                                 //   ...

    rom.array() ++ p(ConfigString).toSeq
  }

面倒なので、とりあえずテストベンチでforce文を突っ込んでデバッグ例外を発生させてみよう。

  • ./tb/tb_freechips_boom2.v
  initial begin
    force tb.duv.uncore_io_prci_0_interrupts_debug = 1'b0;
    #(100ps);
    force tb.duv.uncore_io_prci_0_interrupts_debug = 1'b1;
    #(10ps);
  end

成程、デバッグ例外を挿入すると無限ループから脱出できた。

f:id:msyksphinz:20180221233752p:plain