基于MIPS的五级流水线微处理器CPU设计,verilog编写,modelsim和vivado仿真通过

摘 要
本设计为一个五级流水线CPU,此CPU结构为MIPS结构。流水线CPU与单周期和多周期CPU相比较,提高了指令的执行速度,改善了CPU的整体吞吐率,提高了CPU的性能。流水线CPU相对单周期CPU和多周期CPU,硬件设计上也更复杂,并且还有许多使流水线断流的因素。在设计中,重点解决影响流水线的数据相关、结构相关、控制相关,做到充分流水。

获取程序verilog源码 设计文档 联系企鹅号 3270516346
一、流水线概述
1、设计内容
本设计为一个五级流水线CPU,采用MIPS结构。此CPU支持多种指令集,能够满足基本的功能需求。在此设计中,编写了一个求平均数和实现位反转的程序,以验证CPU功能的正确性以及是否充分流水。
2、流水线原理
cpu流水线技术是一种将指令分解为多步,并让不同指令的各步操作重叠,从而实现几条指令并行处理,以加速程序运行过程的技术。指令的每步有各自独立的电路来处理,每完成一步,就进到下一步,而前一步则处理后续指令。

3、指令执行阶段
取指令(IF)、译码(ID)、执行(EX)、访存(MEM)、回写(WB)
二、指令系统设计
1、指令格式
① R型指令:寄存器操作

op: 操作码
rs、rt :源操作数
rd :目的操作数
funct : 使用操作码告诉计算机执行什么操作
shamt:位移指令的位移量,不是位移指令就为0
②I型指令:立即数型

op : 操作码
rs : 源操作数
rt : 目的操作数
imm: 立即数
③J型指令:跳转类型

op : 操作数
addr: 立即数,跳转地址
2、CPU寄存器
MIPS架构的CPU拥有32个通用寄存器和32个浮点寄存器,每个寄存器的编号、代号以及用途如下:

3、设计的指令及功能
add: R-R型,寄存器相加运算

sub : R-R型,寄存器相减运算

and :R-R型,寄存器与运算

div :R-R型,寄存器相除

addi : R-I型,立即数与寄存器相加运算

j :跳转指令

lw : R-I型指令 ,加载存储字

sw : R-I型指令 ,存储字

ben :R-I型,相等时转移

4、10个数累加并求平均数的指令设计
使用了S0到S5这六个寄存器

对应的二进制代码为:

三、模块详细设计
1、寄存器模块设计
①程序计数器设计(PC)
程序计数器为一个32位的寄存器,它决定下一条将要执行的命令的地址。PC有一个输入端输入下一地址,一个输出端决定当前程序执行地址,在此PC中还加入了一个使能位,以配合流水线CPU的控制。
②次地址计算单元(NPC)
次地址计算单元的功能为计算下一地址,当指令不是转移类指令时,它就是加4,次地址为连续的下一地址单元。
③指令寄存器(IM)
IM用于存储指令,为只读存储器。它有两个端口,一个为地址输入,另一个为指定地址处的数据输出。

④寄存器堆(RF)
寄存器堆为CPU中指令执行时所用到的寄存器,在该寄存器堆设计中,用两个读取数据端,对应指令中两个源操作数,一个数据写入段,用于对数据的写入。

⑤数据存储器(DM)
DM为RAM型存储器,它有一个读写控制端,读写控制端为低电平时,对DM读操作,高电平时,对DM读操作。

2、算数逻辑单元(ALU)
ALU的功能为对输入的数据进行加减等算数运算,ALU有一个控制端F,根据输入F的不同,对输入的数据进行不同的运算。

3、数据扩展模块(EXT)
对于立即数类的指令,立即数为16位数据,立即数要与RF输出的32位数据进行运算。为了在运算时对数据进行匹配,需要将立即数扩展为32位,对输入的立即数进行补码扩展。

4、主控制器(CU)
控制器的职责为根据指令的操作码和funct域计算出每条指令在执行时控制信号应取何值。
控制器首先判断OP位是否为零(即指令是否为寄存器型指令),当OP为为零时,CU则根据funct位输出相应的控制信号,否则则根据OP位输出相应的控制信号。
CU有7种输出控制信号:cs为NPC的控制位,决定NPC如何计算下一地址;rf_wr为寄存器堆读写控制信号;dm_wr为数据存储器控制信号;ALUop为ALU的控制信号,决定ALU做怎样的运算;m1sel、m2sel、m3sel为三个MUX的选择控制信号。
四、流水线的数据通路
1、在流水线的两级之间插入寄存器用于数据的暂存,各级之间插入的寄存器
IF_ID:取指令与指令译码之间的寄存器,为配合流水线相关控制,该寄存器有使能端和异步复位端。
ID_EX:指令译码与指令执行阶段之间的寄存器,该寄存器有异步复位端。该寄存器除要传输数据外,还需要传输控制信号以及指令的信息。
EX_MEM:执行阶段与读存储器阶段之间的寄存器,需要传递控制信号与相应的指令信息。
MEM_WB:读存储器与回写阶段之间的寄存器,需要传递控制信号与相应的指令信息。

