JAL命令(その2)
https://gyazo.com/b124d1b370cab549e518d1c8ee522388
https://gyazo.com/975566c04bc5617a8752737249714512
オフセット値の指定
戻りアドレスをレジスタへ格納
オフセットなし、
オフセット0のジャンプ( jal x0, 0 )を実装したので、次はイミディエイトで指定したオフセットへジャンプできるようにする( jal x0, (現在の番地からのオフセット値))。
命令のバイト列からイミディエイトを取り出す回路 immgen は、以前作成したものを使い回す。
immgen
また、ジャンプ命令かどうかを簡単に判断できるよう、命令のバイト列から各種制御信号を取り出す decoder も今のタイミングで導入する(これも以前作成したものを再利用する)
docoder
以下、オフセットを指定したジャンプの実装。
code:diff
diff --git a/rtl/cpu.sv b/rtl/cpu.sv
index a6da9a9..8d0656c 100644
--- a/rtl/cpu.sv
+++ b/rtl/cpu.sv
@@ -31,10 +31,12 @@ module cpu(
);
end
+ //-------------------------------------
// CPU ステージ
// IF_STAGE: 命令フェッチ
// EX_STAGE: 実行
// ERR_STAGE: エラー
+ //-------------------------------------
typedef enum {
IF_STAGE,
EX_STAGE,
@@ -45,13 +47,54 @@ module cpu(
logic 31:0 pc_reg, pc_next; // PC logic 31:0 instr_reg, instr_next; // フェッチした命令 + //-------------------------------------
// 出力信号
+ //-------------------------------------
assign mem_valid = (stage_reg == IF_STAGE) ? 1'b1 : 1'b0;
assign mem_instr = (stage_reg == IF_STAGE) ? 1'b1 : 1'b0;
assign mem_addr = (stage_reg == IF_STAGE) ? pc_reg : 32'h00000000;
assign mem_wdata = 32'h00000000; // TODO
assign mem_wstrb = 4'b0000; // TODO
+
+ //-------------------------------------
+ // デコーダ
+ // 命令をデコードして制御信号を生成する
+ //-------------------------------------
+ logic dc_mem_write;
+ logic dc_reg_write;
+ logic alu_in1_src;
+ logic alu_in2_src;
+ logic dc_mem_to_reg;
+ logic branch;
+ logic jump;
+ logic jump_reg;
+
+ decoder decoder_inst(
+ .instr(instr_reg),
+ .memWrite(dc_mem_write),
+ .regWrite(dc_reg_write),
+ .aluIn1Src(alu_in1_src),
+ .aluIn2Src(alu_in2_src),
+ .aluOp(alu_op),
+ .memToReg(dc_mem_to_reg),
+ .branch(branch),
+ .jump(jump),
+ .jumpReg(jump_reg)
+ );
+
+ //-------------------------------------
+ // 即値生成器
+ // 命令から即値を取り出す
+ //-------------------------------------
+
+ immgen immgen_inst(
+ .instr(instr_reg),
+ .imm(imm)
+ );
+
always_ff @(posedge clk) begin
if (!reset_n) begin
stage_reg <= IF_STAGE;
@@ -81,9 +124,8 @@ module cpu(
end
end
EX_STAGE: begin
- if (instr_reg == 32'h0000006F) begin
- // j x0, 0 の場合は、現在のアドレスで無限ループ
- pc_next = pc_reg + 0;
+ if (jump) begin
+ pc_next = pc_reg + imm;
end else begin
// それ以外の場合は次の命令へ
pc_next = pc_reg + 4;
@@ -98,3 +140,171 @@ module cpu(
endcase
end
endmodule
+
+// 命令デコーダ
+module decoder(
+ input logic 31:0 instr, + output logic memWrite,
+ output logic regWrite,
+ output logic aluIn1Src,
+ output logic aluIn2Src,
+ output logic 3:0 aluOp, + output logic memToReg,
+ output logic branch,
+ output logic jump,
+ output logic jumpReg
+);
+
+ // 00: ADD, 01: SUB, 10: Funct7 + Funct3, 11: Funct3
+
+ alu_controller ac(
+ preAluOp,
+ funct3,
+ funct7,
+ aluOp
+ );
+
+ assign opCode = instr6:0; +
+ assign memWrite = (opCode === 7'b0100011) ? 1'b1 : 1'b0;
+
+ assign regWrite = (opCode === 7'b0000011) ? 1'b1 : // lw
+ (opCode === 7'b0010011) ? 1'b1 : // addi, ori
+ (opCode === 7'b0110011) ? 1'b1 : // R type (add)
+ (opCode === 7'b0110111) ? 1'b1 : // lui
+ (opCode === 7'b1101111) ? 1'b1 : // jal
+ (opCode === 7'b1100111) ? 1'b1 // jalr
+ : 1'b0;
+
+ // select alu.in1 src
+ // 0: 32'b0
+ // 1: ds1
+ assign aluIn1Src = (opCode === 7'b0110111) ? 1'b0 // lui
+ : 1'b1;
+
+ // 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;
+
+ // lw
+ assign memToReg = (opCode == 7'b0000011) ? 1'b1 : 1'b0;
+
+ assign branch = (opCode === 7'b1100011) ? 1'b1 // B type
+ : 1'b0;
+
+ assign jump = (opCode === 7'b1101111) ? 1'b1 : // jal
+ (opCode === 7'b1100111) ? 1'b1 // jalr
+ : 1'b0;
+
+ assign jumpReg = (opCode === 7'b1100111) ? 1'b1 // jalr
+ : 1'b0;
+
+ assign funct3 = instr14:12; + assign funct7 = instr31:25; + assign preAluOp = (opCode == 7'b1100011) ? 2'b01 : // B type => sub
+ (opCode == 7'b0000011) ? 2'b00 : // lw => add
+ (opCode == 7'b0100011) ? 2'b00 : // sw => add
+ (opCode == 7'b1101111) ? 2'b00 : // jal => add
+ (opCode == 7'b1100111) ? 2'b00 : // jalr => add
+ (opCode == 7'b0110111) ? 2'b00 : // lui => add
+ (opCode == 7'b0010011) ? 2'b11 // addi, ori => funct3
+ : 2'b10; // funct
+
+ // always @(*) begin
+ // $display("aluIn2Src %b", aluIn2Src);
+ // $display("preAluOp %b", preAluOp);
+ // $display("aluOp %b", aluOp);
+ // end
+endmodule
+
+// alu_controller
+//
+// preAluOp | case
+// -------------------
+// 00 | add
+// 01 | sub
+// 10 | funct7 + funct3
+// 11 | funct3
+module alu_controller(
+ input logic 1:0 preAluOp, + input logic 2:0 funct3, + input logic 6:0 funct7, +);
+
+ assign funct = {funct7, funct3};
+
+ always_comb begin
+ case(preAluOp)
+ 2'b00: aluOp = 4'b0000; // add
+ 2'b01: aluOp = 4'b0001; // sub
+ 2'b11: case(funct3)
+ 3'b000: aluOp = 4'b0000; // addi => add
+ 3'b110: aluOp = 4'b1000; // ori => or
+ default: aluOp = 4'bXXXX;
+ endcase
+ default: case(funct)
+ 10'b0000000000: aluOp = 4'b0000; // add
+ 10'b0100000000: aluOp = 4'b0001; // sub
+ 10'b0000000111: aluOp = 4'b1001; // and
+ 10'b0000000110: aluOp = 4'b1000; // or
+ 10'b0000000010: aluOp = 4'b0011; // slt
+ 10'b0000000011: aluOp = 4'b0100; // sltu
+ 10'b0000000001: aluOp = 4'b0010; // sll
+ 10'b0000000101: aluOp = 4'b0110; // srl
+ 10'b0100000101: aluOp = 4'b0111; // sra
+ default: aluOp = 4'bXXXX;
+ endcase
+ endcase
+ end
+endmodule
+
+// 即値生成器
+// 命令列から即値を取り出す
+module immgen(
+ input logic 31:0 instr, +);
+
+ assign opCode = instr6:0; +
+ assign imm12 = (opCode == 7'b0100011) ? {instr31:25, instr11:7} // S type +
+ // B type
+ assign imm13 = {instr31, instr7, instr30:25, instr11:8, 1'b0}; +
+ // J type
+
+ // U type
+ 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} + : {{20{imm1211}}, imm12}; +
+ // always @(imm) begin
+ // $display("instr %b", instr);
+ // $display("imm12 %b", imm12);
+ // $display("imm13 %b", imm13);
+ // $display("imm21 %b", imm21);
+ // $display("imm %b", imm);
+ // end
+endmodule
+
diff --git a/rtl/cpu_test.sv b/rtl/cpu_test.sv
index 4c731c7..fe49603 100644
--- a/rtl/cpu_test.sv
+++ b/rtl/cpu_test.sv
@@ -72,10 +72,10 @@ module cpu_test;
* プログラムの書き込み
*/
- instructions0 = 32'h00000013; // nop - instructions1 = 32'h00000013; // nop - instructions2 = 32'h00000013; // nop - instructions3 = 32'h0000006F; // jal x0, 0(無限ループ) + instructions0 = add(0, 0, 0); // nop + instructions1 = add(0, 0, 0); // nop + instructions2 = add(0, 0, 0); // nop + instructions3 = jal(0, (-12 / 2)); // jal x0, -12 (0番地へ戻る) mem_monitor_on = 1;
addr = 32'h00000000;
テストベンチでは以下のプログラムをメモリへ書き込む。
code:verilog
instructions0 = 32'h00000013; // NOP instructions1 = 32'h00000013; // NOP instructions2 = 32'h00000013; // NOP instructions3 = 32'hFF5FF06F; // jal x0, -12 (3つ前の命令のアドレスへジャンプ) テストベンチを実行してみる。pc の値を見ると、00000000 → 00000004 → 00000008 → 0000000c → 00000000 → ... とグルグル回っていることが確認できる。ちゃんと動いてるみたい。
code:sh
7855: pc = 00000000, stage = 0, instr = ff5ff06f, imm = fffffff4, mem_valid = 0, mem_ready = 0, mem_addr = 0000000c, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = ff5ff06f
7865: pc = 00000000, stage = 0, instr = ff5ff06f, imm = fffffff4, mem_valid = 1, mem_ready = 0, mem_addr = 00000000, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = ff5ff06f
7875: pc = 00000000, stage = 0, instr = ff5ff06f, imm = fffffff4, mem_valid = 1, mem_ready = 0, mem_addr = 00000000, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
7885: pc = 00000000, stage = 0, instr = ff5ff06f, imm = fffffff4, mem_valid = 1, mem_ready = 1, mem_addr = 00000000, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
7895: pc = 00000000, stage = 1, instr = 00000013, imm = 00000000, mem_valid = 0, mem_ready = 0, mem_addr = 00000000, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
7905: pc = 00000004, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 0, mem_ready = 0, mem_addr = 00000000, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
7915: pc = 00000004, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 1, mem_ready = 0, mem_addr = 00000004, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
7935: pc = 00000004, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 1, mem_ready = 1, mem_addr = 00000004, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
7945: pc = 00000004, stage = 1, instr = 00000013, imm = 00000000, mem_valid = 0, mem_ready = 0, mem_addr = 00000004, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
7955: pc = 00000008, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 0, mem_ready = 0, mem_addr = 00000004, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
7965: pc = 00000008, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 1, mem_ready = 0, mem_addr = 00000008, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
7985: pc = 00000008, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 1, mem_ready = 1, mem_addr = 00000008, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
7995: pc = 00000008, stage = 1, instr = 00000013, imm = 00000000, mem_valid = 0, mem_ready = 0, mem_addr = 00000008, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
8005: pc = 0000000c, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 0, mem_ready = 0, mem_addr = 00000008, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
8015: pc = 0000000c, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 1, mem_ready = 0, mem_addr = 0000000c, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
8025: pc = 0000000c, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 1, mem_ready = 0, mem_addr = 0000000c, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = ff5ff06f
8035: pc = 0000000c, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 1, mem_ready = 1, mem_addr = 0000000c, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = ff5ff06f
8045: pc = 0000000c, stage = 1, instr = ff5ff06f, imm = fffffff4, mem_valid = 0, mem_ready = 0, mem_addr = 0000000c, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = ff5ff06f
8055: pc = 00000000, stage = 0, instr = ff5ff06f, imm = fffffff4, mem_valid = 0, mem_ready = 0, mem_addr = 0000000c, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = ff5ff06f
8065: pc = 00000000, stage = 0, instr = ff5ff06f, imm = fffffff4, mem_valid = 1, mem_ready = 0, mem_addr = 00000000, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = ff5ff06f
8075: pc = 00000000, stage = 0, instr = ff5ff06f, imm = fffffff4, mem_valid = 1, mem_ready = 0, mem_addr = 00000000, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
8085: pc = 00000000, stage = 0, instr = ff5ff06f, imm = fffffff4, mem_valid = 1, mem_ready = 1, mem_addr = 00000000, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
8095: pc = 00000000, stage = 1, instr = 00000013, imm = 00000000, mem_valid = 0, mem_ready = 0, mem_addr = 00000000, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
8105: pc = 00000004, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 0, mem_ready = 0, mem_addr = 00000000, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
8115: pc = 00000004, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 1, mem_ready = 0, mem_addr = 00000004, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
8135: pc = 00000004, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 1, mem_ready = 1, mem_addr = 00000004, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
8145: pc = 00000004, stage = 1, instr = 00000013, imm = 00000000, mem_valid = 0, mem_ready = 0, mem_addr = 00000004, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
8155: pc = 00000008, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 0, mem_ready = 0, mem_addr = 00000004, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013
8165: pc = 00000008, stage = 0, instr = 00000013, imm = 00000000, mem_valid = 1, mem_ready = 0, mem_addr = 00000008, mem_wdata = 00000000, mem_wstrb = 0000, mem_rdata = 00000013