FLW命令とFSW命令
#RISC-V #Verilog #FPGA #自作CPU
from マルチサイクル RISC-V CPU を作成したい
浮動小数点命令の第一歩に FLW 命令を実装する。
opcode(7ビット)
0000111
funct3(3ビット)
010
imm(12ビット)
rd = M(rs1+imm)
やること
浮動小数点レジスタを追加
FLW命令を実装
FSW命令を実装
min_caml_print_float を実装
浮動小数点レジスタを追加
regfile モジュールへ浮動小数点レジスタを追加。readRegType と writeRegType で整数レジスタと浮動小数点レジスタの切り替えを行えるようにした。
(追記)
fsw 実装の流れで、readRegType を、レジスタ1用の readRegType1 とレジスタ2用の readRegType2 に分割した。以下のコードは分割前のものなので注意。
code:diff
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index 27be8e9..4067b2f 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -90,6 +90,8 @@ module cpu(
logic branch;
logic jump;
logic jump_reg;
+ logic read_reg_type;
+ logic write_reg_type;
decoder decoder_inst(
.instr(instr_reg),
@@ -101,7 +103,9 @@ module cpu(
.memToReg(dc_mem_to_reg),
.branch(branch),
.jump(jump),
- .jumpReg(jump_reg)
+ .jumpReg(jump_reg),
+ .readRegType(read_reg_type),
+ .writeRegType(write_reg_type)
);
//-------------------------------------
@@ -174,6 +178,8 @@ module cpu(
.addr2(rf_addr2),
.addr3(rf_addr3),
.writeData3(rf_write_data3),
+ .readRegType(read_reg_type),
+ .writeRegType(write_reg_type),
.readData1(rf_read_data1),
.readData2(rf_read_data2)
);
@@ -261,7 +267,9 @@ module decoder(
output logic memToReg,
output logic branch,
output logic jump,
- output logic jumpReg
+ output logic jumpReg,
+ output logic readRegType,
+ output logic writeRegType
);
logic 6:0 opCode;
logic 2:0 funct3;
@@ -330,6 +338,9 @@ module decoder(
(opCode == 7'b0010011) ? 2'b11 // addi, ori => funct3
: 2'b10; // funct
+ assign readRegType = 1'b0; // 整数レジスタを参照
+ assign writeRegType = 1'b0; // 整数レジスタに書き込む
+
// always @(*) begin
// $display("opCode %b", opCode);
// $display("funct7 %b", funct7);
@@ -527,22 +538,42 @@ module regfile(
input logic we3,
input logic 4:0 addr1, addr2, addr3,
input logic 31:0 writeData3,
+ input logic readRegType, // 0: 整数レジスタ, 1: 浮動小数点レジスタ
+ input logic writeRegType, // 0: 整数レジスタ, 1: 浮動小数点レジスタ
output logic 31:0 readData1, readData2
);
+ logic 31:0 intReadData1, intReadData2;
+ logic 31:0 fpReadData1, fpReadData2;
- // $0 へは書き込めるけど参照しても常に 0 が返る
+ // 整数レジスタ
logic 31:0 registers 0:31;
- assign readData1 = (addr1 === 5'b0) ? 32'b0 : registersaddr1;
- assign readData2 = (addr2 === 5'b0) ? 32'b0 : registersaddr2;
+ // 浮動小数点レジスタ
+ logic 31:0 fpRegisters 0:31;
+
+ // $0 へは書き込めるけど参照しても常に 0 が返る
+ assign intReadData1 = (addr1 === 5'b0) ? 32'b0 : registersaddr1;
+ assign intReadData2 = (addr2 === 5'b0) ? 32'b0 : registersaddr2;
+
+ assign fpReadData1 = fpRegistersaddr1;
+ assign fpReadData2 = fpRegistersaddr2;
+
+ // readRegType が 0 の場合は整数レジスタ、1 の場合は浮動小数点レジスタを参照する
+ assign readData1 = (readRegType) ? intReadData1 : fpReadData1;
+ assign readData2 = (readRegType) ? intReadData2 : fpReadData2;
always_ff @(posedge clk) begin
if (!reset_n) begin
for (int i = 0; i < 32; i++) begin
registersi <= 32'b0;
+ fpRegistersi <= 32'b0;
end
end else if (we3) begin
- registersaddr3 <= writeData3;
+ // writeRegType が 0 の場合は整数レジスタ、1 の場合は浮動小数点レジスタに書き込む
+ if (writeRegType)
+ registersaddr3 <= writeData3;
+ else
+ fpRegistersaddr3 <= writeData3;
end
end
FLW命令とFSW命令
続いて FLW 命令と FSW 命令を実装。
code:diff
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index 4067b2f..fc18f43 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -90,7 +90,8 @@ module cpu(
logic branch;
logic jump;
logic jump_reg;
- logic read_reg_type;
+ logic read_reg_type1;
+ logic read_reg_type2;
logic write_reg_type;
decoder decoder_inst(
@@ -104,7 +105,8 @@ module cpu(
.branch(branch),
.jump(jump),
.jumpReg(jump_reg),
- .readRegType(read_reg_type),
+ .readRegType1(read_reg_type1),
+ .readRegType2(read_reg_type2),
.writeRegType(write_reg_type)
);
@@ -178,7 +180,8 @@ module cpu(
.addr2(rf_addr2),
.addr3(rf_addr3),
.writeData3(rf_write_data3),
- .readRegType(read_reg_type),
+ .readRegType1(read_reg_type1),
+ .readRegType2(read_reg_type2),
.writeRegType(write_reg_type),
.readData1(rf_read_data1),
.readData2(rf_read_data2)
@@ -268,7 +271,8 @@ module decoder(
output logic branch,
output logic jump,
output logic jumpReg,
- output logic readRegType,
+ output logic readRegType1,
+ output logic readRegType2,
output logic writeRegType
);
logic 6:0 opCode;
@@ -287,9 +291,12 @@ module decoder(
assign opCode = instr6:0;
- assign memWrite = (opCode === 7'b0100011) ? 1'b1 : 1'b0;
+ assign memWrite = (opCode === 7'b0100011) ? 1'b1 : // sw
+ (opCode === 7'b0100111) ? 1'b1 // fsw
+ : 1'b0;
assign regWrite = (opCode === 7'b0000011) ? 1'b1 : // lw
+ (opCode === 7'b0000111) ? 1'b1 : // flw
(opCode === 7'b0010011) ? 1'b1 : // addi, ori
(opCode === 7'b0110011) ? 1'b1 : // R type (add)
(opCode === 7'b0110111) ? 1'b1 : // lui
@@ -313,8 +320,9 @@ module decoder(
(opCode === 7'b1100011) ? 1'b1 // B type
: 1'b0;
- // lw
- assign memToReg = (opCode == 7'b0000011) ? 1'b1 : 1'b0;
+ assign memToReg = (opCode == 7'b0000011) ? 1'b1 : // lw
+ (opCode == 7'b0000111) ? 1'b1 // flw
+ : 1'b0;
assign branch = (opCode === 7'b1100011) ? 1'b1 // B type
: 1'b0;
@@ -330,7 +338,10 @@ module decoder(
assign funct7 = instr31:25;
assign preAluOp = (opCode == 7'b1100011) ? 2'b01 : // B type => sub
(opCode == 7'b0000011) ? 2'b00 : // lw => add
+ (opCode == 7'b0000111) ? 2'b00 : // flw => add
+ (opCode == 7'b0000111) ? 2'b00 : // flw => add
(opCode == 7'b0100011) ? 2'b00 : // sw => add
+ (opCode == 7'b0100111) ? 2'b00 : // fsw => add
(opCode == 7'b1101111) ? 2'b00 : // jal => add
(opCode == 7'b1100111) ? 2'b00 : // jalr => add
(opCode == 7'b0110111) ? 2'b00 : // lui => add
@@ -338,8 +349,14 @@ module decoder(
(opCode == 7'b0010011) ? 2'b11 // addi, ori => funct3
: 2'b10; // funct
- assign readRegType = 1'b0; // 整数レジスタを参照
- assign writeRegType = 1'b0; // 整数レジスタに書き込む
+ assign readRegType1 = 1'b0; // 整数レジスタを参照
+
+ assign readRegType2 = (opCode === 7'b0100111) ? 1'b1 // 浮動小数点レジスタを参照 (fsw)
+ : 1'b0; // 整数レジスタを参照
+
+ assign writeRegType = (opCode === 7'b0000111) ? 1'b1 // 浮動小数点レジスタへ書き込み (flw)
+ : 1'b0; // 整数レジスタへ書き込み
+
// always @(*) begin
// $display("opCode %b", opCode);
@@ -538,8 +555,9 @@ module regfile(
input logic we3,
input logic 4:0 addr1, addr2, addr3,
input logic 31:0 writeData3,
- input logic readRegType, // 0: 整数レジスタ, 1: 浮動小数点レジスタ
- input logic writeRegType, // 0: 整数レジスタ, 1: 浮動小数点レジスタ
+ input logic readRegType1, // 0: 整数レジスタ, 1: 浮動小数点レジスタ
+ input logic readRegType2, // 0: 整数レジスタ, 1: 浮動小数点レジスタ
+ input logic writeRegType, // 0: 整数レジスタ, 1: 浮動小数点レジスタ
output logic 31:0 readData1, readData2
);
logic 31:0 intReadData1, intReadData2;
@@ -559,8 +577,12 @@ module regfile(
assign fpReadData2 = fpRegistersaddr2;
// readRegType が 0 の場合は整数レジスタ、1 の場合は浮動小数点レジスタを参照する
- assign readData1 = (readRegType) ? intReadData1 : fpReadData1;
- assign readData2 = (readRegType) ? intReadData2 : fpReadData2;
+ assign readData1 = (readRegType1 === 1'b0) ? intReadData1 :
+ (readRegType1 === 1'b1) ? fpReadData1
+ : 32'hxxxxxxxx;
+ assign readData2 = (readRegType2 === 1'b0) ? intReadData2 :
+ (readRegType2 === 1'b1) ? fpReadData2
+ : 32'hxxxxxxxx;
always_ff @(posedge clk) begin
if (!reset_n) begin
@@ -570,10 +592,10 @@ module regfile(
end
end else if (we3) begin
// writeRegType が 0 の場合は整数レジスタ、1 の場合は浮動小数点レジスタに書き込む
- if (writeRegType)
- registersaddr3 <= writeData3;
- else
- fpRegistersaddr3 <= writeData3;
+ case (writeRegType)
+ 1'b0: registersaddr3 <= writeData3;
+ 1'b1: fpRegistersaddr3 <= writeData3;
+ endcase
end
end
@@ -586,5 +608,6 @@ module regfile(
// $display("$2 %b", registers2);
// $display("$30 %b", registers30);
// $display("$31 %b", registers31);
+ // $display("f5 %d", fpRegisters5);
end
endmodule
diff --git a/rtl/cpu_test.sv b/rtl/cpu_test.sv
index cf6bc33..805a5cc 100644
--- a/rtl/cpu_test.sv
+++ b/rtl/cpu_test.sv
@@ -71,7 +71,8 @@ module cpu_test;
$monitoroff; // プログラム書き込み中は $monitor を一時停止
- `include "test_program/beq_and_auipc.sv"
+ // `include "test_program/beq_and_auipc.sv"
+ `include "test_program/fp.sv"
$finish;
end
diff --git a/rtl/test_program/fp.sv b/rtl/test_program/fp.sv
new file mode 100644
index 0000000..6e296f3
--- /dev/null
+++ b/rtl/test_program/fp.sv
@@ -0,0 +1,59 @@
+/**
+ * 浮動小数点数のテストプログラム
+ */
+
+instructions0 = flw(5, 0, 32'h100); // f5 = Mx0+0x100
+instructions1 = fsw(0, 5, 32'h104); // Mx0+0x104 = f5
+instructions2 = flw(6, 0, 32'h104); // f6 = Mx0+0x104
+instructions3 = jal(0, 0); // 無限ループ
+
+
+// テスト用の浮動小数点数データ
+instructions64 = 32'h10000000; // M0x100 = -0.0
+
+
+/**
+ * プログラムの書き込み
+ */
+
+mem_monitor_on = 1;
+addr = 32'h00000000;
+for (int i = 0; i < 255; i++) begin
+ mem_monitor_valid_reg = 1;
+ mem_monitor_addr_reg = addr;
+ mem_monitor_wdata_reg = instructionsi;
+ mem_monitor_wstrb_reg = 4'b1111;
+ #10;
+ wait(mem_ready);
+ mem_monitor_valid_reg = 0;
+ #10;
+
+ // 書き込みアドレスを進める
+ addr = addr + 4;
+end
+mem_monitor_on = 0;
+
+$monitoron; // $monitor を再開
+
+/**
+ * リセットして、0番地からプログラムを実行
+ */
+reset_n = 0;
+#10;
+reset_n = 1;
+#10;
+
+#1000;
+
+// 実行が終わった頃合いを見て、メモリの内容を確認
+mem_monitor_on = 1;
+mem_monitor_valid_reg = 1;
+mem_monitor_addr_reg = 32'h104;
+mem_monitor_wstrb_reg = 4'b0000;
+#10;
+wait(mem_ready);
+assert(
+ mem_rdata === 32'h10000000
+) $display("PASSED"); else $display("FAILED: %d", mem_rdata);
+mem_monitor_valid_reg = 0;
+#10;
テストプログラム
テストプログラムはこんな感じ。
code:verilog
instructions0 = flw(5, 0, 32'h100); // f5 = Mx0+0x100
instructions1 = fsw(0, 5, 32'h104); // Mx0+0x104 = f5
instructions2 = flw(6, 0, 32'h104); // f6 = Mx0+0x104
instructions3 = jal(0, 0); // 無限ループ
// テスト用の浮動小数点数データ
instructions64 = 32'h10000000; // M0x100 = -0.0
命令表
https://gyazo.com/b37c4f124e6dacd8dea842924d1f535e
浮動小数点レジスタ
https://gyazo.com/3a3611cd2925f9986603e1b942253fbe
FLWかFSWがバグってるような?
https://gyazo.com/bc26cd895b79d8d9ab790818349f8f00
Immediate がバグってたので修正した。
code:diff
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index fc18f43..131f644 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -439,8 +439,9 @@ module immgen(
assign imm5 = instr24:20;
- assign imm12 = (opCode == 7'b0100011) ? {instr31:25, instr11:7} // S type
- : instr31:20; // other
+ assign imm12 = (opCode == 7'b0100011) ? {instr31:25, instr11:7} : // sw
+ (opCode == 7'b0100111) ? {instr31:25, instr11:7} // fsw
+ : instr31:20; // other
// B type
assign imm13 = {instr31, instr7, instr30:25, instr11:8, 1'b0};
diff --git a/rtl/test_program/fp.sv b/rtl/test_program/fp.sv
index 6e296f3..8f14c9d 100644
--- a/rtl/test_program/fp.sv
+++ b/rtl/test_program/fp.sv
@@ -3,13 +3,13 @@
*/
instructions0 = flw(5, 0, 32'h100); // f5 = Mx0+0x100
-instructions1 = fsw(0, 5, 32'h104); // Mx0+0x104 = f5
-instructions2 = flw(6, 0, 32'h104); // f6 = Mx0+0x104
-instructions3 = jal(0, 0); // 無限ループ
-
+instructions1 = fsw(0, 5, 32'h104); // M0x104 = f5
+instructions2 = flw(6, 0, 32'h104); // f6 = M0x104
+instructions3 = fsw(0, 6, 32'h108); // M0x108 = f6
+instructions4 = jal(0, 0); // 無限ループ
// テスト用の浮動小数点数データ
-instructions64 = 32'h10000000; // M0x100 = -0.0
+instructions64 = 32'h3f800000; // M0x100 = 1.0
/**
@@ -53,7 +53,19 @@ mem_monitor_wstrb_reg = 4'b0000;
#10;
wait(mem_ready);
assert(
- mem_rdata === 32'h10000000
+ mem_rdata === 32'h3f800000
+) $display("PASSED"); else $display("FAILED: %d", mem_rdata);
+mem_monitor_valid_reg = 0;
+#10;
+
+mem_monitor_on = 1;
+mem_monitor_valid_reg = 1;
+mem_monitor_addr_reg = 32'h108;
+mem_monitor_wstrb_reg = 4'b0000;
+#10;
+wait(mem_ready);
+assert(
+ mem_rdata === 32'h3f800000
) $display("PASSED"); else $display("FAILED: %d", mem_rdata);
mem_monitor_valid_reg = 0;
#10;