LW命令
#自作CPU #RISC-V #Verilog #ULX3S
from マルチサイクル RISC-V CPU を作成したい
https://gyazo.com/ebb181d8b25ec330405182f4a6aa28ea
https://gyazo.com/41c11fbfcda5be725f053a61c1791bf0
次は、メモリからレジスタに値を読み出すLW命令(load word)を実装する。
LW命令のフォーマット
LW は I-type の命令
opcode
rs1(5bit)
imm(12bit)
rd
opcode = 0000011
funct3 = 0x2
データ読み出し先のメモリアドレス: レジスタrs1の値 + オフセット値(即値)
読み出したデータの格納先のレジスタ:レジスタ rd
データパス
https://gyazo.com/20646eff97e8edc11363d174ca80a49e
実装してみた。
code:diff
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index c71cf71..892b2d7 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -48,8 +48,9 @@ module cpu(
} stage_t;
stage_t stage_reg, stage_next;
- logic 31:0 pc_reg, pc_next; // PC
- logic 31:0 instr_reg, instr_next; // フェッチした命令
+ logic 31:0 pc_reg, pc_next; // PC
+ logic 31:0 instr_reg, instr_next; // フェッチした命令
+ logic 31:0 mem_rdata_reg, mem_rdata_next; // lw でメモリから読み込んだデータ
//-------------------------------------
// 出力信号
@@ -107,10 +108,12 @@ module cpu(
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
@@ -143,7 +146,9 @@ module cpu(
assign rf_addr2 = instr_reg24:20; // rs2
assign rf_addr3 = instr_reg11:7; // rd
assign rf_we3 = (stage_reg == WB_STAGE) && dc_reg_write;
- assign rf_write_data3 = alu_result;
+
+ assign rf_write_data3 = (dc_mem_to_reg) ? mem_rdata_reg
+ : alu_result;
// assign rf_write_data3 = (dc_mem_to_reg) ? mem_rdata :
// (jump) ? pc_reg + 4
// : alu_result;
@@ -164,16 +169,17 @@ module cpu(
// デフォルト値
stage_next = stage_reg;
instr_next = instr_reg;
+ mem_rdata_next = mem_rdata_reg;
pc_next = pc_reg;
case (stage_reg)
// 命令フェッチ
IF_STAGE: begin
if (mem_ready) begin
+ // メモリから命令を取得
instr_next = mem_rdata;
stage_next = EX_STAGE;
end else begin
- instr_next = instr_reg;
stage_next = IF_STAGE;
end
end
@@ -183,12 +189,13 @@ module cpu(
end
// メモリアクセス
MEM_STAGE: begin
- // メモリへの書き込み
- if (dc_mem_write) begin
- // TODO
+ if (mem_ready) begin
+ // メモリからデータを取得
+ mem_rdata_next = mem_rdata;
+ stage_next = WB_STAGE;
+ end else begin
+ stage_next = MEM_STAGE;
end
-
- stage_next = WB_STAGE;
end
// レジスタ書き戻し
WB_STAGE: begin
diff --git a/rtl/cpu_test.sv b/rtl/cpu_test.sv
index aa6ef84..07b8750 100644
--- a/rtl/cpu_test.sv
+++ b/rtl/cpu_test.sv
@@ -76,7 +76,9 @@ module cpu_test;
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 = jal(0, -16 >> 1); // jal x0, -16 (0番地へ戻る)
+ 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番地へ戻る)
mem_monitor_on = 1;
addr = 32'h00000000;
@@ -118,6 +120,16 @@ module cpu_test;
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;
$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 = jal(0, -24 >> 1); // jal x0, -24 (0番地へ戻る)
メモリの 0x80 番地と 0x84 番地に 30 が書かれていればOK
code:sh
$ make test
...
mem0x80 = 30
mem0x84 = 30
...