自己动手写CPU 第七章学习笔记

第七章源代码可以直接下载使用

第一部分  简单算数操作指令

一共有15条,包括加法、减法、比较、乘法等指令,这些指令在流水线的执行阶段只需要一个周期,且只需要修改译码和执行阶段就可以实现。

第二部分  乘累加、乘累减指令

重点 流水线暂停机制

实现方法:保持取指令地址PC的值不变,同时保持流水线各个阶段的寄存器不变。

优化方法:假如位于第n阶段的指令请求流水线暂停,那么就保持取指令地址PC值不变,同时保持流水线第n阶段、第n阶段之前的各个阶段的寄存器不变,而第n阶段后面的指令继续运行。

(1)当处于流水线执行阶段的指令请求暂停时(即stallreq_from_ex等于Stop),按照OpenMIPS 流水线暂停机制的设计,要求取指、译码、执行阶段暂停,而访存、回写阶段继续所以设置 stall 为 6'b001111。
(2)当处于流水线译码阶段的指令请求暂停时(即stallreq_from_id等于Stop),按照OpenMIPS 流水线暂停机制的设计,要求取指、译码阶段暂停,而执行、访存、回写阶段继续所以设置 stall 为 6'b000111。
(3)其余情况下,设置stall为6b000000,表示不暂停流水线。

`include "defines.v"

module ctrl(

	input wire		rst,

	input wire      stallreq_from_id,
    //来自执行阶段的暂停请求
	input wire      stallreq_from_ex,
	output reg[5:0]    stall       
	//stall[0]表示PC是否不变,为1暂停
    //stall[1]取指是否暂停,stall[2]译码,stall[3]执行,stall[4]访存,stall[5]回写,为1暂停
);


	always @ (*) begin
		if(rst == `RstEnable) begin
			stall <= 6'b000000;
		end else if(stallreq_from_ex == `Stop) begin
			stall <= 6'b001111;
		end else if(stallreq_from_id == `Stop) begin
			stall <= 6'b000111;			
		end else begin
			stall <= 6'b000000;
		end    //if
	end      //always
			

endmodule

此外,还需要对其他模块进行修改,添加接口,这里不做叙述。

分为有符号运算、无符号运算,其中,乘累加运算madd(有符号)、maddu(无符号)相乘后,再与HI、LO寄存器的值相加,乘累减运算msub(有符号)、msubu(无符号)相乘后,再与HI、LO寄存器的值相减,即这四条指令都需要两个时钟周期。

必须保存的信息:(1)当前是第几个时钟周期

                             (2)乘法结果

执行阶段 EX 模块的输出 hilo_temp_o是乘法结果,传递到EX/MEM 模块,并在下一个时钟周期送回 EX模块,参与第二个时钟周期的加/减法运算。
执行阶段 EX模块的输出cnt_o代表当前是第几个时钟周期,传递到 EX/MEM 模块,并在下一个时钟周期送回 EX 模块,后者据此判断当前处于乘累加、乘累减指令的第几个执行周期。

第三部分  除法指令

同样有有符号与无符号的区别,这本书使用了试商法完成,但是对于32位的除法,至少需要32个流水线周期完成。

试商法

设被除数是m,除数是n,商保存在s中,被除数的位数是k,其计算步骤如下(为了便
于说明,在此处将所有数据的最低位称为第1位,而不称为第0位)。

1.取出被除数的最高位 m[k],使用被除数的最高位减去除数n,如果结果大于等于0,则商的 s[k]为 1,反之为 0。

