浮動小数点数の比較命令
feq.s
flt.s
fle.s
どどーんと実装した。
(追記)勢いで実装したせいでFLE.Sがいっぱいバグってた!バグ修正箇所を後述する
code:diff
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index 297a55a..6b61886 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -420,6 +420,8 @@ module decoder(
// readRegType1 は 0 で、writeRegType は 1 になる
// fcvt.w.s (float to int) の場合(opCode = 1010011, funct7 = 1100000)
// readRegType1 は 1 で、writeRegType は 0 になる
+ // feq, flt, fle の場合(opCode = 1010011, funct7 = 1010000)
+ // writeRegType は 0 になる
assign readRegType1 = (opCode === 7'b1010011 && funct7 === 7'b1101000) ? 1'b0 : // 整数レジスタを参照 (fcvt.s.w = int to float)
(opCode === 7'b1010011) ? 1'b1 // 浮動小数点レジスタを参照 (fcvt.s.w 以外の RV32F の R-type 命令)
@@ -431,6 +433,7 @@ module decoder(
assign writeRegType = (opCode === 7'b0000111) ? 1'b1 : // 浮動小数点レジスタへ書き込み (flw)
(opCode === 7'b1010011 && funct7 === 7'b1100000) ? 1'b0 : // 整数レジスタへ書き込み (fcvt.w.s = float to int)
+ (opCode === 7'b1010011 && funct7 === 7'b1010000) ? 1'b0 : // 整数レジスタへ書き込み (feq, flt, fle)
(opCode === 7'b1010011) ? 1'b1 // 浮動小数点レジスタを参照(RV32F の R type)
: 1'b0; // 整数レジスタへ書き込み
@@ -441,6 +444,9 @@ module decoder(
(funct7 === 7'b1100000) ? 4'b0101 : // fcvt.w.s (float to int)
(funct7 === 7'b0010000 && funct3 === 3'b000) ? 4'b0110 : // fsgnj
(funct7 === 7'b0010000 && funct3 === 3'b001) ? 4'b0111 : // fsgnjn
+ (funct7 === 7'b1010000 && funct3 === 3'b010) ? 4'b1000 : // feq
+ (funct7 === 7'b1010000 && funct3 === 3'b001) ? 4'b1001 : // flt
+ (funct7 === 7'b1010000 && funct3 === 3'b000) ? 4'b1010 : // fle
(funct7 === 7'b0000100) ? 4'b0001 : // fsub
(funct7 === 7'b0001000) ? 4'b0010 : // fmul
(funct7 === 7'b0001100) ? 4'b0011 // fdiv
@@ -692,7 +698,7 @@ module regfile(
// $display("x1 %d", registers1); // $display("x2 %d", registers2); // $display("x3 %d", registers3); - // $display("x5 %d", registers5); + // $display("x7 %d", registers5); // $display("x10 %d", registers10); // $display("$2 %b", registers2); // $display("$30 %b", registers30); diff --git a/rtl/fpu_controller.sv b/rtl/fpu_controller.sv
index a482456..61524a0 100644
--- a/rtl/fpu_controller.sv
+++ b/rtl/fpu_controller.sv
@@ -61,6 +61,9 @@ module fpu_controller(
(op === 4'b0101) ? float2int_in1_ack :
(op === 4'b0110) ? fsgnj_in1_ack :
(op === 4'b0111) ? fsgnjn_in1_ack :
+ (op === 4'b1000) ? feq_in1_ack :
+ (op === 4'b1001) ? flt_in1_ack :
+ (op === 4'b1010) ? fle_in1_ack :
(op === 4'b0010) ? multiplier_in1_ack :
(op === 4'b0011) ? divider_in1_ack
: 1'bx;
@@ -69,6 +72,9 @@ module fpu_controller(
(op === 4'b0101) ? float2int_in1_ack :
(op === 4'b0110) ? fsgnj_in2_ack :
(op === 4'b0111) ? fsgnjn_in2_ack :
+ (op === 4'b1000) ? feq_in2_ack :
+ (op === 4'b1001) ? flt_in2_ack :
+ (op === 4'b1010) ? fle_in2_ack :
(op === 4'b0010) ? multiplier_in2_ack :
(op === 4'b0011) ? divider_in2_ack
:1'bx;
@@ -77,6 +83,9 @@ module fpu_controller(
(op === 4'b0101) ? float2int_out_stb :
(op === 4'b0110) ? fsgnj_out_stb :
(op === 4'b0111) ? fsgnjn_out_stb :
+ (op === 4'b1000) ? feq_out_stb :
+ (op === 4'b1001) ? flt_out_stb :
+ (op === 4'b1010) ? fle_out_stb :
(op === 4'b0010) ? multiplier_out_stb :
(op === 4'b0011) ? divider_out_stb
: 1'bx;
@@ -85,6 +94,9 @@ module fpu_controller(
(op === 4'b0101) ? float2int_out :
(op === 4'b0110) ? fsgnj_out :
(op === 4'b0111) ? fsgnjn_out :
+ (op === 4'b1000) ? feq_out :
+ (op === 4'b1001) ? flt_out :
+ (op === 4'b1010) ? fle_out :
(op === 4'b0010) ? multiplier_out :
(op === 4'b0011) ? divider_out
: 32'bx;
@@ -238,4 +250,70 @@ module fpu_controller(
assign fsgnjn_in2_ack = 1'b1;
assign fsgnjn_out_stb = 1'b1;
+ //------------------------------------------------------------------------------
+ // 浮動小数点数比較演算(FEQ, FLT, FLE)
+ //------------------------------------------------------------------------------
+
+ logic fp_sign1;
+ logic fp_sign2;
+
+ assign fp_sign1 = in131; + assign fp_sign2 = in231; + assign fp_exp1 = in130:23; + assign fp_exp2 = in230:23; + assign fp_frac1 = in122:0; + assign fp_frac2 = in222:0; +
+ // FEQ
+ // NOTE: 雑に実装しているので、NaN などの特殊なケースは考慮していない
+ logic feq_in1_ack;
+ logic feq_in2_ack;
+ logic feq_out_stb;
+
+ assign feq_out = (fp_sign1 === fp_sign2 && fp_exp1 === fp_exp2 && fp_frac1 === fp_frac2) ? 32'h00000001 : 32'h00000000;
+ assign feq_in1_ack = 1'b1;
+ assign feq_in2_ack = 1'b1;
+ assign feq_out_stb = 1'b1;
+
+ // FLT
+ // NOTE: 雑に実装しているので、NaN などの特殊なケースは考慮していない
+ logic flt_in1_ack;
+ logic flt_in2_ack;
+ logic flt_out_stb;
+
+ assign flt_out = (fp_sign1 === fp_sign2 && fp_exp1 === fp_exp2 && fp_frac1 === fp_frac2) ? 32'h00000000 :
+ (fp_sign1 === 1 && fp_sign2 === 0) ? 32'h00000001 :
+ (fp_sign1 === 0 && fp_sign2 === 1) ? 32'h00000000 :
+ (fp_sign1 === 0 && fp_sign2 === 0 && fp_exp1 < fp_exp2) ? 32'h00000001 :
+ (fp_sign1 === 1 && fp_sign2 === 1 && fp_exp1 > fp_exp2) ? 32'h00000000 :
+ (fp_sign1 === 0 && fp_sign2 === 0 && fp_exp1 === fp_exp2 && fp_frac1 < fp_frac2) ? 32'h00000001
+ : 32'h00000000;
+ assign flt_in1_ack = 1'b1;
+ assign flt_in2_ack = 1'b1;
+ assign flt_out_stb = 1'b1;
+
+ // FLE
+ // NOTE: 雑に実装しているので、NaN などの特殊なケースは考慮していない
+ logic fle_in1_ack;
+ logic fle_in2_ack;
+ logic fle_out_stb;
+
+ assign fle_out = (fp_sign1 === fp_sign2 && fp_exp1 === fp_exp2 && fp_frac1 === fp_frac2) ? 32'h00000001 :
+ (fp_sign1 === 1 && fp_sign2 === 0) ? 32'h00000001 :
+ (fp_sign1 === 0 && fp_sign2 === 1) ? 32'h00000000 :
+ (fp_sign1 === 0 && fp_sign2 === 0 && fp_exp1 < fp_exp2) ? 32'h00000001 :
+ (fp_sign1 === 1 && fp_sign2 === 1 && fp_exp1 > fp_exp2) ? 32'h00000000 :
+ (fp_sign1 === 0 && fp_sign2 === 0 && fp_exp1 === fp_exp2 && fp_frac1 < fp_frac2) ? 32'h00000001
+ : 32'h00000000;
+ assign fle_in1_ack = 1'b1;
+ assign fle_in2_ack = 1'b1;
+ assign fle_out_stb = 1'b1;
+
endmodule
diff --git a/rtl/test_program/fp.sv b/rtl/test_program/fp.sv
index 88bcedf..b11f2ff 100644
--- a/rtl/test_program/fp.sv
+++ b/rtl/test_program/fp.sv
@@ -51,7 +51,34 @@ instructions27 = fsw(0, 6, 32'h134); // M0x134 = f6 instructions28 = fsgnjn_s(7, 5, 5); // f7 = -f5 instructions29 = fsw(0, 7, 32'h138); // M0x138 = f7 -instructions30 = jal(0, 0); // 無限ループ +// FEQ 系のテスト
+// f5 = 2.0
+// f6 = 3.0
+// x5 = feq(f5, f6) // 0
+// x5 = feq(f5, f5) // 1
+// x5 = flt(f5, f6) // 1
+// x5 = flt(f6, f5) // 0
+// x5 = fle(f5, f6) // 1
+// x5 = fle(f6, f5) // 0
+// x5 = fle(f5, f5) // 1
+instructions30 = flw(5, 0, 32'h104); // f5 = 2.0 +instructions31 = flw(6, 0, 32'h108); // f6 = 3.0 +instructions32 = feq_s(7, 5, 6); // x7 = (f5 == f6) ? 1 : 0 +instructions33 = sw(0, 7, 32'h13c); // M0x13c = x7 (== 0) +instructions34 = feq_s(7, 5, 5); // x7 = (f5 == f5) ? 1 : 0 +instructions35 = sw(0, 7, 32'h140); // M0x140 = x7 (== 1) +instructions36 = flt_s(7, 5, 6); // x7 = (f5 < f6) ? 1 : 0 +instructions37 = sw(0, 7, 32'h144); // M0x144 = x7 (== 1) +instructions38 = flt_s(7, 6, 5); // x7 = (f6 < f5) ? 1 : 0 +instructions39 = sw(0, 7, 32'h148); // M0x148 = x7 (== 0) +instructions40 = fle_s(7, 5, 6); // x7 = (f5 <= f6) ? 1 : 0 +instructions41 = sw(0, 7, 32'h14c); // M0x14c = x7 (== 1) +instructions42 = fle_s(7, 6, 5); // x7 = (f6 <= f5) ? 1 : 0 +instructions43 = sw(0, 7, 32'h150); // M0x150 = x7 (== 0) +instructions44 = fle_s(7, 5, 5); // x7 = (f5 <= f5) ? 1 : 0 +instructions45 = sw(0, 7, 32'h154); // M0x154 = x7 (== 1) +
+instructions46 = jal(0, 0); // 無限ループ // テスト用の浮動小数点数データ
instructions64 = 32'h3f800000; // M0x100 = 1.0 @@ -90,7 +117,7 @@ reset_n = 0;
reset_n = 1;
-#10000;
+#20000;
// 実行が終わった頃合いを見て、メモリの内容を確認
mem_monitor_on = 1;
@@ -215,3 +242,92 @@ assert(
) $display("PASSED"); else $display("FAILED: %h", mem_rdata);
mem_monitor_valid_reg = 0;
+
+// FEQ
+mem_monitor_on = 1;
+mem_monitor_valid_reg = 1;
+mem_monitor_addr_reg = 32'h13c;
+mem_monitor_wstrb_reg = 4'b0000;
+#10;
+wait(mem_ready);
+assert(
+ mem_rdata === 32'h0
+) $display("PASSED"); else $display("@FAILED: %h", mem_rdata);
+mem_monitor_valid_reg = 0;
+#10;
+
+
+mem_monitor_on = 1;
+mem_monitor_valid_reg = 1;
+mem_monitor_addr_reg = 32'h140;
+mem_monitor_wstrb_reg = 4'b0000;
+#10;
+wait(mem_ready);
+assert(
+ mem_rdata === 32'h1
+) $display("PASSED"); else $display("FAILED: %h", mem_rdata);
+mem_monitor_valid_reg = 0;
+#10;
+
+mem_monitor_on = 1;
+mem_monitor_valid_reg = 1;
+mem_monitor_addr_reg = 32'h144;
+mem_monitor_wstrb_reg = 4'b0000;
+#10;
+wait(mem_ready);
+assert(
+ mem_rdata === 32'h1
+) $display("PASSED"); else $display("FAILED: %h", mem_rdata);
+mem_monitor_valid_reg = 0;
+#10;
+
+mem_monitor_on = 1;
+mem_monitor_valid_reg = 1;
+mem_monitor_addr_reg = 32'h148;
+mem_monitor_wstrb_reg = 4'b0000;
+#10;
+wait(mem_ready);
+assert(
+ mem_rdata === 32'h0
+) $display("PASSED"); else $display("FAILED: %h", mem_rdata);
+mem_monitor_valid_reg = 0;
+#10;
+
+mem_monitor_on = 1;
+mem_monitor_valid_reg = 1;
+mem_monitor_addr_reg = 32'h14c;
+mem_monitor_wstrb_reg = 4'b0000;
+#10;
+wait(mem_ready);
+assert(
+ mem_rdata === 32'h1
+) $display("PASSED"); else $display("FAILED: %h", mem_rdata);
+mem_monitor_valid_reg = 0;
+#10;
+
+mem_monitor_on = 1;
+mem_monitor_valid_reg = 1;
+mem_monitor_addr_reg = 32'h150;
+mem_monitor_wstrb_reg = 4'b0000;
+#10;
+wait(mem_ready);
+assert(
+ mem_rdata === 32'h0
+) $display("PASSED"); else $display("FAILED: %h", mem_rdata);
+mem_monitor_valid_reg = 0;
+#10;
+
+mem_monitor_on = 1;
+mem_monitor_valid_reg = 1;
+mem_monitor_addr_reg = 32'h154;
+mem_monitor_wstrb_reg = 4'b0000;
+#10;
+wait(mem_ready);
+assert(
+ mem_rdata === 32'h1
+) $display("PASSED"); else $display("FAILED: %h", mem_rdata);
+mem_monitor_valid_reg = 0;
+#10;
FLE.Sのバグ
負の値同士のFLE.Sがバグってた
FLE.Sがまたバグってた...