2、CPU的数据通通路
五级流水线CPU的数据通路为在单周期CPU的数据通路中插入四个流水线寄存器实现。并且要注意在指令执行过程中,指令的相关信息必须沿流水线传递到数据执行结束。
五、流水线相关性的解决
1、相关控制器
为了使设计更加简洁,构造了一个相关控制器专门用于检测相关并进行处理。在相关控制器中分为检测机构与执行机构,检测机构用于检测相关性,执行机构用于检测到相关时如何进行处理。

2、结构相关
结构相关是由于 多条指令需要同时争用同一个功能部件引起的冲突。在次CPU设计中,采用了IM与DM分离的设计,故流水线可以支持读指令与读写数据的并行执行,不存在存储器争用问题。对于同时出现的寄存器堆读写操作来说,虽然寄存器堆只有一个,但其结构采用了读写双端口设计,这样就能同时支持对寄存器堆的读写两个操作,不存在寄存器堆的资源冲突。所以在此设计的CPU中,不存在结构相关。
3、数据相关
数据相关是因为后进入流水线的指令需要引用先期进入流水线正在执行的指令计算结果而引起的冲突。相应的解决方案有数据转发和数据暂停机制。
①数据转发:对于数据相关,如果寄存器新值已经出现在流水线中的某级寄存器了,则可以将这个数据从该寄存器直接输入致需要使用该寄存器值的功能部件,这种技术成为转发(或旁路)。
为了实现转发,首先需要分析目的寄存器的新值会出现在那些流水线寄存器中,其次对于一条指令来说,要分析它真正使用新值的地方。
②数据暂停:当出现lw等指令时,如果下一条指令需要lw的执行结果,但lw的执行结果最早也要在其第四个时钟周期才出现,转发无法解决这一问题,办法只有暂停后续指令的执行。

暂停包括两个环节。首先暂停取指令,必须禁止PC继续执行+4计数(PC寄存器的使能端置零)。其次向后级流水线注入空操作,将后级流水线清空(使能寄存器的异步清楚操作)。
4、控制相关
因为在程序中存在分支、函数调用等,所以在指令执行过程中会存在导致指令执行方向改变的相关指令。控制相关就是指指令执行方向改变,但流水线在取下一条指令时却不知道该从那个方向取指令。如下例所示如果流水线不对执行过程中做任何处理,那么本不该执行的add与sub均会被错误的执行。可以通过缩短分支延时解决。

缩短分支延时:流水线中,尽早完成分支的决策,可以减少性能损失。(以beq指令为例)假设图中分支目标地址的输出阶段从MEM级前移到EX级,那么图中(下图)的指令序列只需要被阻塞2个时钟周期就能解决控制冒险因为正确的目标地址在EX级形成,EX级结束时就更新了PC寄存器。
六、设计结果
采用Modelsim进行仿真,对顶层模块外加时钟信号和复位信号,仿真波形如下:

从使用的寄存器值的变化可以看出指令执行过程正确,最终的的结果正确(图中y0~y5代表寄存器s0~s5,y为最终存放结果的存储器)。

图中为PC的使能信号与流水线寄存器的使能和控制信号,可见在指令执行过程中,当出现控制相关时,这些控制信号在不断变化,证明当存在指令的控制相关时,这些控制信号在不断解决这些相关。
图中为在相关控制器中解决数据相关的控制信号,在指令执行过程中,在出现数据相关的位置,控制信号不断变化,可以证明解决了数据相关的问题。

参考文献
[1] 唐朔飞. 计算机组成原理.第 2 版. 高等教育出版社, 2020.
[2] 高小鹏. 计算机组成与实现.第1版. 高等教育出版社, 2018.
附录
1、完整流水线CPU结构图:

