足りてない命令を追加(RV32I)
from マルチサイクル RISC-V CPU を作成したい
RV32I系の命令の実装状況を確認。
R-type 命令
すべて実装済み
add
sub
xor
or
and
sll
srl
sra
slt
sltu
I-type 命令
実装済み
addi
xori
ori
andi
slli
srli
srai
slti
sltiu
(ロード系、lw 以外は未実装)
lw
lb(未実装)
lh(未実装)
lw(未実装)
lbu(未実装)
lhu(未実装)
(ジャンプ系)
jalr
(気が向いたら調べる)
ecall
ebreak
S-type 命令
(ストア系、sw以外は未実装)
sw
sb(未実装)
sh(未実装)
B-type 命令
実装済み
beq
bne
blt
bge
bltu
bgeu
U-type 命令
lui
auipc
Barnch系の実装
https://gyazo.com/8edc50a0b9314167bb82913bc7b114b0
実装した
code:diff
diff --git a/Makefile b/Makefile
index 9047973..9123e18 100644
--- a/Makefile
+++ b/Makefile
@@ -28,10 +28,13 @@ prog: ulx3s.bit
prog_flash: ulx3s.bit
fujprog -j FLASH ulx3s.bit
+unit-test:
+ iverilog -g 2012 -s branch_unit_test rtl/instructions.sv rtl/cpu.sv rtl/branch_unit_test.sv && ./a.out
+
test:
- cd rtl && iverilog -g 2012 -s cpu_test cpu_test.sv cpu.sv bram_controller.sv && ./a.out
+ iverilog -g 2012 -s cpu_test rtl/instructions.sv rtl/cpu_test.sv rtl/bram_controller.sv rtl/cpu.sv && ./a.out
-FIRMWARE_TARGET = mem_test.S
+FIRMWARE_TARGET = branch_test.S
firmware/firmware.hex: firmware/$(FIRMWARE_TARGET)
riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -Wl,-Ttext=0x00000000 $< -o firmware/firmware.elf
diff --git a/firmware/branch_test.S b/firmware/branch_test.S
new file mode 100644
index 0000000..dbebf4c
--- /dev/null
+++ b/firmware/branch_test.S
@@ -0,0 +1,167 @@
+// 条件分岐系のテスト
+// - beq
+// - bne
+// - blt
+// - bge
+// - bltu
+// - bgeu
+
+ .text
+ .globl _start
+_start:
+ lui x10, 0xf0001 // LEDアクセス用 gp
+ li s0, 1
+
+ //----------------------------------------
+ // beq
+ //----------------------------------------
+
+ li t0, 0x00000000
+ li t1, 0xFFFFFFFF
+
+ // beq (1): eq の場合ジャンプ
+ beq t0, t0, beq1_ok
+fail:
+ j fail
+beq1_ok:
+ sw s0, 0(x10) // LEDに1を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ // beq (2): eq じゃない場合ジャンプしない
+ beq t0, t1, fail
+beq2_ok:
+ sw s0, 0(x10) // LEDに2を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ //----------------------------------------
+ // bne
+ //----------------------------------------
+
+ li t0, 0x00000000
+ li t1, 0xFFFFFFFF
+
+ // bne (1): eq の場合ジャンプしない
+ bne t0, t0, fail
+bne1_ok:
+ sw s0, 0(x10) // LEDに3を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ // bne (2): eq じゃない場合ジャンプ
+ bne t0, t1, bne2_ok
+ j fail
+bne2_ok:
+ sw s3, 0(x10) // LEDに4を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+
+
+ //----------------------------------------
+ // blt (signed rs1 < signed rs2)
+ //----------------------------------------
+
+ li t0, -1
+ li t1, 0
+
+ // blt (1): rs1 < rs2 の場合ジャンプ
+ blt t0, t1, blt1_ok
+ j fail
+blt1_ok:
+ sw s0, 0(x10) // LEDに5を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ // blt (2): rs1 >= rs2 の場合ジャンプしない
+ blt t1, t0, fail
+blt2_ok:
+ sw s0, 0(x10) // LEDに6を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ // blt (3): rs1 == rs2 の場合ジャンプしない
+ blt t0, t0, fail
+blt3_ok:
+ sw s0, 0(x10) // LEDに7を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ //----------------------------------------
+ // bge (signed rs1 >= signed rs2)
+ //----------------------------------------
+
+ li t0, -1
+ li t1, 0
+
+ // bge (1): rs1 >= rs2 の場合ジャンプ
+ bge t1, t0, bge1_ok
+ j fail
+bge1_ok:
+ sw s0, 0(x10) // LEDに8を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ // bge (2): rs1 < rs2 の場合ジャンプしない
+ bge t0, t1, fail
+bge2_ok:
+ sw s0, 0(x10) // LEDに9を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ // bge (3): rs1 == rs2 の場合ジャンプ
+ bge t0, t0, bge3_ok
+ j fail
+bge3_ok:
+ sw s0, 0(x10) // LEDに10を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ //----------------------------------------
+ // bltu (unsigned rs1 < unsigned rs2)
+ //----------------------------------------
+
+ li t0, 0xFFFFFFFF
+ li t1, 0
+
+ // bltu (1): rs1 < rs2 の場合ジャンプ
+ bltu t1, t0, bltu1_ok
+ j fail
+bltu1_ok:
+ sw s0, 0(x10) // LEDに11を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ // bltu (2): rs1 >= rs2 の場合ジャンプしない
+ bltu t0, t1, fail
+bltu2_ok:
+ sw s0, 0(x10) // LEDに12を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ // bltu (3): rs1 == rs2 の場合ジャンプしない
+ bltu t0, t0, fail
+bltu3_ok:
+ sw s0, 0(x10) // LEDに13を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+
+ //----------------------------------------
+ // bgeu (unsigned rs1 >= unsigned rs2)
+ //----------------------------------------
+
+ li t0, 0xFFFFFFFF
+ li t1, 0
+
+ // bgeu (1): rs1 >= rs2 の場合ジャンプ
+ bgeu t0, t1, bgeu1_ok
+ j fail
+bgeu1_ok:
+ sw s0, 0(x10) // LEDに14を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ // bgeu (2): rs1 < rs2 の場合ジャンプしない
+ bgeu t1, t0, fail
+bgeu2_ok:
+ sw s0, 0(x10) // LEDに15を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ // bgeu (3): rs1 == rs2 の場合ジャンプ
+ bgeu t0, t0, bgeu3_ok
+ j fail
+bgeu3_ok:
+ sw s0, 0(x10) // LEDに16を出力
+ addi s0, s0, 1 // s0 = s0 + 1
+
+ // 無限ループ
+loop:
+ jal x0, loop
diff --git a/rtl/branch_unit_test.sv b/rtl/branch_unit_test.sv
new file mode 100644
index 0000000..45a37d4
--- /dev/null
+++ b/rtl/branch_unit_test.sv
@@ -0,0 +1,203 @@
+// 条件分岐判定ユニットのテストベンチ
+module branch_unit_test;
+ logic 31:0 in1, in2;
+ logic 2:0 funct3;
+ logic result;
+
+ branch_unit dut(
+ .in1(in1),
+ .in2(in2),
+ .funct3(funct3),
+ .result(result)
+ );
+
+ initial begin
+ //------------------------------------------------------------------
+ // beq
+ //------------------------------------------------------------------
+
+ // 0x00000000 == 0x00000000
+ in1 = 32'h00000000; in2 = 32'h00000000;
+ funct3 = 3'b000;
+ #10
+ assert (
+ result === 1'b1
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0x00000000 == 0xFFFFFFFF
+ in1 = 32'h00000000; in2 = 32'hFFFFFFFF;
+ funct3 = 3'b000;
+ #10
+ assert (
+ result === 1'b0
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ //------------------------------------------------------------------
+ // bne
+ //------------------------------------------------------------------
+
+ // 0x00000000 != 0x00000000
+ in1 = 32'h00000000; in2 = 32'h00000000;
+ funct3 = 3'b001;
+ #10
+ assert (
+ result === 1'b0
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0x00000000 != 0xFFFFFFFF
+ in1 = 32'h00000000; in2 = 32'hFFFFFFFF;
+ funct3 = 3'b001;
+ #10
+ assert (
+ result === 1'b1
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ //------------------------------------------------------------------
+ // blt
+ //------------------------------------------------------------------
+
+ // 0x00000000 < 0x00000000
+ in1 = 32'h00000000; in2 = 32'h00000000;
+ funct3 = 3'b100;
+ #10
+ assert (
+ result === 1'b0
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0x00000000 < 0xFFFFFFFF
+ // 符号付きで比較されるため、0xFFFFFFFF は -1 として扱われる
+ in1 = 32'h00000000; in2 = 32'hFFFFFFFF;
+ funct3 = 3'b100;
+ #10
+ assert (
+ result === 1'b0
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0xFFFFFFFF < 0x00000000
+ // 符号付きで比較されるため、0xFFFFFFFF は -1 として扱われる
+ in1 = 32'hFFFFFFFF; in2 = 32'h00000000;
+ funct3 = 3'b100;
+ #10
+ assert (
+ result === 1'b1
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0xFFFFFFFF < 0xFFFFFFFF
+ // 符号付きで比較されるため、0xFFFFFFFF は -1 として扱われる
+ in1 = 32'hFFFFFFFF; in2 = 32'hFFFFFFFF;
+ funct3 = 3'b100;
+ #10
+ assert (
+ result === 1'b0
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ //------------------------------------------------------------------
+ // bge
+ //------------------------------------------------------------------
+
+ // 0x00000000 >= 0x00000000
+ in1 = 32'h00000000; in2 = 32'h00000000;
+ funct3 = 3'b101;
+ #10
+ assert (
+ result === 1'b1
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0x00000000 >= 0xFFFFFFFF
+ // 符号付きで比較されるため、0xFFFFFFFF は -1 として扱われる
+ in1 = 32'h00000000; in2 = 32'hFFFFFFFF;
+ funct3 = 3'b101;
+ #10
+ assert (
+ result === 1'b1
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0xFFFFFFFF >= 0x00000000
+ // 符号付きで比較されるため、0xFFFFFFFF は -1 として扱われる
+ in1 = 32'hFFFFFFFF; in2 = 32'h00000000;
+ funct3 = 3'b101;
+ #10
+ assert (
+ result === 1'b0
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0xFFFFFFFF >= 0xFFFFFFFF
+ // 符号付きで比較されるため、0xFFFFFFFF は -1 として扱われる
+ in1 = 32'hFFFFFFFF; in2 = 32'hFFFFFFFF;
+ funct3 = 3'b101;
+ #10
+ assert (
+ result === 1'b1
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ //------------------------------------------------------------------
+ // bltu
+ //------------------------------------------------------------------
+
+ // 0x00000000 < 0x00000000
+ in1 = 32'h00000000; in2 = 32'h00000000;
+ funct3 = 3'b110;
+ #10
+ assert (
+ result === 1'b0
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0x00000000 < 0xFFFFFFFF
+ // 符号なしで比較されるため、0xFFFFFFFF は 4294967295 として扱われる
+ in1 = 32'h00000000; in2 = 32'hFFFFFFFF;
+ funct3 = 3'b110;
+ #10
+ assert (
+ result === 1'b1
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0xFFFFFFFF < 0x00000000
+ // 符号なしで比較されるため、0xFFFFFFFF は 4294967295 として扱われる
+ in1 = 32'hFFFFFFFF; in2 = 32'h00000000;
+ funct3 = 3'b110;
+ #10
+ assert (
+ result === 1'b0
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0xFFFFFFFF < 0xFFFFFFFF
+ // 符号なしで比較されるため、0xFFFFFFFF は 4294967295 として扱われる
+ in1 = 32'hFFFFFFFF; in2 = 32'hFFFFFFFF;
+ funct3 = 3'b110;
+ #10
+ assert (
+ result === 1'b0
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ //------------------------------------------------------------------
+ // bgeu
+ //------------------------------------------------------------------
+
+ // 0x00000000 >= 0x00000000
+ in1 = 32'h00000000; in2 = 32'h00000000;
+ funct3 = 3'b111;
+ #10
+ assert (
+ result === 1'b1
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0x00000000 >= 0xFFFFFFFF
+ // 符号なしで比較されるため、0xFFFFFFFF は 4294967295 として扱われる
+ in1 = 32'h00000000; in2 = 32'hFFFFFFFF;
+ funct3 = 3'b111;
+ #10
+ assert (
+ result === 1'b0
+ ) $display("PASSED"); else $display("FAILED %h", result);
+
+ // 0xFFFFFFFF >= 0x00000000
+ // 符号なしで比較されるため、0xFFFFFFFF は 4294967295 として扱われる
+ in1 = 32'hFFFFFFFF; in2 = 32'h00000000;
+ funct3 = 3'b111;
+ #10
+ assert (
+ result === 1'b1
+ ) $display("PASSED"); else $display("FAILED %h", result);
+ end
+
+endmodule
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index 5bbdb17..e79e586 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -28,19 +28,19 @@ module cpu(
//-------------------------------------
initial begin
// デバッグ用モニタ
- $monitor(
- "%t: pc = %h, stage = %d, instr = %h, mem_valid = %b, mem_ready = %b, mem_addr = %h, mem_wdata = %h, mem_wstrb = %b, mem_rdata = %h",
- $time,
- pc_reg,
- stage_reg,
- instr_reg,
- mem_valid,
- mem_ready,
- mem_addr,
- mem_wdata,
- mem_wstrb,
- mem_rdata
- );
+ // $monitor(
+ // "%t: pc = %h, stage = %d, instr = %h, mem_valid = %b, mem_ready = %b, mem_addr = %h, mem_wdata = %h, mem_wstrb = %b, mem_rdata = %h",
+ // $time,
+ // pc_reg,
+ // stage_reg,
+ // instr_reg,
+ // mem_valid,
+ // mem_ready,
+ // mem_addr,
+ // mem_wdata,
+ // mem_wstrb,
+ // mem_rdata
+ // );
end
//-------------------------------------
@@ -133,6 +133,21 @@ module cpu(
.zero(alu_zero)
);
+ //-------------------------------------
+ // 条件分岐判定ユニット
+ //-------------------------------------
+ logic branch_result;
+ logic 2:0 branch_funct3;
+
+ assign branch_funct3 = instr_reg14:12;
+
+ branch_unit branch_unit_inst(
+ .in1(rf_read_data1),
+ .in2(rf_read_data2),
+ .funct3(branch_funct3),
+ .result(branch_result)
+ );
+
//-------------------------------------
// Register File
//-------------------------------------
@@ -218,9 +233,9 @@ module cpu(
end
// レジスタ書き戻し
WB_STAGE: begin
- pc_next = (branch && alu_zero) ? pc_reg + imm :
- (jump_reg) ? alu_result & 32'hfffffffe :
- (jump) ? pc_reg + imm
+ pc_next = (branch && branch_result) ? pc_reg + imm :
+ (jump_reg) ? alu_result & 32'hfffffffe :
+ (jump) ? pc_reg + imm
: pc_reg + 4;
stage_next = IF_STAGE;
end
@@ -451,6 +466,33 @@ module alu(
// end
endmodule
+
+// 条件分岐判定ユニット
+//
+// funct3 | operation
+// ------------------
+// 000 | beq (===)
+// 001 | bne (!==)
+// 100 | blt (signed <)
+// 101 | bge (signed >=)
+// 110 | bltu (unsigned <)
+// 111 | bgeu (unsigned >=)
+// ------------------
+module branch_unit(
+ input logic 31:0 in1, in2,
+ input logic 2:0 funct3,
+ output logic result
+);
+ assign result = (funct3 == 3'b000) ? (in1 === in2) : // beq
+ (funct3 == 3'b001) ? (in1 !== in2) : // bne
+ (funct3 == 3'b100) ? ($signed(in1) < $signed(in2)) : // blt
+ (funct3 == 3'b101) ? ($signed(in1) >= $signed(in2)) : // bge
+ (funct3 == 3'b110) ? (in1 < in2) : // bltu
+ (funct3 == 3'b111) ? (in1 >= in2) // bgeu
+ : 1'b0;
+endmodule
+
+
// Register File
module regfile(
input logic clk,
@@ -478,11 +520,11 @@ module regfile(
end
always @(*) begin
- $display("x1 %d", registers1);
- $display("x2 %d", registers2);
- $display("x3 %d", registers3);
- $display("x5 %d", registers5);
- $display("x10 %d", registers10);
+ // $display("x1 %d", registers1);
+ // $display("x2 %d", registers2);
+ // $display("x3 %d", registers3);
+ // $display("x5 %d", registers5);
+ // $display("x10 %d", registers10);
// $display("$2 %b", registers2);
// $display("$30 %b", registers30);
// $display("$31 %b", registers31);
diff --git a/rtl/cpu_test.sv b/rtl/cpu_test.sv
index daae11b..3fe93b9 100644
--- a/rtl/cpu_test.sv
+++ b/rtl/cpu_test.sv
@@ -3,6 +3,7 @@
`endif
// CPUのテストベンチ
+// iverilog -g 2012 -s cpu_test rtl/instructions.sv rtl/cpu_test.sv rtl/bram_controller.sv rtl/cpu.sv && ./a.out
module cpu_test;
// CPUのインスタンス
@@ -74,36 +75,13 @@ module cpu_test;
* プログラムの書き込み
*/
- // lw と sw のテスト
- instructions0 = addi(1, 0, 10); // addi x1, x0, 10
- instructions1 = add(2, 1, 1); // addi x2, x1, x1
- instructions2 = add(3, 1, 2); // addi x3, x1, x2
- instructions3 = sw(0, 3, 32'h80); // sw x3, 0x80(x0) (メモリの 0x80 番地へ x3 の値を格納する)
- instructions4 = lw(4, 0, 32'h80); // lw x4, 0x80(x0) (メモリの 0x80 番地から x4 へ読み込む)
- instructions5 = sw(0, 4, 32'h84); // sw x4, 0x84(x0) (メモリの 0x84 番地へ x4 の値を格納する)
// beq のテスト
- instructions6 = addi(5, 0, 42); // x5 = x0 + 42
- instructions7 = addi(6, 0, 42); // x6 = x0 + 42
- instructions8 = beq(5, 6, 8 >> 1); // if (x5 == x6) 2つ先の命令(8バイト)へジャンプ
- instructions9 = jal(0, 0); // 無限ループ
- instructions10 = sw(0, 5, 32'h88); // sw x5, 0x88(x0) (メモリの 0x88 番地へ x5 の値を格納する)
- // lui と ori のテスト
- instructions11 = lui(7, 32'h12345000 >> 12); // x7 = 0x12345000
- instructions12 = sw(0, 7, 32'h8C); // M0x8C = x7
- instructions13 = ori(7, 7, 32'h0678); // x7 = x7 | 0x678
- instructions14 = sw(0, 7, 32'h90); // M0x90 = x7
- // jal と jalr で関数呼び出し
- instructions15 = addi(10, 0, 100); // x10 = 100
- instructions16 = jal(1, 12 >> 1); // jal x1, 12 (double 関数を呼び出す)
- instructions17 = sw(0, 10, 32'h94); // M0x94 = x10
- // 無限ループ
- instructions18 = jal(0, 0);
- // double 関数
- // x10 に渡した値を二倍にして x10 へ入れて返す関数
- instructions19 = addi(5, 10, 0); // x5 = x10
- instructions20 = add(10, 5, 5); // x10 = x5 + x5
- instructions21 = jalr(0, 1, 0); // jalr x0, x1, 0
-
+ instructions0 = addi(5, 0, 42); // x5 = x0 + 42
+ instructions1 = addi(6, 0, 42); // x6 = x0 + 42
+ instructions2 = beq(5, 6, 8 >> 1); // if (x5 == x6) 2つ先の命令(8バイト)へジャンプ
+ instructions3 = jal(0, 0); // 無限ループ
+ instructions4 = sw(0, 5, 32'h3000); // sw x5, 0x88(x0) (メモリの 0x88 番地へ x5 の値を格納する)
+ instructions5 = jal(0, 0); // 無限ループ
mem_monitor_on = 1;
addr = 32'h00000000;
@@ -122,7 +100,7 @@ module cpu_test;
end
mem_monitor_on = 0;
- $monitoron; // $monitor を再開
+ // $monitoron; // $monitor を再開
/**
* リセットして、0番地からプログラムを実行
@@ -141,62 +119,9 @@ module cpu_test;
mem_monitor_wstrb_reg = 4'b0000;
#10;
wait(mem_ready);
- $display("mem0x80 = %d", mem_rdata);
- mem_monitor_valid_reg = 0;
- #10;
-
- // メモリの 0x84 番地の内容を確認
- mem_monitor_on = 1;
- mem_monitor_valid_reg = 1;
- mem_monitor_addr_reg = 32'h00000084;
- mem_monitor_wstrb_reg = 4'b0000;
- #10;
- wait(mem_ready);
- $display("mem0x84 = %d", mem_rdata);
- mem_monitor_valid_reg = 0;
- #10;
-
- // メモリの 0x88 番地の内容を確認
- mem_monitor_on = 1;
- mem_monitor_valid_reg = 1;
- mem_monitor_addr_reg = 32'h00000088;
- mem_monitor_wstrb_reg = 4'b0000;
- #10;
- wait(mem_ready);
- $display("mem0x88 = %d", mem_rdata);
- mem_monitor_valid_reg = 0;
- #10;
-
- // メモリの 0x8C 番地の内容を確認
- mem_monitor_on = 1;
- mem_monitor_valid_reg = 1;
- mem_monitor_addr_reg = 32'h0000008C;
- mem_monitor_wstrb_reg = 4'b0000;
- #10;
- wait(mem_ready);
- $display("mem0x8C = %h", mem_rdata);
- mem_monitor_valid_reg = 0;
- #10;
-
- // メモリの 0x90 番地の内容を確認
- mem_monitor_on = 1;
- mem_monitor_valid_reg = 1;
- mem_monitor_addr_reg = 32'h00000090;
- mem_monitor_wstrb_reg = 4'b0000;
- #10;
- wait(mem_ready);
- $display("mem0x90 = %h", mem_rdata);
- mem_monitor_valid_reg = 0;
- #10;
-
- // メモリの 0x94 番地の内容を確認
- mem_monitor_on = 1;
- mem_monitor_valid_reg = 1;
- mem_monitor_addr_reg = 32'h00000094;
- mem_monitor_wstrb_reg = 4'b0000;
- #10;
- wait(mem_ready);
- $display("mem0x94 = %d", mem_rdata);
+ assert(
+ mem_rdata === 42
+ ) $display("PASSED"); else $display("expected 42 but actual %d", mem_rdata);
mem_monitor_valid_reg = 0;
#10;
diff --git a/rtl/instructions.sv b/rtl/instructions.sv
index 0d621d8..d4fbf2a 100644
--- a/rtl/instructions.sv
+++ b/rtl/instructions.sv
@@ -186,6 +186,24 @@ function 31:0 beq(
};
endfunction
+// bne rs1, rs2, imm
+function 31:0 bne(
+ input logic 4:0 rs1,
+ input logic 4:0 rs2,
+ input logic 11:0 imm
+);
+ bne = {
+ imm11,
+ imm9:4,
+ rs2,
+ rs1,
+ 3'b001, // funct3
+ imm3:0,
+ imm10,
+ 7'b1100011 // opCode
+ };
+endfunction
+
// jal rd, offset
function 31:0 jal(
input logic 4:0 rd,
I形式の命令
addi 以外は未実装だったので追加した。
code:diff
commit 129907592410a1883694039037ea39a6115e2fb4
Author: takashi hatakeyama <takashi.hatakeyama@gmail.com>
Date: Thu Dec 26 23:36:26 2024 +0900
addi 以外の I 形式の命令を追加した
diff --git a/Makefile b/Makefile
index 9123e18..8c3c80c 100644
--- a/Makefile
+++ b/Makefile
@@ -34,7 +34,7 @@ unit-test:
test:
iverilog -g 2012 -s cpu_test rtl/instructions.sv rtl/cpu_test.sv rtl/bram_controller.sv rtl/cpu.sv && ./a.out
-FIRMWARE_TARGET = branch_test.S
+FIRMWARE_TARGET = i_type_test.S
firmware/firmware.hex: firmware/$(FIRMWARE_TARGET)
riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -Wl,-Ttext=0x00000000 $< -o firmware/firmware.elf
diff --git a/firmware/i_type_test.S b/firmware/i_type_test.S
new file mode 100644
index 0000000..871862f
--- /dev/null
+++ b/firmware/i_type_test.S
@@ -0,0 +1,171 @@
+// I形式の命令のテスト
+// - xori
+// - ori
+// - andi
+// - slli
+// - srli
+// - srai
+// - slti
+// - sltiu
+
+ .text
+ .globl _start
+_start:
+ lui gp, 0xf0001 // LEDアクセス用 gp
+ li s0, 1
+ j test_start
+
+fail:
+ // 失敗時は LED に 1 を出力
+ li t0, 1
+ sw t0, 0(gp)
+ j fail
+
+test_start:
+
+ //----------------------------------------
+ // xori
+ //----------------------------------------
+
+ // 1111 xor 1010 = 0101
+ li t0, 0b1111
+
+ xori t1, t0, 0b1010 // actual
+ li t2, 0b0101 // expected
+ bne t1, t2, fail
+
+ //----------------------------------------
+ // ori
+ //----------------------------------------
+
+ // 1100 or 1010 = 1110
+ li t0, 0b1100
+ ori t1, t0, 0b1010 // actual
+ li t2, 0b1110 // expected
+ bne t1, t2, fail
+
+ //----------------------------------------
+ // andi
+ //----------------------------------------
+
+ // 1100 and 1010 = 1000
+ li t0, 0b1100
+ andi t1, t0, 0b1010 // actual
+ li t2, 0b1000 // expected
+ bne t1, t2, fail
+
+ //----------------------------------------
+ // slli
+ //----------------------------------------
+
+ // 0011_1100 << 2 = 1111_000
+ li t0, 0b00111100
+ slli t1, t0, 2 // actual
+ li t2, 0b11110000 // expected
+ bne t1, t2, fail
+
+ //----------------------------------------
+ // srli
+ //----------------------------------------
+
+ // 0011_1100 >> 5 = 0000_0001
+ li t0, 0b00111100
+ srli t1, t0, 5 // actual
+ li t2, 0b00000001 // expected
+ bne t1, t2, fail
+
+ // 0011_1100 >> 2 = 0000_1111
+ li t0, 0b00111100
+ srli t1, t0, 2 // actual
+ li t2, 0b00001111 // expected
+ bne t1, t2, fail
+
+ // 0xFFFF_0000 >> 4 = 0x0FFF_F000
+ li t0, 0xFFFF0000
+ srli t1, t0, 4 // actual
+ li t2, 0x0FFFF000 // expected
+ bne t1, t2, fail
+
+ //----------------------------------------
+ // srai
+ //----------------------------------------
+
+ // 0011_1100 >> 5 = 0000_0001
+ li t0, 0b00111100
+ srai t1, t0, 5 // actual
+ li t2, 0b00000001 // expected
+ bne t1, t2, fail
+
+ // 0xFFFF_0000 >> 4 = 0x0FFF_F000
+ li t0, 0xFFFF0000
+ srai t1, t0, 4 // actual
+ li t2, 0xFFFFF000 // expected
+ bne t1, t2, fail
+
+ //----------------------------------------
+ // slti
+ //----------------------------------------
+
+ // 1 < 2 = 1
+ li t0, 1
+
+ // slti を正しい書き方でお願い
+ slti t2, t0, 2 // actual
+ li t3, 1 // expected
+ bne t2, t3, fail
+
+ // 1 < 1 = 0
+ li t0, 1
+ slti t2, t0, 1 // actual
+ li t3, 0 // expected
+ bne t2, t3, fail
+
+ // 2 < 1 = 0
+ li t0, 2
+ slti t2, t0, 1 // actual
+ li t3, 0 // expected
+ bne t2, t3, fail
+
+ // 0xffffffff(-1) < 0 = 1
+ li t0, 0xffffffff
+ slti t2, t0, 1 // actual
+ li t3, 1 // expected
+ bne t2, t3, fail
+
+
+ //----------------------------------------
+ // sltiu
+ //----------------------------------------
+
+ // 1 < 2 = 1
+ li t0, 1
+ sltiu t2, t0, 2 // actual
+ li t3, 1 // expected
+ bne t2, t3, fail
+
+ // 1 < 1 = 0
+ li t0, 1
+ sltiu t2, t0, 1 // actual
+ li t3, 0 // expected
+ bne t2, t3, fail
+
+ // 2 < 1 = 0
+ li t0, 2
+ sltiu t2, t0, 1 // actual
+ li t3, 0 // expected
+ bne t2, t3, fail
+
+ // 0xffffffff < 0 = 0
+ li t0, 0xffffffff
+ sltiu t2, t0, 0 // actual
+ li t3, 0 // expected
+ bne t2, t3, fail
+
+
+ // Success
+ // LED に 128 を出力
+ li t0, 128
+ sw t0, 0(gp)
+
+loop:
+ j loop
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index e79e586..e2d2389 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -324,7 +324,9 @@ module decoder(
: 2'b10; // funct
// always @(*) begin
- // $display("aluIn2Src %b", aluIn2Src);
+ // $display("opCode %b", opCode);
+ // $display("funct7 %b", funct7);
+ // $display("funct3 %b", funct3);
// $display("preAluOp %b", preAluOp);
// $display("aluOp %b", aluOp);
// end
@@ -354,7 +356,16 @@ module alu_controller(
2'b01: aluOp = 4'b0001; // sub
2'b11: case(funct3)
3'b000: aluOp = 4'b0000; // addi => add
- 3'b110: aluOp = 4'b1000; // ori => or
+ 3'b001: aluOp = 4'b0010; // x slli => sll
+ 3'b010: aluOp = 4'b0011; // x slti => slt
+ 3'b011: aluOp = 4'b0100; // x sltiu => sltu
+ 3'b100: aluOp = 4'b0101; // x xori => xor
+ 3'b101: aluOp =
+ (funct7 === 7'h00) ? 4'b0110 : // x srli => srl
+ (funct7 === 7'h20) ? 4'b0111 // x srai => sra
+ : 4'bxxxx;
+ 3'b110: aluOp = 4'b1000; // x ori => or
+ 3'b111: aluOp = 4'b1001; // x andi => and
default: aluOp = 4'bXXXX;
endcase
default: case(funct)
@@ -380,12 +391,17 @@ module immgen(
output logic 31:0 imm
);
logic 6:0 opCode;
+ logic 2:0 funct3;
+ logic 4:0 imm5;
logic 11:0 imm12;
logic 12:0 imm13;
logic 19:0 imm20;
logic 20:0 imm21;
assign opCode = instr6:0;
+ assign funct3 = instr14:12;
+
+ assign imm5 = instr24:20;
assign imm12 = (opCode == 7'b0100011) ? {instr31:25, instr11:7} // S type
: instr31:20; // other
@@ -400,9 +416,11 @@ module immgen(
assign imm20 = instr31:12;
// sign extend or shift
- assign imm = (opCode == 7'b0110111) ? {imm20, 12'b0} : // U type (lui)
- (opCode == 7'b1100011) ? {{19{imm1312}}, imm13} :
- (opCode == 7'b1101111) ? {{11{imm2120}}, imm21}
+ assign imm = (opCode === 7'b0110111) ? {imm20, 12'b0} : // U type (lui)
+ (opCode === 7'b1100011) ? {{19{imm1312}}, imm13} :
+ (opCode === 7'b1101111) ? {{11{imm2120}}, imm21} :
+ (opCode === 7'b0010011 && funct3 === 3'h1) ? {{27{imm54}}, imm5} : // slli
+ (opCode === 7'b0010011 && funct3 === 3'h5) ? {{27{imm54}}, imm5} // srli, srai
: {{20{imm1211}}, imm12};
// always @(imm) begin
@@ -443,16 +461,16 @@ module alu(
assign sltResult = ($signed(in1) < $signed(in2)) ? 32'b1 : 32'b0;
assign sltuResult = (in1 < in2) ? 32'b1 : 32'b0;
- assign result = (op == 4'b0000) ? (in1 + in2) : // plus
- (op == 4'b0001) ? (in1 - in2) : // minus
- (op == 4'b1001) ? (in1 & in2) : // and
- (op == 4'b1000) ? (in1 | in2) : // or
- (op == 4'b0101) ? (in1 ^ in2) : // xor
- (op == 4'b0010) ? (in1 << in2) : // sll (shift left logical)
- (op == 4'b0110) ? (in1 >> in2) : // srl (shift right logical)
- (op == 4'b0111) ? sraResult : // sra (shift right arithmetic)
- (op == 4'b0011) ? sltResult : // slt
- (op == 4'b0100) ? sltuResult // sltu
+ assign result = (op === 4'b0000) ? (in1 + in2) : // plus
+ (op === 4'b0001) ? (in1 - in2) : // minus
+ (op === 4'b1001) ? (in1 & in2) : // and
+ (op === 4'b1000) ? (in1 | in2) : // or
+ (op === 4'b0101) ? (in1 ^ in2) : // xor
+ (op === 4'b0010) ? (in1 << in2) : // sll (shift left logical)
+ (op === 4'b0110) ? (in1 >> in2) : // srl (shift right logical)
+ (op === 4'b0111) ? sraResult : // sra (shift right arithmetic)
+ (op === 4'b0011) ? sltResult : // slt
+ (op === 4'b0100) ? sltuResult // sltu
: 32'hxxxxxxxx;
assign negative = result31;
assign zero = ~|result;
auipcの実装
auipc 命令も実装した。このへんまで実装すれば、Cコンパイラが吐いたコードも動かせそうな気がする。
https://gyazo.com/9d461c14d748468b28ffbbd77fddaf9c
code:diff
diff --git a/firmware/i_type_test.S b/firmware/i_type_test.S
index 871862f..6cb4c84 100644
--- a/firmware/i_type_test.S
+++ b/firmware/i_type_test.S
@@ -7,6 +7,7 @@
// - srai
// - slti
// - sltiu
+// - (おまけ)auipc
.text
.globl _start
@@ -158,9 +159,21 @@ test_start:
// 0xffffffff < 0 = 0
li t0, 0xffffffff
sltiu t2, t0, 0 // actual
- li t3, 0 // expected
+ li t3, 0 // expected
bne t2, t3, fail
+ //----------------------------------------
+ // auipc
+ //----------------------------------------
+
+ // PCの値(0x128) + 0x12345000 を t0 に格納
+ // NOTE: プログラムを書き換えると、この値も変わるので注意
+ auipc t0, 0x12345 // t0 = pc + 0x12345000
+ // t1 に期待値を格納
+ li t1, 0x12345000
+ addi t1, t1, 0x128 // expected
+ // テスト
+ bne t0, t1, fail
// Success
// LED に 128 を出力
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index e2d2389..c20d185 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -83,7 +83,7 @@ module cpu(
//-------------------------------------
logic dc_mem_write;
logic dc_reg_write;
- logic alu_in1_src;
+ logic 1:0 alu_in1_src;
logic alu_in2_src;
logic 3:0 alu_op;
logic dc_mem_to_reg;
@@ -121,7 +121,10 @@ module cpu(
logic 31:0 alu_in1, alu_in2, alu_result;
logic alu_negative, alu_zero;
- assign alu_in1 = (alu_in1_src) ? rf_read_data1 : 32'b0;
+ assign alu_in1 = (alu_in1_src === 2'b00) ? 32'b0 : // lui
+ (alu_in1_src === 2'b01) ? rf_read_data1 : // それ以外
+ (alu_in1_src === 2'b10) ? pc_reg // auipc
+ : 32'b0;
assign alu_in2 = (alu_in2_src) ? rf_read_data2 : imm;
alu alu_inst(
@@ -252,7 +255,7 @@ module decoder(
input logic 31:0 instr,
output logic memWrite,
output logic regWrite,
- output logic aluIn1Src,
+ output logic 1:0 aluIn1Src,
output logic aluIn2Src,
output logic 3:0 aluOp,
output logic memToReg,
@@ -282,6 +285,7 @@ module decoder(
(opCode === 7'b0010011) ? 1'b1 : // addi, ori
(opCode === 7'b0110011) ? 1'b1 : // R type (add)
(opCode === 7'b0110111) ? 1'b1 : // lui
+ (opCode === 7'b0010111) ? 1'b1 : // auipc
(opCode === 7'b1101111) ? 1'b1 : // jal
(opCode === 7'b1100111) ? 1'b1 // jalr
: 1'b0;
@@ -289,15 +293,17 @@ module decoder(
// select alu.in1 src
// 0: 32'b0
// 1: ds1
- assign aluIn1Src = (opCode === 7'b0110111) ? 1'b0 // lui
- : 1'b1;
+ // 2: pc
+ assign aluIn1Src = (opCode === 7'b0110111) ? 2'd0 : // lui
+ (opCode === 7'b0010111) ? 2'd2 // auipc
+ : 2'd1;
// select alu.in2 src
// 0: imm
// 1: ds2 (B, R type)
assign aluIn2Src = (opCode === 7'b0110011) ? 1'b1 : // R type
- (opCode === 7'b1100011) ? 1'b1 // B type
- : 1'b0;
+ (opCode === 7'b1100011) ? 1'b1 // B type
+ : 1'b0;
// lw
assign memToReg = (opCode == 7'b0000011) ? 1'b1 : 1'b0;
@@ -320,6 +326,7 @@ module decoder(
(opCode == 7'b1101111) ? 2'b00 : // jal => add
(opCode == 7'b1100111) ? 2'b00 : // jalr => add
(opCode == 7'b0110111) ? 2'b00 : // lui => add
+ (opCode == 7'b0010111) ? 2'b00 : // auipc => add
(opCode == 7'b0010011) ? 2'b11 // addi, ori => funct3
: 2'b10; // funct
@@ -417,6 +424,7 @@ module immgen(
// sign extend or shift
assign imm = (opCode === 7'b0110111) ? {imm20, 12'b0} : // U type (lui)
+ (opCode === 7'b0010111) ? {imm20, 12'b0} : // U type (auipc)
(opCode === 7'b1100011) ? {{19{imm1312}}, imm13} :
(opCode === 7'b1101111) ? {{11{imm2120}}, imm21} :
(opCode === 7'b0010011 && funct3 === 3'h1) ? {{27{imm54}}, imm5} : // slli
diff --git a/rtl/cpu_test.sv b/rtl/cpu_test.sv
index 3fe93b9..d1559b2 100644
--- a/rtl/cpu_test.sv
+++ b/rtl/cpu_test.sv
@@ -80,8 +80,12 @@ module cpu_test;
instructions1 = addi(6, 0, 42); // x6 = x0 + 42
instructions2 = beq(5, 6, 8 >> 1); // if (x5 == x6) 2つ先の命令(8バイト)へジャンプ
instructions3 = jal(0, 0); // 無限ループ
- instructions4 = sw(0, 5, 32'h3000); // sw x5, 0x88(x0) (メモリの 0x88 番地へ x5 の値を格納する)
- instructions5 = jal(0, 0); // 無限ループ
+ instructions4 = sw(0, 5, 12'h400); // M0x400 = x5
+
+ // auipc のテスト
+ instructions5 = auipc(5, 20'h12345); // x5 = pc + 0x12345000
+ instructions6 = sw(0, 5, 12'h404); // M0x404 = x5
+ instructions7 = jal(0, 0); // 無限ループ
mem_monitor_on = 1;
addr = 32'h00000000;
@@ -112,10 +116,10 @@ module cpu_test;
#2000;
- // 実行が終わった頃合いを見て、メモリの 0x80 番地の内容を確認
+ // 実行が終わった頃合いを見て、メモリの内容を確認
mem_monitor_on = 1;
mem_monitor_valid_reg = 1;
- mem_monitor_addr_reg = 32'h00000080;
+ mem_monitor_addr_reg = 32'h00000400;
mem_monitor_wstrb_reg = 4'b0000;
#10;
wait(mem_ready);
@@ -125,6 +129,17 @@ module cpu_test;
mem_monitor_valid_reg = 0;
#10;
+ mem_monitor_valid_reg = 1;
+ mem_monitor_addr_reg = 32'h00000404;
+ mem_monitor_wstrb_reg = 4'b0000;
+ #10;
+ wait(mem_ready);
+ assert(
+ mem_rdata === 32'h12345014
+ ) $display("PASSED"); else $display("expected 42 but actual %h", mem_rdata);
+ mem_monitor_valid_reg = 0;
+ #10;
+
$finish;
end
diff --git a/rtl/instructions.sv b/rtl/instructions.sv
index d4fbf2a..9c2bf4a 100644
--- a/rtl/instructions.sv
+++ b/rtl/instructions.sv
@@ -246,6 +246,18 @@ function 31:0 lui(
};
endfunction
+// auipc rd, imm
+function 31:0 auipc(
+ input logic 4:0 rd,
+ input logic 19:0 imm
+);
+ auipc = {
+ imm,
+ rd,
+ 7'b0010111 // opCode
+ };
+endfunction
+
// ori rd, rs1, immediate
// rd = rs1 | immediate
function 31:0 ori(
@@ -256,9 +268,9 @@ function 31:0 ori(
ori = {
imm,
rs1,
- 3'b110, // funct3
+ 3'b110, // funct3
rd,
- 7'b0010011 // opCode
+ 7'b0010111 // opCode
};
endfunction