还剩27页未读,继续阅读
本资源只提供10页预览,全部文档请下载后查看!喜欢就下载吧,查找使用更方便
文本内容:
数字系统设计实验Ⅱ流水线MIPS微处理器设计
一、实验目的
(1)了解提高CPU性能的方法
(2)理解数据冒险、控制冒险的概念以及流水线冲突的解决方法
(3)掌握流水线MIPS微处理器的工作原理
(4)掌握流水线MIPS微处理器的测试方法
二、实验任务设计一个32位流水线MIPS微处理器具体要求如下设计要求
(1)至少执行下列MIPS32指令算数运算指令ADDADDUSUBSUBUADDIADDU逻辑运算指令ANDORNORXORANDIORIXORISLTSLTISLTUSLTIU移位指令SLLSLLVSRLSRLVSRA条件分支指令BEQBNEBGEZBGTZBLEZBLTZ无条件跳转指令JJR数据传输指令LWSW空指令NOP
(2)在用5级流水线技术,对数据冒险实现转发或阻塞功能
(3)在XUPVietex-IIPro开发系统中实现MIPS微处理器,要求CPU的运行速度大于25MHz.
三、实验原理
1.流水线MIPSCPU总体设计流水线是数字系统中一种提高系统稳定性和工作速度的方法,广泛应用在高档CPU的构建中根据MIPS处理器指令的特点,将整体的处理过程分为取指令(IF)、指令译码、执行、存储器访问和寄存器写回五级,对应多周期CPU的五个处理阶段如图1所示,一个指令的执行需要五个时钟周期,每个时钟周期上升沿来临时,此指令所代表的一系列数据和控制信息转移到下一级处理图1流水线流水作业示意图一条MIPS指令分为五个处理步骤,即五级流水线,的具体执行过程如图2所示图2五级流水线MIPSCPU初步原理框图流水线寄存器为了在其它四级流水线中各条指令保持各自的值,从指令存储器中读出的指令必须保存在寄存器中同样的方法应用到每个流水线步骤中,需要在上图中各级之间加入寄存器,如图3所示图
3.在各级分割线添加寄存器后的框图由于在流水线中,数据和控制信息将在时钟上升沿转移到下一级,所以规定流水线转移的变量命名遵守如下格式名称_流水线级名称例如,在ID级指令译码电路(DECODE)产生的寄存器写允许信号RegWrite在ID级、EX级、MEM级和WB级上的命名分别为RegWrite_id、RegWrite_ex、RegWrite_men和RegWrite_wb在顶层文件中,类似的变量名称有近百个,这样的命名方式起到了很好的识别作用1.MIPS指令格式
①R型指令格式本实验需要实现的R型指令有I算术逻辑运算指令ADD、ADDU、SUB、SUBU、AND、OR、NOR、XOR、SLT、SLTUII移位指令SLLV、SRLV、SRAV、SLL、SRL、SRAIII寄存器跳转指令JR
②I型指令格式本实验需要实现的I型指令有I存储器访问指令LW、SWII立即数算术逻辑运算指令ADDI、ADDIU、ANDI、ORI、XORI、SLTI、SLTIUIII分支指令BEQ、BNE、BGEZ、BGTZ、BLEZ、BLTZ分支地址为PC+4+sign-extendImm2
③J型指令格式本实验需要实现的J型指令只有无条件跳转指令J跳转地址为{PC[31:28]IR[25:0]2’b00}特别注意寄存器跳转指令JR不是J型指令,而是R型指令,其指令格式为跳转地址为$ra,寄存器号为rs移位指令SLL、SRL、SRA只有rt一个源操作数
3、取字指令的操作过程rt=Mem[rs+sign_extendimm]存字指令的操作过程Mem[rs+sign_extendimm]=rt
4、I型指令中立即数算术逻辑运算指令对立即数(Imm)的处理应分为两类情况考虑当指令为ADDI、ADDIU、SLTI、SLTIU时,指令中的16位立即数(Imm)应做符号扩展为32位此符号扩展电路在ID级完成当指令为ANDI、ORI、XORI时,Imm应做“0”扩展为32位考虑到资源的限制,在执行ANDI、ORI、XORI指令时,“0”扩展功能放在ALU内部(即EX级完成流水线中的控制信号为保证CPU按照指令正常运行,分别设置各个级的控制信号IF级取指令级从ROM中读取指令,并在下一个时钟沿到来时把指令送到ID级的指令缓冲器中该级控制信号决定下一个指令指针的PCSource信号、阻塞流水线的PC_IFwrite信号、清空流水线的IF_flush信号ID级指令译码器对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级上的存储器
3、流水线冒险在流水线CPU中,多条指令通知执行,由于各种各样的原因,在下一个时钟周期中下一条指令不能执行,这种情况称为冒险冒险分为三类
①结构冒险硬件不支持多条指令在同一个时钟周期内执行MIPS指令集专为流水线设计,因此在MIPSCPU中不存在此类冒险
②数据冒险在一个操作必须等待另一操作完成后才能进行时,流水线必须停顿,这种情况称为数据冒险数据冒险分为两类ⅰ数据相关流水线内部其中任何一条指令要用到任何其他指令的计算结果时,将导致数据冒险通常可以用数据转发(数据定向)来解决此类冒险ⅱ数据冒险此类冒险发生在当定向的目标阶段在时序上早于定向的源阶段时,数据转发无效通常是引入流水线阻塞,即气泡(bubble)来解决
③控制冒险CPU需要根据分支指令的结果做出决策,而此时其他指令可能还在执行中,这时会出现控制冒险,也称为分支冒险解决此类冒险的常用方法是延迟分支
3.1数据相关与转发下面通过具体例子来阐述数据相关见图
3.1后4条指令都依赖于第一条指令得到寄存器$2的结果,但sub指令要在第五周期才写回寄存器$2,但在第
三、
四、五个时钟周期$2分别要被and、or和add三个指令用到,所以这三个指令得到的是错误的未更新的数据,会引起错误的结果;而第六个时钟周期$2要被sw指令用到,此时得到的才是正确的已更新的数据这种数据之间的互相关联引起的冒险就是数据相关可以看出,当一条依赖关系的方向与时间轴的方向相反时,就会产生数据冒险
(1)一阶数据相关与转发(EX冒险)首先讨论指令sub与and之间的相关问题sub指令在第五周期写回寄存器$2,而and指令在第四周期就对sub指令的结果$2提出申请,显然将得到错误的未更新的数据像这类第I条指令的源操作寄存器与第I-1条指令(即上一条指令)的目标寄存器相重,导致的数据相关称为一阶数据相关见图
3.2中实线所示图
3.2一阶数据相关实例图可以发现,sub指令的结果其实在EX级结尾,即第三周期末就产生了;而and指令在第四时钟周期向sub指令结果发出请求,请求时间晚于结果产生时间,所以只需要sub指令结果产生之后直接将其转发给and指令就可以避免一阶数据相关如图
3.3虚线所示转发数据为ALUResult_mem数据转发由Forwardingunit单元控制,判断转发条件是否成立转发机制硬件实现见图
3.3图
3.3转发机制的硬件实现转发条件ForwardA、ForwardB作为数据选择器的地址信号,转发条件不成立时,ALU操作数从ID/EX流水线寄存器中读取;转发条件成立时,ALU操作数取自数据旁路转发条件MEM级指令是写操作,即RegWrite_mem=1;MEM级指令写回的目标寄存器不是$0,即RegWriteAddr_mem≠0;MEM级指令写回的目标寄存器与在EX级指令的源寄存器是同一寄存器,即RegWriteAddr_mem=RsAddr_ex或RegWriteAddr_mem=RtAddr_ex
(2)二阶数据相关与转发(MEM冒险)现在讨论sub指令与or指令之间的相关问题sub指令在第5时钟周期写回寄存器,而or指令也在第5时钟周期对sub指令的结果提出了请求,很显然or指令读取的数据是未被更新的错误内容这类数据相关称为二阶数据相关见图
3.4中实线所示图
3.4二阶数据相关实例图如前所述,or指令在第五时钟周期向sub指令结果发出请求时,sub指令的结果已经产生所以,我们同样采用“转发”,即通过MEM/WB流水线寄存器,将sub指令结果转发给or指令,而不需要先写回寄存器堆如图
3.4中虚线所示转发数据为RegWriteData_wb转发条件WB级指令是写操作,即RegWrite_wb=1;WB级指令写回的目标寄存器不是$0,即RegWriteAddr_wb≠0;WB级指令写回的目标寄存器与在EX级指令的源寄存器是同一寄存器,即RegWriteAddr_wb=RsAddr_ex或RegWriteAddr_wb=RtAddr_ex;EX冒险不成立,即RegWriteAddr_mem≠RsAddr_ex或RegWriteAddr_mem=RtAddr_ex3三阶数据相关与转发最后讨论sub指令与add指令之间的相关问题sub指令与add指令在第五时钟周期内同时读写同一个寄存器这类数据相关称之为三阶数据相关如图
3.5中实线所示图
3.5三阶数据相关实例图假设寄存器的写操作发生在时钟周期的上升沿,而读操作发生在时钟周期的下降沿,那么读操作将读取到最新写入的内容在这种假设条件下将不会发生数据冒险这就要求流水线中的寄存器具有“先写后读(ReadAfterWrite)”的特性这类“写操作发生在时钟周期的上升沿,读操作发生在时钟周期的下降沿”的寄存器虽然在理论上是可实现的,但是不适合应用于同步系统,因为它不但影响系统的运行速度,而且影响系统的稳定性,是不可取的因此,我们采用“转发”机制来解决三阶数据相关冒险该部分转发电路我们放在寄存器堆的设计中完成如图
3.5中虚线所示转发数据为RegWriteData_wb转发条件为WB级指令是写操作,即RegWrite_wb=1;WB级指令写回的目标寄存器不是$0,即RegWriteAddr_wb≠0;WB级指令写回的目标寄存器与在ID级指令的源寄存器是同一寄存器,即RegWriteAddr_wb=RsAddr_id或RegWriteAddr_wb=RtAddr_id
3.2数据冒险与阻塞当一条指令试图读取一个寄存器,而它前一条指令是lw指令,并且该lw指令写入的是同一个寄存器时,定向转发的方法就无法解决问题如图
3.6所示这类冒险不同于数据相关冒险,需要单独一个“冒险检测单元(HazardDetector)”,它在ID级完成冒险成立的条件为上一条指令是lw指令,即MemRead_ex=1;在EX级的lw指令与在ID级的指令读写的是同一个寄存器,即RegWriteAddr_ex=RsAddr_id或RegWriteAddr_ex=RtAddr_id冒险的解决引入流水线阻塞当HazardDetector检测到冒险条件成立时,在lw指令和下一条指令之间插入阻塞,即流水线气泡(bubble),使后一条指令延迟一个时钟周期执行,这样就将该冒险转化为二阶数据相关,可用转发解决如图
3.7所示图
3.7流水线气泡的引入需要注意的是,如果处于ID级的指令被阻塞,那么处于IF级的指令也必须阻塞,否则,处于ID级的指令就会丢失防止这两条指令继续执行的方法是保持PC寄存器和IF/ID流水线寄存器不变,同时插入一个流水线气泡具体实现方法如下流水线气泡的插入在ID级检测到冒险条件时,HazardDetector输出一个信号stall,将ID/EX流水线寄存器中的EX、MEM和WB级控制信号全部清零这些信号传递到流水线后面的各级,由于控制信号均为零,所以不会对任何寄存器和存储器进行写操作保持PC寄存器和IF/ID流水线寄存器不变在ID级检测到冒险条件时,HazardDetector输出一个信号PC_IFWrite,作为使能信号同时送给PC寄存器和IF/ID流水线寄存器冒险成立时,该信号为低电平,禁止PC寄存器和IF/ID流水线寄存器接收新数据
3.3分支冒险还有一类冒险是包含分支的流水线冒险,下图图
3.8分支冒险实例流水线每个时钟周期都得取指令才能维持运行,但分支指令必须等到MEM级才能确定是否执行分支这种为了确定预取正确的指令而导致的延迟叫做控制冒险或分支冒险一种比较普遍的提高分支阻塞速度的方法是假设分支不发生,并继续执行顺序的指令流如果分支发生的话,就丢弃已经预取并译码的指令,指令的执行沿着分支目标继续由于分支指令直到MEM级才能确定下一条指令的PC,这就意味着为了丢弃指令必须将流水线中的IF、ID和EX级的指令都清除掉(flush)这种优化方法的代价较大,效率较低如果我们能在流水线中提前分支指令的执行过程,那么就能减少需要清除的指令数这是一种提高分支效率的方法,降低了执行分支的代价因此我们采用提前分支指令的方法解决分支冒险提前分支指令需要提前完成两个操作计算分支的目的地址由于已经有了PC值和IF/ID流水线寄存器中的指令值,所以可以很方便地将EX级的分支地址计算电路移到ID级我们针对所有指令都执行分支地址的计算过程,但只有在需要它的时候才会用到判断分支指令的跳转条件我们将用于判断分支指令成立的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综合以上分析,最终的无冒险的流水线MIPSCPU原理框图如图
3.9图
3.9无冒险的流水线MIPSCPU总体原理框图
四、流水线MIPS微处理器的设计根据流水线不同阶段,将系统划分为IF、ID、EX和MEM四大模块,WB部分功能电路非常简单,可直接在顶层文件中设计另外,系统还包括IF/ID、ID/EX、EX/MEM、MEM/WB四个流水线寄存器
1、取指令级模块(IF)的设计IF模块由指令指针寄存器PC、指令存储器子模块InstructionROM、指令指针选择器MUX和一个32位加法器组成,IF级模块接口信息如下表所示引脚名称方向说明clkInput系统时钟reset系统复位信号,高电平有效Z分支指令的条件判断结果J跳转指令JR寄存器跳转指令PC_IFWrite阻塞流水线的信号,低电平有效JumpAddr[31:0]J指令跳转地址JrAddr[31:0]JR指令跳转地址BranchAddr[31:0]条件分支地址Instruction_if[31:0]Output指令机器NextPC_if[31:0]下一个PC值指令存储器ROM用XilinxCOREGenerator实现产生的ROM无法满足流水线CPU的指令要求,我们需用VerilogHDL设计一个ROM阵列考虑到FPGA的资源,指令存储器可设计为容量各为26×32bit的ROM设计ROM时需将测试的机器码写入,课程提供一段简单测试程序的机器码,机器码存于PipelineDemo.coe文件中课程已提供该ROM的代码,已设计好上述测试程序机器码的指令存储器,文件名为InstructionROM.v指令指针选择器(PC)指令指针选择器为一8选1数据选择器,选择信号为PCSource={JR,J,Z},具体含义如下表地址PC来源{JRJZ}=100JR指令的跳转地址{JRJZ}=010J指令的跳转地址{JRJZ}=001Branch指令的分支地址{JRJZ}=000下一条指令地址PC+4PC寄存器当发生数据冒险时,需要保持PC寄存器不变,因此PC寄存器是一个带使能端的D型寄存器,使能信号为PC_IFWrite
2、指令译码模块(ID)的设计指令译码模块的主要作用是从机器码中解析出指令,并根据解析结果输出各种控制信号ID模块主要有指令译码(Decode)、寄存器堆(Registers)、冒险监测、分支检测和加法器等组成ID模块的接口信息如下表所示引脚名称方向说明clkInput系统时钟Instruction_id[31:0]指令机器码NextPC_id[31:0]指令指针RegWrite_wb寄存器写允许信号,高电平有效RegWriteAddr_wb[4:0]寄存器的写地址RegWriteData_wb[31:0]写入寄存器的数据MemRead_ex冒险检测的输入RegWriteAddr_ex[4:0]MemtoReg_idOutput决定回写的数据来源(0ALU1存储器)RegWrite_id寄存器写允许信号,高电平有效MemWrite_id存储器写允许信号,高电平有效MemRead_id存储器读允许信号,高电平有效ALUCode_id[4:0]决定ALU采用何种运算ALUSrcA_id决定ALU的A操作数的来源(0rs1:SaALUSrcB_id决定ALU的B操作数的来源(0rt1:ImmRegDst_id决定Register回写是采用的地址(rt/rdStallID/EX寄存器清空信号,高电平插入一个流水线气泡Z分支指令的条件判断结果J跳转指令JR寄存器跳转指令PC_IFWrite阻塞流水线的信号,低电平有效BranchAddr[31:0]条件分支地址JumpAddr[31:0]跳转地址Imm_id[31:0]符号扩展成32位的立即数Sa_id[31:0]0扩展成32位的移位立即数RsData_id[31:0]Rs寄存器数据RtData_id[31:0]Rt寄存器数据RdAddr_id[4:0]Rd寄存器地址RsAddr_id[4:0]Rs寄存器地址RtAddr_id[4:0]Rt寄存器地址指令译码(Decode)子模块的设计Decode控制器的主要作用是根据指令确定各个控制信号的值,是一个组合电路我们将指令分成八类R_type1ADD、ADDU、SUB、SUBU、AND、OR、NOR、XOR、SLT、SLTU、SLLV、SRLV、SRAVR_type2SLL、SRL、SRAJR_type JRJ_type JI_type ADDI、ADDIU、ANDI、ORI、XORI、SLTI、SLTIUBranch BEQ、BNE、BGEZ、BGTZ、BLEZ、BLTZLW LWSWSWDecode输出的九组控制信号RegWrite决定是否对寄存器(Registers)进行写操作当RegWrite高电平有效时,将数据写入指定的寄存器中需要写回寄存器的指令有LW、R_type
1、R_type2和I_type,则RegWrite_id=LW||R_type1||R_type2||I_typeRegDst决定目标寄存器是rt还是rd当RegDst=0时,rt为目标寄存器;当RegDst=1时,rd为目标寄存器需要写回寄存器的指令类型有LW、R_type
1、R_type2和I_type其中,R_type1和R_type2的目标寄存器是rd,而LW和I_type的目标寄存器是rt所以RegDst_id=R_type1||R_type2MemWrite决定是否对数据存储器进行写操作当MemWrite有效时,将数据写入数据存储器指定的位置需要对写存储器的指令只有SW,所以MemWrite_id=SWMemRead决定是否对数据存储器进行读操作当MemRead有效时,读取数据存储器指定位置的数据需要对读存储器的指令只有LW,所以MemRead_id=LWMemtoReg决定写入寄存器registers的数据来自ALU还是数据存储器当MemtoReg=0时,数据来自ALU;当MemtoReg=1时,数据来自数据存储器需要写回寄存器的指令类型有LW、R_type
1、R_type2和I_type其中,只有LW写回寄存器的数据取自存储器,所以MemtoReg_id=LWALUSrcA决定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_type2ALUSrcB决定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_typePCSource决定写入PC寄存器的来源PCSource由JR_tpye、J_tpye及Z决定即PCSource={JRJZ}PCSource=000时,写入值为下一条指令的地址PC+4;PCSource=001时,写入值为Branch指令的分支地址;PCSource=010时,写入值为J指令的跳转地址;PCSource=100时,写入值为JR指令的跳转地址ALUCode决定ALU的功能,由指令中的op段、rt段和funct段决定功能表如下opfunctionrt运算ALUcodeBEQ_opxxxxxxxxxxZ=A==B5d10BNE_opxxxxxxxxxxZ=~A==B5d11BGEZ_opxxxxx5d1Z=A=05d12BGTZ_opxxxxx5d0Z=A05d13BLEZ_opxxxxx5d0Z=A=15d14BLTZ_opxxxxx5d0Z=A05d15R_type_opADD_functxxxxx加5d0ADDU_functxxxxxAND_functxxxxx与5d1XOR_functxxxxx异或5d2OR_functxxxxx或5d3NOR_functxxxxx或非5d4SUB_functxxxxx减5d5SUBU_functxxxxxSLT_OP_functxxxxxAB1:05d19SLTU_OP_functxxxxxAB1:0(无符号数)5d20SLL_functxxxxxBA5d16SLLV_functxxxxxSRL_functxxxxxBA5d17SRLV_functxxxxxSRA_functxxxxxBA5d18ARAV_functxxxxxADDI_opxxxxxxxxxx加5d0ADDIU_opxxxxxxxxxxANDI_opxxxxxxxxxx与5d1XORI_opxxxxxxxxxx异或5d2ORI_opxxxxxxxxxx或5d3SLTI_opxxxxxxxxxxAB1:05d19SLTIU_opxxxxxxxxxxAB1:0(无符号数)5d20SW_opxxxxxxxxxx加(计算地址)5d0LW_opxxxxxxxxxx分支检测(BranchTest)电路的设计Zero检测电路主要用于判断Branch指令的分支条件是否成立,其中BEQ、BNE两个操作数为RsData与RtData,而BGEZ、BGTZ、BLEZ和BLTZ指令则为RsData与常数0比较,所以输出信号Z的表达式为RsData
[31]||~|RsData[31:0];ALUCode=alu_blezRsData
[31];ALUCode=alu_bltz~RsData
[31]|RsData[31:0];ALUCode=alu_bgtzZ=~RsData
[31];ALUCode=alu_bgez|RsData[31:0]^RtData[31:0];ALUCode=alu_bneRsData[31:0]~^RtData[31:0];ALUCode=alu_beq0;ALUCode=OTHER寄存器堆(Registers)子模块的设计寄存器堆由32个32位寄存器组成,这些寄存器通过寄存器号进行读写存取寄存器堆的原理框图如下图所示因为读取寄存器不会更改其内容,故只需提供寄存号即可读出该寄存器内容读取端口采用数据选择器即可实现读取功能应注意“0”号寄存器为常数
0.对于往寄存器里写数据,需要目标寄存器号(WriteRegister)、待写入数据(WriteData)、写允许信号(RegWrite)三个变量图
3.13中5位二进制译码器完成地址译码,其输出控制目标寄存器的写使能信号EN,决定将数据WriteData写入哪个寄存器在流水线CPU设计中,寄存器堆设计还应解决三阶数据相关的数据转发问题当满足三阶数据相关条件时,寄存器具有ReadafterWrite的特性为实现该功能,在寄存器堆的基础上加一转发电路如下图所示图中转发检测电路的输出表达式为RsSel=RegWrite_wb~RegWriteAddr_wb==0RegWriteAddr_wb==RsAddr_idRtSel=RegWrite_wb~RegWriteAddr_wb==0RegWriteAddr_wb==RtAddr_id冒险检测功能电路(HazardDeterctor)的设计冒险成立的条件为上一条指令是LW指令,即MemRead_ex=1;在EX级的LW指令与在ID级的指令读写的是同一个寄存器,即RegWriteAddr_ex=RsAddr_id或RegWriteAddr_ex=RtAddr_id解决冒险的方法为插入一个流水线气泡Stall清空ID/EX寄存器并且阻塞流水线ID级、IF级流水线,有Stall=RegWriteAddr_ex==RsAddr_id||RegWriteAddr_ex==RtAddr_idMemRead_ex保持PC寄存器和IF/ID流水线寄存器不变,有PC_IFWrite=~Stall其它单元电路的设计Branch指令分支地址的计算电路BranchAddr=NextPC_id+sign-extendImm_id2JR指令跳转地址的计算电路JRAddr=RsData_idJ指令跳转地址的计算电路Jaddr={NextPC_id[31:28]IR_id[25:0]2’b00}符号扩展的方法—针对有符号数如果最高位(即符号位)是0,则要扩展的高位用0补齐;如果最高位是1,则用1补齐例8位的+1,表示为二进制为00000001,扩展成16位的话,符号扩展为0000000000000001;8位的-1,表示成二进制为11111111,扩展成16位的话,符号扩展为11111111111111110扩展的方法—针对无符号数要扩展的高位用0补齐例16位二进制0xFFFF0扩展成32位为0x0000FFFF
3、执行模块(EX)的设计执行模块主要有ALU子模块、转发电路Forwarding以及若干数据选择器组成这行模块的接口信息如下表所示引脚名称方向说明RegDst_exInput决定Register回写时采用的地址(rt/rd)ALUCode_ex[4:0]决定ALU采用何种运算ALUSrcA_ex决定ALU的A操作数的来源(rs/Sa)ALUSrcB_ex决定ALU的B操作数的来源(rt/Imm)Imm_ex[31:0]立即数Sa_ex[31:0]移位位数RsAddr_ex[4:0]Rs寄存器地址,即Instruction_id[25:21]RtAddr_ex[4:0]Rt寄存器地址,即Instruction_id[20:16]RdAddr_ex[4:0]Rd寄存器地址,即Instruction_id[15:11]RsData_ex[31:0]Rs寄存器数据RtData_ex[31:0]Rt寄存器数据RegWriteData_wb[31:0]写入寄存器的数据ALUResult_mem[31:0]ALU输出数据RegWriteAddr_wb[4:0]寄存器的写地址RegWriteAddr_mem[4:0]RegWrite_wb寄存器写允许信号RegWrite_memRegWriteAddr_ex[4:0]Output寄存器的写地址ALUResult_ex[31:0]ALU运算结果MemWriteData_ex[31:0]寄存器的回写数据ALU_A[31:0]ALU操作数,测试时使用ALU_B[31:0]ALU子模块的设计ALU是提供CPU基本运算能力的重要电路ALU执行何种运算,由控制单元中的ALU控制器输出的ALUCode信号决定ALU功能见下表为了提高运算速度,可将各种运算同时执行,得到的运算结果由ALUCode信号进行挑选ALU的基本结构如下图所示加、减电路的设计考虑减法、比较(SLT、SLTI)均可用加法器和必要的辅助电路来实现上图中的Binvert信号控制加减运算Binvert=~ALUCode==alu_add对ALU来说,它的两个操作数输入时都已经是补码形式,当要完成两个操作数的减法时,即A补-B补,可将减法转换为加法,利用加法器来实现A补-B补=A补+-B补=A补+B补补=A补+(B补)反+1加法器完成的功能为sum=A+B^{32{Binvert}}+Binvert当Binvert=0时,sum=A+B^0+0=A+B;当Binvert=1时,sum=A+B^{32’b1}+1=A-B;(B^{32’b1}即对B按位取反)即可完成加减运算由于32位加法器的运算速度影响着CPU频率的高低,因此设计一个高速加法器尤为重要,本实验采用lab7中介绍的32位进位选择加法器比较电路的设计考虑对于比较运算,如果最高为不同,即A
[31]≠B
[31],则根据A
[31]、B
[31]决定比较结果,但应注意有符号数和无符号数比较运算的区别
1、在有符号数比较SLT运算中,判断AB的方法为若A为负数、B为0或正数A
[31]~B
[31]若A、B符号相同,A-B为负A
[31]~^B
[31]sum
[31]则SLTResult=A
[31]~B
[31]||A
[31]~^B
[31]sum
[31]
2、在无符号数比较SLT运算中,判断AB的方法为若A最高位为
0、B最高位为1~A
[31]B
[31]若A、B最高位相同,A-B为负A
[31]~^B
[31]sum
[31]则SLTResult=~A
[31]B
[31]||A
[31]~^B
[31]sum
[31]算术右移运算电路的设计考虑VerilogHDL的算术右移的运算符为“”要实现算术右移应注意,被移位的对象必须定义为reg类型,但是在SRA指令,被移位的对象操作数B为输入信号,不能定义为reg类型,因此必须引入reg类型中间变量B_reg,相应的VerilogHDL语句为regsigned[31:0]B_reg;always@BbeginB_reg=B;end引入reg类型的中间变量B_reg后,就可对B_reg进行算术右移操作逻辑运算与、或、或非、异或、逻辑移位等运算较为简单,只是要注意一点,ANDI、XORI、ORI三条指令的立即数为16位无符号数,应“0扩展”为32位无符号数,在运算的同时完成“0扩展”如ADDI指令的运算为A{16’b0,B[15:0]}转发电路Forwarding的设计操作数A和B由数据选择器决定,数据选择器的地址信号即为ForwardA和ForwardB其含义如下表地址操作数来源说明ForwardA=00RsData_ex操作数A来自寄存器堆ForwardA=01RegWriteData_wb操作数A来自二阶数据相关的转发数据ForwardA=10ALUresult_mem操作数A来自一阶数据相关的转发数据ForwardB=00RtData_ex操作数B来自寄存器堆ForwardB=01RegWriteData_wb操作数B来自二阶数据相关的转发数据ForwardB=10ALUresult_mem操作数B来自一阶数据相关的转发数据ForwardA
[0]=RegWrite_wbRegWriteAddr_wb!=0RegWriteAddr_mem!=RsAddr_exRegWriteAddr_wb==RsAddr_ex;ForwardA
[1]=RegWrite_memRegWriteAddr_mem!=0RegWriteAddr_mem==RsAddr_ex;ForwardB
[0]=RegWrite_wbRegWriteAddr_wb!=0RegWriteAddr_mem!=RtAddr_exRegWriteAddr_wb==RtAddr_ex;ForwardB
[1]=RegWrite_memRegWriteAddr_mem!=0RegWriteAddr_mem==RtAddr_ex;
四、存储器访问MEM模块的设计数据存储器利用XilinxCoreGenerator实现考虑到FPGA的资源,数据存储器可设计为容量各为26×32bit单端口RAM由于MIPS系统的32位字地址由4个字节组成,根据“对齐限制”要求字地址必须是4的倍数,也就是说字地址的低两位必须是0,所以字地址的低两位不接入电路故我们设计的数据RAM的地址应该接的信号是ALUResult_mem[7:2]由于Virtex-IIPro系列的FPGA只能产生带寄存器的内核RAM,所以存储器输出绕过MEM/WB流水线寄存器,直接接入WB级的数据选择器
五、流水线寄存器的设计流水线寄存器负责将流水线的各部分分开,共有IF/ID、ID/EX、EX/MEM、MEM/WB四组根据前面的介绍可知,四组流水线寄存器要求不完全相同,因此设计也有不同考虑1EX/MEM、MEM/WB两组流水线寄存器只是普通D型寄存器2当流水线发生数据冒险时,需清空ID/EX流水线寄存器而插入一个气泡,因此ID/EX流水线寄存器是一个带同步清零功能的D型寄存器,清零信号为Stall3当流水线发生数据冒险时,需保持IF/ID流水线寄存器不变,因此IF/ID流水线寄存器具有使能信号输入,使能信号为PC_IFWrite;当流水线发生分支冒险时,需清空IF/ID流水线寄存器,清零信号为IF_flush因此,IF/ID流水线寄存器是一个带使能功能、同步清零功能的D型寄存器需要注意的是,由于仿真对初始值的要求,上述寄存器都应考虑有reset信号的接入,以提供仿真时各寄存器的初值
六、顶层文件的设计按照流水线MIPS微处理器的原理框图连接各模块即可为方便测试,可将关键变量输出,关键变量有指令指针PC、指令码Instruction、流水线插入气泡标志Stall、分支标志JumpFlag(即{JR,J,Z})、ALU输入输出(ALU_A、ALU_B、ALUResult)和数据存储器的输出MemDout_wb
五、实验代码见上传的solution
六、实验设备装有ISE、ModelSimSE和ChipScopePro软件的计算机;XUPVirtex-ⅡPro开发系统一套;SVGA显示器一台实验仿真结果与分析
1.IF级仿真分析
1.1顺序执行分析起初reset信号高电平,初始PC为0在之后的一段时间内,{JRJZ}一直都是{0,0,0},没有跳转和分支指令,CPU每运行一周期,PC递增
41.2JR跳转分析当JR为高电平时,PC的为0x34,与JrAddr相同,完成JR跳转
1.3J跳转分析J=1,当时钟上升沿到来时执行无条件跳转指令,PC跳到JumpAddr=32h2c;NextPC_if=PC+
41.4Branch分支分析Z=1,当时钟上升沿到来时执行Branch指令,PC跳到BranchAddr=32h4;NextPC_if=PC+4综上,PC能够根据{JRJZ},完成顺序执行、JR跳转、J跳转、Branch分支ALU仿真分析仿真整体波形ALUCodeABALUResultoverflow验证ALUResult00000(add)000040121000200f1000602101000602100000(add)40000000400000008000000018000000000001(and)ff0c0e1010df30ff100c00100100c001000010(xor)ff0c0e1010df30ffefd33eef0efd33eef00011(or)ff0c0e1010df30ffffdf3eff0ffdf3eff00100(nor)ff0c0e1010df30ff0020c10000020c10000101(sub)70f0c0e01000305460f0908c160f0908c00110(andi)ff0c0e10ffffe0ff0000001000000001000111(xori)ff0c0e10ffffe0ffff0ceeef0ff0ceeef01000(ori)ff0c0e10ffffe0ffff0ceeff0ff0ceeff10000(sll)00000004ffffe0fffffe0ff00fffe0ff010001(srl)00000004ffffe0ff0ffffe0f00ffffe0f10010(sra)00000004ffffe0fffffffe0f0fffffe0f10011(slt)ff000004700000ff0000000100000000110100(sltu)ff000004700000ff00000000000000000分析由仿真波形可知,设计的ALU能够完成实验所要求的算术运算和逻辑运算Decode仿真分析
3.1Instruction=32h20080042;//addi$t0$042观察Instruction前六位,op=Instruction[31:26]=001000=ADDI_op因此该指令ALUCode=alu_addi=00000此外MemWrite=0,MemRead=0,Regwrite=1目标寄存器为rd,MemToReg=0(用ALUResult作为输出),ALUSrcA=0,ALUSrcB=1(此时操作数B=Imm_ex)因此译码结果完全符合ADDi操作的要求.
3.2Instruction=32h000c4080;//sll$t0$t42观察Instruction前六位,op=Instruction[31:26]=000000=R_type_opfunc=Instruction[5:0]=000000=SLL_funct因此该指令ALUCode=alu_sll=5d’16=10000,此外MemWrite=0,MemRead=0,Regwrite=1,RegDst=1(写目标寄存器为rd,MemToReg=0(用ALUResult作为输出),ALUSrcA=1(此时操作数A=Sa_ex),ALUSrcB=0因此译码结果完全符合SLL操作的要求.综上,验证其他控制信号均与Instruction相符合Top仿真分析
4.1整体波形分析与教材表
7.20测试结果仔细对照得,变量的变化情况符合要求
4.2Zero检测电路观察Z信号可发现其满足式子RsData
[31]||~|RsData[31:0];ALUCode=alu_blezRsData
[31];ALUCode=alu_bltz~RsData
[31]|RsData[31:0];ALUCode=alu_bgtzZ=~RsData
[31];ALUCode=alu_bgez|RsData[31:0]^RtData[31:0];ALUCode=alu_bneRsData[31:0]~^RtData[31:0];ALUCode=alu_beq0;ALUCode=OTHER
4.3阻塞分析分析Stall信号为高电平时,Instruction与PC均持续两个时钟周期,即发生了阻塞
4.4branch分支指令分析JumpFlag=001,下一个时钟上升沿PC发生分支具体分支地址计算如下Imm_id={{16{Instruction_id
[15]}}Instruction_id[15:0]}=fffffff4;Imm_id_shift=Imm_id2=ffffffd0;NextPC_id=32’h34=00000034;BranchAddress=NextPC_id+Imm_id_shift=00000004;与波形相符,仿真正确
4.5IF_flush信号分析分析JumpFlag0时,发生跳转,Flush信号为高电平,下一个时钟信号上升沿到来时,将Instruction_id、NextPC_id冲刷掉,置零,符合设计要求
4.6Jump分析注意到在波形的最后阶段出现PC的循环,JumpFlag=010,发生跳转指令分析JumpAddr={NextPC_id[31:28]Instruction_id[25:0]2b00}=00000028H下一个时钟信号上升沿到来时,PC=00000028H,与波形一致,最后PC出现循环心得实验收获颇丰,真的很感谢自己当初做了对的选择,选了数字系统设计实验Ⅱ,虽然学分只有
0.5分,但在实验过程中收获的不仅仅是知识,更是一种心态,只有在良好的心态下去调试代码,才能发现代码中的错误我们不是久经沙场的程序员,在调试代码时难免有点焦虑,操之过急我就是将ID模块中Imm_id粗心写成了imm_id导致流水线CPU顶层仿真出错,经过长时间的低效率的调试愣是没找到错误,心情越来越烦躁、沮丧,愣愣的对着电脑发呆后来,自己经过反思,痛定思痛,决心静心好好再调试一次,从reset=1开始排查代码中的错误,终究是发现了问题出现在有一条线没有连接上的,即imm_id,修改后,流水线工作正常,符合设计要求,并且成功通过唐老师验收,自己也掌握了高效率的排错方法,内心的自豪感油然而生,一切都是那么美好,越是绝望之时,越是希望出现之时自己对VerilogHDL语言还是很感兴趣的,假期里也看了VerilogHDL语言的详细教程一位工程师能掌握VerilogHDL语言是多么酷的一件本领啊,只要给你一张原理框图以及每个模块的功能,工程师就可以用VerilogHDL语言轻松地将它描述出,并且下载到FPGA中实现功能从数字系统设计实验Ⅰ开始接触VerilogHDL语言,到数字系统设计实验Ⅱ进一步掌握VerilogHDL语言,我深深的感受到她的魅力课程结束后,我仍会坚持学习和使用VerilogHDL语言其次,我觉老师给的代码是不能实现BGEZ与BLTZ两条指令的编写代码是我发现BGEZ与BLTZ指令的opcode都是000001,如果要区别两条指令还要附加判断rt,BGEZ_rt=00001,BLTZ_rt=00000而Decode模块给的初始代码中直接通过caseop就赋值相应的ALUCode这对于BGEZ与BLTZ两条指令是无效的!其实,我们可以通过模仿op==R_type_op时确定ALUCode的编码方式确定执行BGEZ、BLTZ指令时的ALUCode,即always@*ifop==000001casert00001:R_ALUCode_Temp=alu_bgez;00000:R_ALUCode_Temp=alu_bltz;default:R_ALUCode_Temp=5b0;endcasealways@*caseopBEQ_op:ALUCode=alu_beq;BNE_op:ALUCode=alu_bne;BGTZ_op:ALUCode=alu_bgtz;BLEZ_op:ALUCode=alu_blez;R_type_op:ALUCode=R_ALUCode_Temp;000001ALUCode=R_ALUCode_Temp;ADDI_op:ALUCode=alu_add;ADDIU_op:ALUCode=alu_add;ANDI_op:ALUCode=alu_andi;XORI_op:ALUCode=alu_xori;ORI_op:ALUCode=alu_ori;SLTI_op:ALUCode=alu_slt;SLTIU_op:ALUCode=alu_sltu;SW_op:ALUCode=alu_add;LW_op:ALUCode=alu_add;default:ALUCode=5b0;endcase接下来分享自己在代码编写,编译过程中的一些体会首先,要编完一个没有错误的程序很难,有错误是可以理解的,也是值得庆幸的,语法错误我们可以通过编译排查,程序功能错误、逻辑错误就需要我们通过细心的ISE仿真排查查错和仿真能力的提高很重要的在这次实验中我遇到了之很多常见错误,比如在always赋值时,变量名必须是reg类型;case语句不能嵌套,且需要default排除异常情况,这些错我都做了总结;在做top仿真时,出现很多变量名命名问题,这极大锻炼了我排错能力,对于ISE仿真软件的使用更是得心应手,可以调出任意定义变量的波形由于Decode、ALU、IF、EX、ID子模块的原理,书上讲的很详细,老师给的初始代码结构清晰,这给我们编写提供了很大的方便如果没有老师给的初始代码,进行简单的端口说明和参数定义,自己在编写代码过程中一定会出现更多error在顶层模块中存在各级数据转发,同一指令各级运行时间不同,错误的排查有一定困难实验中我出现的错误也主要在顶层仿真中出现错误时我一般会先观察错误波形所对应的输入波形是否正确,若正确再顺着输入信号向前一级排查直至信号源如果输入信号全部正常,则问题在于错误波形所在模块,仔细检查模块的各个输入输出信号之间的逻辑关系是否正确,再具体排查代码中是否存在问题,基本就可以发现错误所在设计完流水线MIPS微处理器后,我对五级流水线、数据相关、数据冒险、分支冒险概念更清楚了,记得唐老师问我的问题就是关于分支冒险的,我很从容的将分支冒险在波形上的特征一一讲出,并且将Instruction_if信号调出,指出当分支成立时,被冲刷的指令就是分支指令下一个时钟周期的Instruction_if没有前期的努力,那有今日的从容淡定啊之前在计算机组成理论课上已经学习五级MIPS流水线处理器的相关知识,但在实验课上学到的相同的知识又感到另有一番滋味,同时也极大提升了我面对工程的信心在紧张地冬学期里,能利用自己休息的时间完成这样一个有意义的数字系统设计实验,自己感到很满足,虽然少了周末的休息时间,但内心多了一份不懈追求的勇气与执着图
3.1数据相关性。