FPGA開発日記

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

独自RISC-V CPUコアの実装 (リネームの続き)

リネームの続きだが、例えば5つの命令が同時に発行されたとして以下のような依存関係があるとき、

addi  x10, x0, 1
addi x11, x10, 2
addi x12, x11, 3
addi x13, x12, 4
addi x14, x13, 5

それぞれの命令は整数レジスタで依存関係を持っている。この場合リネームIDについても依存関係を元に取得しなければならず、例えばそれぞれの命令で新たに割り当てられるリネームIDが、

addi  x10, x0, 1       # x10 が ID 100に割り当て
addi x11, x10, 2      # x10はID 100を待つ。 x11 がID 105に割り当て
addi x12, x11, 3      # x11はID 105を待つ。 x11 がID 110に割り当て
addi x13, x12, 4      # x12はID 110を待つ。 x11 がID 115に割り当て
addi x14, x13, 5      # x13はID 115を待つ。 x11 がID 120に割り当て

となるように、この場合5命令を同時にリネームする場合には前の命令のリネームの結果をもとに後続の命令のオペランドのリネームIDを決めなければならない。

これを一般化してどのようにSystemVerilogで実装しようかと迷った結果、以下のようになった。

mrh_rename_map u_mrh_rename_map
  (
   .i_clk     (i_clk),
   .i_reset_n (i_reset_n),

   .i_arch_valid (w_archreg_valid),
   .i_arch_id    (w_archreg),
   .o_rnid       (w_rnid),

   .i_update         ({mrh_pkg::DISP_SIZE{1'b0}}),
   .i_update_arch_id (w_update_arch_id),
   .i_update_rnid    (w_update_rnid   )
   );

...
generate for (genvar d_idx = 0; d_idx < mrh_pkg::DISP_SIZE; d_idx++) begin : src_rn_loop
  always_comb begin
    disp_to_scheduler_d.valid = disp_from_frontend.valid;

...
    /* verilator lint_off UNSIGNED */
    for (int p_idx = 1; p_idx < d_idx; p_idx++) begin: prev_rd_loop
      if (disp_from_frontend.inst[p_idx].rd_valid &&
          disp_from_frontend.inst[p_idx].rd_type   == disp_from_frontend.inst[d_idx].rs1_type &&
          disp_from_frontend.inst[p_idx].rd_regidx == disp_from_frontend.inst[d_idx].rs1_regidx) begin
        rs1_rnid_tmp_valid[p_idx] = 1'b1;
        rs1_rnid_tmp[p_idx] = rd_rnid[p_idx];
      end else begin
        rs1_rnid_tmp_valid[p_idx] = rs1_rnid_tmp_valid[p_idx-1];
        rs1_rnid_tmp      [p_idx] = rs1_rnid_tmp      [p_idx-1];
      end // else: !if(disp_from_frontend.inst[p_idx].rd_valid &&...
...
  /* verilator lint_off SELRANGE */
  assign rs1_rnid_fwd[d_idx] = (d_idx == 0) ? w_rnid[0] : rs1_rnid_tmp[d_idx-1];
  assign rs2_rnid_fwd[d_idx] = (d_idx == 0) ? w_rnid[1] : rs2_rnid_tmp[d_idx-1];

  assign disp_to_scheduler_d.valid = disp_from_frontend.valid;
  assign disp_to_scheduler_d.inst[d_idx] = mrh_pkg::assign_disp_rename (disp_from_frontend.inst[d_idx],
                                                                        rd_rnid     [d_idx],
                                                                        rs1_rnid_fwd[d_idx],
                                                                        rs2_rnid_fwd[d_idx]);

前の命令のリネーム結果を見ながら、アーキテクチャ的なレジスタ番号が一致する場合には前のリネームIDをフォワードして使用する。このようにすれば一応すべての命令で矛盾なくリネーム結果を取得することができる。波形的には以下のようになった。

f:id:msyksphinz:20210206231619p:plain

依存関係がある命令同士で、リネームIDの受け渡しが上手くできている。