BEQ命令
https://gyazo.com/ebb181d8b25ec330405182f4a6aa28ea
https://gyazo.com/03cd418c608457eae19441fec2fdd118
次は、条件分岐を行うBEQ命令(branch equal)を実装する。
SW命令のフォーマット
BEQ は B-type の命令
opcode = 1100011
funct3 = 0x0
パラメータ
opcode
funct3
rs1
rs2
imm(13ビットの整数の上位12ビットを命令バイト列に埋め込む)
操作内容
if(rs1 == rs2) PC += imm
実装
jal 命令と同じ感じに実装する。
beq の時は ALU で rs1 - rs2 が行われる。ALUの返す結果が 0 の場合は rs1 == rs2 が成立するので pc = pc + imm へジャンプする。
code:verilog
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index 4b06c9f..f1c63ba 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -202,6 +202,11 @@ module cpu(
if (jump) begin
// jal 命令の場合
pc_next = pc_reg + imm;
+ end else if (branch) begin
+ // beq 命令の場合
+ pc_next = (alu_zero) ?
+ pc_reg + imm : // rs1 == rs2 の場合
+ pc_reg + 4;
end else begin
// それ以外の場合は次の命令へ
pc_next = pc_reg + 4;
diff --git a/rtl/cpu_test.sv b/rtl/cpu_test.sv
index 07b8750..9d61ce4 100644
--- a/rtl/cpu_test.sv
+++ b/rtl/cpu_test.sv
@@ -72,13 +72,18 @@ module cpu_test;
* プログラムの書き込み
*/
- 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 の値を格納する) - instructions6 = jal(0, -24 >> 1); // jal x0, -24 (0番地へ戻る) + 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 の値を格納する) + 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 の値を格納する) + instructions11 = jal(0, -40 >> 1); // jal x0, -40 (0番地へ戻る) mem_monitor_on = 1;
addr = 32'h00000000;
@@ -131,6 +136,17 @@ module cpu_test;
mem_monitor_valid_reg = 0;
+ // メモリの 0x88 番地の内容を確認
+ mem_monitor_on = 1;
+ mem_monitor_valid_reg = 1;
+ mem_monitor_addr_reg = 32'h00000088;
+ mem_monitor_wstrb_reg = 4'b0000;
+ wait(mem_ready);
+ $display("mem0x88 = %d", mem_rdata); + mem_monitor_valid_reg = 0;
+
$finish;
end
動作確認
テストベンチで以下のテストコードを追加し、テストベンチを実行すると...
code:verilog
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 の値を格納する) 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 の値を格納する) instructions11 = jal(0, -40 >> 1); // jal x0, -40 (0番地へ戻る) メモリの 0x88 番地に 42 が格納される。ちゃんと動いてそう。
https://gyazo.com/c520cbc912a0860347d5673a6713d53e