FPGA開発日記

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

オープンソース・アウトオブオーダCPU NaxRiscvを概観する (GShareのソースコードを概観する1)

NaxRiscvのGShareに関連するソースコードを読んでいきたいと思う。

基本的にはSpinal-HDLのブロックを引っ張り出してVerilogのモジュールでまとめ上げて、わかりにくい展開されたような論理をまとめていったうえで中身を解析する。

まず、GShareのためのメモリとしては8-bit x 4096エントリのものが用意されている。8-bitというのは、フェッチが64ビットでその中に16-bit命令が4つ入れられるため、2-bit counter x 4という構成になってるのだともう。

logic [ 7: 0]  mem_counter [0:4095];

まずはGshareの読み込みについてみてみよう。Fetchステージ0において、まずはGShareのテーブルを参照するようだ。

always @(posedge clk) begin
  if(FetchPlugin_stages_0_ready) begin
    w_mem_counter_rd_data <= mem_counter[FetchPlugin_stages_0_HASH];
  end
end

FetchPlugin_stages_0_HASHの作り方は以下のようになっている。現在のPC(なぜかビット位置が全部反転してあったが)とこれまでの分岐履歴をXORする形で参照エントリが決められる。これがステージ1でそのままモジュール外に出力されて分岐予測に使われるようだ(そこから先は見てない)。

assign FetchPlugin_stages_0_HASH = {<<1{FetchPlugin_stages_0_Fetch_FETCH_PC[14 : 3]}} ^ FetchPlugin_stages_0_BRANCH_HISTORY[11: 0];

次に、GShareテーブルのアップデートについて見ていく。メモリへの書き込み論理は以下だ。

always @(posedge clk) begin
  if(mem_write_valid) begin
    mem_counter[mem_write_payload_address] <= mem_write_payload_data;
  end
end

各スライスにおいて、アップデートが必要な部分を修正する。

generate for (genvar s_idx = 0; s_idx < SLICE_COUNT; s_idx++) begin
  logic [ 1: 0]  onLearn_updated;

  assign onLearn_updated               = BranchContextPlugin_free_learn_GSHARE_COUNTER[s_idx] + (BranchContextPlugin_learn_BRANCH_FINAL_pcOnLastSlice[2 : 1] == s_idx ? onLearn_incrValue : 2'b00);
  assign when_GSharePlugin_l123[s_idx] =  BranchContextPlugin_learn_BRANCH_FINAL_taken && !BranchContextPlugin_free_learn_GSHARE_COUNTER[s_idx][1] && !onLearn_updated[s_idx][1] ||
                                         !BranchContextPlugin_learn_BRANCH_FINAL_taken &&  BranchContextPlugin_free_learn_GSHARE_COUNTER[s_idx][1] &&  onLearn_updated[s_idx][1];  assign mem_write_payload_data[s_idx * 2 +: 2] = onLearn_updated;
end endgenerate

基本的には、BranchContextPluginからの結果を使用しているのだが、分岐命令のターゲット・スライス(BranchContextPlugin_learn_BRANCH_FINAL_pcOnLastSlice[2 : 1])に対してインクリメント・デクリメントを決める。そして「分岐Taken & UnTakenと予測」もしくは「分岐UnTaken & Takenと予測」の場合にアップデートを行う。

書き込みのアドレスも、アップデート対象のPCと分岐履歴を使ってアップデートを行う。

assign onLearn_hash      = {<<1{BranchContextPlugin_learn_BRANCH_FINAL_pcOnLastSlice[14 : 3]}} ^ BranchContextPlugin_free_learn_BRANCH_HISTORY;
assign onLearn_incrValue = (BranchContextPlugin_learn_BRANCH_FINAL_taken ? 2'b01 : 2'b11);

assign mem_write_valid = ((BranchContextPlugin_setup_learnValid && BranchContextPlugin_free_learn_Prediction_IS_BRANCH) && (! onLearn_overflow));
assign mem_write_payload_address = onLearn_hash;