MIPS流水线CPU的verilog实现Word文档格式.docx
《MIPS流水线CPU的verilog实现Word文档格式.docx》由会员分享,可在线阅读,更多相关《MIPS流水线CPU的verilog实现Word文档格式.docx(56页珍藏版)》请在冰点文库上搜索。
对IF级来的指令进行译码,并产生相应的控制信号。
整个CPU的控制信号基本都是在这级上产生。
该级自身不需任何控制信号。
流水线冒险检测也在该级进行,冒险检测电路需要上一条指令的MemRead,即在检测到冒险条件成立时,冒险检测电路产生stall信号清空ID/EX寄存器,插入一个流水线气泡。
(3)EX级:
执行级。
该级进行算术或逻辑操作。
此外LW、SW指令所用的RAM访问地址也是在本级上实现。
控制信号有ALUCode、ALUSrcA、ALUScrB和RegDst,根据这些信号确定ALU操作、选择两个ALU操作数A、B,并确定目标寄存器。
另外,数据转发也在该级完成。
数据转发控制电路产生ForwardA和ForwardB两组控制信号。
(4)MEM级:
存储器访问级。
只有在执行LW、SW指令时才对存储器进行读写,对其他指令只起到一个周期的作用。
该级只需存储器写操作允许信号MemWrite。
(5)WB级:
写回级。
该级把指令执行的结果回写到寄存器文件中。
该级设置信号MemtoReg和寄存器写操作允许信号RegWrite,其中MemtoReg决定写入寄存器的数据来自于MEM级上的缓冲值或来自于MEM级上的存储器。
2)流水线冒险
在流水线CPU中,多条指令通知执行,由于各种各样的原因,在下一个时钟周期中下一条指令不能执行,这种情况称为冒险。
冒险分为三类:
①结构冒险:
硬件不支持多条指令在同一个时钟周期内执行。
MIPS指令集专为流水线设计,因此在MIPSCPU中不存在此类冒险。
②数据冒险:
在一个操作必须等待另一操作完成后才能进行时,流水线必须停顿,这种情况称为数据冒险。
数据冒险分为两类:
ⅰ数据相关:
流水线内部其中任何一条指令要用到任何其他指令的计算结果时,将导致数据冒险。
通常可以用数据转发(数据定向)来解决此类冒险。
ⅱ数据冒险:
此类冒险发生在当定向的目标阶段在时序上早于定向的源阶段时,数据转发无效。
通常是引入流水线阻塞,即气泡(bubble)来解决。
。
③控制冒险:
CPU需要根据分支指令的结果做出决策,而此时其他指令可能还在执行中,这时会出现控制冒险,也称为分支冒险。
解决此类冒险的常用方法是延迟分支。
2.1)数据相关与转发
下面通过具体例子来阐述数据相关。
见图3.2
图3.2数据相关性问题实例图
可见,后4条指令都依赖于第一条指令得到寄存器$2的结果,但sub指令要在第五周期才写回寄存器$2,但在第三、四、五个时钟周期$2分别要被and、or和add三个指令用到,所以这三个指令得到的是错误的未更新的数据,会引起错误的结果;
而第六个时钟周期$2要被sw指令用到,此时得到的才是正确的已更新的数据。
这种数据之间的互相关联引起的冒险就是数据相关。
可以看出,当一条依赖关系的方向与时间轴的方向相反时,就会产生数据冒险。
(1)一阶数据相关与转发(EX冒险)
首先讨论指令sub与and之间的相关问题。
sub指令在第五周期写回寄存器$2,而and指令在第四周期就对sub指令的结果$2提出申请,显然将得到错误的未更新的数据。
像这类第I条指令的源操作寄存器与第I-1条指令(即上一条指令)的目标寄存器相重,导致的数据相关称为一阶数据相关。
见图3.3中实线所示。
图3.3一阶数据相关实例图
可以发现,sub指令的结果其实在EX级结尾,即第三周期末就产生了;
而and指令在第四时钟周期向sub指令结果发出请求,请求时间晚于结果产生时间,所以只需要sub指令结果产生之后直接将其转发给and指令就可以避免一阶数据相关。
如图3.3虚线所示。
转发数据为ALUResult_mem
数据转发由Forwardingunit单元控制,判断转发条件是否成立。
转发机制硬件实现见图3.4
图3.4转发机制的硬件实现
转发条件ForwardA、ForwardB作为数据选择器的地址信号,转发条件不成立时,ALU操作数从ID/EX流水线寄存器中读取;
转发条件成立时,ALU操作数取自数据旁路。
转发条件:
1MEM级指令是写操作,即RegWrite_mem=1;
2MEM级指令写回的目标寄存器不是$0,即RegWriteAddr_mem≠0;
3MEM级指令写回的目标寄存器与在EX级指令的源寄存器是同一寄存器,即RegWriteAddr_mem=RsAddr_ex或RegWriteAddr_mem=RtAddr_ex。
(2)二阶数据相关与转发(MEM冒险)
接下来讨论sub指令与or指令之间的相关问题。
sub指令在第5时钟周期写回寄存器,而or指令也在第5时钟周期对sub指令的结果提出了请求,很显然or指令读取的数据是未被更新的错误内容。
这类第I条指令的源操作寄存器与第I-2条指令(即之上第二条指令)的目标寄存器相重,导致的数据相关称为二阶数据相关。
见图3.5中实线所示。
图3.5一阶数据相关实例图
如前所述,or指令在第五时钟周期向sub指令结果发出请求时,sub指令的结果已经产生。
所以,我们同样采用“转发”,即通过MEM/WB流水线寄存器,将sub指令结果转发给or指令,而不需要先写回寄存器堆。
如图3.5中虚线所示。
转发数据为RegWriteData_wb
1WB级指令是写操作,即RegWrite_wb=1;
2WB级指令写回的目标寄存器不是$0,即RegWriteAddr_wb≠0;
3WB级指令写回的目标寄存器与在EX级指令的源寄存器是同一寄存器,即RegWriteAddr_wb=RsAddr_ex或RegWriteAddr_wb=RtAddr_ex;
4EX冒险不成立,即RegWriteAddr_mem≠RsAddr_ex或RegWriteAddr_mem=RtAddr_ex。
(3)三阶数据相关与转发
最后讨论sub指令与add指令之间的相关问题。
sub指令与add指令在第五时钟周期内同时读写同一个寄存器。
这类同一周期内同时读写同一个寄存器的数据相关称之为三阶数据相关。
如图3.6中实线所示。
图3.6三阶数据相关实例图
假设寄存器的写操作发生在时钟周期的上升沿,而读操作发生在时钟周期的下降沿,那么读操作将读取到最新写入的内容。
在这种假设条件下将不会发生数据冒险。
这就要求流水线中的寄存器具有“先写后读(ReadAfterWrite)”的特性。
这类“写操作发生在时钟周期的上升沿,读操作发生在时钟周期的下降沿”的寄存器虽然在理论上是可实现的,但是不适合应用于同步系统,因为它不但影响系统的运行速度,而且影响系统的稳定性,是不可取的。
因此,我们采用“转发”机制来解决三阶数据相关冒险。
该部分转发电路我们放在寄存器堆的设计中完成。
如图3.6中虚线所示。
转发数据为RegWriteData_wb。
转发条件为:
3WB级指令写回的目标寄存器与在ID级指令的源寄存器是同一寄存器,即RegWriteAddr_wb=RsAddr_id或RegWriteAddr_wb=RtAddr_id。
2.2)数据冒险与阻塞
当一条指令试图读取一个寄存器,而它前一条指令是lw指令,并且该lw指令写入的是同一个寄存器时,定向转发的方法就无法解决问题。
如图3.7所示
这类冒险不同于数据相关冒险,需要单独一个“冒险检测单元(HazardDetector)”,它在ID级完成。
冒险成立的条件为:
1上一条指令是lw指令,即MemRead_ex=1;
2在EX级的lw指令与在ID级的指令读写的是同一个寄存器,即RegWriteAddr_ex=RsAddr_id或RegWriteAddr_ex=RtAddr_id。
冒险的解决:
为解决数据冒险,我们引入流水线阻塞。
当HazardDetector检测到冒险条件成立时,在lw指令和下一条指令之间插入阻塞,即流水线气泡(bubble),使后一条指令延迟一个时钟周期执行,这样就将该冒险转化为二阶数据相关,可用转发解决。
如图3.8所示。
图3.8流水线气泡的引入
需要注意的是,如果处于ID级的指令被阻塞,那么处于IF级的指令也必须阻塞,否则,处于ID级的指令就会丢失。
防止这两条指令继续执行的方法是:
保持PC寄存器和IF/ID流水线寄存器不变,同时插入一个流水线气泡。
具体实现方法如下:
在ID级检测到冒险条件时,HazardDetector输出两个信号:
Stall与PC_IFWrite。
Stall信号将ID/EX流水线寄存器中的EX、MEM和WB级控制信号全部清零。
这些信号传递到流水线后面的各级,由于控制信号均为零,所以不会对任何寄存器和存储器进行写操作,高电平有效。
PC_IFWrite信号禁止PC寄存器和IF/ID流水线寄存器接收新数据,低电平有效。
2.3)分支冒险
还有一类冒险是包含分支的流水线冒险,下图。
图3.10分支冒险实例
流水线每个时钟周期都得取指令才能维持运行,但分支指令必须等到MEM级才能确定是否执行分支。
这种为了确定预取正确的指令而导致的延迟叫做控制冒险或分支冒险。
一种比较普遍的提高分支阻塞速度的方法是假设分支不发生,并继续执行顺序的指令流。
如果分支发生的话,就丢弃已经预取并译码的指令,指令的执行沿着分支目标继续。
由于分支指令直到MEM级才能确定下一条指令的PC,这就意味着为了丢弃指令必须将流水线中的IF、ID和EX级的指令都清除掉(flush)。
这种优化方法的代价较大,效率较低。
如果我们能在流水线中提前分支指令的执行过程,那么就能减少需要清除的指令数。
这是一种提高分支效率的方法,降低了执行分支的代价。
因此我们采用提前分支指令的方法解决分支冒险。
提前分支指令需要提前完成两个操作:
1计算分支的目的地址:
由于已经有了PC值和IF/ID流水线寄存器中的指令值,所以可以很方便地将EX级的分支地址计算电路移到ID级。
我们针对所有指令都执行分支地址的计算过程,但只有在需要它的时候才会用到。
2判断分支指令的跳转条件:
我们将用于判断分支指令成立的Zero信号检测电路(Ztest)从ALU中独立出来,并将它从EX级提前至ID级。
具体的设计将在ID级设计中介绍。
在提前完成以上两个操作之外,我们还需丢弃IF级的指令。
具体做法是:
加入一个控制信号IF_flush,做为IF/ID流水线寄存器的清零信号。
当分支冒险成立,即Z=1,则IF_flush=1,否则IF_flush=0,故IF_flush=Z。
考虑到本系统还要实现的无条件跳转指令:
J和JR,在执行这两个指令时也必须要对IF/ID流水线寄存器进行清空,因此,IF_flush的表达式应表示为:
IF_flush=Z||J||JR。
2.MIPS指令格式
1)R型指令格式
op、funct:
共同决定指令名称,都为6位;
rs:
指定第一操作数的寄存器地址,为5位;
rt:
指定第二操作数的寄存器地址,为5位;
rd:
指定目标寄存器地址,为5位;
sa:
位移运算的移动位数,为5位。
本实验要实现的R型指令有:
1算术逻辑运算指令:
ADD、ADDU、SUB、SUBU、AND、OR、NOR、XOR、SLT、SLTU
2移位指令:
SLLV、SRLV、SRAV、SLL、SRL、SRA
3寄存器跳转指令:
JR
2)I型指令格式
op:
决定指令名称,为6位;
储存结果的寄存器地址,为5位;
Imm:
立即数,为16位。
本实验要实现的I型指令有:
1存储器访问指令:
LW、SW
2立即数算术逻辑运算指令:
ADDI、ADDIU、ANDI、ORI、XORI、SLTI、SLTIU
3分支指令:
BEQ、BNE、BGEZ、BGTZ、BLEZ、BLTZ
注:
1I型指令中立即数算术逻辑运算指令对立即数(Imm)的处理应分为两类情况考虑:
›当指令为ADDI、ADDIU、SLTI、SLTIU时,指令中的16位立即数(Imm)应做符号扩展为32位:
sign-extend(Imm)。
此符号扩展电路在ID级完成。
›当指令为ANDI、ORI、XORI时,Imm应做“0”扩展为32位。
考虑到资源的限制,在执行ANDI、ORI、XORI指令时,“0”扩展功能放在ALU内部(即EX级)完成。
2对于条件分支指令:
不执行分支语句时,跳转地址应为下一条地址;
执行分支语句时,跳转地址应为下一条地址加上跳转指令数,即为立即数。
由于跳转方向有两个:
向前与向后,故立即数存在正负性,应该有符号扩展为32位:
如果能够将立即数的位数扩充,跳转指令的范围将大大增加。
由于CPU中指令的起始地址都是4的倍数,因此它们地址的后两位都是0,那么跳过的指令数后两位也应为2’b00,故可将16为立即数左移两位扩充为18位,寻址地址范围也扩大4倍。
因此分支地址可表示为{PC+4+sign-extend(Imm)<
<
2}.
3对于取字指令LW操作为:
rt<
=Mem[rs+sign_extend(Imm)]
对于存字指令SW操作为:
Mem[rs+sign_extend(Imm)]<
=rt
由于地址的变化是在内存中进行,故立即数只需进行有符号位扩展,不能位移。
3)J型指令格式
address:
跳转的地址,为26位。
无条件跳转指令:
J
指令都是32位,因此我们要把26位跳转地址扩展为32位。
由于CPU中指令的起始地址都是4的倍数,因此它们地址的后两位都是0,可以把26位的address扩展为28位:
{26-bitsaddress,2’b00};
剩下的最高4位则直接从PC中取,即跳转地址扩展为:
{PC[31:
28],26-bitsaddress,2’b00}。
3.
指令译码模块ID的设计
指令译码模块的主要作用是从机器码中解析出指令,并根据解析结果输出各种控制信号。
ID模块主要有指令译码(Decode)、寄存器堆(Registers)、冒险监测、分支检测和加法器等组成。
ID模块的接口信息如下表所示:
引脚名称
方向
说明
clk
Input
系统时钟
Instruction_id[31:
0]
指令机器码
NextPC_id[31:
指令指针
RegWrite_wb
寄存器写允许信号,高电平有效
RegWriteAddr_wb[4:
寄存器的写地址
RegWriteData_wb[31:
0]
写入寄存器的数据
MemRead_ex
冒险检测的输入
RegWriteAddr_ex[4:
MemtoReg_id
Output
决定回写的数据来源(0:
ALU1:
存储器)
RegWrite_id
MemWrite_id
存储器写允许信号,高电平有效
MemRead_id
存储器读允许信号,高电平有效
ALUCode_id[4:
决定ALU采用何种运算
ALUSrcA_id
决定ALU的A操作数的来源(0:
rs1:
Sa)
ALUSrcB_id
决定ALU的B操作数的来源(0:
rt1:
Imm)
RegDst_id
决定Register回写是采用的地址(rt/rd)
Stall
ID/EX寄存器清空信号,高电平插入一个流水线气泡
Z
分支指令的条件判断结果
跳转指令
寄存器跳转指令
PC_IFWrite
阻塞流水线的信号,低电平有效
BranchAddr[31:
条件分支地址
JumpAddr[31:
跳转地址
Imm_id[31:
符号扩展成32位的立即数
Sa_id[31:
0扩展成32位的移位立即数
RsData_id[31:
Rs寄存器数据
RtData_id[31:
Rt寄存器数据
RdAddr_id[4:
Rd寄存器地址
RsAddr_id[4:
Rs寄存器地址
RtAddr_id[4:
Rt寄存器地址
(1)指令译码(Decode)子模块的设计
Decode控制器的主要作用是根据指令确定各个控制信号的值,是一个组合电路。
我们将指令分成八类:
R_type1:
ADD、ADDU、SUB、SUBU、AND、OR、
NOR、XOR、SLT、SLTU、SLLV、SRLV、SRAVR型指令
R_type2:
SLL、SRL、SRA
JR_type:
J_type:
JJ型指令
I_type:
Branch:
BEQ、BNE、BGEZ、BGTZ、BLEZ、BLTZI型指令
LW:
LW
SW:
SW
Decode输出的九组控制信号:
1RegWrite
决定是否对寄存器(Registers)进行写操作。
当RegWrite高电平有效时,将数据写入指定的寄存器中。
需要写回寄存器的指令有:
LW、R_type1、R_type2和I_type,则
RegWrite_id=LW||R_type1||R_type2||I_type
2RegDst
决定目标寄存器是rt还是rd。
当RegDst=0时,rt为目标寄存器;
当RegDst=1时,rd为目标寄存器。
需要写回寄存器的指令类型有:
LW、R_type1、R_type2和I_type。
其中,R_type1和R_type2的目标寄存器是rd,而LW和I_type的目标寄存器是rt。
所以:
RegDst_id=R_type1||R_type2
3MemWrite
决定是否对数据存储器进行写操作。
当MemWrite有效时,将数据写入数据存储器指定的位置。
需要对写存储器的指令只有SW,所以:
MemWrite_id=SW
4MemRead
决定是否对数据存储器进行读操作。
当MemRead有效时,读取数据存储器指定位置的数据。
需要对读存储器的指令只有LW,所以:
MemRead_id=LW
5MemtoReg
决定写入寄存器(registers)的数据来自ALU还是数据存储器。
›当MemtoReg=0时,数据来自ALU;
当MemtoReg=1时,数据来自数据存储器。
其中,只有LW写回寄存器的数据取自存储器,所以:
MemtoReg_id=LW
6ALUSrcA
决定ALU第一操作数来源。
当ALUSrcA=0时,ALU第一操作数A(详见转发电路设计);
当ALUSrcA=1时,ALU第一操作数来源于0扩展的用于移位指令的5位sa。
八种指令类型中J_tpye、JR_tpye及Branch类型指令没有使用ALU;
其他使用ALU的指令类型中LW、SW、R_type1和I_type均采用的rs作为ALU第一操作数,只有R_type2的第一操作数采用的是0扩展的sa,所以:
ALUSrcA_id=R_type2
7ALUSrcB
决定ALU第二操作数来源。
当ALUSrcB=0时,ALU第二操作数B。
当ALUSrcB=1时,ALU第二操作数来源于符号扩展的16位Imm。
八种指令类型中J_tpye、JR_tpye及Branch类型指令没有使用ALU。
其他使用ALU的指令类型中R_type1和R_type2采用rt作为ALU第二操作数,而LW、SW、I_type的第二操作数采用的是符号扩展的立即数Imm段,所以:
ALUSrcB_id=LW||SW||I_type
8PCSource
决定写入PC寄存器的来源。
PCSource由JR_tpye、J_tpye及Z决定:
即:
PCSource={JR,J,Z}
›PCSource=000时,写入值为下一条指令的地址PC+4;
›PCSource=001时,写入值为Branch指令的分支地址;
›PCSource=010时,写入值为J指令的跳转地址;
›PCSource=100时,写入值为JR指令的跳转地址。
9ALUCode
决定ALU的功能,由指令中的op段、rt段和funct段决定。
功能表如下:
op
funct
rt
运算
ALUCode
BEQ_op
×
Z=(A==B)
5'
d10
BNE_op
Z=~(A==B)
d11
BGEZ_op
d1
Z=(A≥0)
d12
BGTZ_op
d0
Z=(A>
0)
d13
BLEZ_op
Z=(A≤0)
d14
BLTZ_op
Z=(A<
d15
R_type_op
ADD_funct