跳转 jump 分支branch指令
通过汇编文件测试cpu,bin文件通过编译生成的二进制文件,bin文件可以转换成txt文件。
dump为反汇编文件。
jal: jump and link 跳转并链接,rd = PC+4, PC += imm,把当前指令设置为PC指令加立即数,下一条指令存入目的寄存器。
bne: branch not equal , if(rs1!= rs2) PC+=imm
lui:load upper imm , 加载立即数 rd<<12
addi 立即数加
add 寄存器加
R型 I型, S型,B型,U型,J型
流水线冲刷机制
bne rs1,rs2,label:
分支指令,如果rs1和rs2不相等,则跳转到label 执行。
当 bne在执行阶段,条件成立,出现冲突,需要对下一条正在译码的指令进行流水线冲刷。
ctrl控制模块发送跳转hold信号。 发给if_id,id_ex模块。将jump_addr_0 跳转地址和jump_en使能发给PC指令地址,告诉PC跳转到jump指令的地址,label。if_id和id_ex发送NOP指令进行空操作。
即就是ex执行bne分支指令时候,通过控制模块产生跳转hold信号给if_id,id_ex模块。给PC寄存器跳转地址和跳转使能信号。
译码模块
`include "defines.v"
module id(
always @(*)begin
inst_o = inst_i;
inst_addr_o = inst_addr_i;
case(opcode)
`INST_TYPE_I:begin
case(func3)
`INST_ADDI:begin
rs1_addr_o = rs1;
rs2_addr_o = 5'b0;
op1_o = rs1_data_i;
op2_o = {{20{imm[11]}},imm};
rd_addr_o = rd;
reg_wen = 1'b1;
end
default:begin
rs1_addr_o = 5'b0;
rs2_addr_o = 5'b0;
op1_o = 32'b0;
op2_o = 32'b0;
rd_addr_o = 5'b0;
reg_wen = 1'b0;
end
endcase
end
`INST_TYPE_R_M:begin
case(func3)
`INST_ADD_SUB:begin
rs1_addr_o = rs1;
rs2_addr_o = rs2;
op1_o = rs1_data_i;
op2_o = rs2_data_i;
rd_addr_o = rd;
reg_wen = 1'b1;
end
default:begin
rs1_addr_o = 5'b0;
rs2_addr_o = 5'b0;
op1_o = 32'b0;
op2_o = 32'b0;
rd_addr_o = 5'b0;
reg_wen = 1'b0;
end
endcase
end
// B型指令,INST_TYPE_B,fun3为001,
`INST_TYPE_B:begin
case(func3)
`INST_BNE,`INST_BEQ:begin
rs1_addr_o = rs1;
rs2_addr_o = rs2;
op1_o = rs1_data_i;
op2_o = rs2_data_i;
rd_addr_o = 5'b0;
reg_wen = 1'b0;
end
default:begin
rs1_addr_o = 5'b0;
rs2_addr_o = 5'b0;
op1_o = 32'b0;
op2_o = 32'b0;
rd_addr_o = 5'b0;
reg_wen = 1'b0;
end
endcase
end
`INST_JAL:begin
rs1_addr_o = 5'b0;
rs2_addr_o = 5'b0;
op1_o = {{12{inst_i[31]}}, inst_i[19:12], inst_i[20], inst_i[30:21], 1'b0};
op2_o = 32'b0;
rd_addr_o = rd;
reg_wen = 1'b1;
end
`INST_LUI:begin
rs1_addr_o = 5'b0;
rs2_addr_o = 5'b0;
op1_o = {inst_i[31:12],12'b0};
op2_o = 32'b0;
rd_addr_o = rd;
reg_wen = 1'b1;
end
default:begin
rs1_addr_o = 5'b0;
rs2_addr_o = 5'b0;
op1_o = 32'b0;
op2_o = 32'b0;
rd_addr_o = 5'b0;
reg_wen = 1'b0;
end
endcase
end
endmodule
执行模块ex的补充
产生jump_addr_0
jump_en_o
hold_flag_o
rd_data_o = 32'b0;
rd_addr_o = 5'b0;
rd_wen_o = 1'b0;
case(func3)
`INST_BEQ:begin
jump_addr_o = (inst_addr_i + jump_imm) & {32{(op1_i_equal_op2_i)}};
jump_en_o = op1_i_equal_op2_i;
hold_flag_o = 1'b0;
end
`INST_BNE:begin
jump_addr_o = (inst_addr_i + jump_imm) & {32{(~op1_i_equal_op2_i)}};
jump_en_o = ~op1_i_equal_op2_i;
hold_flag_o = 1'b0;
end
default:begin
jump_addr_o = 32'b0;
jump_en_o = 1'b0;
hold_flag_o = 1'b0;
end
endcase
end
跳转地址的计算
jump_imm
// branch
wire[31:0] jump_imm = {{19{inst_i[31]}},inst_i[31],inst_i[7],inst_i[30:25],inst_i[11:8],1'b0}; //改 21+6+4+1
wire op1_i_equal_op2_i;
assign op1_i_equal_op2_i = (op1_i == op2_i)?1'b1:1'b0;
jump_addr_o = (inst_addr_i + jump_imm) & {32{(~op1_i_equal_op2_i)}};
jump_en_o = ~op1_i_equal_op2_i;
hold_flag_o = 1'b0;
BEQ为相等跳转,BNE为不等跳转。
ctrl模块
input wire [31:0]jump_addr_i,
input wire jump_en_i,
input wire hold_flag_ex_i,
output reg [31:0]jump_addr_o,
output reg jump_en_o,
output reg hold_flag_o
);
always @(*)begin
jump_addr_o = jump_addr_i;
jump_en_o = jump_en_i;
if( jump_en_i || hold_flag_ex_i)begin
hold_flag_o = 1'b1;
end
else begin
hold_flag_o = 1'b0;
end
end
endmodule
id_ex和if_id进行修改,加入hold_flag_i 信号,控制产生NOP信号
PC_reg进行修改,指向jump_addr
加入 JAL指令 为J型指令 无条件跳转
把当前指令设置为PC指令加立即数,下一条指令存入目的寄存器。
加入LUI指令。 U型指令 加载立即数指令。
加载立即数指令。
$display(p1,p2, …,pn);
$write(p1,p2, …,pn);
这两个函数和系统任务的作用都是用来输出信息,即将参数p2到pn按参数p1给定的格式输出。参数p1通常称为:“格式控制”,参数p2至pn通常称为“输出列表”