tiny - riscv - jump、分支b指令、lui

410 阅读3分钟

跳转 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 寄存器加

image.png

image.png

image.png

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寄存器跳转地址和跳转使能信号。

译码模块

image.png

`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通常称为“输出列表”