FPGA開発日記

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

自作CPUのOoO発行の様子を計測するための仕組みの作成

自作CPUはアウト・オブ・オーダ命令発行できるような構成にしているのだが、そのうちどれくらいが実際にアウト・オブ・オーダ発行できているのかをちゃんと計測できていない。

いい機会なので、命令スケジューラに統計情報を処理する機能を追加して、全体命令のうちどれくらいがアウト・オブ・オーダ発行されているのかを計測してみることにする。

必要なことは: 1. 全体のスケジューラのうち最も古い命令を把握する 2. 各スケジューラで発行された命令が、最も古い命令よりも若いかを判定し、該当すれば、それをアウト・オブ・オーダ発行ととらえる

まず、1. が結構面倒くさくて、分散スケジューラの方式をとっており、しかも各スケジューラの数は可変にしているのでちゃんと全部把握するためのSystemVerilogのコードを書くのが面倒くさい。

とりあえず、すべてのスケジューラからエントリ情報を取得して、そのうちのもっとも古いものを抜き出すようにかなり無理やりな実装にしている。

   // ---------------------------
   // ALU Issue Unit Information                                                                                                                                                                                                                                                                                                                                                          
   // ---------------------------
   for (genvar alu_idx = 0; alu_idx < mycpu_conf_pkg::ALU_INST_NUM; alu_idx++) begin : alu_issue_loop
     /* verilator lint_off UNOPTFLAT */
     mycpu_pkg::cmt_id_t w_alu_oldest_cmt_id[mycpu_conf_pkg::RV_ALU_ENTRY_SIZE];
     mycpu_pkg::grp_id_t w_alu_oldest_grp_id[mycpu_conf_pkg::RV_ALU_ENTRY_SIZE];

     for (genvar entry_idx = 0; entry_idx < mycpu_conf_pkg::RV_ALU_ENTRY_SIZE; entry_idx++) begin
     logic w_id0_is_older_than_id1;
       assign w_id0_is_older_than_id1 = mycpu_pkg::id0_is_older_than_id1 (entry_idx == 'h0 ? 'h0 : w_alu_oldest_cmt_id[entry_idx-1],
                                                                           entry_idx == 'h0 ? 'h0 : w_alu_oldest_grp_id[entry_idx-1],
                                                                           u_mycpu_subsystem_wrapper.u_mycpu_subsystem.u_tile.alu_loop[alu_idx].u_alu.u_mycpu_issue_unit.w_entry[entry_idx].cmt_id,
                                                                           u_mycpu_subsystem_wrapper.u_mycpu_subsystem.u_tile.alu_loop[alu_idx].u_alu.u_mycpu_issue_unit.w_entry[entry_idx].grp_id);

       assign {w_alu_oldest_cmt_id[entry_idx],
               w_alu_oldest_grp_id[entry_idx]} = u_mycpu_subsystem_wrapper.u_mycpu_subsystem.u_tile.alu_loop[alu_idx].u_alu.u_mycpu_issue_unit.w_entry[entry_idx].valid &
                                                 w_id0_is_older_than_id1 ?
                                                 {u_mycpu_subsystem_wrapper.u_mycpu_subsystem.u_tile.alu_loop[alu_idx].u_alu.u_mycpu_issue_unit.w_entry[entry_idx].cmt_id,
                                                  u_mycpu_subsystem_wrapper.u_mycpu_subsystem.u_tile.alu_loop[alu_idx].u_alu.u_mycpu_issue_unit.w_entry[entry_idx].grp_id} :
                                                 entry_idx == 'h0 ? 'h0 :
                                                 {w_alu_oldest_cmt_id[entry_idx-1], w_alu_oldest_grp_id[entry_idx-1]};

     end // for (genvar entry_idx = 0; entry_idx < u_mycpu_subsystem_wrapper.u_mycpu_subsystem.u_tile.alu_loop[alu_idx].u_alu.u_mycpu_issue_unit.ENTRY_SIZE; entry_idx++)                                                                                                                                                                                                              

     always_ff @ (negedge w_clk, negedge w_mycpu_reset_n) begin
       issue_entries[ALU_ISSUE_BASE + alu_idx] <= {w_alu_oldest_cmt_id[mycpu_conf_pkg::RV_ALU_ENTRY_SIZE-1],
                                                   w_alu_oldest_grp_id[mycpu_conf_pkg::RV_ALU_ENTRY_SIZE-1]};
     end
   end // block: alu_issue_loop

