FPGA開発日記

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

自作CPUコアとBOOMv3のPPA(Performance, Power, Area)を比較する (8. フォワーディングパスの軽量化)

今のところ自作CPUは面積が大きすぎる。Vivadoでの論理合成結果をもっと詳細に見ていくことにする。

各種フォワーディングパスの負荷が大きすぎる。 各命令は、フォワーディングパスの状態通知に応じてデータを取得するのだが、物理レジスタからデータを取得するか、フォワーディングパスからデータを取得するのかは実行パイプライン中でその場で判断していた。

つまり、命令実行に関してオペランドの受け取りには2種類あり: - 物理レジスタから読み込む。 - フォワーディングパスから読み込む。

が考えられる。どっちから読むのか、あるいはフォワーディングパスのうち、どのパスを使うのかはパイプライン中で動的に判断していた。

イメージとしては以下。パイプライン中で、フォワーディングパスから流れてきた物理IDを全部比較し、ヒットすればそれを優先的に取得する。

generate for (genvar rs_idx = 0; rs_idx < 3; rs_idx++) begin : ex1_rs_loop
  scariv_pkg::alen_t w_ex1_tgt_data [scariv_pkg::TGT_BUS_SIZE];
  for (genvar tgt_idx = 0; tgt_idx < scariv_pkg::TGT_BUS_SIZE; tgt_idx++) begin : rs_tgt_loop
    assign w_ex1_rs_fwd_valid[rs_idx][tgt_idx] = r_ex1_issue.rd_regs[rs_idx].valid & ex1_i_phy_wr[tgt_idx].valid &
                                                (r_ex1_issue.rd_regs[rs_idx].typ  == ex1_i_phy_wr[tgt_idx].rd_type) &
                                                (r_ex1_issue.rd_regs[rs_idx].rnid == ex1_i_phy_wr[tgt_idx].rd_rnid);
    assign w_ex1_tgt_data[tgt_idx] = ex1_i_phy_wr[tgt_idx].rd_data;
  end
    bit_oh_or #(
      .T(scariv_pkg::alen_t),
      .WORDS(scariv_pkg::TGT_BUS_SIZE)
  ) u_rs_data_select (
      .i_oh      (w_ex1_rs_fwd_valid[rs_idx]),
      .i_data    (w_ex1_tgt_data            ),
      .o_selected(w_ex1_rs_fwd_data [rs_idx])
  );
end endgenerate

しかし、よく考えてみればこれは動的に判断する必要はない。命令キューによる発行待機中に、

  • Wakeupパスの通知により命令がReady状態になる:これは実行パイプライン中にフォワーディングパスからオペランドを受け取ることになる。
  • 命令キュー格納時にすでに当該オペランドはReady状態:これはフォワーディングパスを使用せず物理レジスタから取得する。

のどちらかなので、命令キュー内で待機中に決定できる。 また、どの実行パイプラインからのWakeupが通知されたかにより、どのフォワーディングパスからオペランドがやってくるのかを決定できるので、命令実行時に比較は全く不要となる。

究極的に、フォワーディングパスによるオペランド取得の論理は比較を全く使わずに実現する。

generate for (genvar rs_idx = 0; rs_idx < 2; rs_idx++) begin : ex1_rs_loop
  riscv_pkg::xlen_t w_ex1_tgt_data [scariv_pkg::TGT_BUS_SIZE];
  for (genvar tgt_idx = 0; tgt_idx < scariv_pkg::TGT_BUS_SIZE; tgt_idx++) begin : rs_tgt_loop
    assign w_ex1_tgt_data[tgt_idx] = ex1_i_phy_wr[tgt_idx].rd_data;
  end
  assign w_ex1_rs_fwd_data [rs_idx] = w_ex1_tgt_data[r_ex1_issue.rd_regs[rs_idx].early_index];
end endgenerate // block: ex1_rs_loop

assign w_ex1_rs1_selected_data = r_ex1_issue.rd_regs[0].predict_ready[0] ? w_ex1_rs_fwd_data[0] : r_ex1_rs1_data;
assign w_ex1_rs2_selected_data = r_ex1_issue.rd_regs[1].predict_ready[0] ? w_ex1_rs_fwd_data[1] : r_ex1_rs2_data;

フォワーディングの論理は、クリティカルパスになりやすい部分なので、これでかなり軽量化できたと思う。