FPGA開発日記

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

自作CPUにシンプルなデータプリフェッチャを実装する

いろいろ勉強したCPUのデータプリフェッチャについて、理解を深めるために自作CPUにデータプリフェッチャを実装してみることにした。

まずは簡単なConstant Strideのプリフェッチだ。 LSUのパイプラインが1つ以上あるので、トレーニングテーブルの更新はその部分を気を付けなければならない。 つまり、2つのLSUが同時に同じPCを更新するときに、その学習を正しく更新しなければならない。 そのためにはもう少しアービトレーションが必要なのだが、とりあえずそれはおいておいて、2つのLSUが正しくテーブルを更新できるものとしてRTLを構築した。したがってまだこのRTLは合成できない。

localparam PC_TAG_LEN = 9;

typedef struct packed {
  logic                valid;
  logic [PC_TAG_LEN:1] pc_tag;
  logic [11: 0]        last_line_offset;
  logic [11: 0]        stride;
  logic [ 1: 0]        confidence;
} ipt_entry_t;

学習のところはとりあえず省略して、プリフェッチ・リクエストが生成された後のフィルタ部分が以下だ。

scariv_pkg::vaddr_t [15: 0] r_rr_filter;
logic [ 3: 0] r_rr_push_ptr;
logic [15: 0] w_hit_rr_filter;
logic         w_pref_gen_valid;

generate for (genvar rr_idx = 0; rr_idx < 16; rr_idx++) begin : rr_filter_search_loop
  assign w_hit_rr_filter[rr_idx] = r_rr_filter[rr_idx][riscv_pkg::VADDR_W-1: $clog2(scariv_conf_pkg::DCACHE_DATA_W/8)] ==
                                   r_pref_vaddr       [riscv_pkg::VADDR_W-1: $clog2(scariv_conf_pkg::DCACHE_DATA_W/8)];
end endgenerate
assign w_pref_gen_valid = |w_hit_rr_filter & (r_pref_state == PREF_GEN);


always_ff @ (posedge i_clk, negedge i_reset_n) begin
  if (!i_reset_n) begin
    r_rr_push_ptr <= 'h0;
  end else begin
    if (r_pref_state == PREF_GEN) begin
      if (~|w_hit_rr_filter) begin
        r_rr_push_ptr <= r_rr_push_ptr + 'h1;
        r_rr_filter[r_rr_push_ptr] <= r_pref_vaddr;
      end
    end
  end // else: !if(!i_reset_n)
end // always_ff @ (posedge i_clk, negedge i_reset_n)

最後に、LSUパイプラインがBusyであった場合の待ち合わせバッファを置いて、これでとりあえず簡易的なプリフェッチャは完成とする。

ring_fifo
  #(.T(scariv_pkg::vaddr_t))
u_pref_req_fifo
  (
   .i_clk     (i_clk                 ),
   .i_reset_n (i_reset_n             ),
   .i_push    (w_pref_gen_valid      ),
   .i_data    (r_pref_vaddr          ),
   .o_empty   (w_pref_ring_fifo_empty),
   .o_full    (                      ),
   .i_pop     (w_pref_ring_fifo_pop  ),
   .o_data    (w_pref_pipe_vaddr     )
   );


always_comb begin
  o_pref_issue = 'h0;
  if (w_pref_gen_valid) begin
    o_pref_issue.valid = 1'b1;
    o_pref_issue.paddr = w_pref_pipe_vaddr;
    o_pref_issue.is_prefetch = 1'b1;
  end
end

簡単なmemcpyで、その動作を確認してみよう。

一応プリフェッチは出るようになった。でもアドレスが何かおかしいな?デバッグしていく。