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命令同時発行)が公開されている。
- RISC-V BOOMについて、とビルド試行 (1. チェックアウトとビルド、シミュレーションまで)
- RISC-V BOOMについて (2. ビルド試行とVagrant環境構築)
- CPUのパイプライントレースビューアGem5 (RISC-V BOOMプロセッサのパイプラインを分解)
- RISC-VのOoOプロセッサ、BOOMの性能を読み解く(1)
- RISC-VのOoOプロセッサ、BOOMの性能を読み解く(2)
- BOOM RISC-V実装RTLにてベンチマークを計測する
- RocketChip/BOOMプロセッサの波形ダンプの方法
- RISC-Vのアウトオブオーダ実装 BOOM v2の内部構成
BOOMプロセッサを搭載したSoCシミュレーション環境を作る
これまでRocket-Core, SiFive Freedom SoCを使って作ってきたように、BOOMのコアのRTLを抜き出して、自分のオリジナルSoCを作る環境を構築したい。
BOOMはRocket-Coreと同様にChiselで記述されているので、ChiselからVerilog-hDLに変換し、そのRTLをテストベンチに搭載してRTLシミュレーション環境を構築する。
BOOMのRTLファイルを生成する
Chiselで記述されているBOOMプロセッサのVerilog-HDLを生成するためには、Rocket-Chip GeneratorのリポジトリでBOOMのブランチに移動し、RTLファイルを生成する。
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を何度もフェッチしているようだ。 これはどういうことだろう?
というわけで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
成程、デバッグ例外を挿入すると無限ループから脱出できた。