自作RISC-V CPUコアの方は、いくつかのコンフィグレーションにおいてDhrystoneを完走させることができるくらいになってきた。 次に、どこがボトルネックになっているのかを見てみるために、いくつか性能ダンパーを追加することにした。
基本的に見てみたいのは、どのキューがいっぱいになっているのか。例えば1000サイクルの間で、どのキューがどの程度のサイクルいっぱいになっているのかが分かれば、性能改善の余地がある。
- 命令バッファエントリ
- ROBエントリ
- ALUのスケジューラエントリ
- LDQスケジューラエントリ
- STQスケジューラエントリ
- BRUスケジューラエントリ
について、平均的なエントリ充填率と、エントリがいっぱいになった時間を測定する。
例えばLDQの場合はこういうようなエントリロガーを実装する。
logic [63: 0] r_cycle_count; logic [63: 0] r_ldq_max_period; logic [63: 0] r_ldq_entry_count; always_ff @ (negedge i_clk, negedge i_reset_n) begin if (!i_reset_n) begin r_ldq_max_period <= 'h0; r_ldq_entry_count <= 'h0; r_cycle_count <= 'h0; end else begin r_cycle_count <= r_cycle_count + 'h1; if (r_cycle_count % sim_pkg::COUNT_UNIT == sim_pkg::COUNT_UNIT-1) begin r_ldq_max_period <= 'h0; r_ldq_entry_count <= 'h0; end else begin if (|w_ldq_valid) begin if (&w_ldq_valid) begin r_ldq_max_period <= r_ldq_max_period + 'h1; end r_ldq_entry_count <= r_ldq_entry_count + $countones(w_ldq_valid); end end // else: !if(r_cycle_count % sim_pkg::COUNT_UNIT == sim_pkg::COUNT_UNIT-1) end // else: !if(!i_reset_n) end // always_ff @ (negedge i_clk, negedge i_reset_n) function void dump_perf (int fp); $fwrite(fp, " \"ldq\" : {"); $fwrite(fp, " \"max_period\" : %5d, ", r_ldq_max_period); $fwrite(fp, " \"average count\" : %5f},\n", r_ldq_entry_count / 1000.0); endfunction // dump_perf
これを1000サイクルに一回、各モジュールに対して送信して情報を取得する。
"inst_buffer" : { "max_period" : 146, "average count" : 4.058000}, "rob_entry" : { "max_period" : 0, "average count" : 9.331000}, "ldq" : { "max_period" : 37, "average count" : 8.216000}, "stq" : { "max_period" : 0, "average count" : 5.438000}, "alu" : { "max_period" : 0, "average count" : 5.368000}, "alu" : { "max_period" : 0, "average count" : 2.608000},
- 命令バッファは1000サイクル中146サイクルで埋まっている。エントリ自体は8個あるのだが、これは改善の余地がありそう。ちなみに平均で4個しか埋まっていないので、何らかの段階で命令バッファが埋まってしまっている。
- ROBを使い切っている場面はない
- LDQはちょいちょい使い切っている。
- それ以外のエントリは使い切っていない
つまり、命令バッファによる命令供給能力にそもそも問題がありそうだということが分かってくる。もう少し解析したい。