自己动手写CPU(10)——转移指令的实现

1. 系统结构框图

1.1

2. 代码模块

2.1. 宏定义defines

// ***************  与具体指令有关的宏定义  ***************                                                                                                                                                                                                                                                  
`define	EXE_J					6'b000010			// op_j                                                                                                    
`define	EXE_JAL					6'b000011			// op_jal									save the instruction following the delay slot instruction to rd / $31            
`define	EXE_JR					6'b001000			// func_jr                                                                                                 
`define	EXE_JALR				6'b001001			// func_jalr							save the return address in $31                                                   
`define	EXE_BEQ					6'b000100			// op_beq			rs = rt                                                                                    
`define	EXE_BGEZ				5'b00001			// rt_bgez			rs ≥ 0                                                                                    
`define	EXE_BGEZAL				5'b10001			// rt_bgezal		rs ≥ 0		save the return address in $31                                                   
`define	EXE_BGTZ				6'b000111			// op_bgtz			rs > 0                                                                                     
`define	EXE_BLEZ				6'b000110			// op_blez			rs ≤ 0                                                                                    
`define	EXE_BLTZ				5'b00000			// rt_bltz			rs < 0                                                                                     
`define	EXE_BLTZAL				5'b10000			// rt_bltzal		rs < 0		save the return address in $31                                                   
`define	EXE_BNE					6'b000101			// op_bne			rs ≠ rt                                                                                   
// b is a special case of beq, 	bal is a special case of begzal                                                                                  

//AluOp   子类型 
// 为防止和其他指令冲突,高两位看情况给定
`define	EXE_J_OP			8'b01000010		// 42   
`define	EXE_JAL_OP			8'b10000011		// 83   
`define	EXE_JR_OP			8'b10001000		// 88   
`define	EXE_JALR_OP			8'b10001001		// 89   
`define	EXE_BEQ_OP			8'b10000100		// 84   
`define	EXE_BGEZ_OP			8'b10000001		// 81   
`define	EXE_BGEZAL_OP		8'b10010001		// 91   
`define	EXE_BGTZ_OP			8'b00000111		// 07   
`define	EXE_BLEZ_OP			8'b00000110		// 06   
`define	EXE_BLTZ_OP			8'b11000000		// c0   
`define	EXE_BLTZAL_OP		8'b10010000		// 90   
`define	EXE_BNE_OP			8'b10000101		// 85   

2.2. 取指pc

