JALまわりのバグを修正
#Odeeen #RISC-V #Verilog #FPGA
from マルチサイクル RISC-V CPU を作成したい
オフセットがマイナス方向への JAL 命令が動かない問題が発生して困ってたんだけど、よく分からないうちに直った。
以下やったこと。
& や | を、 && や || に直した
pc_next の取得ロジックを R4(初代自作CPU)から持ってきた
メモリアクセス不要な場合は MEM_STAGE をスキップするようにした
回路のデバッグは print デバッグができなくて難儀したんだけど、以下の方法でなんとか乗り切った。
LEDにPCの値を表示
内部データを見るかわりに、内部データがある特定の範囲内にあったら無限ループ(jal x0, 0)を仕込んだ番地へ飛ばすことで、二分探索を行なった
以下修正箇所。
code:diff
diff --git a/rtl/bram_controller.sv b/rtl/bram_controller.sv
index fe16098..1ea32fa 100644
--- a/rtl/bram_controller.sv
+++ b/rtl/bram_controller.sv
@@ -37,11 +37,35 @@ module bram_controller(
// メモリの初期化
initial begin
- // LED へ 0b1001_1001 を表示する
- mem0 = addi(1, 0, 8'b10011001); // x1 = 0b10011001
- mem1 = lui(2, 32'hf0001000 >> 12); // x2 = 0xf0001000
- mem2 = sw(2, 1, 0); // Mx2+0 = x1
- mem3 = jal(0, 0); // jal x0, 0 (無限ループ)
+ // デバッグ用の各種変数
+ mem0 = lui(10, 32'hf0001000 >> 12); // x10 = 0xf0001000
+ mem1 = addi(11, 0, 1);
+ mem2 = addi(12, 0, 2);
+ mem3 = addi(13, 0, 4);
+ mem4 = addi(14, 0, 8);
+ mem5 = addi(15, 0, 16);
+
+ //----------------------------------------
+ // 1 から 10 までの合計を LED 出力するプログラム
+ //----------------------------------------
+ mem6 = sw(10, 11, 0); // LED に 1 を点灯
+ mem7 = addi(1, 0, 0); // sum = 0
+ mem8 = addi(2, 0, 10); // n = 10
+ // loop:
+ mem9 = sw(10, 12, 0); // LED に 2 を点灯
+ mem10 = beq(2, 0, 8); // if (n == 0) break
+ mem11 = add(1, 1, 2); // sum = sum + n
+ mem12 = addi(2, 2, -1); // n = n - 1
+ mem13 = jal(0, -8); // jump loop
+ // break:
+ mem14 = sw(10, 13, 0); // LED に 3 を点灯
+ mem15 = sw(10, 1, 0); // Mx2+0 = x1
+ mem16 = add(0, 0, 0); // nop
+ mem17 = jal(0, 0); // jal x0, 0 (無限ループ)
+
+ // デバッグ
+ // mem20 = jal(0, 0); // 無限ループ
+ // mem24 = jal(0, 0); // 無限ループ
end
always_ff @(posedge clk) begin
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index 5259e59..0ae7e9a 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -1,3 +1,7 @@
+`ifndef __INSTRUCTIONS__
+`include "instructions.sv"
+`endif
+
module cpu(
input logic clk,
input logic reset_n,
@@ -9,8 +13,16 @@ module cpu(
output logic 31:0 mem_addr,
output logic 31:0 mem_wdata,
output logic 3:0 mem_wstrb,
- input logic 31:0 mem_rdata
+ input logic 31:0 mem_rdata,
+
+ output logic 31:0 peek
);
+
+ // デバッグ用の信号線
+ // 今は PC の値を出力しているが、必要に応じて変更して良い
+ assign peek = pc_reg;
+
+
//-------------------------------------
// デバッグ用モニタ
//-------------------------------------
@@ -55,10 +67,10 @@ module cpu(
//-------------------------------------
// 出力信号
//-------------------------------------
- assign mem_valid = (stage_reg == IF_STAGE | stage_reg == MEM_STAGE) ? 1'b1 : 1'b0; // メモリの読み書きを行う場合、アドレスを指定すると共に VALID 信号をアサート
- assign mem_instr = (stage_reg == IF_STAGE) ? 1'b1 : 1'b0; // 今回はこの信号を使わないけど、念のため実装しておく
- assign mem_addr = (stage_reg == IF_STAGE) ? pc_reg : // 命令フェッチの場合
- (stage_reg == MEM_STAGE) ? alu_result // lw または sw の場合
+ assign mem_valid = (stage_reg == IF_STAGE || stage_reg == MEM_STAGE) ? 1'b1 : 1'b0; // メモリの読み書きを行う場合、アドレスを指定すると共に VALID 信号をアサート
+ assign mem_instr = (stage_reg == IF_STAGE) ? 1'b1 : 1'b0; // 今回はこの信号を使わないけど、念のため実装しておく
+ assign mem_addr = (stage_reg == IF_STAGE) ? pc_reg : // 命令フェッチの場合
+ (stage_reg == MEM_STAGE) ? alu_result // lw または sw の場合
: 32'h00000000;
assign mem_wdata = (stage_reg == MEM_STAGE && dc_mem_write) ? rf_read_data2 // sw の場合
: 32'h00000000;
@@ -103,20 +115,6 @@ module cpu(
.imm(imm)
);
- always_ff @(posedge clk) begin
- if (!reset_n) begin
- stage_reg <= IF_STAGE;
- pc_reg <= 0;
- instr_reg <= 32'h00000000;
- mem_rdata_reg <= 32'h00000000;
- end else begin
- stage_reg <= stage_next;
- pc_reg <= pc_next;
- instr_reg <= instr_next;
- mem_rdata_reg <= mem_rdata_next;
- end
- end
-
//-------------------------------------
// ALU
//-------------------------------------
@@ -162,6 +160,21 @@ module cpu(
.readData2(rf_read_data2)
);
+
+ always_ff @(posedge clk) begin
+ if (!reset_n) begin
+ stage_reg <= IF_STAGE;
+ pc_reg <= 0;
+ instr_reg <= 32'h00000000;
+ mem_rdata_reg <= 32'h00000000;
+ end else begin
+ stage_reg <= stage_next;
+ pc_reg <= pc_next;
+ instr_reg <= instr_next;
+ mem_rdata_reg <= mem_rdata_next;
+ end
+ end
+
always_comb begin
// デフォルト値
stage_next = stage_reg;
@@ -182,7 +195,10 @@ module cpu(
end
// 実行
EX_STAGE: begin
- stage_next = MEM_STAGE;
+ // メモリアクセスが不要な場合は、MEM_STAGE を飛ばして WB_STAGE へ遷移する
+ stage_next = (dc_mem_write) ? MEM_STAGE : // sw の場合
+ (dc_mem_to_reg) ? MEM_STAGE // lw の場合
+ : WB_STAGE;
end
// メモリアクセス
MEM_STAGE: begin
@@ -196,19 +212,10 @@ module cpu(
end
// レジスタ書き戻し
WB_STAGE: begin
- if (jump) begin
- pc_next = (jump_reg) ? rf_read_data1 + imm // jalr の場合
- : pc_reg + imm; // jal の場合
- 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;
- end
-
+ pc_next = (branch && alu_zero) ? pc_reg + imm :
+ (jump_reg) ? alu_result & 32'hfffffffe :
+ (jump) ? pc_reg + imm
+ : pc_reg + 4;
stage_next = IF_STAGE;
end
ERR_STAGE: begin
diff --git a/rtl/ulx3s_top.sv b/rtl/ulx3s_top.sv
index 17bc501..909359c 100644
--- a/rtl/ulx3s_top.sv
+++ b/rtl/ulx3s_top.sv
@@ -17,7 +17,8 @@ module ulx3s_top(
.mem_addr(mem_addr),
.mem_wdata(mem_wdata),
.mem_wstrb(mem_wstrb),
- .mem_rdata(mem_rdata)
+ .mem_rdata(mem_rdata),
+ .peek(peek)
);
logic clk, reset_n;
@@ -25,12 +26,14 @@ module ulx3s_top(
logic 31:0 mem_addr, mem_wdata, mem_rdata;
logic 3:0 mem_wstrb;
+ // デバッグ用の信号線
+ logic 31:0 peek;
// メモリコントローラのインスタンス
bram_controller bram_ctl (
.clk(clk),
.reset_n(reset_n),
- .mem_valid(bram_en & mem_valid),
+ .mem_valid(bram_en && mem_valid),
.mem_ready(bram_mem_ready),
.mem_addr(mem_addr),
.mem_wdata(mem_wdata),
@@ -46,7 +49,7 @@ module ulx3s_top(
led_controller led_ctl (
.clk(clk),
.reset_n(reset_n),
- .mem_valid(led_ctl_en & mem_valid),
+ .mem_valid(led_ctl_en && mem_valid),
.mem_ready(led_ctl_mem_ready),
.mem_addr(mem_addr),
.mem_wdata(mem_wdata),
@@ -80,7 +83,10 @@ module ulx3s_top(
//------------------------------------------------------------------
assign clk = clk_25mhz;
assign reset_n = btn0; // btn0 は押すとデアサートされる
+
assign led = led_ctl_mem_rdata7:0;
+ // assign led = peek7:0;
+
endmodule