自作RISC-Vアウトオブオーダコアの実装、FPUについて実装の調査を始めた。 FPUの演算器自体は自分で作るのは荷が重すぎる。既にオープンソースとして公開されているFPU実装を使ってみようと思う。
Rocket-ChipやBOOMなどはChiselで記述されたFPUエンジンであるHardFloatを使っているようだが、ChiselをわざわざVerilogに変換して実装されているHardFloatはgtkwaveでのデバッグが非常に難しいので、ちゃんと最初からSystemVerilogで記述されているFPNewを使ってみることにした。
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, FNMSUB
とADD, MUL
で少し変更しなければならないのが注意。
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を更新するパスを付けていない。これは今後実装して行く。