module pc_reg (                                                                                                           
  // 来自ID模块,与转移指令有关                                                        
  input		wire									branch_flag_i,						// 是否发生转移              
  input		wire	[`RegBus]				branch_target_address_i		// 转移到的目标地址          
);                                                                                     
                                                                                                                                                 
  always @ (posedge clk) begin                                                         
    if (ce == `ChipDisable) begin                                                      
      pc <= 32'h00000000;        			// 指令存储器禁用的时候,pc为0                   
    end else  if (branch_flag_i == `Branch)	begin                                      
    	pc <= branch_target_address_i;		// 转移指令发生,pc修改为目标地址                
    end else	begin                                                                    
      pc <= pc + 4'h4;       		 		// 指令存储器使能的时候,pc值每时钟周期+4字节    
    end                                                                                
  end                                                                                  
                                                                                                                                                                  
endmodule                                                                              

2.3. 译码id

module id (                                                                                  
  // 转移指令有关端口                                                                        
  output	reg								branch_flag_o,				// 是否转移                        
  output	reg		[`RegBus]				branch_target_address_o,	// 转移目标地址                    
  output	reg		[`RegBus]				link_addr_o,				// 要保存的指令地址              
);  

	// 有关转移指令                     
	wire	[`RegBus]	pc_plus_4;          
	wire	[`RegBus]	imm_sll2_signedext; 
	
	assign pc_plus_4 = pc_i + 4;			// 保存当前指令后面紧接着的指令的地址 
	// imm_sll2_signedext对应分支指令中offset左移两位,再符号扩展至32位的值 
	assign imm_sll2_signedext = { {14{inst_i[15]}}, inst_i[15:0], 2'b00 };  

  always @ (*) begin                                                                                     
    if (rst == `RstEnable) begin                                                                                                                      
      link_addr_o				<=	`ZeroWord;		// 转移指令要保存的目标地址                              
      branch_flag_o				<=	`NotBranch;		// 不发生转移                                            
      branch_target_address_o	<=	`ZeroWord;		// 转移的目标地址                                        
    end else begin                                                                                                                                                        
      link_addr_o				<=	`ZeroWord;                                                             
      branch_flag_o				<=	`NotBranch;                                                            
      branch_target_address_o	<=	`ZeroWord;                                                             
                                                                                                         
      case (op)                                                                                          
        `EXE_SPECIAL_INST:  begin                                                                        
          case  (op2)                 // [10:6]sa为0                                                     
            5'b00000: begin                                                                              
              case (op3)              // [5:0]func                                                                                                                                        
				`EXE_JR:		begin								// jr			jump to (rs)                                 
					wreg_o				<=	`WriteDisable;                                                     
					aluop_o				<=	`EXE_J_OP;                                                         
					alusel_o			<=	`EXE_RES_JUMP_BRANCH;                                              
					reg1_read_o			<=	1'b1;                                                              
					reg2_read_o			<=	1'b0;                                                              
					instvalid			<=	`InstValid;                                                        
					link_addr_o				<=	`ZeroWord;	// 转移不需要保存返回地址                    
					branch_flag_o			<=	`Branch;	// 发生转移                                  
					branch_target_address_o	<=	reg1_o;		// 转移到的目标地址                          
				end                                                                                      
				`EXE_JALR:	begin									// jalr		jump to (rs)		save                         
					wreg_o				<=	`WriteEnable;                                                      
					aluop_o				<=	`EXE_JALR_OP;                                                      
					alusel_o			<=	`EXE_RES_JUMP_BRANCH;                                              
					reg1_read_o			<=	1'b1;                                                              
					reg2_read_o			<=	1'b0;                                                              
					if ( inst_i[25:11] == 5'b 00000)	begin	// 如果没有指定保存寄存器,则默认保存到$31   
						wd_o			<=	5'b 11111;                                                         
					end else	begin	                                                                       
						wd_o			<=	inst_i[15:11];                                                     
					end                                                                                    
					link_addr_o				<=	pc_plus_4;                                                 
					branch_flag_o			<=	`Branch;                                                   
					branch_target_address_o	<=	reg1_o;                                                    
				end									                                                                     
                default:    begin                                                                        
                end                                                                                      
              endcase   // case (op3)                                                                    
            end                                                                                          
            default:  begin                                                                              
            end                                                                                          
          endcase   // case(op2)                                                                         
        end                                                                                              
		`EXE_J:			begin											// j                                                      
			wreg_o					<=	`WriteDisable;                                                              
			aluop_o					<=	`EXE_J_OP;                                                                  
			alusel_o				<=	`EXE_RES_JUMP_BRANCH;                                                       
			reg1_read_o				<=	1'b0;                                                                       
			reg2_read_o				<=	1'b0;                                                                       
			instvalid				<=	`InstValid;                                                                 
			link_addr_o					<=	`ZeroWord;                                                          
			branch_flag_o				<=	`Branch;                                                            
			branch_target_address_o		<=	{ pc_plus_4[31:28], inst_i[25:0], 2'b00 };                          
		end                                                                                                 
		`EXE_JAL:		begin											// jal		save                                            
			wreg_o					<=	`WriteEnable;                                                               
			aluop_o					<=	`EXE_JAL_OP;                                                                
			alusel_o				<=	`EXE_RES_JUMP_BRANCH;                                                       
			reg1_read_o				<=	1'b0;                                                                       
			reg2_read_o				<=	1'b0;                                                                       
			wd_o					<=	5'b11111;                                                                   
			instvalid				<=	`InstValid;                                                                 
			link_addr_o					<=	pc_plus_4;                                                          
			branch_flag_o				<=	`Branch;                                                            
			branch_target_address_o		<=	{ pc_plus_4[31:28], inst_i[25:0], 2'b00 };                          
		end                                                                                                 
		// 分支指令跳转地址计算:将offset左移2位,并符号扩展值32位,然后与分支指令下一条指令的地址相加      
		`EXE_BEQ:		begin											// beq		rs == rt                                        
			wreg_o					<=	`WriteDisable;                                                              
			aluop_o					<=	`EXE_BEQ_OP;                                                                
			alusel_o				<=	`EXE_RES_JUMP_BRANCH;                                                       
			reg1_read_o				<=	1'b1;                                                                       
			reg2_read_o				<=	1'b1;                                                                       
			instvalid				<=	`InstValid;                                                                 
			link_addr_o					<=	`ZeroWord;                                                          
			if (reg1_o == reg2_o)	begin                                                                       
				branch_flag_o			<=	`Branch;                                                            
				branch_target_address_o	<=	pc_plus_4 + imm_sll2_signedext;                                     
			end                                                                                               
		end                                                                                                 
		`EXE_BGTZ:	begin												// bgtz		rs > 0                                          
			wreg_o					<=	`WriteDisable;                                                              
			aluop_o					<=	`EXE_BGTZ_OP;                                                               
			alusel_o				<=	`EXE_RES_JUMP_BRANCH;                                                       
			reg1_read_o				<=	1'b1;                                                                       
			reg2_read_o				<=	1'b0;                                                                       
			instvalid					<=	`InstValid;                                                                 
			if ( ( reg1_o[31] == 1'b0 ) && ( reg1_o != `ZeroWord ) )	begin                                   
				branch_flag_o			<=	`Branch;                                                            
				branch_target_address_o	<=	pc_plus_4 + imm_sll2_signedext;                                     
			end                                                                                               
		end                                                                                                 
		`EXE_BLEZ:	begin												// blez		rs ≤ 0                                           
			wreg_o					<=	`WriteDisable;                                                              
			aluop_o					<=	`EXE_BLEZ_OP;                                                               
			alusel_o				<=	`EXE_RES_JUMP_BRANCH;                                                       
			reg1_read_o				<=	1'b1;                                                                       
			reg2_read_o				<=	1'b0;                                                                       
			instvalid					<=	`InstValid;                                                                 
			if ( ( reg1_o[31] == 1'b1 ) || ( reg1_o == `ZeroWord ) )	begin                                   
				branch_flag_o			<=	`Branch;                                                            
				branch_target_address_o	<=	pc_plus_4 + imm_sll2_signedext;                                     
			end                                                                                               
		end                                                                                                 
		`EXE_BNE:		begin											// bne		rs ≠ rt                                          
			wreg_o					<=	`WriteDisable;                                                              
			aluop_o					<=	`EXE_BNE_OP;                                                                
			alusel_o				<=	`EXE_RES_JUMP_BRANCH;                                                       
			reg1_read_o				<=	1'b1;                                                                       
			reg2_read_o				<=	1'b1;                                                                       
			instvalid					<=	`InstValid;                                                                 
			if ( reg1_o != reg2_o )	begin                                                                     
				branch_flag_o			<=	`Branch;                                                            
				branch_target_address_o	<=	pc_plus_4 + imm_sll2_signedext;                                     
			end                                                                                               
		end                                                                                                 
		`EXE_REGIMM_INST:	begin                                                                             
			case (op4)                                                                                        
				`EXE_BGEZ:	begin										// bgez		rs ≥ 0                                           
					wreg_o				<=	`WriteDisable;                                                            
					aluop_o				<=	`EXE_BGEZ_OP;                                                             
					alusel_o			<=	`EXE_RES_JUMP_BRANCH;                                                     
					reg1_read_o			<=	1'b1;                                                                     
					reg2_read_o			<=	1'b0;                                                                     
					instvalid			<=	`InstValid;                                                               
					if ( reg1_o[31] == 1'b0 )	begin                                                               
						branch_flag_o			<=	`Branch;                                                          
						branch_target_address_o	<=	pc_plus_4 + imm_sll2_signedext;                                 
					end                                                                                           
				end                                                                                             
				`EXE_BGEZAL:	begin									// bgezal	rs ≥ 0		save                                    
					wreg_o				<=	`WriteEnable;                                                             
					aluop_o				<=	`EXE_BGEZAL_OP;                                                           
					alusel_o			<=	`EXE_RES_JUMP_BRANCH;                                                     
					reg1_read_o			<=	1'b1;                                                                     
					reg2_read_o			<=	1'b0;                                                                     
					link_addr_o			<=	pc_plus_4;                                                                
					wd_o				<=	5'b11111;                                                                 
					instvalid			<=	`InstValid;                                                               
					if ( reg1_o[31] == 1'b0 )	begin                                                               
						branch_flag_o			<=	`Branch;                                                        
						branch_target_address_o	<=	pc_plus_4 + imm_sll2_signedext;                                 
					end                                                                                           
				end                                                                                             
				`EXE_BLTZ:		begin									// bltz		rs < 0                                            
					wreg_o				<=	`WriteDisable;                                                            
					aluop_o				<=	`EXE_BLTZ_OP;                                                             
					alusel_o			<=	`EXE_RES_JUMP_BRANCH;                                                     
					reg1_read_o			<=	1'b1;                                                                     
					reg2_read_o			<=	1'b0;                                                                     
					instvalid			<=	`InstValid;                                                               
					if ( reg1_o[31] == 1'b1 )	begin                                                               
						branch_flag_o			<=	`Branch;                                                        
						branch_target_address_o	<=	pc_plus_4 + imm_sll2_signedext;                                 
					end                                                                                           
				end                                                                                             
				`EXE_BLTZAL:	begin									// bltzal	rs < 0                                            
					wreg_o				<=	`WriteEnable;                                                             
					aluop_o				<=	`EXE_BLTZAL_OP;                                                           
					alusel_o			<=	`EXE_RES_JUMP_BRANCH;                                                     
					reg1_read_o			<=	1'b1;                                                                     
					reg2_read_o			<=	1'b0;                                                                     
					link_addr_o			<=	pc_plus_4;                                                                
					wd_o				<=	5'b11111;                                                                 
					instvalid			<=	`InstValid;                                                               
					if ( reg1_o[31] == 1'b1 )	begin                                                               
						branch_flag_o			<=	`Branch;                                                        
						branch_target_address_o	<=	pc_plus_4 + imm_sll2_signedext;                                 
					end							                                                                              
				end                                                                                             
				default:	begin                                                                                 
				end                                                                                             
			endcase                                                                                           
		end                                                                                                 
        default:	begin    
        end                
      endcase // case(op)  
    end //else
  end //always

2.4. 执行ex

module ex (                                                                               
  // 与转移指令相关的返回地址                                                      
  input		wire 	[`RegBus]				link_address_i		// 需要保存的返回地址            
);                                                                                 

// *****************************************************************************************
// ********** stage4: according to the type, determine the last information to be written **
// *****************************************************************************************
  always @ (*) begin                                                                                                                                      
    case (alusel_i)                                                                                                                          
      `EXE_RES_JUMP_BRANCH:	begin		// 如果是转移指令,将返回地址写入目的寄存器             
      	wdata_o	<=	link_address_i;                                                         
      end                                                                                                                                                                   
    endcase // case                                                                         
  end // always                                                                             

3. 测试模块

3.1 跳转指令

.org 0x0                                                                                           
.set noat                                                                                          
.set noreorder      # 加入这个伪操作,指示编译器不要对程序做出不论什么优化或是修改                 
.set nomacro                                                                                       
.global _start                                                                                     
_start:                                                                                            
						 #	PC  指令地址下标                                                           
	 ori  $1,$0,0x0001   #	000		001)$1 = 0x1                                                
	 j    0x20           #	004		01	   转移到0x20处                                                
	 ori  $1,$0,0x0002   #	008		02	                                                               
	 ori  $1,$0,0x1111   #	00c		03	                                                               
	 ori  $1,$0,0x1100   #	010		04	                                                               
	                     					                                                                   
	 .org 0x20           					                                                                   
	 ori  $1,$0,0x0003   #	080		202)$1 = 0x3                                                
	 jal  0x40           #	084		21	   转移到0x40处,同一时候设置$310x88                         
	 ori  $3,$0,0x0004   #	088		223)$1 = 0x4                                                
	                     	                                                                           
	 ori  $1,$0,0x0005   #	08c		234)$1 = 0x5                                                
	 ori  $1,$0,0x0006   #	090		245)$1 = 0x6                                                
	 j    0x60           #	094		25	   转移到0x60处                                                
	 nop                 #	098		26	                                                               
	                    					                                                                   
	 .org 0x40           						                                                                 
	 jalr $2,$31         #	100		40	   此时$310x88,所以转移到0x88,同一时候设置$20x104        
	 or   $1,$2,$0       #	104		417)$1 = 0x104                                              
	                    					                                                                   
	 ori  $1,$0,0x0009   #	108		428)$1 = 0x9                                                
	 ori  $1,$0,0x000a   #	10c		439)$1 = 0xa                                                
	 j 0x80              #	110		44	   转移到0x80处                                                
	 nop                 #	114		45	                                                               
	                    					                                                                   
	 .org 0x60           				                                                                     
	 ori  $1,$0,0x0007   #	180		616)$1 = 0x7                                                
	 jr   $2             #	184		62	   此时$20x104,所以转移到0x104处                            
	 ori  $1,$0,0x0008   #	188		63	                                                               
	 ori  $1,$0,0x1111   #	18c		64   	                                                             
	 ori  $1,$0,0x1100   #	190		65	                                                               
	                                                                                                 
	.org 0x80            				                                                                     
	 nop                 #	200		80	                                                               
	                                                                                                 
	_loop:                                                                                           
	   j _loop                                                                                       
	   nop                                                                                           

指令寄存器
在这里插入图片描述
仿真波形
在这里插入图片描述
在这里插入图片描述

3.2 分支指令

.org 0x0                                                                                                                                                
   .set noat                                                                   
   .set noreorder                                                              
   .set nomacro                                                                
   .global _start                                                              
_start:                
							#	PC  指令地址下标                                                          
   ori  $3,$0,0x8000       	#	000		001)$3 = 0x00008000                                                   
   sll  $3,16              	#	004		012)$3 = 0x80000000                              
   ori  $1,$0,0x0001       	#	008		023)$1 = 0x1                                     
   b    s1                 	#	00c		03	    转移到s1处                                       
   ori  $1,$0,0x0002        #	010		04	                      
1:                           				                                                         
   ori  $1,$0,0x1111        #	014 	05	    				                                                         
   ori  $1,$0,0x1100        #	018		06	                                                        
                             	                                                     
   .org 0x20                 	                                                     
s1:                                                                                          
   ori  $1,$0,0x0003       	#	080		204)$1 = 0x3                                     
   bal  s2                 	#	084		21	    转移到s2处,同一时候设置$310x88              
   ori  $1,$0,0x000f        #	088		22	   
                             		            
   ori  $1,$0,0x1100        #	08c		23	                                                           
   ori  $1,$0,0x1111        #	090		24	    					                                                       
   bne  $1,$0,s3            #	094		25	                                                     
   nop                      #	098		26	                                                     
   ori  $1,$0,0x1100        #	09c		27	    				                                                         
   ori  $1,$0,0x1111        #	0a0		28	                                                     
                                                                       
   .org 0x50                                                           
s2:                         	                                                     
   ori  $1,$0,0x0004      	#	140		505)$1 = 0x4                                       
   beq  $3,$3,s3          	#	144		51      $3等于$3,所以会发生转移,目的地址是s3             
   or   $1,$31,$0         	#	148		52	                       
   ori  $1,$0,0x1111        #	14c		53	                                                     
   ori  $1,$0,0x1100        #	150		54	                                                     
2:                             	                                                   
   ori  $1,$0,0x0007      	#	154		557)$1 = 0x7                                       
   ori  $1,$0,0x0008      	# 	158		568)$1 = 0x8                                      
   bgtz $1,s4             	#	15c		57      此时$10x8,大于0,所以转移至标号s4处             
   ori  $1,$0,0x0009      	#	160		58	                         
   ori  $1,$0,0x1111        # 	164		59                                         
   ori  $1,$0,0x1100        # 	168		60                                         
                                                                               
   .org 0x80                                                                   
s3:                                                                            
   ori  $1,$0,0x0005      	# 	200		806)$1 = 0x5                                       
   bgez $1,2b             	# 	204		81		 此时$10x5,大于0,所以转移至前面的标号2处        
   ori  $1,$0,0x0006      	# 	208		82			                   
   ori  $1,$0,0x1111        # 	20c		83			                                                 
   ori  $1,$0,0x1100        # 	210		84			                                                 
                                                                               
   .org 0x100                                                                  
s4:                                                                            
   ori  $1,$0,0x000a      	# 	400		1009)$1 = 0xa                                      
   bgezal $3,s3          	# 	404		101		此时$30x80000000,小于0,所以不发生转移          
   or   $1,$0,$31         	#	408		10210)$1 = 0x408                                    
   ori  $1,$0,0x000b      	# 	40c		10311)$1 = 0xb                                      
   ori  $1,$0,0x000c     	# 	410		10412)$1 = 0xc                                      
   ori  $1,$0,0x000d      	# 	414		10513)$1 = 0xd                                      
   ori  $1,$0,0x000e      	# 	418		10614)$1 = 0xe                                      
   bltz $3,s5             	# 	41c		107		 此时$30x80000000,小于0,所以发生转移,转移至s5处
   ori  $1,$0,0x000f      	# 	420		108			                    
   ori  $1,$0,0x1100        # 	424		109			                                                  
                                                                               
                                                                               
   .org 0x130                                                                  
s5:                        	                                                    
   ori  $1,$0,0x0010      	# 	4c0		13015)$1 = 0x10                                     
   blez $1,2b             	# 	4c4		131		此时$10x10,大于0,所以不发生转移                
   ori  $1,$0,0x0011      	# 	4c8		13216)$1 = 0x11                                     
   ori  $1,$0,0x0012      	# 	4cc		13317)$1 = 0x12                                     
   ori  $1,$0,0x0013      	# 	4d0		13418)$1 = 0x13                                     
   bltzal $3,s6           	# 	4d4		135		 此时$30x80000000,小于0,所以发生转移,转移到s6处
   or   $1,$0,$31         	# 	4d8		136			                    
   ori  $1,$0,0x1100        # 	4dc		137			                                                  
                                                                               
                                                                               
   .org 0x160                                                                  
s6:                                                                            
   ori $1,$0,0x0014       	# 	580		16019)$1 = 0x14                                     
   nop                      # 	584		16420)                                               
                                                                               
                                                                               
                                                                               
_loop:                                                                         
   j _loop                                                                     
   nop                                                                         

指令寄存器
在这里插入图片描述

仿真波形
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. 补充

有关pc值计算:指令地址下标 * 4,转化成十六进制

以“al”结尾的需要保存返回地址的指令,不管是否满足转移的条件,返回地址都会被保存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值