さらに全体で最も古い命令を取り出す。この時SystemVerilogはmin()的なものが素直に使えるものがないので、結構苦労する。

  scariv_pkg::cmt_id_t w_issue_oldest_cmt_id;
  scariv_pkg::grp_id_t w_issue_oldest_grp_id;
  int                  w_issue_oldest_id;

  always_comb begin
    w_issue_oldest_cmt_id = '0;
    w_issue_oldest_grp_id = '0;
    w_issue_oldest_id = 'h0;
    for (int i = 0; i < scariv_conf_pkg::ALU_INST_NUM + scariv_conf_pkg::LSU_INST_NUM + 1 + 1 + scariv_conf_pkg::FPU_INST_NUM; i++) begin
      if ((issue_entries[i] != 'h0) &
          (({w_issue_oldest_cmt_id, w_issue_oldest_grp_id} == 'h0) |
           ({w_issue_oldest_cmt_id, w_issue_oldest_grp_id}) > issue_entries[i])) begin
        {w_issue_oldest_cmt_id, w_issue_oldest_grp_id} = issue_entries[i];
        w_issue_oldest_id = issue_entries[i];
      end
    end
  end

次に2. だ。各ユニットについて、アウト・オブ・オーダ発行をカウントしていく。

  // ----------------------
  // Counting Up OoO issue
  // ----------------------
  logic [ 9: 0] r_issue_num[ISSUE_UNIT_NUM-1: 0];
  logic [ 9: 0] r_ooo_issue_num[ISSUE_UNIT_NUM-1: 0];

  logic [ 9: 0]   r_count;
  logic           w_count_reset;
  always_ff @ (posedge w_clk, negedge w_scariv_reset_n) begin
    if (!w_scariv_reset_n) begin
      r_count <= 'h0;
    end else begin
      if (r_count == 1000-1) begin
        r_count <= 'h0;
      end else begin
        r_count <= r_count + 'h1;
      end
    end
  end
  assign w_count_reset = r_count == 1000-1;

  for (genvar alu_idx = 0; alu_idx < scariv_conf_pkg::ALU_INST_NUM; alu_idx++) begin : alu_issue_count_loop
    always_ff @ (negedge w_clk, negedge w_scariv_reset_n) begin
      if (!w_scariv_reset_n | w_count_reset) begin
        r_issue_num    [ALU_ISSUE_BASE + alu_idx] <= 'h0;
        r_ooo_issue_num[ALU_ISSUE_BASE + alu_idx] <= 'h0;
      end else begin
        if (u_scariv_subsystem_wrapper.u_scariv_subsystem.u_tile.alu_loop[alu_idx].u_alu.w_rv0_issue.valid) begin
          r_issue_num    [ALU_ISSUE_BASE + alu_idx] <= r_issue_num[ALU_ISSUE_BASE + alu_idx] + 'h1;
          if (scariv_pkg::id0_is_older_than_id1 (w_issue_oldest_cmt_id, w_issue_oldest_grp_id,
                                                 u_scariv_subsystem_wrapper.u_scariv_subsystem.u_tile.alu_loop[alu_idx].u_alu.w_rv0_issue.cmt_id,
                                                 u_scariv_subsystem_wrapper.u_scariv_subsystem.u_tile.alu_loop[alu_idx].u_alu.w_rv0_issue.grp_id)) begin
            r_ooo_issue_num[ALU_ISSUE_BASE + alu_idx] <= r_ooo_issue_num[ALU_ISSUE_BASE + alu_idx] + 'h1;
          end
        end
      end // else: !if(!w_scariv_reset_n | w_count_reset)
    end // always_ff @ (negedge w_clk, negedge w_scariv_reset_n)
  end

とりあえずこれでできたかな。ログを吐き出すようにしている。

alu[0]  91 / 212, alu[1]  48 / 120, lsu[0]  90 / 224, lsu[1]  41 /  82, bru  65 / 132, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0]  96 / 264, alu[1]  39 / 136, lsu[0] 140 / 358, lsu[1]  37 /  71, bru  93 / 254, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 120 / 295, alu[1]  41 / 153, lsu[0] 186 / 383, lsu[1]  44 /  90, bru  96 / 282, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0]  87 / 245, alu[1] 100 / 165, lsu[0] 131 / 316, lsu[1]  41 / 113, bru  64 / 183, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 101 / 266, alu[1]  53 / 163, lsu[0] 140 / 338, lsu[1]  16 /  44, bru  77 / 266, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 131 / 299, alu[1]  96 / 194, lsu[0] 139 / 383, lsu[1]  40 /  87, bru 119 / 289, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 194 / 437, alu[1] 145 / 295, lsu[0] 229 / 593, lsu[1]  75 / 117, bru 164 / 431, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 189 / 473, alu[1] 162 / 322, lsu[0] 232 / 669, lsu[1]  41 / 138, bru 171 / 461, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 184 / 469, alu[1] 153 / 315, lsu[0] 247 / 660, lsu[1]  51 / 138, bru 161 / 455, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 210 / 457, alu[1] 160 / 318, lsu[0] 253 / 652, lsu[1]  96 / 134, bru 158 / 460, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 218 / 472, alu[1] 151 / 325, lsu[0] 229 / 658, lsu[1]  91 / 121, bru 162 / 476, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 201 / 472, alu[1] 159 / 320, lsu[0] 247 / 668, lsu[1]  59 / 139, bru 177 / 479, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 185 / 472, alu[1] 150 / 315, lsu[0] 243 / 648, lsu[1]  49 / 140, bru 168 / 448, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 211 / 471, alu[1] 146 / 321, lsu[0] 236 / 666, lsu[1]  89 / 132, bru 155 / 469, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 216 / 465, alu[1] 148 / 322, lsu[0] 233 / 669, lsu[1]  97 / 140, bru 163 / 469, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 201 / 471, alu[1] 164 / 319, lsu[0] 263 / 644, lsu[1]  54 / 124, bru 181 / 473, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 182 / 480, alu[1] 163 / 329, lsu[0] 241 / 680, lsu[1]  52 / 138, bru 161 / 478, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 217 / 475, alu[1] 150 / 319, lsu[0] 226 / 656, lsu[1]  96 / 141, bru 162 / 452, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 210 / 471, alu[1] 145 / 321, lsu[0] 231 / 673, lsu[1]  98 / 136, bru 168 / 470, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 
alu[0] 203 / 474, alu[1] 169 / 326, lsu[0] 236 / 678, lsu[1]  46 / 132, bru 177 / 477, csu   0 / 999, fpu[0]   0 /   0, fpu[1]   0 /   0, 

なんか結構な割合でアウト・オブ・オーダ発行できている気がするな。ただもうちょっと精査が必要かな。