FCVT.S.W命令とFCVT.W.S命令
from マルチサイクル RISC-V CPU を作成したい
FPUコアの機能を使って、整数から浮動小数点数、浮動小数点数から整数へ変換する命令を追加する。
FCVT.S.W
Floating-point Convert to Single from Word
32ビット整数(Word)から単精度浮動小数点数(Single)へ変換する
(整数レジスタ→浮動小数点レジスタ)
FCVT.W.S
Floating-point Convert to Word from Single
単精度浮動小数点数(Single)から32ビット整数(Word)へ変換する
(浮動小数点レジスタ→整数レジスタ)
FPUコアの機能を利用するだけなので、特に難しいところは無し。
code:diff
diff --git a/Makefile b/Makefile
index 174ce1e..43b6faf 100644
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,16 @@ unit-test:
iverilog -g 2012 -s alu_test rtl/instructions.sv rtl/cpu.sv rtl/alu_test.sv && ./a.out
test:
- iverilog -g 2012 -s cpu_test -I rtl rtl/cpu_test.sv rtl/bram_controller.sv rtl/fpu/adder/adder.v rtl/fpu/multiplier/multiplier.v rtl/fpu/divider/divider.v rtl/fpu_controller.sv rtl/cpu.sv && ./a.out
+ iverilog -g 2012 -s cpu_test -I rtl rtl/cpu_test.sv \
+ rtl/bram_controller.sv \
+ rtl/fpu/adder/adder.v \
+ rtl/fpu/multiplier/multiplier.v \
+ rtl/fpu/divider/divider.v \
+ rtl/fpu/int_to_float/int_to_float.v \
+ rtl/fpu/float_to_int/float_to_int.v \
+ rtl/fpu_controller.sv \
+ rtl/cpu.sv \
+ && ./a.out
firmware/firmware.hex:
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index 345e023..4be8dd2 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -415,21 +415,30 @@ module decoder(
(opCode == 7'b0010011) ? 2'b11 // addi, ori => funct3
: 2'b10; // funct
- // assign readRegType1 = 1'b0; // 整数レジスタを参照
- assign readRegType1 = (opCode === 7'b1010011) ? 1'b1 // 浮動小数点レジスタを参照(RV32F の R type)
- : 1'b0; // 整数レジスタを参照
+ // NOTE:
+ // fcvt.s.w (int to float) の場合(opCode = 1010011, funct7 = 1101000)
+ // readRegType1 は 0 で、writeRegType は 1 になる
+ // fcvt.w.s (float to int) の場合(opCode = 1010011, funct7 = 1100000)
+ // readRegType1 は 1 で、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 命令)
+ : 1'b0; // 整数レジスタを参照
assign readRegType2 = (opCode === 7'b0100111) ? 1'b1 : // 浮動小数点レジスタを参照 (fsw)
(opCode === 7'b1010011) ? 1'b1 // 浮動小数点レジスタを参照(RV32F の R type)
: 1'b0; // 整数レジスタを参照
- assign writeRegType = (opCode === 7'b0000111) ? 1'b1 : // 浮動小数点レジスタへ書き込み (flw)
- (opCode === 7'b1010011) ? 1'b1 // 浮動小数点レジスタを参照(RV32F の R type)
- : 1'b0; // 整数レジスタへ書き込み
+ 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) ? 1'b1 // 浮動小数点レジスタを参照(RV32F の R type)
+ : 1'b0; // 整数レジスタへ書き込み
assign fpu = (opCode === 7'b1010011) ? 1'b1 : 1'b0; // FPU を使う命令(RV32F R-type)
assign fpuOp = (funct7 === 7'b0000000) ? 4'b0000 : // fadd
+ (funct7 === 7'b1101000) ? 4'b0100 : // fcvt.s.w (int to float)
+ (funct7 === 7'b1100000) ? 4'b0101 : // fcvt.w.s (float to int)
(funct7 === 7'b0000100) ? 4'b0001 : // fsub
(funct7 === 7'b0001000) ? 4'b0010 : // fmul
(funct7 === 7'b0001100) ? 4'b0011 // fdiv
diff --git a/rtl/fpu_controller.sv b/rtl/fpu_controller.sv
index 1062a74..d024148 100644
--- a/rtl/fpu_controller.sv
+++ b/rtl/fpu_controller.sv
@@ -7,8 +7,8 @@
// 0001 | fsub
// 0010 | fmul
// 0011 | fdiv
-// 0100 | fcvt.s.w
-// 0101 | fcvt.w.s
+// 0100 | fcvt.s.w (int to float)
+// 0101 | fcvt.w.s (float to int)
// 0110 | feq
// 0111 | flt
// 1000 | fle
@@ -44,24 +44,37 @@ module fpu_controller(
assign divider_in2_stb = (op === 4'b0011) ? in1_stb : 1'b0;
assign divider_out_ack = (op === 4'b0011) ? out_ack : 1'b0;
+ assign int2float_in1_stb = (op === 4'b0100) ? in1_stb : 1'b0;
+ assign int2float_out_ack = (op === 4'b0100) ? out_ack : 1'b0;
+
+ assign float2int_in1_stb = (op === 4'b0101) ? in1_stb : 1'b0;
+ assign float2int_out_ack = (op === 4'b0101) ? out_ack : 1'b0;
//------------------------------------------------------------------------------
// 出力セレクタ
//------------------------------------------------------------------------------
assign in1_ack = (op === 4'b0000 || op === 4'b0001) ? adder_in1_ack :
+ (op === 4'b0100) ? int2float_in1_ack :
+ (op === 4'b0101) ? float2int_in1_ack :
(op === 4'b0010) ? multiplier_in1_ack :
(op === 4'b0011) ? divider_in1_ack
: 1'bx;
assign in2_ack = (op === 4'b0000 || op === 4'b0001) ? adder_in2_ack :
+ (op === 4'b0100) ? int2float_in1_ack :
+ (op === 4'b0101) ? float2int_in1_ack :
(op === 4'b0010) ? multiplier_in2_ack :
(op === 4'b0011) ? divider_in2_ack
:1'bx;
assign out_stb = (op === 4'b0000 || op === 4'b0001) ? adder_out_stb :
+ (op === 4'b0100) ? int2float_out_stb :
+ (op === 4'b0101) ? float2int_out_stb :
(op === 4'b0010) ? multiplier_out_stb :
(op === 4'b0011) ? divider_out_stb
: 1'bx;
assign out = (op === 4'b0000 || op === 4'b0001) ? adder_out :
+ (op === 4'b0100) ? int2float_out :
+ (op === 4'b0101) ? float2int_out :
(op === 4'b0010) ? multiplier_out :
(op === 4'b0011) ? divider_out
: 32'bx;
@@ -145,4 +158,46 @@ module fpu_controller(
.output_z_ack(divider_out_ack)
);
+ //------------------------------------------------------------------------------
+ // Floating-point Converter (int to float)
+ //------------------------------------------------------------------------------
+
+ logic int2float_in1_stb; // in1 が有効になったらアサートする
+ logic int2float_in1_ack; // 受け手側で in1 の読み込みが終わったらアサートされる
+ logic 31:0 int2float_out;
+ logic int2float_out_stb; // 計算結果が out に返ってきたらアサートされる
+ logic int2float_out_ack; // out の読み込みが終わったらアサートしてあげる
+
+ int_to_float int_to_float_inst (
+ .clk(clk),
+ .rst(~reset_n),
+ .input_a(in1),
+ .input_a_stb(int2float_in1_stb),
+ .input_a_ack(int2float_in1_ack),
+ .output_z(int2float_out),
+ .output_z_stb(int2float_out_stb),
+ .output_z_ack(int2float_out_ack)
+ );
+
+ //------------------------------------------------------------------------------
+ // Floating-point Converter (float to int)
+ //------------------------------------------------------------------------------
+
+ logic float2int_in1_stb; // in1 が有効になったらアサートする
+ logic float2int_in1_ack; // 受け手側で in1 の読み込みが終わったらアサートされる
+ logic 31:0 float2int_out;
+ logic float2int_out_stb; // 計算結果が out に返ってきたらアサートされる
+ logic float2int_out_ack; // out の読み込みが終わったらアサートしてあげる
+
+ float_to_int float_to_int_inst (
+ .clk(clk),
+ .rst(~reset_n),
+ .input_a(in1),
+ .input_a_stb(float2int_in1_stb),
+ .input_a_ack(float2int_in1_ack),
+ .output_z(float2int_out),
+ .output_z_stb(float2int_out_stb),
+ .output_z_ack(float2int_out_ack)
+ );
+
endmodule
diff --git a/rtl/instructions.sv b/rtl/instructions.sv
index a4259e5..5663438 100644
--- a/rtl/instructions.sv
+++ b/rtl/instructions.sv
@@ -425,33 +425,120 @@ endfunction
// fcvt.s.w
// Floating-point Convert to Single from Word)
// 単精度浮動小数点数 -> 32ビット整数
+function 31:0 fcvt_s_w(
+ input logic 4:0 rd,
+ input logic 4:0 rs1
+);
+ fcvt_s_w = {
+ 7'b1101000, // funct7
+ 5'b00000, // rs2 = 00000
+ rs1,
+ 3'b111, // roundMode (既存の出力コードに合わせて 111 を入れておく)
+ rd,
+ 7'b1010011 // opCode
+ };
+endfunction
// fcvt.w.s
// Floating-point Convert to Word from Single
// 32ビット整数 -> 単精度浮動小数点数
-
-// feq.s
-
-// flt.s (無くてもいいかも)
-
-// fle.s
+function 31:0 fcvt_w_s(
+ input logic 4:0 rd,
+ input logic 4:0 rs1
+);
+ fcvt_w_s = {
+ 7'b1100000, // funct7
+ 5'b00000, // rs2 = 00000
+ rs1,
+ 3'b111, // roundMode (既存の出力コードに合わせて 111 を入れておく)
+ rd,
+ 7'b1010011 // opCode
+ };
+endfunction
// fsgnj.s
// (Floating-point Sign Inject, Single-Precision)
// frd = {f{rs2]31, frs130:0}
+function 31:0 fsgnj_s(
+ input logic 4:0 rd,
+ input logic 4:0 rs1,
+ input logic 4:0 rs2
+);
+ fsgnj_s = {
+ 7'b0010000, // funct7
+ rs2,
+ rs1,
+ 3'b000, // funct3
+ rd,
+ 7'b1010011 // opCode
+ };
+endfunction
// fsgnjn.s
// (Floating-point Sign Inject-Negate, Single-Precision)
// frd = {~frs231, frs130:0}
+function 31:0 fsgnjn_s(
+ input logic 4:0 rd,
+ input logic 4:0 rs1,
+ input logic 4:0 rs2
+);
+ fsgnjn_s = {
+ 7'b0010000, // funct7
+ rs2,
+ rs1,
+ 3'b001, // funct3
+ rd,
+ 7'b1010011 // opCode
+ };
+endfunction
+// feq.s
+// xrd = (frs1 == frs2) ? 1 : 0
+function 31:0 feq_s(
+ input logic 4:0 rd,
+ input logic 4:0 rs1,
+ input logic 4:0 rs2
+);
+ feq_s = {
+ 7'b1010000, // funct7
+ rs2,
+ rs1,
+ 3'b010, // funct3
+ rd,
+ 7'b1010011 // opCode
+ };
+endfunction
-//(別の命令に展開されるもの)
-
-// fneg.s
-// frd = -frs1
-// => fsgnjn.s rd, rs1, rs1 に展開される
-
-// fmv.s
-// frd = frs1
-// => fsgnj.s rd, rs1, rs1 に展開される
+// flt.s
+// xrd = (frs1 < frs2) ? 1 : 0
+function 31:0 flt_s(
+ input logic 4:0 rd,
+ input logic 4:0 rs1,
+ input logic 4:0 rs2
+);
+ flt_s = {
+ 7'b1010000, // funct7
+ rs2,
+ rs1,
+ 3'b001, // funct3
+ rd,
+ 7'b1010011 // opCode
+ };
+endfunction
+// fle.s
+// xrd = (frs1 <= frs2) ? 1 : 0
+function 31:0 fle_s(
+ input logic 4:0 rd,
+ input logic 4:0 rs1,
+ input logic 4:0 rs2
+);
+ fle_s = {
+ 7'b1010000, // funct7
+ rs2,
+ rs1,
+ 3'b000, // funct3
+ rd,
+ 7'b1010011 // opCode
+ };
+endfunction
diff --git a/rtl/test_program/fp.sv b/rtl/test_program/fp.sv
index 15ac258..a397741 100644
--- a/rtl/test_program/fp.sv
+++ b/rtl/test_program/fp.sv
@@ -34,7 +34,14 @@ instructions17 = fsw(0, 5, 32'h124); // M0x110 = f5
instructions18 = flw(6, 0, 32'h124); // f6 = M0x110
instructions19 = fsw(0, 6, 32'h128); // M0x114 = f6
-instructions20 = jal(0, 0); // 無限ループ
+// FCVT のテスト
+instructions20 = flw(5, 0, 32'h108); // f5 = 3.0
+instructions21 = fcvt_w_s(6, 5); // x6 = (int)f5
+instructions22 = sw(0, 6, 32'h12c); // M0x12c = x6
+instructions23 = fcvt_s_w(7, 6); // f7 = (float)x6
+instructions24 = fsw(0, 7, 32'h130); // M0x130 = f7
+
+instructions25 = jal(0, 0); // 無限ループ
// テスト用の浮動小数点数データ
instructions64 = 32'h3f800000; // M0x100 = 1.0
@@ -150,3 +157,27 @@ assert(
) $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'h12c;
+mem_monitor_wstrb_reg = 4'b0000;
+#10;
+wait(mem_ready);
+assert(
+ mem_rdata === 32'h3
+) $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'h130;
+mem_monitor_wstrb_reg = 4'b0000;
+#10;
+wait(mem_ready);
+assert(
+ mem_rdata === 32'h40400000
+) $display("PASSED"); else $display("FAILED: %h", mem_rdata);
+mem_monitor_valid_reg = 0;
+#10;