自作CPUの実装、ロードストア命令の物理アドレスが決まらないうえでのハザードが性能ボトルネックになっているのを見た。
次に検討するのは、LDQ/STQの最適化だ。以前の記事で説明したとおり、LDQとSTQの間でインタフェースを作成し、STQからのパイプラインが流れて段階でLDQのエントリをチェックするように変更する。
// LDQ Speculative Load Hazard Check assign ldq_haz_check_if.ex2_valid = r_ex2_issue.valid & (r_ex2_issue.cat == decoder_inst_cat_pkg::INST_CAT_ST); assign ldq_haz_check_if.ex2_paddr = r_ex2_paddr; assign ldq_haz_check_if.ex2_cmt_id = r_ex2_issue.cmt_id; assign ldq_haz_check_if.ex2_grp_id = r_ex2_issue.grp_id;
ストア命令において、LDQに対して検索を行う。一方でLDQエントリはサーチ要求に対して当該物理アドレスがヒットするかをチェックする。
// STQ -> LDQ Hazard check for (genvar p_idx = 0; p_idx < msrh_conf_pkg::LSU_INST_NUM; p_idx++) begin : st_ld_haz_loop logic ld_is_younger_than_st; msrh_rough_older_check st_pipe_ldq_older_check ( .i_cmt_id0 (w_ldq_entries[l_idx].cmt_id), .i_grp_id0 (w_ldq_entries[l_idx].grp_id), .i_cmt_id1 (ldq_haz_check_if[p_idx].ex2_cmt_id), .i_grp_id1 (ldq_haz_check_if[p_idx].ex2_grp_id), .o_0_older_than_1 (ld_is_younger_than_st) ); logic w_ex2_same_dw; assign w_ex2_same_dw = |(msrh_lsu_pkg::gen_dw(ldq_haz_check_if[p_idx].ex2_size, ldq_haz_check_if[p_idx].ex2_paddr[2:0]) & msrh_lsu_pkg::gen_dw(w_ldq_entries[l_idx].size, w_ldq_entries[l_idx].paddr[2:0])); assign w_ex2_ldq_stq_haz_vld[p_idx][l_idx] = ldq_haz_check_if[p_idx].ex2_valid & ld_is_younger_than_st & (ldq_haz_check_if[p_idx].ex2_paddr[riscv_pkg::PADDR_W-1: 3] == w_ldq_entries[l_idx].paddr[riscv_pkg::PADDR_W-1: 3]) & w_ex2_same_dw; end // block: st_ld_haz_loop
rough_older_check
とういうのはどちらの命令が若いかをチェックするためのモジュールで、cmt_id
, grp_id
の大小比較で命令の順序を判定している。STQのエントリの方がLDQのエントリよりも古ければ、LDQは投機的に実行されたことになり、さらに物理アドレスの範囲が一致していればLDQの投機実行結果を取り消すことになる。
そのとき、LDQの投機実行命令の内もっとも古い命令を抽出してその命令IDをSTQに伝える。 STQはその命令IDをROBに伝え、それらの命令から以降をフラッシュしてもらうように伝えるという仕組みで、誤った投機結果を打ち消すようになっている。
// ================== // LDQ Flush Hazard // ================== generate for (genvar p_idx = 0; p_idx < msrh_conf_pkg::LSU_INST_NUM; p_idx++) begin : ldq_stq_haz_loop msrh_entry_selector #( .ENTRY_SIZE (msrh_conf_pkg::LDQ_SIZE) ) u_entry_selector ( .i_oh_ptr (w_out_ptr_oh), .i_entry_valids (w_ex2_ldq_stq_haz_vld[p_idx]), .o_entry_valid (w_ex2_ldq_stq_haz_vld_oh[p_idx]) ); ldq_entry_t w_sel_ldq_entry; bit_oh_or #( .T (ldq_entry_t), .WORDS (msrh_conf_pkg::LDQ_SIZE) ) u_flush_sel ( .i_oh (w_ex2_ldq_stq_haz_vld_oh[p_idx]), .i_data (w_ldq_entries), .o_selected(w_sel_ldq_entry) ); always_ff @ (posedge i_clk, negedge i_reset_n) begin if (!i_reset_n) begin ldq_haz_check_if[p_idx].ex3_haz_valid <= 1'b0; end else begin ldq_haz_check_if[p_idx].ex3_haz_valid <= |w_ex2_ldq_stq_haz_vld[p_idx]; ldq_haz_check_if[p_idx].ex3_haz_cmt_id <= w_sel_ldq_entry.cmt_id; ldq_haz_check_if[p_idx].ex3_haz_grp_id <= w_sel_ldq_entry.grp_id; end end end endgenerate