FPGA開発日記

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

RISC-Vのオープンソースベクトル命令実装Araのデータパス実装を調べる (VMERGE/VMV)

RISC-Vのオープンソースベクトル実装のリファレンスとしてAraというものが公開されている。

github.com

ベクトル実装のポイントとして、複数のデータサイズに対して1つのデータパスで対応しなければならないというのがあるのだが、これがどのように実装されているのかを確認した。 まず一番基本となるVMV.V.Xの実装などを確認したかったのだが、これはVMERGE命令に結合されているので、その辺の実装を確認していく。

まず、VMERGEのデータパスを確認した。

github.com

        // Merge instructions
        VMERGE: unique case (vew_i)
            EW8 : for (int b = 0; b < 8; b++) res.w8 [b] = mask_i[1*b] ? opa.w8 [b] : opb.w8 [b];
            EW16: for (int b = 0; b < 4; b++) res.w16[b] = mask_i[2*b] ? opa.w16[b] : opb.w16[b];
            EW32: for (int b = 0; b < 2; b++) res.w32[b] = mask_i[4*b] ? opa.w32[b] : opb.w32[b];
            EW64: for (int b = 0; b < 1; b++) res.w64[b] = mask_i[8*b] ? opa.w64[b] : opb.w64[b];
          endcase

データサイズに応じてそれぞれデータパスが定義されているのか。なるほど。オペランドは以下のようなデータとして定義されている。

  typedef union packed {
    logic [0:0][63:0] w64;
    logic [1:0][31:0] w32;
    logic [3:0][15:0] w16;
    logic [7:0][ 7:0] w8;
  } alu_operand_t;

  alu_operand_t opa, opb, res;

なるほど、unionでそれぞれのデータサイズを表現するように定義されているのか。 それでもって、データサイズの大きさは最大で64-bitになっており、これをレーンで接続するようになっている。

ちなみに、ベクトル命令のデコーダは非常にベタにデコーダ内に書いてあった。

                   6'b010111: begin
                     ara_req_d.op      = ara_pkg::VMERGE;
                     ara_req_d.use_vs2 = !insn.varith_type.vm; // vmv.v.v does not use vs2                                                                                                                                                                                                                                                                                                
                     // With a normal vmv.v.v, copy input eew to output                                                                                                                                                                                                                                                                                                                   
                     // to avoid unnecessary reshuffles                                                                                                                                                                                                                                                                                                                                   
                     if (insn.varith_type.vm) begin
                       ara_req_d.eew_vs1    = eew_q[ara_req_d.vs1];
                       ara_req_d.vtype.vsew = eew_q[ara_req_d.vs1];
                       ara_req_d.vl         = (vl_q << vtype_q.vsew[1:0]) >> ara_req_d.eew_vs1[1:0];
                     end
                   end