第七章源代码可以直接下载使用
第一部分 简单算数操作指令
一共有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,也可以保持当前的值不变,后者对应的就是流水线暂停时的情况。