MIPS五级流水线
流水线五个时钟周期:
MIPS指令集中每条指令都可以在五个时钟周期内实现。
- IF:Instruction fetch cycle
将程序计数器(PC)发送到存储器,从存储器提取当前指令。向程序计数器加4(因为每 条指令的长度为4个字节),将程序计数器更新到下一个连续程序计数器。 - ID:Instruction decode/register fetch cycle
对指令进行译码,并从寄存器堆中读取与寄存器源说明符相对应的寄存器。在读取寄存器时对其进行相等测试,以确定是否为分支。必要时,对指令的偏移量字段进行符号扩展。符号扩展后的偏移量添加到所实现的程序计数器上,计算出可能的分支目标地址。由于一个指令的立即数部分也位于同一位置,所以在需要符号扩
展立即数时,也是在这一周期计算的。 - EX:Execution/effective address cycle
ALU对上一周期准备的操作数进行操作,根据指令类型执行三条指令之一。
存储器引用—ALU将基址寄存器和偏移量加到一起,形成有效地址。
寄存器寄存器-ALU指令—ALU对读自寄存器堆的值执行由ALU操作码指定的操作。
寄存器-立即数ALU指令—ALU对读自寄存器堆的第一个值和符号扩展立即数执行由 ALU操作码指定的操作。 - MEM:Memory access
如果该指令是一条载入指令,则使用上一周期计算的有效地址从存储器中读取数据。如果是一条存储指令,则使用有效地址将从寄存器堆的第二个寄存器读取的数据写入存储器。 - WB: Write-back cycle
将结果写人寄存器堆,无论是来自寄存器系统(对于载入指令),还是来自ALU(对于ALU 指令) 。
MIPS指令
流水线设计
流水线结构:
本实验分支冒险在ID阶段进行判断,因此可能会造成数据冒险(本实验忽略该数据冒险)。
各阶段数据通路:
由于在ID阶段进行分支冒险的判断,ID阶段有些许更改:
数据冒险:
需要转发的情况:
转发实现过程:
代码实现
IF
PC模块:
module PC(clk,rst,stall,branchEn,branchAddr,pc);
input wire clk,rst,branchEn;
input wire[31:0] branchAddr;
input wire stall;
output reg[31:0] pc;
initial begin
pc = 0;
end
always@(posedge clk or negedge rst)begin
if(!rst) begin
pc = 0;
end
else begin
if(!stall)begin //pc未暂停
if(!branchEn)
pc = pc + 1;
else
pc = branchAddr;
end
//若pc暂停,则维持原先的值不动
end
end
endmodule
InsMeomory模块:
module InsMemory(Addr,Ins);
input wire[31:0] Addr; //指令地址
output reg[31:0] Ins;//读出指令寄存器中的指令
reg [31:0]Rom[31:0];//指令寄存器
initial begin
Rom[8'h00]=32'h20010008;//addi $1,$0,8 $1=8 001000 00000 00001 0000000000001000
Rom[8'h01]=32'h3402000C;//ori $2,$0,12 $2=12
Rom[8'h02]=32'h00221820;//add $3,$1,$2 $3=20//数据冒险
Rom[8'h03]=32'h00412022;//sub $4,$2,$1 $4=4
Rom[8'h04]=32'h00222824;//and $5,$1,$2
Rom[8'h05]=32'h00223025;//or $6,$1,$2
Rom[8'h06]=32'h10220002;//beq $1,$2,2 不跳转
Rom[8'h07]=32'h00221820;//add $3,$1,$2 $3=20
Rom[8'h08]=32'h00412022;//sub $4,$2,$1 $4=4
Rom[8'h09]=32'h14220004;// bnq $1,$2,3 //跳转
Rom[8'h0A]=32'hXXXXXXXX;//
Rom[8'h0B]=32'hXXXXXXXX;
Rom[8'h0C]=32'hXXXXXXXX;
Rom[8'h0D]=32'hAD02000A;// sw $2 10($8) memory[$8+10]=12
Rom[8'h0E]=32'h8D04000A;//lw $4 10($8) $4=12
Rom[8'h0F]=32'h00441020;//add $2,$4,2//lw数据冒险
Rom[8'h10]=32'h20210004;//addi $1,$1,4 //00100000001000010000000000000100
Rom[8'h11]=32'h00222824;//and $5,$1,$2
Rom[8'h12]=32'h14220006;//beq $1,$2,6
Rom[8'h13]=32'h30470009;//andi $2,9,$7//控制冒险
Rom[8'h14]=32'hXXXXXXXX;
Rom[8'h15]=32'h00000020;
Rom[8'h16]=32'h00000020;
Rom[8'h17]=32'h00000020;
Rom[8'h18]=32'h00000020;
Rom[8'h19]=32'h00000020;
Rom[8'h1A]=32'h00000020;
Rom[8'h1B]=32'h00000020;
Rom[8'h1D]=32'h00000020;
Rom[8'h1E]=32'h00000020;
Rom[8'h1F]=32'h00000020;
end
always@(*) begin
Ins = Rom[Addr];
end
endmodule
IF/ID模块:
module IF_ID(In_Ins,clk,rst,In_pc,stall,out_Ins,out_pc,branch_stall);
input wire clk,rst;
input wire[31:0] In_pc,In_Ins;
input wire stall,branch_stall;//branch_stall判断是否为分支冒险
output reg[31:0] out_pc,out_Ins;
initial begin
out_pc = 0;
out_Ins=32'b000000_00000_00000_000