FPGA開発日記

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

自作RISC-Vアウトオブオーダコアの実装 (FPNewによる浮動小数点命令のサポート試行)

自作RISC-Vアウトオブオーダコアの実装、FPUについて実装の調査を始めた。 FPUの演算器自体は自分で作るのは荷が重すぎる。既にオープンソースとして公開されているFPU実装を使ってみようと思う。

Rocket-ChipやBOOMなどはChiselで記述されたFPUエンジンであるHardFloatを使っているようだが、ChiselをわざわざVerilogに変換して実装されているHardFloatはgtkwaveでのデバッグが非常に難しいので、ちゃんと最初からSystemVerilogで記述されているFPNewを使ってみることにした。

github.com

arxiv.org

FPNewのインタフェースを確認してみる。本当はfpnew_topインスタンスして全部突っ込んでしまうのが楽なのかもしれないが、コンポーネントの構成が良く分からなかったので直接演算器をインスタンスして動かしてみることにする。

   fpnew_fma
     #(
       .FpFormat   (fpnew_pkg::FP64),
       .NumPipeRegs(1),
       .PipeConfig (fpnew_pkg::BEFORE),
       .TagType    (logic),
       .AuxType    (logic)
       )
   fpnew_64
   (
    .clk_i  (i_clk    ),
    .rst_ni (i_reset_n),
    // Input signals
    .operands_i      (w_fma64_rs       ),  // input logic [2:0][WIDTH-1:0]      // 3 operands
    .is_boxed_i      (w_fma64_boxed    ),  // input logic [2:0]                 // 3 operands
    .rnd_mode_i      (fpnew_pkg::RNE   ),  // input fpnew_pkg::roundmode_e
    .op_i            (w_fpnew_op       ),  // input fpnew_pkg::operation_e
    .op_mod_i        (1'b0             ),  // input logic
    .tag_i           (1'b0             ),  // input TagType
    .aux_i           (1'b0             ),  // input AuxType
    // Input Handshake
    .in_valid_i      (w_fma64_in_valid ),  // input  logic
    .in_ready_o      (o_ready          ),  // output logic
    .flush_i         (1'b0             ),  // input  logic
    // Output signals
    .result_o        (w_fma64_result   ),  // output logic [WIDTH-1:0]
    .status_o        (w_fma64_fflags   ),  // output fpnew_pkg::status_t
    .extension_bit_o (                 ),  // output logic
    .tag_o           (                 ),  // output TagType
    .aux_o           (                 ),  // output AuxType
    // Output handshake
    .out_valid_o     (w_fma64_out_valid),  // output logic
    .out_ready_i     (1'b1             ),  // input  logic
    // Indication of valid data in flight
    .busy_o          (                 )   // output logic
    );

オペランドについては、fpnew_pkg.sv で記述されたものを使用する。

   typedef enum logic [OP_BITS-1:0] {
     FMADD, FNMSUB, ADD, MUL,     // ADDMUL operation group
     DIV, SQRT,                   // DIVSQRT operation group
     SGNJ, MINMAX, CMP, CLASSIFY, // NONCOMP operation group
     F2F, F2I, I2F, CPKAB, CPKCD  // CONV operation group
   } operation_e;
 always_comb begin
   case (i_pipe_ctrl.op)
     OP_FMADD     : w_fpnew_op = fpnew_pkg::FMADD;
     OP_FMSUB     : w_fpnew_op = fpnew_pkg::FMADD;
     OP_FNMSUB    : w_fpnew_op = fpnew_pkg::FNMSUB;
     OP_FNMADD    : w_fpnew_op = fpnew_pkg::FNMSUB;
     OP_FADD      : w_fpnew_op = fpnew_pkg::ADD;
     OP_FSUB      : w_fpnew_op = fpnew_pkg::ADD;
     OP_FMUL      : w_fpnew_op = fpnew_pkg::MUL;
     OP_FDIV      : w_fpnew_op = fpnew_pkg::DIV;
     OP_FSQRT     : w_fpnew_op = fpnew_pkg::SQRT;
     OP_FSGNJ_S   : w_fpnew_op = fpnew_pkg::SGNJ;
     OP_FSGNJN_S  : w_fpnew_op = fpnew_pkg::SGNJ;
     OP_FSGNJX_S  : w_fpnew_op = fpnew_pkg::SGNJ;
     OP_FMIN      : w_fpnew_op = fpnew_pkg::MINMAX;
     OP_FMAX      : w_fpnew_op = fpnew_pkg::MINMAX;
     OP_FCVT_W_S  : w_fpnew_op = fpnew_pkg::F2I;
     OP_FCVT_WU_S : w_fpnew_op = fpnew_pkg::F2I;
     OP_FEQ       : w_fpnew_op = fpnew_pkg::CMP;
     OP_FLT       : w_fpnew_op = fpnew_pkg::CMP;
     OP_FLE       : w_fpnew_op = fpnew_pkg::CMP;
     OP_FCLASS    : w_fpnew_op = fpnew_pkg::CLASSIFY;
     OP_FCVT_S_W  : w_fpnew_op = fpnew_pkg::I2F;
     OP_FCVT_S_WU : w_fpnew_op = fpnew_pkg::I2F;
     OP_FSGNJ_D   : w_fpnew_op = fpnew_pkg::SGNJ;
     OP_FSGNJN_D  : w_fpnew_op = fpnew_pkg::SGNJ;
     OP_FSGNJX_D  : w_fpnew_op = fpnew_pkg::SGNJ;
     OP_FCVT_S_D  : w_fpnew_op = fpnew_pkg::F2F;
     OP_FCVT_D_S  : w_fpnew_op = fpnew_pkg::F2F;
     OP_FCVT_W_D  : w_fpnew_op = fpnew_pkg::F2I;
     OP_FCVT_WU_D : w_fpnew_op = fpnew_pkg::F2I;
     OP_FCVT_D_W  : w_fpnew_op = fpnew_pkg::I2F;
     OP_FCVT_D_WU : w_fpnew_op = fpnew_pkg::I2F;
     OP_FCVT_L_D  : w_fpnew_op = fpnew_pkg::F2I;
     OP_FCVT_LU_D : w_fpnew_op = fpnew_pkg::F2I;
     default      : w_fpnew_op = fpnew_pkg::FMADD;
   endcase // case (i_op)
 end // always_comb

さらに、入力値についてはFMADD, FNMSUBADD, MULで少し変更しなければならないのが注意。

github.com

   assign w_fma64_rs[0] = (w_fpnew_op == fpnew_pkg::ADD | w_fpnew_op == fpnew_pkg::MUL) ? 'h0          : i_rs1[63: 0];
   assign w_fma64_rs[1] = (w_fpnew_op == fpnew_pkg::ADD | w_fpnew_op == fpnew_pkg::MUL) ? i_rs1[63: 0] : i_rs2[63: 0];
   assign w_fma64_rs[2] = (w_fpnew_op == fpnew_pkg::ADD | w_fpnew_op == fpnew_pkg::MUL) ? i_rs2[63: 0] : i_rs3[63: 0];
   assign w_fma64_boxed[2:0] = 3'b111;

   assign w_fma64_in_valid = i_valid & (i_pipe_ctrl.size == SIZE_DW);

とりあえずこれまでのALUパイプラインをそのまま再利用しているので、パイプライン段数は1段とした。絶対論理合成は上手く行かないのだが、後でFPUのパイプラインを伸ばして4~5段くらいまで増やそうと思う。

とりあえず、現状の最もシンプルな構成でFADD.Dが少し動き始めた。演算結果自体は問題なさそうだ。

ただしFFLAGSを更新するパスを付けていない。これは今後実装して行く。