2.如果上一步得出的结果是0,表示当前的被减数小于除数,则取出被除数剩下的值的最高位m[k-1],与当前被减数组合作为下一轮的被减数;如果上一步得出的结果是1,表示当前的被减数大于除数,则利用上一步中减法的结果与被除数剩下的值的最高位m[k-11组合作为下一轮的被减数。然后,设置k等于k-1。
3.新的被减数减去除数,如果结果大于等于0,则商的s[k]为1,否则s[k]为0,后面的步骤重复 2-3,直到k等于1。

新建一个模块 DIV,在其中实现采用试商法的32位除法运算。当流水线执行阶段的EX模块发现当前指令是除法指令时,首先暂停流水线,然后将被除数、除数等信息送到 DIV模块,开始除法运算。DIV模块在除法运算结束后,通知EX模块,并将除法结果送到 EX 模块后者依据除法结果设置H、LO寄存器的写信息,同时取消暂停流水线。

DIV模块的主要部分是一个状态机,共有四个状态:

DivFree:除法模块空闲

DivByZero:除数是0

DivOn:除法运算进行中

DivEnd:除法运算结束

复位的时候,DIV 模块处于DivFree 状态,当输入信号start_i为DivStart,且输入信号annul_i为0时,表示除法操作开始。
如果除数opdata2_i为0,那么进入 DivByZero状态,直接给出除法结果,这里设置为0,余数也为0,然后进入DivEnd状态,并通知EX模块得到除法运算结果,后者会设置 DIV 模块的输入信号start_i为 DivStop,除法运算结束。如果除数 opdata2_i不为0,那么进入 DivOn 状态,使用试商法,经过 32 个时钟周期,得出除法结果,然后进入 DivEnd 状态,并通知EX模块得到除法运算结果,后者会设置 DIV 模块的输入信号starti为 DivStop,除法运算结束。

Div模块:

`include "defines.v"

module div(

	input	wire										clk,
	input wire										rst,
	
	input wire                    signed_div_i,
	input wire[31:0]              opdata1_i,
	input wire[31:0]		   				opdata2_i,
	input wire                    start_i,
	input wire                    annul_i,
	
	output reg[63:0]             result_o,
	output reg			             ready_o
);

	wire[32:0] div_temp;
	reg[5:0] cnt;//记录试商法的周期,32时结束
	reg[64:0] dividend;
	reg[1:0] state;
	reg[31:0] divisor;	 
	reg[31:0] temp_op1;
	reg[31:0] temp_op2;
	
	assign div_temp = {1'b0,dividend[63:32]} - {1'b0,divisor};

	always @ (posedge clk) begin
		if (rst == `RstEnable) begin
			state <= `DivFree;
			ready_o <= `DivResultNotReady;
			result_o <= {`ZeroWord,`ZeroWord};
		end else begin
		  case (state)
		  	`DivFree:			begin               //DivFree状态
		  		if(start_i == `DivStart && annul_i == 1'b0) begin
		  			if(opdata2_i == `ZeroWord) begin
		  				state <= `DivByZero;
		  			end else begin
		  				state <= `DivOn;
		  				cnt <= 6'b000000;
		  				if(signed_div_i == 1'b1 && opdata1_i[31] == 1'b1 ) begin
		  					temp_op1 = ~opdata1_i + 1;
		  				end else begin
		  					temp_op1 = opdata1_i;
		  				end
		  				if(signed_div_i == 1'b1 && opdata2_i[31] == 1'b1 ) begin
		  					temp_op2 = ~opdata2_i + 1;
		  				end else begin
		  					temp_op2 = opdata2_i;
		  				end
		  				dividend <= {`ZeroWord,`ZeroWord};
              dividend[32:1] <= temp_op1;
              divisor <= temp_op2;
             end
          end else begin
						ready_o <= `DivResultNotReady;
						result_o <= {`ZeroWord,`ZeroWord};
				  end          	
		  	end
		  	`DivByZero:		begin               //DivByZero状态
         	dividend <= {`ZeroWord,`ZeroWord};
          state <= `DivEnd;		 		
		  	end
		  	`DivOn:				begin               //DivOn状态
		  		if(annul_i == 1'b0) begin
		  			if(cnt != 6'b100000) begin
               if(div_temp[32] == 1'b1) begin
                  dividend <= {dividend[63:0] , 1'b0};
               end else begin
                  dividend <= {div_temp[31:0] , dividend[31:0] , 1'b1};
               end
               cnt <= cnt + 1;
             end else begin
               if((signed_div_i == 1'b1) && ((opdata1_i[31] ^ opdata2_i[31]) == 1'b1)) begin
                  dividend[31:0] <= (~dividend[31:0] + 1);
               end
               if((signed_div_i == 1'b1) && ((opdata1_i[31] ^ dividend[64]) == 1'b1)) begin              
                  dividend[64:33] <= (~dividend[64:33] + 1);
               end
               state <= `DivEnd;
               cnt <= 6'b000000;            	
             end
		  		end else begin
		  			state <= `DivFree;
		  		end	
		  	end
		  	`DivEnd:			begin               //DivEnd状态
        	result_o <= {dividend[64:33], dividend[31:0]};  
          ready_o <= `DivResultReady;
          if(start_i == `DivStop) begin
          	state <= `DivFree;
						ready_o <= `DivResultNotReady;
						result_o <= {`ZeroWord,`ZeroWord};       	
          end		  	
		  	end
		  endcase
		end
	end

endmodule

对其他模块的修改不再赘述

以下是本章完成后的数据流图,与上一章相比,主要变化就是增加了一个选择器,用来确定PC的值,PC在下一个周期的值可以是PC+4,也可以保持当前的值不变,后者对应的就是流水线暂停时的情况。 

  • 32
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值