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
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
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命令でジャンプが行われる。なるほど、意外とシンプルなモデルだ。