2、顶层文件代码(部分)
module cpu_top(
input clk,
input reset,
);
wire [31:0] pc,npc,ext,exte;
wire m1sel,m2sel,m3sel,rf_wr,dm_wr,zero;
wire m1sele,m2sele,m3sele,dm_wre,rf_wre,m2selw,m2selm;
wire [4:0] rsD,rtD,rdD,rsE,rtE,rdE,dstE,dstM,dstW;
wire [31:0] RDs,RD_o ;
wire [2:0] ALUop,ALUope;
wire [31:0] RD1o,RD2o;
wire PCEnD,FlushD,FlushE,FrsD,FrtD,EnD;
wire [1:0] FrsE,FrtE;
PC PC_inst(
.clk(clk),
.reset(reset),
.di(npc),
.do(pc),
.PCEnD(PCEnD)
);
wire [31:0] ID;
assign rsD=ID[25:21];
assign rtD=ID[20:16];
assign rdD=ID[15:11];
wire [31:0] ALU_out,ALU_outm,ALU_outw,A,B,Bs,Bs_o;
wire [31:0] WD,RD1,RD2,RD1_o,RD2_o;
wire [4:0] A3;
assign RD1_o=(FrsD==1'b0)? RD1:ALU_outm;
assign RD2_o=(FrtD==1'b0)? RD2:ALU_outm;
assign A3=dstW;
assign WD=(m2selw==1'b0)?ALU_outw:RD_o;
assign zero=(RD1_o==RD2_o)?1'b1:1'b0;
RF RF_Inst(
.clk(clk),
.wr(rf_wrw),
.A1(ID[25:21]),
.A2(ID[20:16]),
.A3(A3),
.WD(WD),
.RD1(RD1),
.RD2(RD2),
);
wire [2:0] cs;
NPC NPC_inst(
.pc(pc),
.cs(cs),
.ra(RD1_o),
.imm(ID[25:0]),
.npc(npc),
.zero(zero)
);
wire [31:0] RD;
IM IM_inst(
.A(pc),
.D(RD)
);
IF_ID IF_ID_inst(
.clk(clk),
.FlushD(FlushD),
.EnD(EnD),
.IF(RD),
.ID(ID)
);
EXT EXT_inst(
.in16(ID[15:0]),
.out32(ext)
);
ID_EX ID_EX_inst(
.clk(clk),
.s(FlushE),
.rf_wr(rf_wr),
.dm_wr(dm_wr),
.m1sel(m1sel),
.m2sel(m2sel),
.m3sel(m3sel),
.ext(ext),
.ALUop(ALUop),
.rsD(rsD),
.rtD(rtD),
.rdD(rdD),
.RD1(RD1_o),
.RD2(RD2_o),
.rf_wre(rf_wre),
.dm_wre(dm_wre),
.m1sele(m1sele),
.m2sele(m2sele),
.m3sele(m3sele),
.ALUope(ALUope),
.exte(exte),
.rsE(rsE),
.rtE(rtE),
.rdE(rdE),
.RD1o(RD1o),
.RD2o(RD2o)
);
assign A=(FrsE==2'b00)? RD1o: ((FrsE==2'b01)?WD:ALU_outm);
assign Bs=(FrtE==2'b00)? RD2o: ((FrtE==2'b01)?WD:ALU_outm);
assign B=(m3sele==1'b0)? Bs:exte;
ALU ALU_inst(
.A(A),
.B(B),
.F(ALUope),
.Y(ALU_out)
);
assign dstE=(m1sele==1'b0)?rtE:rdE;
EX_MEM EX_MEM_inst(
.clk(clk),
.rf_wre(rf_wre),
.m2sele(m2sele),
.dm_wre(dm_wre),
.ALU_out(ALU_out),
.wd_i(Bs),
.dste(dstE),
.rf_wrm(rf_wrm),
.m2selm(m2selm),
.dm_wrm(dm_wrm),
.ALU_outm(ALU_outm),
.wd_o(Bs_o),
.dstm(dstM)
);
DM DM_inst(
.A(ALU_outm),
.WD(Bs_o),
.wr(dm_wrm),
.clk(clk),
.RD(RDs),
);
MEM_WB MEM_WB_inst(
.clk(clk),
.rf_wrm(rf_wrm),
.m2selm(m2selm),
.ALU_outm(ALU_outm),
.RD_i(RDs),
.dstm(dstM),
.rf_wrw(rf_wrw),
.m2selw(m2selw),
.ALU_outw(ALU_outw),
.RD_o(RD_o),
.dstw(dstW)
);
CU CU_inst(
.op(ID[31:26]),
.fun(ID[5:0]),
.cs(cs),
.rf_wr(rf_wr),
.dm_wr(dm_wr),
.m1sel(m1sel),
.m2sel(m2sel),
.m3sel(m3sel),
.ALUop(ALUop)
);
CUm CUM_inst(
.rsD(rsD),
.rtD(rtD),
.rsE(rsE),
.rtE(rtE),
.dstE(dstE),
.dstM(dstM),
.dstW(dstW),
.rf_wre(rf_wre),
.rf_wrm(rf_wrm),
.rf_wrw(rf_wrw),
.dm_wre(dm_wre),
.dm_wrm(dm_wrm),
.cs(cs),
.zero(zero),
.PCEnD(PCEnD),
.FlushD(FlushD),
.FlushE(FlushE),
.EnD(EnD),
.FrsD(FrsD),
.FrtD(FrtD),
.FrsE(FrsE),
.FrtE(FrtE),
.m2sele(m2sele)
);
endmodule