FPGA開発日記

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

Sniperの動作原理をトレースする (3. Performance Modelの解析)

Sniperのログを見ながら、Sniperのサイクル計算モデルを検証してみようと思う。

ROBは1サイクル毎に更新されるので、その状態を観察する。

   Outstanding loads: 1  stores: 0
   [  0]  DONE@+37                        3  LOAD      100c: ld          t0, 24(t0)  {0x1018}
   [  1]  READY@+36                       4  EXEC ( 1) 100c: ld          t0, 24(t0)
   [  2]  DONE@+0                         5  EXEC ( 1) 1010: jr          t0
   [  3]  PREROB                          6  EXEC ( 1) 80000000: mv          ra, zero
   [  4]  PREROB                          7  EXEC ( 1) 80000002: mv          sp, zero
   [  5]  PREROB                          8  EXEC ( 1) 80000004: mv          gp, zero
   [  6]  PREROB                          9  EXEC ( 1) 80000006: mv          tp, zero
   [  7]  PREROB                         10  EXEC ( 1) 80000008: mv          t0, zero
   [  8]  PREROB                         11  EXEC ( 1) 8000000a: mv          t1, zero
   [  9]  PREROB                         12  EXEC ( 1) 8000000c: mv          t2, zero
   [ 10]  PREROB                         13  EXEC ( 1) 8000000e: mv          s0, zero
   [ 11]  PREROB                         14  EXEC ( 1) 80000010: mv          s1, zero
  1. PREROB はROBにまだ入っていない状態を指す。命令はDispatchされたがまだROBにインプットされていないということかな? シミュレーション的に、1命令を実行したときに、まずはROBに入らずにフロントエンドでフェッチ状態となる。

以下の状態だと、サイクル0で命令は10個存在し、すべてフェッチできていない状態。で、272サイクル後に命令キャッシュからのレスポンスが返ってくるというわけで、次に272サイクル目に飛ぶ。

Running cycle 0
-- icache miss(272)
** ROB state @ 0  size(0) total(10)
   Front-end stalled until 272, in I-cache miss
   RS entries: 0
   Outstanding loads: 0  stores: 0
   [  0]  PREROB                          0  EXEC ( 1) 1000: auipc       t0, pc + 0
   [  1]  PREROB                          1  EXEC ( 1) 1004: addi        a1, t0, 32
   [  2]  PREROB                          2  EXEC ( 1) 1008: csrrs       a0, mhartid, zero
   [  3]  PREROB                          3  LOAD      100c: ld          t0, 24(t0)  {0x1018}
   [  4]  PREROB                          4  EXEC ( 1) 100c: ld          t0, 24(t0)
   [  5]  PREROB                          5  EXEC ( 1) 1010: jr          t0
   [  6]  PREROB                          6  EXEC ( 1) 80000000: mv          ra, zero
   [  7]  PREROB                          7  EXEC ( 1) 80000002: mv          sp, zero
   [  8]  PREROB                          8  EXEC ( 1) 80000004: mv          gp, zero
   [  9]  PREROB                          9  EXEC ( 1) 80000006: mv          tp, zero
  1. READYはROBに入って実行可能状態になっている。ただ、命令的には2は1に依存しているし、3はSerializeに実行しなければならない気がするのだがそれは違うのか?

READY@+1 というのは、1サイクル後にREADY状態になることを意味している。

Running cycle 272
-- icache return
DISPATCH  EXEC   (auipc   :0x2   )
DISPATCH  EXEC   (addi    :0x13  )
DISPATCH  EXEC   (csrrs   :0x7e  )
DISPATCH  LOAD   (ld      :0x29  )
DISPATCH  EXEC   (ld      :0x29  )
** ROB state @ 272  size(5) total(10)
   RS entries: 5
   Outstanding loads: 0  stores: 0
   [  0]  READY@+1                        0  EXEC ( 1) 1000: auipc       t0, pc + 0
   [  1]  READY@+1                        1  EXEC ( 1) 1004: addi        a1, t0, 32
   [  2]  READY@+1                        2  EXEC ( 1) 1008: csrrs       a0, mhartid, zero
   [  3]  READY@+1                        3  LOAD      100c: ld          t0, 24(t0)  {0x1018}
DynamicMicroOp::getDependency(0) Length=0
   [  4]  DEPS 3                          4  EXEC ( 1) 100c: ld          t0, 24(t0)

次のサイクルでは、READY状態から完了してDONE状態になっている。

Running cycle 273
DISPATCH  EXEC   (jr      :0x133 )
-- icache miss(297)
ISSUE     EXEC   (auipc   :0x2   )   latency=1
ISSUE     EXEC   (addi    :0x13  )   latency=1
ISSUE     EXEC   (csrrs   :0x7e  )   latency=1
** ROB state @ 273  size(6) total(15)
   Front-end stalled until 570, in I-cache miss
   RS entries: 3
   Outstanding loads: 0  stores: 0
   [  0]  DONE@+2                         0  EXEC ( 1) 1000: auipc       t0, pc + 0
   [  1]  DONE@+2                         1  EXEC ( 1) 1004: addi        a1, t0, 32
   [  2]  DONE@+2                         2  EXEC ( 1) 1008: csrrs       a0, mhartid, zero
   [  3]  READY@+0                        3  LOAD      100c: ld          t0, 24(t0)  {0x1018}
DynamicMicroOp::getDependency(0) Length=0
   [  4]  DEPS 3                          4  EXEC ( 1) 100c: ld          t0, 24(t0)
   [  5]  READY@+1                        5  EXEC ( 1) 1010: jr          t0

次のサイクルでは、最初の命令があと1命令で実行終了し、それに基づいて0x100cのLD命令が動き出す。これはキャッシュミス(というかUCアクセス)で40サイクルかかる計算になっている。

Running cycle 274
ISSUE     LOAD   (ld      :0x29  )   latency=39
ISSUE     EXEC   (jr      :0x133 )   latency=1
** ROB state @ 274  size(6) total(15)
   Front-end stalled until 570, in I-cache miss
   RS entries: 1
   Outstanding loads: 1  stores: 0
   [  0]  DONE@+1                         0  EXEC ( 1) 1000: auipc       t0, pc + 0
   [  1]  DONE@+1                         1  EXEC ( 1) 1004: addi        a1, t0, 32
   [  2]  DONE@+1                         2  EXEC ( 1) 1008: csrrs       a0, mhartid, zero
   [  3]  DONE@+40                        3  LOAD      100c: ld          t0, 24(t0)  {0x1018}
   [  4]  READY@+39                       4  EXEC ( 1) 100c: ld          t0, 24(t0)
   [  5]  DONE@+2                         5  EXEC ( 1) 1010: jr          t0

40サイクル後にLDが完了し、それとともにJR命令でジャンプが行われる。なるほど、意外とシンプルなモデルだ。